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..51a59ac 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.8.0" 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)); + } +}