aboutsummaryrefslogtreecommitdiff
path: root/src/lsp/server.zc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lsp/server.zc')
-rw-r--r--src/lsp/server.zc75
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;
}