Skip to content

Commit 0daae52

Browse files
authored
Merge pull request #74 from xdBronch/more-requests
add documentHighlight and linkedEditingRange support for tags
2 parents 6ce62c9 + 884908d commit 0daae52

File tree

1 file changed

+111
-69
lines changed

1 file changed

+111
-69
lines changed

src/cli/lsp.zig

Lines changed: 111 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
353358
pub 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+
439425
pub 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

Comments
 (0)