From 2358952f8e4136ae7580571a51037b4f5b7648e7 Mon Sep 17 00:00:00 2001 From: Anton Vasiljev Date: Thu, 21 May 2026 02:25:54 +0300 Subject: [PATCH 1/7] =?UTF-8?q?=F0=9F=8E=89=20feat(parsing):=20add=20Svelt?= =?UTF-8?q?e=20parser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add tree-sitter-svelte-next = "0.1.1" dependency - New src/parsing/svelte/: SvelteParser, SvelteBehavior, SvelteLanguage - SvelteParser re-parses + +

Hello

+"#; + let symbols = parser.parse(code, file_id(), &mut counter); + assert!( + symbols.iter().any(|s| s.name.as_ref() == "greet"), + "should extract greet function; got: {:?}", + symbols.iter().map(|s| s.name.as_ref()).collect::>() + ); + } + + #[test] + fn test_snippet_symbols() { + let mut parser = SvelteParser::new().unwrap(); + let mut counter = SymbolCounter::new(); + let code = r#" + +{#snippet card(item)} +
{item.name}
+{/snippet} +"#; + let symbols = parser.parse(code, file_id(), &mut counter); + assert!( + symbols.iter().any(|s| s.name.as_ref() == "card"), + "should extract snippet 'card'; got: {:?}", + symbols.iter().map(|s| s.name.as_ref()).collect::>() + ); + } + + #[test] + fn test_range_offset() { + // Script starts at line 1 (0-indexed), col 0 + let r = Range::new(0, 4, 0, 9); // line 0, col 4-9 in script + let offset = SvelteParser::offset_range(r, 1, 0); + assert_eq!(offset.start_line, 1); + assert_eq!(offset.start_column, 4); + + // Multi-line symbol: line 2 in script → line 3 in file + let r2 = Range::new(2, 0, 4, 1); + let offset2 = SvelteParser::offset_range(r2, 1, 0); + assert_eq!(offset2.start_line, 3); + assert_eq!(offset2.end_line, 5); + } + + #[test] + fn test_find_imports() { + let mut parser = SvelteParser::new().unwrap(); + let code = r#" + +"#; + let fid = file_id(); + let imports = parser.find_imports(code, fid); + assert!( + imports.iter().any(|i| i.path.contains("math")), + "should find math import; got: {:?}", + imports.iter().map(|i| &i.path).collect::>() + ); + } +} From aab6155d24f9f5a16c92756a4e6e263c155a932a Mon Sep 17 00:00:00 2001 From: Anton Vasiljev Date: Thu, 21 May 2026 09:20:00 +0300 Subject: [PATCH 2/7] =?UTF-8?q?=F0=9F=94=A8=20fix(fmt):=20cargo=20fmt=20fi?= =?UTF-8?q?xes=20for=20svelte=20parser=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/parsing/factory.rs | 3 +-- src/parsing/svelte/behavior.rs | 2 +- src/parsing/svelte/mod.rs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/parsing/factory.rs b/src/parsing/factory.rs index 939f68b5..f29e8791 100644 --- a/src/parsing/factory.rs +++ b/src/parsing/factory.rs @@ -337,8 +337,7 @@ impl ParserFactory { } } Language::Svelte => { - let parser = - SvelteParser::new().map_err(|e| IndexError::General(e.to_string()))?; + let parser = SvelteParser::new().map_err(|e| IndexError::General(e.to_string()))?; ParserWithBehavior { parser: Box::new(parser), behavior: Box::new(SvelteBehavior::new()), diff --git a/src/parsing/svelte/behavior.rs b/src/parsing/svelte/behavior.rs index 80fc7e6c..be9b0b80 100644 --- a/src/parsing/svelte/behavior.rs +++ b/src/parsing/svelte/behavior.rs @@ -1,7 +1,7 @@ //! Svelte language behavior -use crate::parsing::LanguageBehavior; use crate::Visibility; +use crate::parsing::LanguageBehavior; use tree_sitter::Language; pub struct SvelteBehavior; diff --git a/src/parsing/svelte/mod.rs b/src/parsing/svelte/mod.rs index 217c908a..59b317a1 100644 --- a/src/parsing/svelte/mod.rs +++ b/src/parsing/svelte/mod.rs @@ -6,5 +6,5 @@ pub mod parser; pub use behavior::SvelteBehavior; pub use definition::SvelteLanguage; -pub use parser::SvelteParser; pub(crate) use definition::register; +pub use parser::SvelteParser; From 92b1d47dcf4e8b5a163f576b36157302f2c8914d Mon Sep 17 00:00:00 2001 From: Anton Vasiljev Date: Thu, 21 May 2026 09:23:07 +0300 Subject: [PATCH 3/7] =?UTF-8?q?=F0=9F=90=9B=20fix(parsing):=20add=20Langua?= =?UTF-8?q?ge::Svelte=20to=20non-exhaustive=20matches?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Svelte arm to the tree-sitter language match in io/parse.rs and to the standalone parser factory match in factory.rs. --- src/io/parse.rs | 1 + src/parsing/factory.rs | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/io/parse.rs b/src/io/parse.rs index b059d337..3385d5aa 100644 --- a/src/io/parse.rs +++ b/src/io/parse.rs @@ -265,6 +265,7 @@ pub fn execute_parse( Language::Kotlin => tree_sitter_kotlin::language(), Language::Lua => tree_sitter_lua::LANGUAGE.into(), Language::Swift => tree_sitter_swift::LANGUAGE.into(), + Language::Svelte => tree_sitter_svelte_next::LANGUAGE.into(), }; parser diff --git a/src/parsing/factory.rs b/src/parsing/factory.rs index f29e8791..1ed75a02 100644 --- a/src/parsing/factory.rs +++ b/src/parsing/factory.rs @@ -190,6 +190,10 @@ impl ParserFactory { let parser = SwiftParser::new().map_err(|e| IndexError::General(e.to_string()))?; Ok(Box::new(parser)) } + Language::Svelte => { + let parser = SvelteParser::new().map_err(|e| IndexError::General(e.to_string()))?; + Ok(Box::new(parser)) + } } } From 67e49ee4d02602144c295b79bfcbe005b24c926a Mon Sep 17 00:00:00 2001 From: Anton Vasiljev Date: Thu, 21 May 2026 09:44:41 +0300 Subject: [PATCH 4/7] =?UTF-8?q?=F0=9F=A7=B9=20chore(flake):=20update=20rus?= =?UTF-8?q?t-overlay=20to=20rustc=201.95.0=20(2026-05-21)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index fe873248..5feb4391 100644 --- a/flake.lock +++ b/flake.lock @@ -109,11 +109,11 @@ ] }, "locked": { - "lastModified": 1774062094, - "narHash": "sha256-ba3c+hS7KzEiwtZRGHagIAYdcmdY3rCSWVCyn64rx7s=", + "lastModified": 1779333539, + "narHash": "sha256-lpmN2lrBDZDPjov2cbD3bOOJsI0fkKolKXasYPCqSys=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "c807e83cc2e32adc35f51138b3bdef722c0812ab", + "rev": "672fa5fc5608d5cd82286a6f69aaf84a40b4fe41", "type": "github" }, "original": { From 67813abcc9a05d619dfd5748696c293ed21c8f12 Mon Sep 17 00:00:00 2001 From: Anton Vasiljev Date: Mon, 25 May 2026 16:22:43 +0300 Subject: [PATCH 5/7] =?UTF-8?q?=E2=9C=A8=20enhance(parsing):=20route=20Sve?= =?UTF-8?q?lte=20 + +

Hello

+"#; + let symbols = parser.parse(code, file_id(), &mut counter); + let names: Vec<&str> = symbols.iter().map(|s| s.name.as_ref()).collect(); + assert!( + names.contains(&"greet"), + "should extract greet function from TS block; got: {names:?}" + ); + assert!( + names.contains(&"User"), + "should extract User interface (TS-only construct); got: {names:?}" + ); + } + + #[test] + fn test_module_and_instance_scripts() { + // Both the module ` + + + +

{VERSION}

+"#; + let symbols = parser.parse(code, file_id(), &mut counter); + let names: Vec<&str> = symbols.iter().map(|s| s.name.as_ref()).collect(); + assert!( + names.contains(&"VERSION"), + "should extract VERSION from module script; got: {names:?}" + ); + assert!( + names.contains(&"start"), + "should extract start from instance script; got: {names:?}" + ); + } + + #[test] + fn test_typescript_imports() { + let mut parser = SvelteParser::new().unwrap(); + let code = r#" +"#; + let imports = parser.find_imports(code, file_id()); + let paths: Vec<&String> = imports.iter().map(|i| &i.path).collect(); + assert!( + paths.iter().any(|p| p.contains("api")), + "should find api import from TS block; got: {paths:?}" + ); + } } From 7369683154ec09be135e0574ca0295df0bd05675 Mon Sep 17 00:00:00 2001 From: Anton Vasiljev Date: Mon, 25 May 2026 16:22:53 +0300 Subject: [PATCH 6/7] =?UTF-8?q?=E2=9C=A8=20enhance(parsing):=20add=20Svelt?= =?UTF-8?q?e=20resolution=20context?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Svelte + + + +
+

{title} (v{COMPONENT_VERSION})

+ + + {#snippet userRow(user: User)} +
  • {greeting(user)}
  • + {/snippet} + + + +

    Doubled: {doubled}

    + + {#if isMaxed} + Maxed out! + {:else} + + + {/if} + +
      + {#each users as user (user.id)} + {@render userRow(user)} + {/each} +
    + + +
    diff --git a/src/parsing/svelte/audit.rs b/src/parsing/svelte/audit.rs new file mode 100644 index 00000000..108f767c --- /dev/null +++ b/src/parsing/svelte/audit.rs @@ -0,0 +1,250 @@ +//! Svelte parser audit module +//! +//! Tracks which AST nodes the parser handles vs what's available in the grammar. +//! +//! Svelte symbol extraction is split: ` + +{#snippet card(item)} +
    {item}
    +{/snippet} +"#; + + let audit = SvelteParserAudit::audit_code(code).unwrap(); + + assert!(audit.grammar_nodes.contains_key("script_element")); + assert!(audit.grammar_nodes.contains_key("snippet_statement")); + + // Script body delegates to TS: greet is a Function. + assert!(audit.extracted_symbol_kinds.contains("Function")); + + // Svelte-level nodes the parser acts on are registered. + assert!(audit.implemented_nodes.contains("script_element")); + assert!(audit.implemented_nodes.contains("snippet_statement")); + } + + #[test] + fn test_template_node_names() { + let code = r#"{#if ready} +

    ok

    +{/if} +{#each items as item} + {@render row(item)} +{/each} +"#; + + let audit = SvelteParserAudit::audit_code(code).unwrap(); + + assert!(audit.grammar_nodes.contains_key("if_statement")); + assert!(audit.grammar_nodes.contains_key("each_statement")); + assert!(audit.grammar_nodes.contains_key("render_tag")); + } +} diff --git a/src/parsing/svelte/mod.rs b/src/parsing/svelte/mod.rs index 760dc0fd..7a633029 100644 --- a/src/parsing/svelte/mod.rs +++ b/src/parsing/svelte/mod.rs @@ -14,7 +14,9 @@ //! - [`definition`]: language registration //! - [`resolution`]: symbol resolution, delegating to the JS resolver since //! `\n

    {x}

    \n", + &node_categories(), + |path| { + let audit = SvelteParserAudit::audit_file(path).map_err(|e| e.to_string())?; + let report = audit.generate_report(); + Ok(( + AuditData::new( + audit.grammar_nodes, + audit.implemented_nodes, + audit.extracted_symbol_kinds, + ), + report, + )) + }, + ); +}