From 034f787f9ab79546f5c8097f7dcddd0ec3d7c963 Mon Sep 17 00:00:00 2001 From: Lukas Sabota Date: Fri, 26 Dec 2025 16:50:56 -0500 Subject: [PATCH 1/2] Add support for the swift programming language --- Cargo.lock | 11 ++ Cargo.toml | 1 + README.md | 1 + crates/codebook/Cargo.toml | 1 + crates/codebook/src/queries.rs | 9 ++ crates/codebook/src/queries/swift.scm | 20 ++++ crates/codebook/tests/test_swift.rs | 154 ++++++++++++++++++++++++++ 7 files changed, 197 insertions(+) create mode 100644 crates/codebook/src/queries/swift.scm create mode 100644 crates/codebook/tests/test_swift.rs diff --git a/Cargo.lock b/Cargo.lock index d5856b9..c8b0899 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -406,6 +406,7 @@ dependencies = [ "tree-sitter-r", "tree-sitter-ruby", "tree-sitter-rust", + "tree-sitter-swift", "tree-sitter-toml-ng", "tree-sitter-typescript", "tree-sitter-zig", @@ -2983,6 +2984,16 @@ dependencies = [ "tree-sitter-language", ] +[[package]] +name = "tree-sitter-swift" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef216011c3e3df4fa864736f347cb8d509b1066cf0c8549fb1fd81ac9832e59" +dependencies = [ + "cc", + "tree-sitter-language", +] + [[package]] name = "tree-sitter-toml-ng" version = "0.7.0" diff --git a/Cargo.toml b/Cargo.toml index 1a7c9ad..6d55811 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,7 @@ tree-sitter-python = "<0.26.0" tree-sitter-r = "1.1.0" tree-sitter-ruby = "0.23.1" tree-sitter-rust = "<0.25.0" +tree-sitter-swift = "0.7.1" tree-sitter-toml-ng = "<0.8.0" tree-sitter-typescript = "0.23.2" tree-sitter-zig = "<2" diff --git a/README.md b/README.md index 3c1eaf0..d22e01c 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,7 @@ Codebook is in active development. As better dictionaries are added, words that | Python | ✅ | | Ruby | ✅ | | Rust | ✅ | +| Swift | ⚠️ | | TOML | ✅ | | TypeScript | ✅ | | Typst | ⚠️ | diff --git a/crates/codebook/Cargo.toml b/crates/codebook/Cargo.toml index e490d32..158b233 100644 --- a/crates/codebook/Cargo.toml +++ b/crates/codebook/Cargo.toml @@ -46,6 +46,7 @@ tree-sitter-python.workspace = true tree-sitter-r.workspace = true tree-sitter-ruby.workspace = true tree-sitter-rust.workspace = true +tree-sitter-swift.workspace = true tree-sitter-toml-ng.workspace = true tree-sitter-typescript.workspace = true codebook-tree-sitter-typst.workspace = true diff --git a/crates/codebook/src/queries.rs b/crates/codebook/src/queries.rs index b6e3031..54893ca 100644 --- a/crates/codebook/src/queries.rs +++ b/crates/codebook/src/queries.rs @@ -22,6 +22,7 @@ pub enum LanguageType { R, Ruby, Rust, + Swift, TOML, Text, Typescript, @@ -151,6 +152,13 @@ pub static LANGUAGE_SETTINGS: &[LanguageSetting] = &[ query: include_str!("queries/go.scm"), extensions: &["go"], }, + LanguageSetting { + type_: LanguageType::Swift, + ids: &["swift"], + dictionary_ids: &["swift"], + query: include_str!("queries/swift.scm"), + extensions: &["swift"], + }, LanguageSetting { type_: LanguageType::TOML, ids: &["toml"], @@ -248,6 +256,7 @@ impl LanguageSetting { LanguageType::R => Some(tree_sitter_r::LANGUAGE.into()), LanguageType::Ruby => Some(tree_sitter_ruby::LANGUAGE.into()), LanguageType::Rust => Some(tree_sitter_rust::LANGUAGE.into()), + LanguageType::Swift => Some(tree_sitter_swift::LANGUAGE.into()), LanguageType::TOML => Some(tree_sitter_toml_ng::LANGUAGE.into()), LanguageType::Text => None, LanguageType::Typescript => Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()), diff --git a/crates/codebook/src/queries/swift.scm b/crates/codebook/src/queries/swift.scm new file mode 100644 index 0000000..41fd386 --- /dev/null +++ b/crates/codebook/src/queries/swift.scm @@ -0,0 +1,20 @@ +(comment) @comment +(multiline_comment) @comment + +(class_declaration + name: (type_identifier) @identifier) + +(function_declaration + name: (simple_identifier) @identifier) + +(protocol_declaration + name: (type_identifier) @identifier) + +(property_declaration + name: (pattern) @identifier) + +(parameter + name: (simple_identifier) @identifier) + +(line_string_literal) @string +(multi_line_string_literal) @string diff --git a/crates/codebook/tests/test_swift.rs b/crates/codebook/tests/test_swift.rs new file mode 100644 index 0000000..36a061b --- /dev/null +++ b/crates/codebook/tests/test_swift.rs @@ -0,0 +1,154 @@ +use codebook::{ + parser::{TextRange, WordLocation}, + queries::LanguageType, +}; + +mod utils; + +#[test] +fn test_swift_simple() { + utils::init_logging(); + let processor = utils::get_processor(); + let sample_text = r#" + // Misspell on a sepaate line + class Object { // comment at the end of the lne + // Comment can be inented + func bar() { + } + func opttions() { + } + } + + /* func foobar() + * { + * These wors are + * comented out but should be identified + */ + + func doStuff(_ nunber: Int) + { + } + func doMoar(_ nunber: Int) + { + } + func doAgain(frm: number: Int) + { + } + class Foo2 { + class MyThig { + } + + // frozen_string_lteral: true + + var x = "helo" + + protocol enumrable { + } + "#; + let expected = vec![ + "Moar", "Thig", "comented", "enumrable", "frm", "helo", "inented", "lne", "lteral", "nunber", "opttions", "sepaate", "wors" + ]; + let binding = processor + .spell_check(sample_text, Some(LanguageType::Swift), None) + .to_vec(); + let mut misspelled = binding + .iter() + .map(|r| r.word.as_str()) + .collect::>(); + misspelled.sort(); + println!("Misspelled words: {misspelled:?}"); + assert_eq!(misspelled, expected); +} + +#[test] +fn test_swift_code() { + utils::init_logging(); + let sample_ruby_code = r#" +func send_notfication(to recipient: String, _ subject: String, body: String) +{ + // This method sends an email with potentialy misspelled content + let email = Email(to: recipient, + subject: "URGENT: #{subject}", + body: "Dear valued custommer,\n\n#{body}\n\nRegads,\nSuport Team") + email.send() +} + +if status == "complette" || status == "partialy_compleet" { + mark_as_finnished(item) +} + "#; + + let expected = vec![ + WordLocation::new( + "potentialy".to_string(), + vec![TextRange { + start_byte: 119, + end_byte: 129, + }], + ), + WordLocation::new( + "compleet".to_string(), + vec![TextRange { + start_byte: 368, + end_byte: 376, + }], + ), + WordLocation::new( + "notfication".to_string(), + vec![TextRange { + start_byte: 11, + end_byte: 22, + }], + ), + WordLocation::new( + "Regads".to_string(), + vec![TextRange { + start_byte: 277, + end_byte: 283, + }], + ), + WordLocation::new( + "complette".to_string(), + vec![TextRange { + start_byte: 334, + end_byte: 343, + }], + ), + WordLocation::new( + "custommer".to_string(), + vec![TextRange { + start_byte: 252, + end_byte: 261, + }], + ), + WordLocation::new( + "Suport".to_string(), + vec![TextRange { + start_byte: 286, + end_byte: 292, + }], + ), + WordLocation::new( + "partialy".to_string(), + vec![TextRange { + start_byte: 359, + end_byte: 367, + }], + ), + ]; + let not_expected = vec!["finnished"]; + let processor = utils::get_processor(); + let misspelled = processor + .spell_check(sample_ruby_code, Some(LanguageType::Swift), None) + .to_vec(); + println!("Misspelled words: {misspelled:?}"); + for e in &expected { + let miss = misspelled.iter().find(|r| r.word == e.word).unwrap(); + println!("Expecting: {e:?}"); + assert_eq!(miss.locations, e.locations); + } + for word in not_expected { + println!("Not expecting: {word:?}"); + assert!(!misspelled.iter().any(|r| r.word == word)); + } +} From d044bde690b44edb1be7b25c6818a5274eb49f70 Mon Sep 17 00:00:00 2001 From: Bo Lopker Date: Wed, 31 Dec 2025 15:15:01 -0800 Subject: [PATCH 2/2] Limit version number for swift --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6d55811..51a59ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ tree-sitter-python = "<0.26.0" tree-sitter-r = "1.1.0" tree-sitter-ruby = "0.23.1" tree-sitter-rust = "<0.25.0" -tree-sitter-swift = "0.7.1" +tree-sitter-swift = "<0.8.0" tree-sitter-toml-ng = "<0.8.0" tree-sitter-typescript = "0.23.2" tree-sitter-zig = "<2"