Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/dull-bees-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte-language-server': patch
---

feat: support hierarchical document symbols
2 changes: 1 addition & 1 deletion .changeset/old-carrots-rescue.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
"svelte-check": patch
'svelte-check': patch
---

chore: use machine format when run by Claude Code
61 changes: 60 additions & 1 deletion packages/language-server/src/plugins/PluginHost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ import {
TextEdit,
WorkspaceEdit,
InlayHint,
WorkspaceSymbol
WorkspaceSymbol,
DocumentSymbol
} from 'vscode-languageserver';
import { DocumentManager, getNodeIfIsInHTMLStartTag } from '../lib/documents';
import { Logger } from '../logger';
Expand Down Expand Up @@ -307,6 +308,7 @@ export class PluginHost implements LSProvider, OnWatchFileChanges {
if (cancellationToken.isCancellationRequested) {
return [];
}

return flatten(
await this.execute<SymbolInformation[]>(
'getDocumentSymbols',
Expand All @@ -317,6 +319,63 @@ export class PluginHost implements LSProvider, OnWatchFileChanges {
);
}

private comparePosition(pos1: Position, pos2: Position) {
if (pos1.line < pos2.line) return -1;
if (pos1.line > pos2.line) return 1;
if (pos1.character < pos2.character) return -1;
if (pos1.character > pos2.character) return 1;
return 0;
}

private rangeContains(parent: Range, child: Range) {
return (
this.comparePosition(parent.start, child.start) <= 0 &&
this.comparePosition(child.end, parent.end) <= 0
);
}

async getHierarchicalDocumentSymbols(
textDocument: TextDocumentIdentifier,
cancellationToken: CancellationToken
): Promise<DocumentSymbol[]> {
const flat = await this.getDocumentSymbols(textDocument, cancellationToken);
const symbols = flat
.map((s) =>
DocumentSymbol.create(
s.name,
undefined,
s.kind,
s.location.range,
s.location.range,
[]
)
)
.sort((a, b) => {
const start = this.comparePosition(a.range.start, b.range.start);
if (start !== 0) return start;
return this.comparePosition(b.range.end, a.range.end);
});

const stack: DocumentSymbol[] = [];
const roots: DocumentSymbol[] = [];

for (const node of symbols) {
while (stack.length > 0 && !this.rangeContains(stack.at(-1)!.range, node.range)) {
stack.pop();
}

if (stack.length > 0) {
stack.at(-1)!.children!.push(node);
} else {
roots.push(node);
}

stack.push(node);
}

return roots;
}

async getDefinitions(
textDocument: TextDocumentIdentifier,
position: Position
Expand Down
13 changes: 10 additions & 3 deletions packages/language-server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -429,9 +429,16 @@ export function startServer(options?: LSOptions) {
connection.onColorPresentation((evt) =>
pluginHost.getColorPresentations(evt.textDocument, evt.range, evt.color)
);
connection.onDocumentSymbol((evt, cancellationToken) =>
pluginHost.getDocumentSymbols(evt.textDocument, cancellationToken)
);
connection.onDocumentSymbol((evt, cancellationToken) => {
if (
configManager.getClientCapabilities()?.textDocument?.documentSymbol
?.hierarchicalDocumentSymbolSupport
) {
return pluginHost.getHierarchicalDocumentSymbols(evt.textDocument, cancellationToken);
} else {
return pluginHost.getDocumentSymbols(evt.textDocument, cancellationToken);
}
});
connection.onDefinition((evt) => pluginHost.getDefinitions(evt.textDocument, evt.position));
connection.onReferences((evt, cancellationToken) =>
pluginHost.findReferences(evt.textDocument, evt.position, evt.context, cancellationToken)
Expand Down