mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-13 04:34:21 -10:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa305c4bbe |
No files matched your search
@@ -41,3 +41,5 @@ nodePath = ""
|
||||
problems = { shortenToSingleLine = false }
|
||||
workingDirectory = { mode = "auto" }
|
||||
codeAction = { disableRuleComment = { enable = true, location = "separateLine" }, showDocumentation = { enable = true } }
|
||||
# vscode-eslint reads it for every source.fixAll request (Actions on Save, Code Cleanup).
|
||||
codeActionOnSave = { mode = "all", rules = [] }
|
||||
@@ -75,6 +75,8 @@ nodePath = ""
|
||||
problems = { shortenToSingleLine = false }
|
||||
workingDirectory = { mode = "auto" }
|
||||
codeAction = { disableRuleComment = { enable = true, location = "separateLine" }, showDocumentation = { enable = true } }
|
||||
# vscode-eslint reads it for every source.fixAll request (Actions on Save, Code Cleanup).
|
||||
codeActionOnSave = { mode = "all", rules = [] }
|
||||
|
||||
[debugger]
|
||||
adapter = "js-debug"
|
||||
|
||||
@@ -74,6 +74,8 @@ nodePath = ""
|
||||
problems = { shortenToSingleLine = false }
|
||||
workingDirectory = { mode = "auto" }
|
||||
codeAction = { disableRuleComment = { enable = true, location = "separateLine" }, showDocumentation = { enable = true } }
|
||||
# vscode-eslint reads it for every source.fixAll request (Actions on Save, Code Cleanup).
|
||||
codeActionOnSave = { mode = "all", rules = [] }
|
||||
|
||||
[debugger]
|
||||
adapter = "js-debug"
|
||||
|
||||
@@ -43,3 +43,5 @@ nodePath = ""
|
||||
problems = { shortenToSingleLine = false }
|
||||
workingDirectory = { mode = "auto" }
|
||||
codeAction = { disableRuleComment = { enable = true, location = "separateLine" }, showDocumentation = { enable = true } }
|
||||
# vscode-eslint reads it for every source.fixAll request (Actions on Save, Code Cleanup).
|
||||
codeActionOnSave = { mode = "all", rules = [] }
|
||||
@@ -89,6 +89,8 @@ nodePath = ""
|
||||
problems = { shortenToSingleLine = false }
|
||||
workingDirectory = { mode = "auto" }
|
||||
codeAction = { disableRuleComment = { enable = true, location = "separateLine" }, showDocumentation = { enable = true } }
|
||||
# vscode-eslint reads it for every source.fixAll request (Actions on Save, Code Cleanup).
|
||||
codeActionOnSave = { mode = "all", rules = [] }
|
||||
|
||||
[debugger]
|
||||
adapter = "js-debug"
|
||||
|
||||
@@ -74,6 +74,8 @@ nodePath = ""
|
||||
problems = { shortenToSingleLine = false }
|
||||
workingDirectory = { mode = "auto" }
|
||||
codeAction = { disableRuleComment = { enable = true, location = "separateLine" }, showDocumentation = { enable = true } }
|
||||
# vscode-eslint reads it for every source.fixAll request (Actions on Save, Code Cleanup).
|
||||
codeActionOnSave = { mode = "all", rules = [] }
|
||||
|
||||
[debugger]
|
||||
adapter = "js-debug"
|
||||
|
||||
@@ -68,3 +68,5 @@ nodePath = ""
|
||||
problems = { shortenToSingleLine = false }
|
||||
workingDirectory = { mode = "auto" }
|
||||
codeAction = { disableRuleComment = { enable = true, location = "separateLine" }, showDocumentation = { enable = true } }
|
||||
# vscode-eslint reads it for every source.fixAll request (Actions on Save, Code Cleanup).
|
||||
codeActionOnSave = { mode = "all", rules = [] }
|
||||
@@ -146,6 +146,63 @@ describe("silent code actions", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("fix-all from every server", () => {
|
||||
const fixAll = (server: number, title: string): CodeAction => ({
|
||||
...resolved,
|
||||
title,
|
||||
kind: "source.fixAll",
|
||||
server,
|
||||
edit: null,
|
||||
data: "{}",
|
||||
});
|
||||
const editor = () => {
|
||||
const view = new EditorView({
|
||||
state: EditorState.create({
|
||||
doc: "const a = 'x';",
|
||||
extensions: [lspDoc.of({ doc: 1, path: "/p/a.ts", language: "typescript", uiMode: null, readOnly: false })],
|
||||
}),
|
||||
});
|
||||
views.push(view);
|
||||
return view;
|
||||
};
|
||||
|
||||
it("applies each server's source.fixAll in turn, asking the next one again after an edit", async () => {
|
||||
const view = editor();
|
||||
const ts = fixAll(1, "Fix all fixable JS/TS issues");
|
||||
const eslint = fixAll(2, "Fix all fixable ESLint issues");
|
||||
const eslintAfter = { ...eslint, data: '{"version":2}' };
|
||||
lspCodeActions
|
||||
.mockResolvedValueOnce({ status: "ok", data: [ts, eslint] })
|
||||
.mockResolvedValueOnce({ status: "ok", data: [{ ...ts, data: '{"again":true}' }, eslintAfter] });
|
||||
lspApplyCodeAction.mockResolvedValue({ status: "ok", data: null });
|
||||
expect(await applyKindSilently(view, "source.fixAll")).toBe(true);
|
||||
expect(lspCodeActions).toHaveBeenCalledTimes(2);
|
||||
expect(lspApplyCodeAction.mock.calls.map((c) => (c as unknown[])[1])).toEqual([ts, eslintAfter]);
|
||||
});
|
||||
|
||||
it("goes on with the next server when one server's action fails", async () => {
|
||||
const view = editor();
|
||||
const ts = fixAll(1, "Fix all fixable JS/TS issues");
|
||||
const eslint = fixAll(2, "Fix all fixable ESLint issues");
|
||||
lspCodeActions.mockResolvedValue({ status: "ok", data: [ts, eslint] });
|
||||
lspApplyCodeAction
|
||||
.mockResolvedValueOnce({ status: "error", error: { kind: "internal", message: "boom" } })
|
||||
.mockResolvedValueOnce({ status: "ok", data: null });
|
||||
expect(await applyKindSilently(view, "source.fixAll")).toBe(true);
|
||||
expect(lspApplyCodeAction.mock.calls.map((c) => ((c as unknown[])[1] as CodeAction).server)).toEqual([1, 2]);
|
||||
});
|
||||
|
||||
it("applies a single organize-imports action", async () => {
|
||||
const view = editor();
|
||||
const a = { ...fixAll(1, "Organize Imports"), kind: "source.organizeImports" };
|
||||
const b = { ...fixAll(2, "Organize Imports"), kind: "source.organizeImports" };
|
||||
lspCodeActions.mockResolvedValueOnce({ status: "ok", data: [a, b] });
|
||||
lspApplyCodeAction.mockResolvedValue({ status: "ok", data: null });
|
||||
expect(await applyKindSilently(view, "source.organizeImports")).toBe(true);
|
||||
expect(lspApplyCodeAction).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("intention preview", () => {
|
||||
it("does not resolve lazy plugin actions while previewing", async () => {
|
||||
const view = new EditorView({
|
||||
|
||||
@@ -299,27 +299,61 @@ export function changedLines(before: Text, after: Text): Array<{ from: number; t
|
||||
* Applies the first enabled whole-file action of `kind` without UI (Actions on
|
||||
* Save). Waits until the backend edit reached this editor; false when none.
|
||||
*/
|
||||
/**
|
||||
* Applies the server's `kind` action to the whole document without UI (Actions on Save,
|
||||
* Code Cleanup). Fix-all runs once per server that offers it, one after another, each
|
||||
* asked again for the document as the previous one left it (VS Code's codeActionsOnSave):
|
||||
* vtsls and ESLint both answer `source.fixAll` for a TypeScript file.
|
||||
*/
|
||||
export async function applyKindSilently(view: EditorView, kind: string): Promise<boolean> {
|
||||
const doc = docOf(view);
|
||||
if (!doc || doc.readOnly) return false;
|
||||
const matching = (actions: CodeAction[], server?: CodeAction["server"]) =>
|
||||
actions.filter(
|
||||
(a) =>
|
||||
a.kind?.startsWith(kind) &&
|
||||
!a.disabledReason &&
|
||||
!isLazyPluginAction(a) &&
|
||||
(server === undefined || a.server === server),
|
||||
);
|
||||
const pick = (actions: CodeAction[]) => actions.find((a) => a.isPreferred) ?? actions[0];
|
||||
let actions: CodeAction[];
|
||||
try {
|
||||
actions = (await fetchActions(view, [kind], true)).filter(
|
||||
(a) => a.kind?.startsWith(kind) && !a.disabledReason && !isLazyPluginAction(a),
|
||||
);
|
||||
} catch {
|
||||
actions = matching(await fetchActions(view, [kind], true));
|
||||
} catch (e) {
|
||||
console.warn(`[lsp] ${kind} on ${doc.path} failed`, e);
|
||||
return false;
|
||||
}
|
||||
const action = actions.find((a) => a.isPreferred) ?? actions[0];
|
||||
if (!action) return false;
|
||||
const before = view.state.doc;
|
||||
try {
|
||||
await lspRequest(() => commands.lspApplyCodeAction(doc.doc, action));
|
||||
} catch {
|
||||
return false;
|
||||
const first = pick(actions);
|
||||
if (!first) return false;
|
||||
const servers = kind.startsWith("source.fixAll")
|
||||
? [first.server, ...new Set(actions.map((a) => a.server).filter((s) => s !== first.server))]
|
||||
: [first.server];
|
||||
let applied = false;
|
||||
for (const [i, server] of servers.entries()) {
|
||||
let action: CodeAction | undefined = first;
|
||||
if (i > 0) {
|
||||
// The previous server's edit changed the document: this one's answer must be fresh.
|
||||
try {
|
||||
action = pick(matching(await fetchActions(view, [kind], true), server));
|
||||
} catch (e) {
|
||||
console.warn(`[lsp] ${kind} on ${doc.path} failed`, e);
|
||||
continue;
|
||||
}
|
||||
if (!action) continue;
|
||||
}
|
||||
const before = view.state.doc;
|
||||
const chosen = action;
|
||||
try {
|
||||
await lspRequest(() => commands.lspApplyCodeAction(doc.doc, chosen));
|
||||
} catch (e) {
|
||||
console.warn(`[lsp] applying ${chosen.kind} on ${doc.path} failed`, e);
|
||||
continue;
|
||||
}
|
||||
applied = true;
|
||||
for (let i = 0; i < 25 && view.state.doc === before; i++) await new Promise((r) => setTimeout(r, 20));
|
||||
}
|
||||
for (let i = 0; i < 25 && view.state.doc === before; i++) await new Promise((r) => setTimeout(r, 20));
|
||||
return true;
|
||||
return applied;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user