feat(lsp): quick fixes for the textflag include and the argument area
Test / test (push) Successful in 2m56s
Test / test (push) Successful in 2m56s
Assisted-by: GLM 5.3 Flash
This commit is contained in:
@@ -1014,6 +1014,134 @@ func TestCodeActionsTargetsFlaggedFunctionOnly(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestCodeActionInsertsTextflagInclude offers the include a file must carry
|
||||
// when it uses flag macros: at the top of the file when there is no include
|
||||
// yet, after the last one otherwise.
|
||||
func TestCodeActionInsertsTextflagInclude(t *testing.T) {
|
||||
doc := "TEXT \u00b7f(SB), NOSPLIT, $0\n\tRET\n"
|
||||
withInclude := "#include \"go_asm.h\"\n\nTEXT \u00b7f(SB), NOSPLIT, $0\n\tRET\n"
|
||||
in := session("file:///f_amd64.s", doc) +
|
||||
frame(30, "textDocument/codeAction", map[string]any{
|
||||
"textDocument": map[string]any{"uri": "file:///f_amd64.s"},
|
||||
"range": map[string]any{"start": map[string]any{"line": 0, "character": 0}, "end": map[string]any{"line": 1, "character": 0}},
|
||||
"context": map[string]any{"diagnostics": []map[string]any{
|
||||
{"code": "missing-textflag-include", "range": map[string]any{"start": map[string]any{"line": 0, "character": 0}, "end": map[string]any{"line": 0, "character": 4}}},
|
||||
}},
|
||||
}) +
|
||||
frame(nil, "textDocument/didChange", map[string]any{
|
||||
"textDocument": map[string]any{"uri": "file:///f_amd64.s", "version": 2},
|
||||
"contentChanges": []map[string]any{{"text": withInclude}},
|
||||
}) +
|
||||
frame(31, "textDocument/codeAction", map[string]any{
|
||||
"textDocument": map[string]any{"uri": "file:///f_amd64.s"},
|
||||
"range": map[string]any{"start": map[string]any{"line": 0, "character": 0}, "end": map[string]any{"line": 3, "character": 0}},
|
||||
"context": map[string]any{"diagnostics": []map[string]any{
|
||||
{"code": "missing-textflag-include", "range": map[string]any{"start": map[string]any{"line": 2, "character": 0}, "end": map[string]any{"line": 2, "character": 4}}},
|
||||
}},
|
||||
}) +
|
||||
frame(nil, "exit", nil)
|
||||
msgs := run(t, in)
|
||||
|
||||
resp := findByID(msgs, 30)
|
||||
if resp == nil {
|
||||
t.Fatal("no codeAction response")
|
||||
}
|
||||
var actions []CodeAction
|
||||
if err := json.Unmarshal(mustResult(t, resp), &actions); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(actions) != 1 {
|
||||
t.Fatalf("actions = %d, want 1", len(actions))
|
||||
}
|
||||
edits := actions[0].Edit.Changes["file:///f_amd64.s"]
|
||||
if len(edits) != 1 || edits[0].Range.Start.Line != 0 || edits[0].Range.Start.Character != 0 {
|
||||
t.Fatalf("edit = %+v, want an insertion at line 0 character 0", edits)
|
||||
}
|
||||
if got := applyEdits(doc, edits); !strings.HasPrefix(got, "#include \"textflag.h\"\nTEXT \u00b7f") {
|
||||
t.Errorf("edited document = %q, want the include as the first line", got)
|
||||
}
|
||||
|
||||
resp = findByID(msgs, 31)
|
||||
if resp == nil {
|
||||
t.Fatal("no codeAction response for the with-include variant")
|
||||
}
|
||||
actions = nil
|
||||
if err := json.Unmarshal(mustResult(t, resp), &actions); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(actions) != 1 || actions[0].Edit.Changes["file:///f_amd64.s"][0].Range.Start.Line != 1 {
|
||||
t.Fatalf("actions = %+v, want one insertion after the last include (line 1)", actions)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCodeActionFixesArgSize offers the argument area the // func signature
|
||||
// implies; without a parseable signature there is nothing to offer.
|
||||
func TestCodeActionFixesArgSize(t *testing.T) {
|
||||
doc := "// func Add(x int64) int64\n" +
|
||||
"TEXT \u00b7Add(SB), NOSPLIT, $0-8\n" +
|
||||
"\tMOVQ x+0(FP), AX\n" +
|
||||
"\tADDQ y+8(FP), AX\n" +
|
||||
"\tMOVQ AX, ret+16(FP)\n" +
|
||||
"\tRET\n"
|
||||
bare := "TEXT \u00b7Bare(SB), NOSPLIT, $0-8\n\tRET\n"
|
||||
in := session("file:///f_amd64.s", doc) +
|
||||
frame(32, "textDocument/codeAction", map[string]any{
|
||||
"textDocument": map[string]any{"uri": "file:///f_amd64.s"},
|
||||
"range": map[string]any{"start": map[string]any{"line": 1, "character": 0}, "end": map[string]any{"line": 1, "character": 4}},
|
||||
"context": map[string]any{"diagnostics": []map[string]any{
|
||||
{"code": "abi-argsize", "range": map[string]any{"start": map[string]any{"line": 1, "character": 0}, "end": map[string]any{"line": 1, "character": 4}}},
|
||||
}},
|
||||
}) +
|
||||
frame(nil, "textDocument/didChange", map[string]any{
|
||||
"textDocument": map[string]any{"uri": "file:///f_amd64.s", "version": 2},
|
||||
"contentChanges": []map[string]any{{"text": bare}},
|
||||
}) +
|
||||
frame(33, "textDocument/codeAction", map[string]any{
|
||||
"textDocument": map[string]any{"uri": "file:///f_amd64.s"},
|
||||
"range": map[string]any{"start": map[string]any{"line": 0, "character": 0}, "end": map[string]any{"line": 0, "character": 4}},
|
||||
"context": map[string]any{"diagnostics": []map[string]any{
|
||||
{"code": "abi-argsize", "range": map[string]any{"start": map[string]any{"line": 0, "character": 0}, "end": map[string]any{"line": 0, "character": 4}}},
|
||||
}},
|
||||
}) +
|
||||
frame(nil, "exit", nil)
|
||||
msgs := run(t, in)
|
||||
|
||||
resp := findByID(msgs, 32)
|
||||
if resp == nil {
|
||||
t.Fatal("no codeAction response")
|
||||
}
|
||||
var actions []CodeAction
|
||||
if err := json.Unmarshal(mustResult(t, resp), &actions); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(actions) != 1 {
|
||||
t.Fatalf("actions = %d, want 1", len(actions))
|
||||
}
|
||||
if actions[0].Title != "Set arg size to 16" {
|
||||
t.Errorf("action title = %q, want Set arg size to 16", actions[0].Title)
|
||||
}
|
||||
edits := actions[0].Edit.Changes["file:///f_amd64.s"]
|
||||
if len(edits) != 1 || edits[0].NewText != "-16" || edits[0].Range.Start.Line != 1 {
|
||||
t.Fatalf("edit = %+v, want -16 on line 1", edits)
|
||||
}
|
||||
if got := applyEdits(doc, edits); !strings.Contains(got, "$0-16") {
|
||||
t.Errorf("edited document = %q, want $0-16", got)
|
||||
}
|
||||
|
||||
// No // func signature, no offer: the expected size cannot be computed.
|
||||
resp = findByID(msgs, 33)
|
||||
if resp == nil {
|
||||
t.Fatal("no codeAction response for the bare variant")
|
||||
}
|
||||
actions = nil
|
||||
if err := json.Unmarshal(mustResult(t, resp), &actions); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(actions) != 0 {
|
||||
t.Fatalf("actions = %+v, want none without a // func signature", actions)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCrossFileDefinitionAndReferences opens two documents: docA calls
|
||||
// ·helper(SB), docB defines it. Definition must jump to docB and references
|
||||
// must collect the call site in docA plus the definition in docB.
|
||||
|
||||
Reference in New Issue
Block a user