diff --git a/changelog.d/unreleased/2103.fixed.md b/changelog.d/unreleased/2103.fixed.md new file mode 100644 index 0000000000..0a55b46962 --- /dev/null +++ b/changelog.d/unreleased/2103.fixed.md @@ -0,0 +1,16 @@ +--- +category: fixed +issues: + - 2103 +affected: + - src/CodeIndex/Indexer/Symbols/SymbolExtractor.Markup.cs + - tests/CodeIndex.Tests/SymbolExtractorTests.cs +--- + +## English + +- **HTML class attributes are indexed as individual references (#2103)** — `class` and `className` values now split on whitespace so utility classes such as `btn`, `mx-2`, and Tailwind variants can be searched individually. + +## 日本語 + +- **HTML の class 属性を個別の reference としてインデックスするようになりました (#2103)** — `class` と `className` の値を空白で分割し、`btn`、`mx-2`、Tailwind の variant などを個別に検索できるようにしました。 diff --git a/changelog.d/unreleased/2603.fixed.md b/changelog.d/unreleased/2603.fixed.md new file mode 100644 index 0000000000..cd957b1442 --- /dev/null +++ b/changelog.d/unreleased/2603.fixed.md @@ -0,0 +1,16 @@ +--- +category: fixed +issues: + - 2603 +affected: + - src/CodeIndex/Indexer/Symbols/SymbolExtractor.cs + - tests/CodeIndex.Tests/SymbolExtractorTests.cs +--- + +## English + +- **Rust trait associated type defaults are emitted again (#2603)** — associated type defaults in Rust `trait` bodies now scan the existing `protocol` trait symbols, restoring `property` symbols for declarations such as `type Output = ();`. + +## 日本語 + +- **Rust trait の associated type default を再び出力するようになりました (#2603)** — Rust の `trait` 本体にある associated type default が既存の `protocol` trait symbol を走査するようになり、`type Output = ();` などの `property` symbol が復元されました。 diff --git a/src/CodeIndex/Indexer/Symbols/SymbolExtractor.Markup.cs b/src/CodeIndex/Indexer/Symbols/SymbolExtractor.Markup.cs index 2da592ea11..8e8fbddaf7 100644 --- a/src/CodeIndex/Indexer/Symbols/SymbolExtractor.Markup.cs +++ b/src/CodeIndex/Indexer/Symbols/SymbolExtractor.Markup.cs @@ -240,6 +240,11 @@ private static List ExtractHtmlSymbols(long fileId, string[] lines emitKind = "property"; emittedNames = [attrValue.Trim()]; } + else if (attrNameLower is "class" or "classname") + { + emitKind = "reference"; + emittedNames = EnumerateHtmlClassNames(attrValue).ToList(); + } else if (attrNameLower == "name" && tagNameLower == "slot") { var slotName = attrValue.Trim(); @@ -310,6 +315,12 @@ private static List ExtractHtmlSymbols(long fileId, string[] lines return symbols; } + private static IEnumerable EnumerateHtmlClassNames(string value) + { + foreach (var token in value.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries)) + yield return token; + } + private static bool IsHtmlSemanticStateAttributeName(string attrNameLower) { return (attrNameLower.StartsWith("data-", StringComparison.Ordinal) || diff --git a/src/CodeIndex/Indexer/Symbols/SymbolExtractor.cs b/src/CodeIndex/Indexer/Symbols/SymbolExtractor.cs index e93e36a2fd..854d5957e8 100644 --- a/src/CodeIndex/Indexer/Symbols/SymbolExtractor.cs +++ b/src/CodeIndex/Indexer/Symbols/SymbolExtractor.cs @@ -10038,7 +10038,7 @@ private static bool IsCppTemplateSpecializationSymbol( private static void ExtractRustAssociatedTypeDefaultSymbols(long fileId, string[] lines, string[] structuralLines, List symbols) { var traits = symbols - .Where(symbol => symbol.Kind == "interface" + .Where(symbol => symbol.Kind is "interface" or "protocol" && symbol.BodyStartLine is > 0 && symbol.BodyEndLine is > 0) .OrderBy(symbol => symbol.StartLine) @@ -10070,7 +10070,7 @@ private static void ExtractRustAssociatedTypeDefaultSymbols(long fileId, string[ StartColumn = nameGroup.Index, EndLine = lineNumber, Signature = lines[lineIndex].Trim(), - ContainerKind = trait.Kind, + ContainerKind = "interface", ContainerName = trait.Name, ContainerQualifiedName = trait.ContainerQualifiedName, Visibility = match.Groups["visibility"].Success ? match.Groups["visibility"].Value : null, diff --git a/tests/CodeIndex.Tests/SymbolExtractorTests.cs b/tests/CodeIndex.Tests/SymbolExtractorTests.cs index 638d450497..40055f8f5b 100644 --- a/tests/CodeIndex.Tests/SymbolExtractorTests.cs +++ b/tests/CodeIndex.Tests/SymbolExtractorTests.cs @@ -81,6 +81,33 @@ private void Load() { } Assert.Contains(symbols, symbol => symbol.Kind == "function" && symbol.Name == "Load"); } + [Fact] + public void Extract_HtmlClassAttributes_IndexesIndividualClassReferences() + { + const string content = """ +
+ +
+ """; + + var symbols = SymbolExtractor.Extract(1, "html", content); + var classReferences = symbols + .Where(symbol => symbol.Kind == "reference") + .Select(symbol => (symbol.Name, symbol.Line)) + .ToArray(); + + Assert.Contains(("btn", 1), classReferences); + Assert.Contains(("btn-primary", 1), classReferences); + Assert.Contains(("mx-2", 1), classReferences); + Assert.Contains(("md:flex", 1), classReferences); + Assert.Contains(("hover:bg-red-500", 1), classReferences); + Assert.Contains(("[&>*]:mt-2", 1), classReferences); + Assert.Contains(("inline-flex", 2), classReferences); + Assert.Contains(("items-center", 2), classReferences); + Assert.DoesNotContain(classReferences, symbol => string.IsNullOrWhiteSpace(symbol.Name)); + Assert.DoesNotContain(("btn btn-primary mx-2 md:flex hover:bg-red-500 [&>*]:mt-2", 1), classReferences); + } + [Theory] [InlineData("javascript")] [InlineData("typescript")]