@@ -2,7 +2,8 @@ import { getEmmetCompletionParticipants } from 'vscode-emmet-helper';
22import {
33 getLanguageService ,
44 HTMLDocument ,
5- CompletionItem as HtmlCompletionItem
5+ CompletionItem as HtmlCompletionItem ,
6+ Node
67} from 'vscode-html-languageservice' ;
78import {
89 CompletionList ,
@@ -11,7 +12,9 @@ import {
1112 SymbolInformation ,
1213 CompletionItem ,
1314 CompletionItemKind ,
14- TextEdit
15+ TextEdit ,
16+ Range ,
17+ WorkspaceEdit
1518} from 'vscode-languageserver' ;
1619import {
1720 DocumentManager ,
@@ -21,10 +24,10 @@ import {
2124} from '../../lib/documents' ;
2225import { LSConfigManager , LSHTMLConfig } from '../../ls-config' ;
2326import { svelteHtmlDataProvider } from './dataProvider' ;
24- import { HoverProvider , CompletionsProvider } from '../interfaces' ;
25- import { isInsideMoustacheTag } from '../../lib/documents/utils' ;
27+ import { HoverProvider , CompletionsProvider , RenameProvider } from '../interfaces' ;
28+ import { isInsideMoustacheTag , toRange } from '../../lib/documents/utils' ;
2629
27- export class HTMLPlugin implements HoverProvider , CompletionsProvider {
30+ export class HTMLPlugin implements HoverProvider , CompletionsProvider , RenameProvider {
2831 private configManager : LSConfigManager ;
2932 private lang = getLanguageService ( {
3033 customDataProviders : [ svelteHtmlDataProvider ] ,
@@ -51,10 +54,7 @@ export class HTMLPlugin implements HoverProvider, CompletionsProvider {
5154 }
5255
5356 const node = html . findNodeAt ( document . offsetAt ( position ) ) ;
54- if ( ! node || node . tag ?. [ 0 ] . match ( / [ A - Z ] / ) ) {
55- // The language service is case insensitive, and would provide
56- // hover info for Svelte components like `Option` which have
57- // the same name like a html tag.
57+ if ( ! node || this . possiblyComponent ( node ) ) {
5858 return null ;
5959 }
6060
@@ -206,6 +206,73 @@ export class HTMLPlugin implements HoverProvider, CompletionsProvider {
206206 return this . lang . findDocumentSymbols ( document , html ) ;
207207 }
208208
209+ rename ( document : Document , position : Position , newName : string ) : WorkspaceEdit | null {
210+ if ( ! this . featureEnabled ( 'renameTags' ) ) {
211+ return null ;
212+ }
213+ const html = this . documents . get ( document ) ;
214+ if ( ! html ) {
215+ return null ;
216+ }
217+
218+ const node = html . findNodeAt ( document . offsetAt ( position ) ) ;
219+ if ( ! node || this . possiblyComponent ( node ) ) {
220+ return null ;
221+ }
222+
223+ return this . lang . doRename ( document , position , newName , html ) ;
224+ }
225+
226+ prepareRename ( document : Document , position : Position ) : Range | null {
227+ if ( ! this . featureEnabled ( 'renameTags' ) ) {
228+ return null ;
229+ }
230+
231+ const html = this . documents . get ( document ) ;
232+ if ( ! html ) {
233+ return null ;
234+ }
235+
236+ const offset = document . offsetAt ( position ) ;
237+ const node = html . findNodeAt ( offset ) ;
238+ if (
239+ ! node ||
240+ this . possiblyComponent ( node ) ||
241+ ! node . tag ||
242+ ! this . isRenameAtTag ( node , offset )
243+ ) {
244+ return null ;
245+ }
246+ const tagNameStart = node . start + '<' . length ;
247+
248+ return toRange ( document . getText ( ) , tagNameStart , tagNameStart + node . tag . length ) ;
249+ }
250+
251+ /**
252+ *
253+ * The language service is case insensitive, and would provide
254+ * hover info for Svelte components like `Option` which have
255+ * the same name like a html tag.
256+ */
257+ private possiblyComponent ( node : Node ) : boolean {
258+ return ! ! node . tag ?. [ 0 ] . match ( / [ A - Z ] / ) ;
259+ }
260+
261+ /**
262+ * Returns true if rename happens at the tag name, not anywhere inbetween.
263+ */
264+ private isRenameAtTag ( node : Node , offset : number ) : boolean {
265+ if ( ! node . tag ) {
266+ return false ;
267+ }
268+
269+ const startTagNameEnd = node . start + `<${ node . tag } ` . length ;
270+ const endTagNameStart = node . end - `${ node . tag } >` . length ;
271+ const isAtStartTag = offset > node . start && offset <= startTagNameEnd ;
272+ const isAtEndTag = offset >= endTagNameStart && offset < node . end ;
273+ return isAtStartTag || isAtEndTag ;
274+ }
275+
209276 private featureEnabled ( feature : keyof LSHTMLConfig ) {
210277 return (
211278 this . configManager . enabled ( 'html.enable' ) &&
0 commit comments