feat: add solidity support (#1443)

* feat(harper-comments): add solidity support

* feat(harper-tree-sitter): merge overlapping spans

* feat(harper-comments): add specific solidity parser

* perf(harper-comments): early return

* test(harper-comments): add multiline solidity test

* test(harper-comments): add ignore test

* feat(vscode-plugin): add solidity support

* docs: add Solidity to documentation

* test: fix offset

* test: comment out solidity tests for vscode-plugin

* chore: fix justfile to use /usr/bin/env

The commands don't work on nixOS

* chore: fix more commands

* fix: return Dart support

---------

Co-authored-by: Elijah Potter <me@elijahpotter.dev>
This commit is contained in:
Valentin B.
2025-06-25 18:09:54 +02:00
committed by GitHub
parent f2b8dce022
commit 5b8ae058f8
18 changed files with 783 additions and 494 deletions

877
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,29 +11,30 @@ repository = "https://github.com/automattic/harper"
harper-core = { path = "../harper-core", version = "0.44.0" }
harper-html = { path = "../harper-html", version = "0.44.0" }
harper-tree-sitter = { path = "../harper-tree-sitter", version = "0.44.0" }
tree-sitter = "0.20.10"
tree-sitter-rust = "0.20.4"
tree-sitter-typescript = "0.20.3"
tree-sitter-python = "0.20.4"
tree-sitter-javascript = "0.20.1"
tree-sitter-go = "0.20.0"
tree-sitter-c = "0.20.7"
tree-sitter-cpp = "0.20.5"
tree-sitter-cmake = "=0.4.1"
tree-sitter-ruby = "0.20.1"
tree-sitter-swift = "=0.4.0"
tree-sitter-c-sharp = "0.20.0"
tree-sitter-toml = "0.20.0"
tree-sitter-lua = "0.0.19"
tree-sitter-bash = "0.20.0"
tree-sitter-java = "0.20.0"
tree-sitter-nix = "0.0.1"
tree-sitter-haskell = "0.15.0"
tree-sitter-php = "=0.22.2"
tree-sitter-dart = "0.0.4"
tree-sitter-scala = "0.20.0"
tree-sitter-kotlin = "=0.3.5"
itertools = "0.14.0"
tree-sitter = "0.25.6"
tree-sitter-bash = "0.25.0"
tree-sitter-c = "0.24.1"
tree-sitter-cmake = "0.7.1"
tree-sitter-cpp = "0.23.4"
tree-sitter-c-sharp = "0.23.1"
tree-sitter-go = "0.23.4"
tree-sitter-haskell = "0.23.1"
tree-sitter-java = "0.23.5"
tree-sitter-javascript = "0.23.1"
tree-sitter-kotlin-ng = "1.1.0"
tree-sitter-lua = "0.2.0"
tree-sitter-nix = "0.0.2"
tree-sitter-php = "0.23.11"
tree-sitter-python = "0.23.6"
tree-sitter-ruby = "0.23.1"
tree-sitter-rust = "0.24.0"
tree-sitter-scala = "0.24.0"
tree-sitter-solidity = "1.2.11"
tree-sitter-swift = "0.7.1"
tree-sitter-toml-ng = "0.7.0"
tree-sitter-typescript = "0.23.2"
harper-tree-sitter-dart = "0.0.5"
[dev-dependencies]
paste = "1.0.15"

View File

@@ -1,6 +1,6 @@
use std::path::Path;
use comment_parsers::{Go, JavaDoc, JsDoc, Unit};
use comment_parsers::{Go, JavaDoc, JsDoc, Solidity, Unit};
use harper_core::parsers::{self, MarkdownOptions, Parser};
use harper_core::{MutableDictionary, Token};
use tree_sitter::Node;
@@ -22,44 +22,46 @@ impl CommentParser {
markdown_options: MarkdownOptions,
) -> Option<Self> {
let language = match language_id {
"rust" => tree_sitter_rust::language(),
"typescriptreact" => tree_sitter_typescript::language_tsx(),
"typescript" => tree_sitter_typescript::language_typescript(),
"python" => tree_sitter_python::language(),
"nix" => tree_sitter_nix::language(),
"javascript" => tree_sitter_javascript::language(),
"javascriptreact" => tree_sitter_typescript::language_tsx(),
"go" => tree_sitter_go::language(),
"c" => tree_sitter_c::language(),
"cpp" => tree_sitter_cpp::language(),
"cmake" => tree_sitter_cmake::language(),
"ruby" => tree_sitter_ruby::language(),
"swift" => tree_sitter_swift::language(),
"csharp" => tree_sitter_c_sharp::language(),
"toml" => tree_sitter_toml::language(),
"lua" => tree_sitter_lua::language(),
"shellscript" => tree_sitter_bash::language(),
"java" => tree_sitter_java::language(),
"haskell" => tree_sitter_haskell::language(),
"php" => tree_sitter_php::language_php(),
"dart" => tree_sitter_dart::language(),
"scala" => tree_sitter_scala::language(),
"kotlin" => tree_sitter_kotlin::language(),
"cmake" => tree_sitter_cmake::LANGUAGE,
"cpp" => tree_sitter_cpp::LANGUAGE,
"csharp" => tree_sitter_c_sharp::LANGUAGE,
"c" => tree_sitter_c::LANGUAGE,
"dart" => harper_tree_sitter_dart::LANGUAGE,
"go" => tree_sitter_go::LANGUAGE,
"haskell" => tree_sitter_haskell::LANGUAGE,
"javascriptreact" => tree_sitter_typescript::LANGUAGE_TSX,
"javascript" => tree_sitter_javascript::LANGUAGE,
"java" => tree_sitter_java::LANGUAGE,
"kotlin" => tree_sitter_kotlin_ng::LANGUAGE,
"lua" => tree_sitter_lua::LANGUAGE,
"nix" => tree_sitter_nix::LANGUAGE,
"php" => tree_sitter_php::LANGUAGE_PHP,
"python" => tree_sitter_python::LANGUAGE,
"ruby" => tree_sitter_ruby::LANGUAGE,
"rust" => tree_sitter_rust::LANGUAGE,
"scala" => tree_sitter_scala::LANGUAGE,
"shellscript" => tree_sitter_bash::LANGUAGE,
"solidity" => tree_sitter_solidity::LANGUAGE,
"swift" => tree_sitter_swift::LANGUAGE,
"toml" => tree_sitter_toml_ng::LANGUAGE,
"typescriptreact" => tree_sitter_typescript::LANGUAGE_TSX,
"typescript" => tree_sitter_typescript::LANGUAGE_TYPESCRIPT,
_ => return None,
};
let comment_parser: Box<dyn Parser> = match language_id {
"go" => Box::new(Go::new_markdown(markdown_options)),
"java" => Box::new(JavaDoc::default()),
"javascriptreact" | "typescript" | "typescriptreact" | "javascript" => {
Box::new(JsDoc::new_markdown(markdown_options))
}
"java" => Box::new(JavaDoc::default()),
"go" => Box::new(Go::new_markdown(markdown_options)),
"solidity" => Box::new(Solidity::new_markdown(markdown_options)),
_ => Box::new(Unit::new_markdown(markdown_options)),
};
Some(Self {
inner: parsers::Mask::new(
CommentMasker::new(language, Self::node_condition),
CommentMasker::new(language.into(), Self::node_condition),
comment_parser,
),
})
@@ -77,31 +79,32 @@ impl CommentParser {
/// [`Self::new_from_language_id`]
fn filename_to_filetype(path: &Path) -> Option<&'static str> {
Some(match path.extension()?.to_str()? {
"py" => "python",
"nix" => "nix",
"rs" => "rust",
"ts" => "typescript",
"tsx" => "typescriptreact",
"bash" => "shellscript",
"c" => "c",
"cmake" => "cmake",
"cpp" => "cpp",
"cs" => "csharp",
"dart" => "dart",
"go" => "go",
"h" => "cpp",
"hs" => "haskell",
"java" => "java",
"js" => "javascript",
"jsx" => "javascriptreact",
"go" => "go",
"c" => "c",
"cpp" => "cpp",
"cmake" => "cmake",
"h" => "cpp",
"rb" => "ruby",
"swift" => "swift",
"cs" => "csharp",
"toml" => "toml",
"lua" => "lua",
"sh" => "shellscript",
"bash" => "shellscript",
"java" => "java",
"hs" => "haskell",
"php" => "php",
"dart" => "dart",
"scala" | "sbt" | "mill" => "scala",
"kt" | "kts" => "kotlin",
"lua" => "lua",
"nix" => "nix",
"php" => "php",
"py" => "python",
"rb" => "ruby",
"rs" => "rust",
"scala" | "sbt" | "mill" => "scala",
"sh" => "shellscript",
"sol" => "solidity",
"swift" => "swift",
"toml" => "toml",
"ts" => "typescript",
"tsx" => "typescriptreact",
_ => return None,
})
}

View File

@@ -1,12 +1,14 @@
mod go;
mod javadoc;
mod jsdoc;
mod solidity;
mod unit;
pub use go::Go;
use harper_core::Span;
pub use javadoc::JavaDoc;
pub use jsdoc::JsDoc;
pub use solidity::Solidity;
pub use unit::Unit;
/// Get the span of a tree-sitter-produced comment that doesn't include the

View File

@@ -0,0 +1,80 @@
use harper_core::Lrc;
use harper_core::Span;
use harper_core::Token;
use harper_core::parsers::{Markdown, MarkdownOptions, Parser};
use super::without_initiators;
#[derive(Clone)]
pub struct Solidity {
inner: Lrc<dyn Parser>,
}
impl Solidity {
pub fn new(parser: Lrc<dyn Parser>) -> Self {
Self { inner: parser }
}
pub fn new_markdown(markdown_options: MarkdownOptions) -> Self {
Self::new(Lrc::new(Markdown::new(markdown_options)))
}
}
impl Parser for Solidity {
fn parse(&self, source: &[char]) -> Vec<Token> {
let mut tokens = Vec::new();
let mut chars_traversed = 0;
for line in source.split(|c| *c == '\n') {
let mut new_tokens = parse_line(line, self.inner.clone());
if chars_traversed + line.len() < source.len() {
new_tokens.push(Token::new(
Span::new_with_len(line.len(), 1),
harper_core::TokenKind::Newline(1),
));
}
new_tokens
.iter_mut()
.for_each(|t| t.span.push_by(chars_traversed));
chars_traversed += line.len() + 1;
tokens.append(&mut new_tokens);
}
tokens
}
}
fn parse_line(source: &[char], parser: Lrc<dyn Parser>) -> Vec<Token> {
let mut actual = without_initiators(source);
if actual.is_empty() {
return Vec::new();
}
let mut actual_source = actual.get_content(source);
// ignore the special SPDX-License-Identifier comment
if actual_source.starts_with(&['S', 'P', 'D', 'X', '-']) {
let Some(terminator) = source.iter().position(|c| *c == '\n') else {
return Vec::new();
};
actual.start += terminator;
let Some(new_source) = actual.try_get_content(actual_source) else {
return Vec::new();
};
actual_source = new_source
}
let mut new_tokens = parser.parse(actual_source);
new_tokens
.iter_mut()
.for_each(|t| t.span.push_by(actual.start));
new_tokens
}

View File

@@ -42,7 +42,9 @@ macro_rules! create_test {
create_test!(multiline_comments.cpp, 3);
create_test!(multiline_comments.ts, 3);
create_test!(multiline_comments.sol, 3);
create_test!(clean.rs, 0);
create_test!(clean.sol, 0);
create_test!(jsdoc.ts, 4);
create_test!(issue_96.lua, 0);
create_test!(merged_lines.ts, 1);
@@ -60,6 +62,7 @@ create_test!(basic_kotlin.kt, 0);
// Checks that some comments are masked out
create_test!(ignore_comments.rs, 1);
create_test!(ignore_comments.c, 1);
create_test!(ignore_comments.sol, 1);
// These are to make sure nothing crashes.
create_test!(empty.js, 0);

View File

@@ -0,0 +1,27 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
/// This is an example Solidity file that should produce no Harper lints.
contract TestContract {
/// This is a test function.
/// It has a [link](https://example.com) embedded inside
function testFunction() external {}
/**
* @notice This is another test function.
* @dev It has another [link](https://example.com) embedded inside
* @param p This is a parameter
* @return fooBar The return value.
*/
function testFunction2(uint256 p) external returns (address fooBar) {}
// This is some gibberish to try to trigger a lint for sentences that continue for too long
//
// This is some gibberish to try to trigger a lint for sentences that continue for too long
//
// This is some gibberish to try to trigger a lint for sentences that continue for too long
//
// This is some gibberish to try to trigger a lint for sentences that continue for too long
//
// This is some gibberish to try to trigger a lint for sentences that continue for too long
}

View File

@@ -0,0 +1,12 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
/// spellcheck:ignore splling error
/// Ths applies to the entire comment block
contract Testing {
// spellchecker: ignore ths contrat isnt done yt
uint256 internal foo;
/// Ths comment block is checked
function test() external {}
}

View File

@@ -0,0 +1,15 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
/// This is an example of an problematic comment.
/// It should produce one error.
contract Test {}
/**
* This is an example of a possible error:
* these subsequent lines should not be considered a new sentence and should
* produce no errors.
*/
library FooBar {}
/// Let's aadd a cuple spelling errors for good measure.

View File

@@ -9,8 +9,8 @@ repository = "https://github.com/automattic/harper"
[dependencies]
harper-core = { path = "../harper-core", version = "0.44.0" }
harper-tree-sitter = { path = "../harper-tree-sitter", version = "0.44.0" }
tree-sitter-html = "0.19.0"
tree-sitter = "0.20.10"
tree-sitter-html = "0.23.2"
tree-sitter = "0.25.6"
[dev-dependencies]
paste = "1.0.15"

View File

@@ -18,7 +18,7 @@ impl Default for HtmlParser {
fn default() -> Self {
Self {
inner: parsers::Mask::new(
TreeSitterMasker::new(tree_sitter_html::language(), Self::node_condition),
TreeSitterMasker::new(tree_sitter_html::LANGUAGE.into(), Self::node_condition),
PlainEnglish,
),
}

View File

@@ -8,4 +8,4 @@ repository = "https://github.com/automattic/harper"
[dependencies]
harper-core = { path = "../harper-core", version = "0.44.0" }
tree-sitter = "0.20.10"
tree-sitter = "0.25.6"

View File

@@ -20,7 +20,7 @@ impl TreeSitterMasker {
fn parse_root(&self, text: &str) -> Option<Tree> {
let mut parser = tree_sitter::Parser::new();
parser.set_language(self.language).unwrap();
parser.set_language(&self.language).unwrap();
// TODO: Use incremental parsing
parser.parse(text, None)
@@ -39,7 +39,7 @@ impl TreeSitterMasker {
}
});
byte_spans_to_char_spans(&mut ident_spans, &text);
let ident_spans = byte_spans_to_char_spans(ident_spans, &text);
let mut idents = HashSet::new();
@@ -101,7 +101,7 @@ impl Masker for TreeSitterMasker {
let mut comments_spans = Vec::new();
self.extract_comments(&mut root.walk(), &mut comments_spans);
byte_spans_to_char_spans(&mut comments_spans, &text);
let comments_spans = byte_spans_to_char_spans(comments_spans, &text);
let mut mask = Mask::new_blank();
@@ -115,29 +115,29 @@ impl Masker for TreeSitterMasker {
}
}
/// Converts a set of byte-indexed [`Span`]s to char-index Spans, in-place.
/// Converts a set of byte-indexed [`Span`]s to char-index Spans and returns them.
/// NOTE: Will sort the given slice by their [`Span::start`].
///
/// If any spans overlap, it will remove the second one.
fn byte_spans_to_char_spans(byte_spans: &mut Vec<Span>, source: &str) {
byte_spans.sort_by_key(|s| s.start);
/// If any spans overlap, it will merge them.
fn byte_spans_to_char_spans(mut byte_spans: Vec<Span>, source: &str) -> Vec<Span> {
byte_spans.sort_unstable_by_key(|s| s.start);
let cloned = byte_spans.clone();
let mut i: usize = 0;
byte_spans.retain(|cur| {
i += 1;
if let Some(prev) = cloned.get(i.wrapping_sub(2)) {
!cur.overlaps_with(*prev)
} else {
true
// merge overlapping spans
let mut spans = Vec::with_capacity(byte_spans.len());
for span in byte_spans {
match spans.last_mut() {
Some(last) if !span.overlaps_with(*last) => spans.push(span),
Some(last) => {
// ranges overlap, we can merge them
last.end = span.end;
}
None => spans.push(span),
}
});
}
let mut last_byte_pos = 0;
let mut last_char_pos = 0;
byte_spans.iter_mut().for_each(|span| {
spans.iter_mut().for_each(|span| {
let byte_span = *span;
last_char_pos += source[last_byte_pos..byte_span.start].chars().count();
@@ -147,5 +147,6 @@ fn byte_spans_to_char_spans(byte_spans: &mut Vec<Span>, source: &str) {
span.end = last_char_pos;
last_byte_pos = byte_span.end;
})
});
spans
}

View File

@@ -10,7 +10,7 @@ build-wasm:
# Build `harper.js` with all size optimizations available.
build-harperjs: build-wasm
#! /bin/bash
#!/usr/bin/env bash
set -eo pipefail
# Removes a duplicate copy of the WASM binary if Vite is left to its devices.
@@ -24,7 +24,7 @@ build-harperjs: build-wasm
./docs.sh
test-harperjs: build-harperjs
#!/bin/bash
#!/usr/bin/env bash
set -eo pipefail
pnpm install
@@ -37,7 +37,7 @@ test-harperjs: build-harperjs
pnpm start
test-obsidian: build-obsidian
#!/bin/bash
#!/usr/bin/env bash
set -eo pipefail
pnpm install
@@ -45,7 +45,7 @@ test-obsidian: build-obsidian
pnpm test
dev-wp: build-harperjs
#! /bin/bash
#!/usr/bin/env bash
set -eo pipefail
@@ -56,7 +56,7 @@ dev-wp: build-harperjs
# Build the WordPress plugin
build-wp: build-harperjs
#! /bin/bash
#!/usr/bin/env bash
set -eo pipefail
cd "{{justfile_directory()}}/packages/wordpress-plugin"
@@ -66,7 +66,7 @@ build-wp: build-harperjs
# Compile the website's dependencies and start a development server. Note that if you make changes to `harper-wasm`, you will have to re-run this command.
dev-web: build-harperjs
#! /bin/bash
#!/usr/bin/env bash
set -eo pipefail
cd "{{justfile_directory()}}/packages/web"
@@ -75,7 +75,7 @@ dev-web: build-harperjs
# Build the Harper website.
build-web: build-harperjs
#! /bin/bash
#!/usr/bin/env bash
set -eo pipefail
cd "{{justfile_directory()}}/packages/web"
@@ -84,7 +84,7 @@ build-web: build-harperjs
# Build the Harper Obsidian plugin.
build-obsidian: build-harperjs
#! /bin/bash
#!/usr/bin/env bash
set -eo pipefail
cd "{{justfile_directory()}}/packages/obsidian-plugin"
@@ -96,7 +96,7 @@ build-obsidian: build-harperjs
# Build the Chrome extension.
build-chrome-plugin: build-harperjs
#! /bin/bash
#!/usr/bin/env bash
set -eo pipefail
cd "{{justfile_directory()}}/packages/chrome-plugin"
@@ -106,7 +106,7 @@ build-chrome-plugin: build-harperjs
# Start a development server for the Chrome extension.
dev-chrome-plugin: build-harperjs
#! /bin/bash
#!/usr/bin/env bash
set -eo pipefail
cd "{{justfile_directory()}}/packages/chrome-plugin"
@@ -116,7 +116,7 @@ dev-chrome-plugin: build-harperjs
# Build the Firefox extension.
build-firefox-plugin: build-harperjs
#! /bin/bash
#!/usr/bin/env bash
set -eo pipefail
cd "{{justfile_directory()}}/packages/chrome-plugin"
@@ -125,7 +125,7 @@ build-firefox-plugin: build-harperjs
pnpm zip-for-firefox
test-chrome-plugin: build-chrome-plugin
#!/bin/bash
#!/usr/bin/env bash
set -eo pipefail
pnpm install
@@ -135,7 +135,7 @@ test-chrome-plugin: build-chrome-plugin
# Run VSCode plugin unit and integration tests.
test-vscode:
#! /bin/bash
#!/usr/bin/env bash
set -eo pipefail
ext_dir="{{justfile_directory()}}/packages/vscode-plugin"
@@ -162,7 +162,7 @@ test-vscode:
# Build and package the Visual Studio Code extension.
# If `target` is passed, it is assumed that `harper-ls` has been compiled beforehand and is in `packages/vscode-plugin/bin`. This is used in CI.
package-vscode target="":
#! /bin/bash
#!/usr/bin/env bash
set -eo pipefail
ext_dir="{{justfile_directory()}}/packages/vscode-plugin"
@@ -190,7 +190,7 @@ package-vscode target="":
fi
update-vscode-linters:
#! /bin/bash
#!/usr/bin/env bash
set -eo pipefail
linters=$(
@@ -223,7 +223,7 @@ update-vscode-linters:
# Run Rust formatting and linting.
check-rust:
#! /bin/bash
#!/usr/bin/env bash
set -eo pipefail
cargo fmt -- --check
@@ -231,7 +231,7 @@ check-rust:
# Perform format and type checking.
check: check-rust build-web
#! /bin/bash
#!/usr/bin/env bash
set -eo pipefail
pnpm install
@@ -246,7 +246,7 @@ setup: build-harperjs test-harperjs test-vscode build-web build-wp build-obsidia
# Perform full format and type checking, build all projects and run all tests. Run this before pushing your code.
precommit: check test build-harperjs build-obsidian build-web build-wp build-firefox-plugin build-chrome-plugin
#! /bin/bash
#!/usr/bin/env bash
set -eo pipefail
cargo build --all-targets
@@ -259,7 +259,7 @@ install:
# Run `harper-cli` on the Harper repository
dogfood:
#! /bin/bash
#!/usr/bin/env bash
cargo build --release
for file in `fd -e rs`
do
@@ -285,7 +285,7 @@ spans file:
# Add a noun to Harper's curated dictionary.
addnoun noun:
#! /bin/bash
#!/usr/bin/env bash
DICT_FILE=./harper-core/dictionary.dict
cat $DICT_FILE | grep "^{{noun}}/"
@@ -314,7 +314,7 @@ addnoun noun:
# Search Harper's curated dictionary for a specific word
searchdictfor word:
#! /bin/bash
#!/usr/bin/env bash
if command -v rg > /dev/null; then
cargo run --bin harper-cli -- words | rg {{word}}
else
@@ -323,7 +323,7 @@ searchdictfor word:
# Find words in the user's `harper-ls/dictionary.txt` for words already in the curated dictionary.
userdictoverlap:
#! /bin/bash
#!/usr/bin/env bash
USER_DICT_FILE="$HOME/.config/harper-ls/dictionary.txt"
while read -r line; do
@@ -338,7 +338,7 @@ getforms word:
cargo run --bin harper-cli -- forms {{word}}
# Get a random sample of words from Harper's dictionary and list all forms of each.
sampleforms count:
#!/bin/bash
#!/usr/bin/env bash
set -eo pipefail
DICT_FILE=./harper-core/dictionary.dict
# USER_DICT_FILE="$HOME/.config/harper-ls/dictionary.txt"
@@ -364,7 +364,7 @@ sampleforms count:
cargo run --bin harper-cli -- forms $words
bump-versions: update-vscode-linters
#! /bin/bash
#!/usr/bin/env bash
set -eo pipefail
cargo ws version --no-git-push --no-git-tag --force '*'
@@ -397,7 +397,7 @@ bump-versions: update-vscode-linters
# Enter an infinite loop of property testing until a bug is found.
fuzz:
#!/usr/bin/bash
#!/usr/bin/env bash
while true
do
@@ -408,7 +408,7 @@ fuzz:
done
registerlinter module name:
#! /bin/bash
#!/usr/bin/env bash
D="{{justfile_directory()}}/harper-core/src/linting"

View File

@@ -29,7 +29,6 @@
"onLanguage:cmake",
"onLanguage:cpp",
"onLanguage:csharp",
"onLanguage:dart",
"onLanguage:git-commit",
"onLanguage:go",
"onLanguage:haskell",
@@ -41,6 +40,7 @@
"onLanguage:literate haskell",
"onLanguage:lua",
"onLanguage:markdown",
"onLanguage:dart",
"onLanguage:nix",
"onLanguage:php",
"onLanguage:plaintext",
@@ -48,6 +48,7 @@
"onLanguage:ruby",
"onLanguage:rust",
"onLanguage:shellscript",
"onLanguage:solidity",
"onLanguage:swift",
"onLanguage:toml",
"onLanguage:typescript",

View File

@@ -0,0 +1,7 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
/// Errorz
contract Test {
uint256 internal test;
}

View File

@@ -15,7 +15,7 @@ describe('Languages >', () => {
// Uncomment when #265 is fixed.
// { type: 'JavaScript JSX', file: 'javascriptreact.jsx', row: 1, column: 36 },
// VS Code doesn't support CMake, Haskell, Literate Haskell, Nix, TOML, and Typst files out of
// VS Code doesn't support CMake, Haskell, Literate Haskell, Nix, Solidity, TOML, and Typst files out of
// the box. Uncomment when you figure out how to support them during testing.
// { type: 'CMake', file: 'CMakeLists.txt', row: 2, column: 30 },
// { type: 'Haskell', file: 'haskell.hs', row: 1, column: 3 },
@@ -23,6 +23,7 @@ describe('Languages >', () => {
// { type: 'Nix', file: 'nix.nix', row: 1, column: 2 },
// { type: 'TOML', file: 'toml.toml', row: 1, column: 2 },
// { type: 'Typst', file: 'typst.typ', row: 2, column: 1 },
// { type: 'Solidity', file: 'solidity.sol', row: 3, column: 4 },
{ type: 'C', file: 'c.c', row: 2, column: 3 },
{ type: 'C++', file: 'cpp.cpp', row: 3, column: 5 },

View File

@@ -282,6 +282,7 @@ These configs are under the `markdown` key:
| Rust | `rust` | ✅ |
| Scala | `scala` | ✅ |
| Shell/Bash Script | `shellscript` | ✅ |
| Solidity | `solidity` | ✅ |
| Swift | `swift` | ✅ |
| TOML | `toml` | ✅ |
| TypeScript | `typescript` | ✅ |