Skip to content

Commit ad6322e

Browse files
authored
(feat) linked editing for html (#801)
1 parent 038f72b commit ad6322e

File tree

8 files changed

+93
-7
lines changed

8 files changed

+93
-7
lines changed

packages/language-server/src/ls-config.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ const defaultLSConfig: LSConfig = {
3737
completions: { enable: true, emmet: true },
3838
tagComplete: { enable: true },
3939
documentSymbols: { enable: true },
40-
renameTags: { enable: true }
40+
renameTags: { enable: true },
41+
linkedEditing: { enable: true }
4142
},
4243
svelte: {
4344
enable: true,
@@ -156,6 +157,9 @@ export interface LSHTMLConfig {
156157
renameTags: {
157158
enable: boolean;
158159
};
160+
linkedEditing: {
161+
enable: boolean;
162+
};
159163
}
160164

161165
export type CompilerWarningsSettings = Record<string, 'ignore' | 'error'>;

packages/language-server/src/plugins/PluginHost.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
Diagnostic,
1313
FormattingOptions,
1414
Hover,
15+
LinkedEditingRanges,
1516
Location,
1617
Position,
1718
Range,
@@ -415,6 +416,22 @@ export class PluginHost implements LSProvider, OnWatchFileChanges {
415416
);
416417
}
417418

419+
async getLinkedEditingRanges(
420+
textDocument: TextDocumentIdentifier,
421+
position: Position
422+
): Promise<LinkedEditingRanges | null> {
423+
const document = this.getDocument(textDocument.uri);
424+
if (!document) {
425+
throw new Error('Cannot call methods on an unopened document');
426+
}
427+
428+
return await this.execute<LinkedEditingRanges>(
429+
'getLinkedEditingRanges',
430+
[document, position],
431+
ExecuteMode.FirstNonNull
432+
);
433+
}
434+
418435
onWatchFileChanges(onWatchFileChangesParas: OnWatchFileChangesPara[]): void {
419436
for (const support of this.plugins) {
420437
support.onWatchFileChanges?.(onWatchFileChangesParas);

packages/language-server/src/plugins/html/HTMLPlugin.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import {
1414
CompletionItemKind,
1515
TextEdit,
1616
Range,
17-
WorkspaceEdit
17+
WorkspaceEdit,
18+
LinkedEditingRanges
1819
} from 'vscode-languageserver';
1920
import {
2021
DocumentManager,
@@ -24,10 +25,16 @@ import {
2425
} from '../../lib/documents';
2526
import { LSConfigManager, LSHTMLConfig } from '../../ls-config';
2627
import { svelteHtmlDataProvider } from './dataProvider';
27-
import { HoverProvider, CompletionsProvider, RenameProvider } from '../interfaces';
28+
import {
29+
HoverProvider,
30+
CompletionsProvider,
31+
RenameProvider,
32+
LinkedEditingRangesProvider
33+
} from '../interfaces';
2834
import { isInsideMoustacheTag, toRange } from '../../lib/documents/utils';
2935

30-
export class HTMLPlugin implements HoverProvider, CompletionsProvider, RenameProvider {
36+
export class HTMLPlugin
37+
implements HoverProvider, CompletionsProvider, RenameProvider, LinkedEditingRangesProvider {
3138
private configManager: LSConfigManager;
3239
private lang = getLanguageService({
3340
customDataProviders: [svelteHtmlDataProvider],
@@ -210,6 +217,7 @@ export class HTMLPlugin implements HoverProvider, CompletionsProvider, RenamePro
210217
if (!this.featureEnabled('renameTags')) {
211218
return null;
212219
}
220+
213221
const html = this.documents.get(document);
214222
if (!html) {
215223
return null;
@@ -248,6 +256,25 @@ export class HTMLPlugin implements HoverProvider, CompletionsProvider, RenamePro
248256
return toRange(document.getText(), tagNameStart, tagNameStart + node.tag.length);
249257
}
250258

259+
getLinkedEditingRanges(document: Document, position: Position): LinkedEditingRanges | null {
260+
if (!this.featureEnabled('linkedEditing')) {
261+
return null;
262+
}
263+
264+
const html = this.documents.get(document);
265+
if (!html) {
266+
return null;
267+
}
268+
269+
const ranges = this.lang.findLinkedEditingRanges(document, position, html);
270+
271+
if (!ranges) {
272+
return null;
273+
}
274+
275+
return { ranges };
276+
}
277+
251278
/**
252279
*
253280
* The language service is case insensitive, and would provide

packages/language-server/src/plugins/interfaces.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
CompletionContext,
33
FileChangeType,
4+
LinkedEditingRanges,
45
SemanticTokens,
56
SignatureHelpContext,
67
TextDocumentContentChangeEvent
@@ -151,6 +152,13 @@ export interface SemanticTokensProvider {
151152
getSemanticTokens(textDocument: Document, range?: Range): Resolvable<SemanticTokens | null>;
152153
}
153154

155+
export interface LinkedEditingRangesProvider {
156+
getLinkedEditingRanges(
157+
document: Document,
158+
position: Position
159+
): Resolvable<LinkedEditingRanges | null>;
160+
}
161+
154162
export interface OnWatchFileChangesPara {
155163
fileName: string;
156164
changeType: FileChangeType;
@@ -177,7 +185,8 @@ type ProviderBase = DiagnosticsProvider &
177185
FindReferencesProvider &
178186
RenameProvider &
179187
SignatureHelpProvider &
180-
SemanticTokensProvider;
188+
SemanticTokensProvider &
189+
LinkedEditingRangesProvider;
181190

182191
export type LSProvider = ProviderBase & BackwardsCompatibleDefinitionsProvider;
183192

packages/language-server/src/server.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import {
1515
WorkspaceEdit,
1616
SemanticTokensRequest,
1717
SemanticTokensRangeRequest,
18-
DidChangeWatchedFilesParams
18+
DidChangeWatchedFilesParams,
19+
LinkedEditingRangeRequest
1920
} from 'vscode-languageserver';
2021
import { IPCMessageReader, IPCMessageWriter, createConnection } from 'vscode-languageserver/node';
2122
import { DiagnosticsManager } from './lib/DiagnosticsManager';
@@ -203,7 +204,8 @@ export function startServer(options?: LSOptions) {
203204
legend: getSemanticTokenLegends(),
204205
range: true,
205206
full: true
206-
}
207+
},
208+
linkedEditingRangeProvider: true
207209
}
208210
};
209211
});
@@ -329,6 +331,11 @@ export function startServer(options?: LSOptions) {
329331
pluginHost.getSemanticTokens(evt.textDocument, evt.range)
330332
);
331333

334+
connection.onRequest(
335+
LinkedEditingRangeRequest.type,
336+
async (evt) => await pluginHost.getLinkedEditingRanges(evt.textDocument, evt.position)
337+
);
338+
332339
docManager.on(
333340
'documentChange',
334341
_.debounce(async (document: Document) => diagnosticsManager.update(document), 500)

packages/language-server/test/plugins/html/HTMLPlugin.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,16 @@ describe('HTML Plugin', () => {
177177
renameInfo
178178
);
179179
});
180+
181+
it('provides linked editing ranges', async () => {
182+
const { plugin, document } = setup('<div></div>');
183+
184+
const ranges = plugin.getLinkedEditingRanges(document, Position.create(0, 3));
185+
assert.deepStrictEqual(ranges, {
186+
ranges: [
187+
{ start: { line: 0, character: 1 }, end: { line: 0, character: 4 } },
188+
{ start: { line: 0, character: 7 }, end: { line: 0, character: 10 } }
189+
]
190+
});
191+
});
180192
});

packages/svelte-vscode/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,10 @@ Enable HTML tag auto closing. _Default_: `true`
198198

199199
Enable document symbols for HTML. _Default_: `true`
200200

201+
##### `svelte.plugin.html.linkedEditing.enable`
202+
203+
Enable Linked Editing for HTML. _Default_: `true`
204+
201205
##### `svelte.plugin.html.renameTags.enable`
202206

203207
Enable rename tags for the opening/closing tag pairs in HTML. _Default_: `true`

packages/svelte-vscode/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,12 @@
242242
"title": "HTML: Symbols in Outline",
243243
"description": "Enable document symbols for HTML"
244244
},
245+
"svelte.plugin.html.linkedEditing.enable": {
246+
"type": "boolean",
247+
"default": true,
248+
"title": "HTML: Linked Editing",
249+
"description": "Enable Linked Editing for HTML"
250+
},
245251
"svelte.plugin.html.renameTags.enable": {
246252
"type": "boolean",
247253
"default": true,

0 commit comments

Comments
 (0)