Skip to content

Commit f7e1d10

Browse files
committed
lsp: suggest closing tags
1 parent dffa219 commit f7e1d10

File tree

3 files changed

+41
-15
lines changed

3 files changed

+41
-15
lines changed

src/cli/lsp.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ pub fn @"textDocument/completion"(
531531
},
532532
},
533533
.commitCharacters = &.{" >"},
534+
.preselect = cpl.label[0] == '/',
534535
};
535536
}
536537

src/html/Ast.zig

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const Attribute = @import("Attribute.zig");
1616

1717
const log = std.log.scoped(.@"html/ast");
1818
const fmtlog = std.log.scoped(.@"html/ast/fmt");
19+
const cpllog = std.log.scoped(.@"html/ast/completions");
1920

2021
has_syntax_errors: bool,
2122
language: Language,
@@ -1433,7 +1434,9 @@ pub fn completions(
14331434
offset: u32,
14341435
) ![]const Completion {
14351436
for (ast.errors) |err| {
1436-
if (err.tag != .token or offset != err.main_location.start) continue;
1437+
if (err.tag != .token or
1438+
offset < err.main_location.start or
1439+
offset > err.main_location.end) continue;
14371440

14381441
var idx = offset;
14391442
while (idx > 0) {
@@ -1445,27 +1448,27 @@ pub fn completions(
14451448
}
14461449
} else return &.{};
14471450

1448-
log.debug("completions before check", .{});
1451+
cpllog.debug("completions before check", .{});
14491452
const parent_idx = err.node_idx;
14501453
const parent_node = ast.nodes[parent_idx];
14511454
if ((!parent_node.kind.isElement() and
14521455
parent_node.kind != .root) or
14531456
parent_node.kind == .svg or
14541457
parent_node.kind == .math) return &.{};
14551458

1456-
log.debug("completions past check", .{});
1459+
cpllog.debug("completions past check", .{});
14571460

14581461
const e = Element.all.get(parent_node.kind);
1459-
log.debug("===== completions content: {t}", .{parent_node.kind});
1462+
cpllog.debug("===== completions content: {t}", .{parent_node.kind});
14601463
return e.completions(arena, ast, src, parent_idx, offset, .content);
14611464
}
14621465

14631466
const node_idx = ast.findNodeTagsIdx(offset);
1464-
log.debug("===== completions: attrs node: {}", .{node_idx});
1467+
cpllog.debug("===== completions: attrs node: {}", .{node_idx});
14651468
if (node_idx == 0) return &.{};
14661469

14671470
const n = ast.nodes[node_idx];
1468-
log.debug("===== node: {any}", .{n});
1471+
cpllog.debug("===== node: {any}", .{n});
14691472
if (!n.kind.isElement()) return &.{};
14701473
if (offset >= n.open.end) return &.{};
14711474

src/html/Element.zig

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -552,10 +552,10 @@ pub inline fn completions(
552552
offset: u32,
553553
mode: CompletionMode,
554554
) ![]const Ast.Completion {
555-
switch (mode) {
556-
.attrs => {
555+
const children = switch (mode) {
556+
.attrs => blk: {
557557
var stt = ast.nodes[node_idx].startTagIterator(src, ast.language);
558-
return Attribute.completions(
558+
break :blk try Attribute.completions(
559559
arena,
560560
src,
561561
&stt,
@@ -564,37 +564,59 @@ pub inline fn completions(
564564
);
565565
},
566566
.content => content: switch (element.content) {
567-
.custom => |custom| return custom.completions(
567+
.custom => |custom| try custom.completions(
568568
arena,
569569
ast,
570570
src,
571571
node_idx,
572572
offset,
573573
),
574574
.model => continue :content .{ .simple = .{} },
575-
.simple => |simple| return simpleCompletions(
575+
.simple => |simple| try simpleCompletions(
576576
arena,
577577
&.{},
578578
ast.nodes[node_idx].model.content,
579579
element.meta.content_reject,
580580
simple,
581581
),
582-
.anything => {
582+
.anything => blk: {
583583
const start: usize = @intFromEnum(Kind.___) + 1;
584584
const all_elems = all.values[start..];
585-
const anything: [all_elems.len]Ast.Completion = comptime blk: {
585+
const anything: [all_elems.len]Ast.Completion = comptime a: {
586586
var anything: [all_elems.len]Ast.Completion = undefined;
587587
for (all_elems, &anything) |in, *out| out.* = .{
588588
.label = @tagName(in.tag),
589589
.desc = in.desc,
590590
};
591-
break :blk anything;
591+
break :a anything;
592592
};
593593

594-
return &anything;
594+
break :blk &anything;
595595
},
596596
},
597+
};
598+
599+
var result: std.ArrayList(Ast.Completion) = .empty;
600+
601+
var ancestor_idx = node_idx;
602+
while (ancestor_idx != 0) {
603+
const ancestor = ast.nodes[ancestor_idx];
604+
if (!ancestor.isClosed()) {
605+
const name = ancestor.span(src).slice(src);
606+
const slashed = try std.fmt.allocPrint(arena, "/{s}", .{name});
607+
try result.append(arena, .{
608+
.label = slashed,
609+
.value = if (src[offset -| 1] == '/') name else slashed,
610+
.desc = "",
611+
});
612+
break;
613+
}
614+
ancestor_idx = ancestor.parent_idx;
597615
}
616+
617+
try result.ensureTotalCapacityPrecise(arena, result.items.len + children.len);
618+
result.appendSliceAssumeCapacity(children);
619+
return result.items;
598620
}
599621

600622
pub fn simpleCompletions(

0 commit comments

Comments
 (0)