feat: add ink support (#1894)

* feat: add harper_ink crate with impl and tests

* feat: add harper_ink to harper_ls

* docs: add `ink` to the list of supported languages

---------

Co-authored-by: Elijah Potter <me@elijahpotter.dev>
This commit is contained in:
Freddie Gilbraith
2025-09-26 16:10:40 +01:00
committed by GitHub
parent 8a70efa51c
commit 690100cfb3
12 changed files with 157 additions and 1 deletions

23
Cargo.lock generated
View File

@@ -2299,6 +2299,7 @@ dependencies = [
"dirs 6.0.0",
"harper-comments",
"harper-core",
"harper-ink",
"harper-literate-haskell",
"harper-pos-utils",
"harper-stats",
@@ -2393,6 +2394,17 @@ dependencies = [
"tree-sitter-html",
]
[[package]]
name = "harper-ink"
version = "0.65.0"
dependencies = [
"harper-core",
"harper-tree-sitter",
"paste",
"tree-sitter",
"tree-sitter-ink-lbz",
]
[[package]]
name = "harper-literate-haskell"
version = "0.65.0"
@@ -2416,6 +2428,7 @@ dependencies = [
"harper-comments",
"harper-core",
"harper-html",
"harper-ink",
"harper-literate-haskell",
"harper-stats",
"harper-typst",
@@ -4836,6 +4849,16 @@ dependencies = [
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-ink-lbz"
version = "0.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a44bf578477077d12eb671fd67fc47ab71516a1f3913a5dac96346a8faa33210"
dependencies = [
"cc",
"tree-sitter-language",
]
[[package]]
name = "tree-sitter-java"
version = "0.23.5"

View File

@@ -1,5 +1,5 @@
[workspace]
members = [ "harper-cli", "harper-core", "harper-ls", "harper-comments", "harper-wasm", "harper-tree-sitter", "harper-html", "harper-literate-haskell", "harper-typst" , "harper-stats", "harper-pos-utils", "harper-brill"]
members = ["harper-cli", "harper-core", "harper-ls", "harper-comments", "harper-wasm", "harper-tree-sitter", "harper-html", "harper-literate-haskell", "harper-typst", "harper-stats", "harper-pos-utils", "harper-brill", "harper-ink"]
resolver = "2"
# Comment out the below lines if you plan to use a debugger.

View File

@@ -21,6 +21,7 @@ serde = { version = "1.0.226", features = ["derive"] }
serde_json = "1.0.145"
strum = "0.27.2"
strum_macros = "0.27.2"
harper-ink = { version = "0.65.0", path = "../harper-ink" }
[features]
default = []

View File

@@ -20,6 +20,7 @@ use harper_core::{
CharStringExt, Dialect, DictWordMetadata, Document, Span, TokenKind, TokenStringExt,
dict_word_metadata_orthography::OrthFlags, remove_overlaps,
};
use harper_ink::InkParser;
use harper_literate_haskell::LiterateHaskellParser;
#[cfg(feature = "training")]
use harper_pos_utils::{BrillChunker, BrillTagger, BurnChunkerCpu};
@@ -818,6 +819,7 @@ fn load_file(
.map(|v| v.to_str().unwrap())
{
Some("md") => Box::new(Markdown::default()),
Some("ink") => Box::new(InkParser::default()),
Some("lhs") => Box::new(LiterateHaskellParser::new_markdown(
MarkdownOptions::default(),

16
harper-ink/Cargo.toml Normal file
View File

@@ -0,0 +1,16 @@
[package]
name = "harper-ink"
version = "0.65.0"
edition = "2024"
description = "The language checker for developers."
license = "Apache-2.0"
repository = "https://github.com/automattic/harper"
[dependencies]
harper-core = { path = "../harper-core", version = "0.65.0" }
harper-tree-sitter = { path = "../harper-tree-sitter", version = "0.65.0" }
tree-sitter-ink-lbz = "0.0.1"
tree-sitter = "0.25.8"
[dev-dependencies]
paste = "1.0.15"

39
harper-ink/src/lib.rs Normal file
View File

@@ -0,0 +1,39 @@
use harper_core::parsers::{self, Parser, PlainEnglish};
use harper_core::{Token, TokenKind};
use harper_tree_sitter::TreeSitterMasker;
use tree_sitter::Node;
pub struct InkParser {
inner: parsers::Mask<TreeSitterMasker, PlainEnglish>,
}
impl InkParser {
fn node_condition(n: &Node) -> bool {
matches!(n.kind(), "contentText" | "blockComment" | "lineComment")
}
}
impl Default for InkParser {
fn default() -> Self {
Self {
inner: parsers::Mask::new(
TreeSitterMasker::new(tree_sitter_ink_lbz::LANGUAGE.into(), Self::node_condition),
PlainEnglish,
),
}
}
}
impl Parser for InkParser {
fn parse(&self, source: &[char]) -> Vec<Token> {
let mut tokens = self.inner.parse(source);
for token in &mut tokens {
if let TokenKind::Space(v) = &mut token.kind {
*v = (*v).clamp(0, 1);
}
}
tokens
}
}

View File

@@ -0,0 +1,41 @@
use harper_core::linting::{LintGroup, Linter};
use harper_core::spell::FstDictionary;
use harper_core::{Dialect, Document};
use harper_ink::InkParser;
/// Creates a unit test checking that the linting of a Ink document (in
/// `tests_sources`) produces the expected number of lints.
macro_rules! create_test {
($filename:ident.ink, $correct_expected:expr) => {
paste::paste! {
#[test]
fn [<lints_ $filename _correctly>](){
let source = include_str!(
concat!(
"./test_sources/",
concat!(stringify!($filename), ".ink")
)
);
let dict = FstDictionary::curated();
let document = Document::new(&source, &InkParser::default(),
&FstDictionary::curated()
);
let mut linter = LintGroup::new_curated(dict, Dialect::American);
let lints = linter.lint(&document);
dbg!(&lints);
assert_eq!(lints.len(), $correct_expected);
// Make sure that all generated tokens span real characters
for token in document.tokens(){
assert!(token.span.try_get_content(document.get_source()).is_some());
}
}
}
};
}
create_test!(good.ink, 0);
create_test!(bad.ink, 6);

View File

@@ -0,0 +1,15 @@
=== Knot ===
text here is checked: chungus
= Stitch
~identifiersAreNotchecked = "but strings are: chungus"
// comments are also checked -> chungus
/*
chungus
*/
* choices are checked
+ chungus
+ normal text

View File

@@ -0,0 +1,15 @@
=== Knot ===
this is a thing
= Stitch
* an option
+ another option
+ normal text
+ + indented choice
+ + another indented choice
- gather
= ThreeWordStitch
test

View File

@@ -30,6 +30,7 @@ futures = "0.3.31"
serde = { version = "1.0.226", features = ["derive"] }
indexmap = { version = "2.11.1", features = ["serde"] }
globset = "0.4.16"
harper-ink = { version = "0.65.0", path = "../harper-ink" }
[features]
default = []

View File

@@ -20,6 +20,7 @@ use harper_core::parsers::{
use harper_core::spell::{Dictionary, FstDictionary, MergedDictionary, MutableDictionary};
use harper_core::{Dialect, DictWordMetadata, Document, IgnoredLints};
use harper_html::HtmlParser;
use harper_ink::InkParser;
use harper_literate_haskell::LiterateHaskellParser;
use harper_stats::{Record, Stats};
use harper_typst::Typst;
@@ -378,6 +379,7 @@ impl Backend {
Some(Box::new(parser))
}
}
"ink" => Some(Box::new(InkParser::default())),
"markdown" => Some(Box::new(Markdown::new(markdown_options))),
"git-commit" | "gitcommit" => {
Some(Box::new(GitCommitParser::new_markdown(markdown_options)))

View File

@@ -301,6 +301,7 @@ These configs are under the `markdown` key:
| Typst | `typst` | |
| Kotlin | `kotlin` | ✅ |
| Clojure | `clojure` | ✅ |
| Ink | `ink` | |
Want your language added?
Let us know by [commenting on this issue](https://github.com/Automattic/harper/issues/79).