From 87b463990c3fa57a0c5011e1ecc9364564c0a9ea Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Sat, 24 Oct 2026 22:58:23 +0800 Subject: [PATCH] feat(nav): Follow links, file paths and CSS references with Go to Declaration Go to Declaration and Cmd-click said "Cannot find declaration" on HTML href/src, CSS url() and @import, Markdown links and CSS selectors: language servers expose paths as document links, not definitions, and none resolves class/id to selectors. - ide-lsp: textDocument/documentLink (lsp_document_links), resolving links that lack a target. - Editor: when the server has no definition, follow its document links, then URLs and quoted / Markdown / url() paths in any language. Server file links are checked to exist; root-relative paths also resolve against resource roots. Cmd-hover underlines the whole link. - ide-language: tree-sitter web references (class / id attributes, href="#id", var(--x)) and .class / #id / --var definitions from stylesheets and \n
\n"), + ]); + let idx = f.open(); + let found: Vec<(String, u32)> = idx + .symbols_named(".card", Some(SymbolKind::Class), 10) + .into_iter() + .map(|m| (m.rel_path, m.range.start.line)) + .collect(); + assert_eq!(found, vec![("web/a.css".into(), 0), ("web/b.scss".into(), 0), ("web/index.html".into(), 0)]); + assert_eq!(idx.symbols_named("--gap", None, 10).len(), 1); + assert!(idx.symbols_named(".card", Some(SymbolKind::Field), 10).is_empty()); +} diff --git a/crates/ide-language/src/lib.rs b/crates/ide-language/src/lib.rs index 97b1b3520..67388a227 100644 --- a/crates/ide-language/src/lib.rs +++ b/crates/ide-language/src/lib.rs @@ -13,8 +13,8 @@ mod registry; mod syntax; pub use registry::LanguageRegistry; pub use syntax::{ - MAX_CACHED_TREES, MAX_PARSE_BYTES, ParsedTree, Syntax, SyntaxCache, SyntaxCacheStats, SyntaxHighlightKind, - SyntaxHighlights, TreeError, + CSS_CLASS, CSS_ID, CSS_VARIABLE, MAX_CACHED_TREES, MAX_PARSE_BYTES, ParsedTree, Syntax, SyntaxCache, + SyntaxCacheStats, SyntaxHighlightKind, SyntaxHighlights, TreeError, WebReference, WebReferenceKind, }; /// Parsed form of `languages/"##), + Some((CssVariable, "--accent".into(), 21, 29)) + ); + assert_eq!(web_ref("html", "").map(|r| r.1), Some("--x".into())); + assert_eq!(web_ref("css", ".a { color: var(--a|ccent, red) }"), Some((CssVariable, "--accent".into(), 16, 24))); + assert_eq!(web_ref("css", ".a { color: re|d }"), None); + assert_eq!(web_ref("vue", r##""##).map(|r| r.0), Some(Class)); +} + +#[test] +fn web_anchors_find_ids_and_names() { + let html = "
"; + let found: Vec<(u32, u32)> = + syntax().web_anchors(&lang("html"), html, "top").into_iter().map(|r| (r.start.line, r.start.col)).collect(); + assert_eq!(found, vec![(0, 8), (1, 9)]); +} diff --git a/crates/ide-language/src/syntax/web.rs b/crates/ide-language/src/syntax/web.rs new file mode 100644 index 000000000..b221ae721 --- /dev/null +++ b/crates/ide-language/src/syntax/web.rs @@ -0,0 +1,252 @@ +//! Web references that no language server resolves: HTML `class` / `id` +//! attributes to the CSS selectors that style them, `href="#id"` to the element +//! with that id, and `var(--x)` to the custom property's declarations. +//! +//! Definitions (`.card`, `#main`, `--accent`) come from stylesheets and the +//! `