@@ -115,6 +115,10 @@ pub fn initialize(
115115 },
116116 },
117117
118+ .documentHighlightProvider = .{ .bool = true },
119+
120+ .linkedEditingRangeProvider = .{ .bool = true },
121+
118122 .referencesProvider = .{ .bool = true },
119123
120124 .completionProvider = .{
@@ -350,81 +354,25 @@ pub fn @"textDocument/prepareRename"(
350354 .Range = range ,
351355 };
352356}
357+
353358pub fn @"textDocument/rename" (
354359 self : * Handler ,
355360 arena : std.mem.Allocator ,
356361 request : types.RenameParams ,
357362) error {OutOfMemory }! lsp .ResultType ("textDocument/rename" ) {
358- const doc = self .files .getPtr (request .textDocument .uri ) orelse return null ;
359- const offset = lsp .offsets .positionToIndex (
360- doc .src ,
361- request .position ,
362- self .offset_encoding ,
363- );
364-
365- const node_idx : u32 = for (doc .html .errors ) | err | {
366- // Find erroneous end tags in the error list but also any other error that
367- // has a node associated that happens to match our offset.
368- const span = err .main_location ;
369- if (span .start <= offset and span .end > offset ) {
370- if (err .tag == .ast and err .tag .ast == .erroneous_end_tag ) {
371- const edits = try arena .alloc (lsp .types .TextEdit , 1 );
372- edits [0 ] = .{
373- .range = getRange (span , doc .src ),
374- .newText = request .newName ,
375- };
376- return .{
377- .changes = .{
378- .map = try .init (
379- arena ,
380- &.{request .textDocument .uri },
381- &.{edits },
382- ),
383- },
384- };
385- }
386- if (err .node_idx != 0 ) break err .node_idx ;
387- }
388- } else findNode (doc , @intCast (offset ));
389-
390- const node = doc .html .nodes [node_idx ];
391- var edits : []lsp.types.TextEdit = undefined ;
392- blk : switch (node .kind ) {
393- else = > return null ,
394- .element = > {
395- edits = try arena .alloc (lsp .types .TextEdit , 2 );
396-
397- const it = node .startTagIterator (doc .src , doc .language );
398- edits [0 ] = .{
399- .range = getRange (it .name_span , doc .src ),
400- .newText = request .newName ,
401- };
402-
403- const close = node .close ;
404- if (close .end < 2 or close .start > close .end - 2 ) {
405- edits = edits [0.. 1];
406- break :blk ;
407- }
408-
409- edits [1 ] = .{
410- .range = getRange (.{
411- .start = close .start + 1 ,
412- .end = close .end - 1 ,
413- }, doc .src ),
414- .newText = try std .fmt .allocPrint (arena , "/{s}" , .{request .newName }),
415- };
416- },
417- .element_void , .element_self_closing = > {
418- edits = try arena .alloc (lsp .types .TextEdit , 1 );
419-
420- const it = node .startTagIterator (doc .src , doc .language );
421- edits [0 ] = .{
422- .range = getRange (it .name_span , doc .src ),
423- .newText = request .newName ,
424- };
425- },
363+ const ranges = try tagRanges (
364+ self ,
365+ arena ,
366+ .{ .textDocument = request .textDocument , .position = request .position },
367+ ) orelse return null ;
368+ const edits = try arena .alloc (types .TextEdit , ranges .len );
369+
370+ for (edits , ranges ) | * edit , range | {
371+ edit .* = .{
372+ .range = range ,
373+ .newText = request .newName ,
374+ };
426375 }
427-
428376 return .{
429377 .changes = .{
430378 .map = try .init (
@@ -436,6 +384,44 @@ pub fn @"textDocument/rename"(
436384 };
437385}
438386
387+ pub fn @"textDocument/documentHighlight" (
388+ self : * Handler ,
389+ arena : std.mem.Allocator ,
390+ request : types.DocumentHighlightParams ,
391+ ) error {OutOfMemory }! lsp .ResultType ("textDocument/documentHighlight" ) {
392+ const ranges = try tagRanges (
393+ self ,
394+ arena ,
395+ .{ .textDocument = request .textDocument , .position = request .position },
396+ ) orelse return null ;
397+ const highlights = try arena .alloc (types .DocumentHighlight , ranges .len );
398+
399+ for (highlights , ranges ) | * highlight , range | {
400+ highlight .* = .{ .range = range };
401+ }
402+
403+ return highlights ;
404+ }
405+
406+ pub fn @"textDocument/linkedEditingRange" (
407+ self : * Handler ,
408+ arena : std.mem.Allocator ,
409+ request : types.LinkedEditingRangeParams ,
410+ ) error {OutOfMemory }! lsp .ResultType ("textDocument/linkedEditingRange" ) {
411+ const ranges = try tagRanges (
412+ self ,
413+ arena ,
414+ .{ .textDocument = request .textDocument , .position = request .position },
415+ ) orelse return null ;
416+ const highlights = try arena .alloc (types .Range , ranges .len );
417+
418+ for (highlights , ranges ) | * highlight , range | {
419+ highlight .* = range ;
420+ }
421+
422+ return .{ .ranges = highlights };
423+ }
424+
439425pub fn @"textDocument/references" (
440426 self : * Handler ,
441427 arena : std.mem.Allocator ,
@@ -571,3 +557,59 @@ pub fn findNode(doc: *const Document, offset: u32) u32 {
571557
572558 return cur_idx ;
573559}
560+
561+ pub fn tagRanges (
562+ self : * Handler ,
563+ arena : std.mem.Allocator ,
564+ position : types.TextDocumentPositionParams ,
565+ ) error {OutOfMemory }! ? []const types.Range {
566+ const doc = self .files .getPtr (position .textDocument .uri ) orelse return null ;
567+ const offset = lsp .offsets .positionToIndex (
568+ doc .src ,
569+ position .position ,
570+ self .offset_encoding ,
571+ );
572+
573+ const node_idx : u32 = for (doc .html .errors ) | err | {
574+ // Find erroneous end tags in the error list but also any other error that
575+ // has a node associated that happens to match our offset.
576+ const span = err .main_location ;
577+ if (span .start <= offset and span .end > offset ) {
578+ if (err .tag == .ast and err .tag .ast == .erroneous_end_tag ) {
579+ const ranges = try arena .alloc (types .Range , 1 );
580+ ranges [0 ] = getRange (span , doc .src );
581+ return ranges ;
582+ }
583+ if (err .node_idx != 0 ) break err .node_idx ;
584+ }
585+ } else findNode (doc , @intCast (offset ));
586+
587+ const node = doc .html .nodes [node_idx ];
588+ return blk : switch (node .kind ) {
589+ else = > return null ,
590+ .element = > {
591+ const ranges = try arena .alloc (types .Range , 2 );
592+
593+ const it = node .startTagIterator (doc .src , doc .language );
594+ ranges [0 ] = getRange (it .name_span , doc .src );
595+
596+ const close = node .close ;
597+ if (close .end < 2 or close .start > close .end - 2 ) {
598+ break :blk ranges [0.. 1];
599+ }
600+
601+ ranges [1 ] = getRange (.{
602+ .start = @intCast (close .start + "</" .len ),
603+ .end = close .end - 1 ,
604+ }, doc .src );
605+ break :blk ranges ;
606+ },
607+ .element_void , .element_self_closing = > {
608+ const ranges = try arena .alloc (lsp .types .Range , 1 );
609+
610+ const it = node .startTagIterator (doc .src , doc .language );
611+ ranges [0 ] = getRange (it .name_span , doc .src );
612+ break :blk ranges ;
613+ },
614+ };
615+ }
0 commit comments