summaryrefslogtreecommitdiff
path: root/src/lsp/lsp_zls.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/lsp/lsp_zls.zig')
-rw-r--r--src/lsp/lsp_zls.zig36
1 files changed, 16 insertions, 20 deletions
diff --git a/src/lsp/lsp_zls.zig b/src/lsp/lsp_zls.zig
index bf220c47..fb9804e6 100644
--- a/src/lsp/lsp_zls.zig
+++ b/src/lsp/lsp_zls.zig
@@ -1352,11 +1352,11 @@ fn codeActions(
// ---------------------------------------------------------------- format
-/// `=`. The seam returns ROWS, not edits — there is no channel through which a
-/// backend can rewrite the buffer — so format reports rather than formats: one
-/// row per line `zig fmt` would change, in the `+Lsp` buffer. That is the
-/// honest shape of "format" behind a read-only seam, and it is still the
-/// answer to the question the keypress asks.
+/// `=`. The response is `@put` edit records (see lsp.put): one span covering
+/// everything `zig fmt` would change, which the core substitutes as a single
+/// undo step — so `=` FORMATS, exactly like helix. The two answers that are
+/// not edits stay prose rows in `+Lsp`: a file that does not parse cannot be
+/// formatted, and a file already formatted has nothing to apply.
fn formatQuery(arena: std.mem.Allocator, req: lsp.Req, out: *std.Io.Writer) !void {
const path = lsp.rel(req.root, req.path);
var tree: Ast = try .parse(arena, req.source, .zig);
@@ -1377,22 +1377,18 @@ fn formatQuery(arena: std.mem.Allocator, req: lsp.Req, out: *std.Io.Writer) !voi
lsp.row(out, path, 0, 0, "already formatted");
return;
}
- var a = std.mem.splitScalar(u8, req.source, '\n');
- var b = std.mem.splitScalar(u8, formatted, '\n');
- var ln: usize = 0;
- var n: usize = 0;
- while (n < max_rows) : (ln += 1) {
- const x = a.next();
- const y = b.next();
- if (x == null and y == null) break;
- const xs = x orelse "";
- const ys = y orelse "";
- if (std.mem.eql(u8, xs, ys)) continue;
- n += 1;
- lsp.row(out, path, ln, 0, try std.fmt.allocPrint(arena, "- {s} + {s}", .{
- std.mem.trim(u8, xs, " \t"), std.mem.trim(u8, ys, " \t"),
- }));
+ // one record, spanning only what changed: the common prefix and suffix
+ // stay untouched, which is also what keeps the cursor mapping tight
+ var lo: usize = 0;
+ const min_len = @min(req.source.len, formatted.len);
+ while (lo < min_len and req.source[lo] == formatted[lo]) lo += 1;
+ var src_hi = req.source.len;
+ var fmt_hi = formatted.len;
+ while (src_hi > lo and fmt_hi > lo and req.source[src_hi - 1] == formatted[fmt_hi - 1]) {
+ src_hi -= 1;
+ fmt_hi -= 1;
}
+ lsp.put(out, lo, src_hi, formatted[lo..fmt_hi]);
}
// ------------------------------------------------------------------ files