diff options
| author | 2026-10-14 08:30:01 +0000 | |
|---|---|---|
| committer | 2026-10-14 08:30:01 +0000 | |
| commit | d274751e89f75cc0f0e25c6e5e87ef91b64597ee (patch) | |
| tree | ef76f2cfe9d2a09f3fe5d27278cf775649bc162f /src/lsp | |
| parent | lsp: quick fixes for hints and bare library names; comment cleanup (diff) | |
zc: untyped local const, c precedence for && and ||, import fixes for every module
const name = value; inside a function declares a local constant that takes
the initializer's type. the let quick fix now writes a bare const.
the self-hosted compiler bound || tighter than &&, unlike zc0 and the
readme. no existing source mixed them without parentheses, so only new
code was affected.
missing-name diagnostics and their quick fixes now search every module:
rt, core, run, loaded files, and std, icon and actor scanned from source,
including static groups (std.Text.itoa). typos suggest module exports
(putc -> rt.put_c) and get a change-to fix.
Diffstat (limited to 'src/lsp')
| -rw-r--r-- | src/lsp/server.zc | 75 |
1 files changed, 52 insertions, 23 deletions
diff --git a/src/lsp/server.zc b/src/lsp/server.zc index 70f9efd..2587c3b 100644 --- a/src/lsp/server.zc +++ b/src/lsp/server.zc @@ -3149,10 +3149,21 @@ int do_completion(int i) { // import fixes are derived from current compiler errors and qualified exports. // each edit is tied to the document version that was checked. word ca_list; int ca_i, ca_start, ca_end, ca_insert, ca_crlf; -int ca_add(char*.d owner, int qualify) { - word p = ca_list; +// one fix: include owner, and when path is set, replace from..ca_end with owner.path. +// path belongs to the compiler's export list and lives until the next check. +int ca_add(char* owner, char* path, int from) { + word p = ca_list; int n = rt.len_w(owner); char* copy; while (p) { if (rt.eq_w(p[1], owner)) return 0; p = p[0]; } - p = rt.new(3); p[0] = ca_list; p[1] = owner; p[2] = qualify; ca_list = p; + copy = rt.new(n + 1); rt.copy_w(copy, owner, n + 1); + p = rt.new(4); p[0] = ca_list; p[1] = copy; p[2] = path; p[3] = from; ca_list = p; + return 0; +} +// the fixes for every module that exports name, best first +int ca_owners(char* name, int from) { + word p = run.export_owners(name), q = 0, next; + // ca_add prepends, so the list goes in backwards and comes out best first + while (p) { next = rt.new(2); next[0] = q; next[1] = p; q = next; p = p[0]; } + while (q) { p = q[1]; ca_add(p[1], p[2], from); next = q[0]; rt.free(q); q = next; } return 0; } int ca_import_point(int i) { @@ -3176,7 +3187,7 @@ int ca_import_point(int i) { int ca_newline() { if (ca_crlf) hov_put(13); hov_put(10); return 0; } // quick fixes for hints. each record is one edit of the document: it replaces start..end // with text built from its kind. the hint's own range and words say what to change. -const int HF_RENAME = 1, HF_CONST = 2, HF_SHIFT = 3, HF_STACK = 4; +const int HF_RENAME = 1, HF_CONST = 2, HF_SHIFT = 3, HF_STACK = 4, HF_TYPO = 5; word hf_list; // [next, kind, start, end, a, b] int hf_add(int kind, int start, int end, int a, int b) { word p = lsp_grow(0, 0, 6); @@ -3241,7 +3252,7 @@ int hf_operand(int i, int op) { } if (o == op || depth) return 0 - 1; b = o; while (b > 0 && (dc(doff[i], b - 1) == 32 || dc(doff[i], b - 1) == 9)) b--; - if (b > 0) { c = dc(doff[i], b - 1); if (c == '*' || c == '/' || c == '%' || c == '.' || c == '-' && b > 1 && dc(doff[i], b - 2) == '-') return 0 - 1; } + if (b > 0) { c = dc(doff[i], b - 1); if (c == '*' || c == '/' || c == '%' || c == '.' || (c == '-' && b > 1 && dc(doff[i], b - 2) == '-')) return 0 - 1; } return o; } // is the text at o the word w, ending there @@ -3294,6 +3305,7 @@ int hf_title(word p) { if (p[1] == HF_RENAME) { hov_text("rename `"); while (k < p[3]) { hov_put(dc(doff[ca_i], k)); k++; } hov_text("` to _"); } else if (p[1] == HF_CONST) { hov_text("make `"); k = p[3]; while (k < p[4]) { hov_put(dc(doff[ca_i], k)); k++; } hov_text("` a const"); } else if (p[1] == HF_SHIFT) { hov_text("write << "); hov_number(p[4]); } + else if (p[1] == HF_TYPO) { hov_text("change to `"); k = p[4]; while (k < p[5]) { hov_put(dgb[k]); k++; } hov_put('`'); } else { hov_text("use a stack buffer"); } return 0; } @@ -3302,9 +3314,9 @@ int hf_text(word p) { hn = 0; if (p[1] == HF_RENAME) hov_put('_'); else if (p[1] == HF_CONST) { + // let becomes a bare const, which takes the initializer's type hov_text("const "); - if (hf_word(ca_i, p[2], "let")) hov_text("int "); - else { k = p[2]; while (k < p[3]) { hov_put(dc(doff[ca_i], k)); k++; } } + if (!hf_word(ca_i, p[2], "let")) { k = p[2]; while (k < p[3]) { hov_put(dc(doff[ca_i], k)); k++; } } } else if (p[1] == HF_SHIFT) { // (operand << k): the operand's text runs from the record's start to the *, without trailing blanks @@ -3313,6 +3325,7 @@ int hf_text(word p) { while (k < c) { hov_put(dc(doff[ca_i], k)); k++; } hov_text(" << "); hov_number(p[4]); hov_put(')'); } + else if (p[1] == HF_TYPO) { k = p[4]; while (k < p[5]) { hov_put(dgb[k]); k++; } } else { hov_put('['); k = p[4]; while (k < p[5]) { hov_put(dc(doff[ca_i], k)); k++; } hov_put(']'); } return 0; } @@ -3335,7 +3348,7 @@ int gen_code_actions() { if (!first) wc(','); first = 0; imported = run.module_imported(p[1]); hn = 0; if (!imported) { hov_text("include "); hov_text(p[1]); } - if (p[2]) { if (!imported) hov_text(" and "); hov_text("use "); hov_text(p[1]); hov_put('.'); hov_text(hname); } + if (p[2]) { if (!imported) hov_text(" and "); hov_text("use "); hov_text(p[1]); hov_put('.'); hov_text(p[2]); } ws("{\"title\":"); wjs(hov, hn); ws(",\"kind\":\"quickfix\","); if (single) ws("\"isPreferred\":true,"); ws("\"edit\":{\"documentChanges\":[{\"textDocument\":{\"uri\":"); @@ -3348,13 +3361,33 @@ int gen_code_actions() { } if (p[2]) { if (!imported) wc(','); - ws("{\"range\":{\"start\":"); wpos(ca_start); ws(",\"end\":"); wpos(ca_end); ws("},\"newText\":"); - hn = 0; hov_text(p[1]); hov_put('.'); hov_text(hname); wjs(hov, hn); wc('}'); + ws("{\"range\":{\"start\":"); wpos(p[3]); ws(",\"end\":"); wpos(ca_end); ws("},\"newText\":"); + hn = 0; hov_text(p[1]); hov_put('.'); hov_text(p[2]); wjs(hov, hn); wc('}'); } ws("]}]}}"); p = p[0]; } ws("]}"); return 0; } +// "did you mean 'name'?" under the cursor: the name replaces the word. a module's export +// (rt.put_c) also gets its include when the document lacks it. +char[64] ca_owner; char[64] ca_path; +int ca_typo(int at) { + int n = dgb[at + 4], e = n - 2, b, dot, k; + if (!dg_ends(at, "'?")) return 0; + b = e; while (b > 0 && dgb[at + 5 + b - 1] != 39) b--; + if (b == 0 || e - b >= 63) return 0; + dot = b; while (dot < e && dgb[at + 5 + dot] != '.') dot++; + if (dot < e) { + k = 0; while (b + k < dot) { ca_owner[k] = dgb[at + 5 + b + k]; k++; } ca_owner[k] = 0; + if (run.module_known(ca_owner)) { + k = 0; while (dot + 1 + k < e) { ca_path[k] = dgb[at + 6 + dot + k]; k++; } ca_path[k] = 0; + ca_add(ca_owner, ca_path, ca_start); + return 0; + } + } + hf_add(HF_TYPO, ca_start, ca_end, at + 5 + b, at + 5 + e); + return 0; +} int do_code_actions(int i) { int at = 0, error = -1, s, dot, k, n, base = doff[i], start, end, cursor, limit, sl = hline, sc = hchar; ca_list = 0; hf_list = 0; ca_i = i; @@ -3375,22 +3408,18 @@ int do_code_actions(int i) { while (start > 0 && (hid(dc(base, start - 1)) || dc(base, start - 1) == '.')) start--; dot = start; while (dot < ca_end && dc(base, dot) != '.') dot++; if (dot < ca_end) { + // rt.put_c without include rt needs only the include; Text.itoa without std is std.Text.itoa char* owner = rt.new(dot - start + 1); k = start; n = 0; while (k < dot) { owner[n] = dc(base, k); n++; k++; } owner[n] = 0; - if (run.module_known(owner) && !run.module_imported(owner)) ca_add(d*owner, 0); else rt.free(owner); - } else { - s = 0; - while (s < run.nsym) { - if (run.skind[s] != run.K_DOC) { - char* symbol = run.sname[s]; dot = 0; while (symbol[dot] && symbol[dot] != '.') dot++; - if (symbol[dot] == '.' && rt.eq_w(symbol + dot + 1, hname)) { - char* owner = rt.new(dot + 1); rt.copy_w(owner, symbol, dot); owner[dot] = 0; - if (run.module_known(owner)) ca_add(d*owner, 1); else rt.free(owner); - } - } - s++; + if (run.module_known(owner) || run.module_loaded(owner)) { if (!run.module_imported(owner)) ca_add(owner, 0, 0); } + else { + char* dotted = rt.new(ca_end - start + 1); k = start; n = 0; + while (k < ca_end) { dotted[n] = dc(base, k); n++; k++; } dotted[n] = 0; + ca_owners(dotted, start); rt.free(dotted); } - } + rt.free(owner); + } else ca_owners(hname, ca_start); + if (!ca_list) ca_typo(error); send(gen_code_actions); return 0; } |
