feat(lsp): add textDocument/rangeFormatting support
Advertise DocumentRangeFormattingProvider in server capabilities and implement rangeFormat: formats the complete document, then returns an edit covering only the minimal changed-line region that overlaps the client's selection. Also includes gofmt alignment fixes across lint and format packages.
This commit is contained in:
@@ -156,6 +156,76 @@ func TestFormatting(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRangeFormatting(t *testing.T) {
|
||||
// Two statements: first is unformatted, second is already formatted.
|
||||
// Range request covers only the first statement (line 0).
|
||||
sql := "select a, b from t where x = 1;\nSELECT z\nFROM s;\n"
|
||||
uri := "file:///range.sql"
|
||||
|
||||
var input []byte
|
||||
input = append(input, frame(1, "initialize", map[string]interface{}{})...)
|
||||
input = append(input, notifFrame("initialized", nil)...)
|
||||
input = append(input, notifFrame("textDocument/didOpen", map[string]interface{}{
|
||||
"textDocument": map[string]interface{}{
|
||||
"uri": uri, "languageId": "sql", "version": 1, "text": sql,
|
||||
},
|
||||
})...)
|
||||
input = append(input, frame(2, "textDocument/rangeFormatting", map[string]interface{}{
|
||||
"textDocument": map[string]interface{}{"uri": uri},
|
||||
"range": map[string]interface{}{
|
||||
"start": map[string]interface{}{"line": 0, "character": 0},
|
||||
"end": map[string]interface{}{"line": 0, "character": 31},
|
||||
},
|
||||
"options": map[string]interface{}{"tabSize": 2, "insertSpaces": true},
|
||||
})...)
|
||||
input = append(input, frame(3, "shutdown", nil)...)
|
||||
input = append(input, notifFrame("exit", nil)...)
|
||||
|
||||
out := runServer(t, input)
|
||||
|
||||
var resp map[string]interface{}
|
||||
for i := 0; i < 10; i++ {
|
||||
r := readResp(t, out)
|
||||
if r == nil {
|
||||
break
|
||||
}
|
||||
if id, ok := r["id"]; ok && id.(float64) == 2 {
|
||||
resp = r
|
||||
break
|
||||
}
|
||||
}
|
||||
if resp == nil {
|
||||
t.Fatal("no response for rangeFormatting request")
|
||||
}
|
||||
result, ok := resp["result"].([]interface{})
|
||||
if !ok || len(result) == 0 {
|
||||
t.Fatalf("expected non-empty edit array, got %v", resp["result"])
|
||||
}
|
||||
edit := result[0].(map[string]interface{})
|
||||
newText, _ := edit["newText"].(string)
|
||||
if !strings.Contains(newText, "SELECT") {
|
||||
t.Errorf("expected formatted SELECT in edit, got: %q", newText)
|
||||
}
|
||||
if strings.Contains(newText, "SELECT z") {
|
||||
t.Errorf("range formatting edited second statement unexpectedly; got: %q", newText)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitialize_AdvertisesRangeFormatting(t *testing.T) {
|
||||
input := append(frame(1, "initialize", map[string]interface{}{}),
|
||||
notifFrame("initialized", map[string]interface{}{})...)
|
||||
input = append(input, frame(2, "shutdown", nil)...)
|
||||
input = append(input, notifFrame("exit", nil)...)
|
||||
|
||||
out := runServer(t, input)
|
||||
|
||||
resp := readResp(t, out)
|
||||
caps := resp["result"].(map[string]interface{})["capabilities"].(map[string]interface{})
|
||||
if caps["documentRangeFormattingProvider"] != true {
|
||||
t.Error("expected documentRangeFormattingProvider=true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDidClose_ClearsDiagnostics(t *testing.T) {
|
||||
uri := "file:///test.sql"
|
||||
// SQL with a lint violation (SELECT * triggers COR001)
|
||||
|
||||
Reference in New Issue
Block a user