VS Code: decoration-based matching, Ctrl+K bindings, README overhaul (from dev f8451715e657)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-27 17:02:41 +02:00
parent 2eb16791d2
commit 6abb0fd3dd
10 changed files with 255 additions and 39 deletions

View File

@@ -59,7 +59,8 @@ class WorkspaceEdit {
replace(uri, range, newText) { this.edits.push({ uri, range, newText }); }
}
const listeners = { open: [], change: [], close: [] };
const listeners = { open: [], change: [], close: [], selection: [] };
const decorationTypes = []; // in creation order: match, mismatch
const providers = {};
const commands = {};
const collections = {};
@@ -87,10 +88,20 @@ const vscodeStub = {
onDidCloseTextDocument: (fn) => { listeners.close.push(fn); return { dispose() {} }; },
applyEdit: (we) => { appliedEdits.push(we); return Promise.resolve(true); },
},
ThemeColor: class ThemeColor {
constructor(id) { this.id = id; }
},
window: {
createOutputChannel: () => ({ append() {}, dispose() {} }),
showWarningMessage: (m) => { statusMessages.push(m); },
setStatusBarMessage: (m) => { statusMessages.push(m); },
createTextEditorDecorationType: (opts) => {
const t = { opts, dispose() {} };
decorationTypes.push(t);
return t;
},
onDidChangeTextEditorSelection: (fn) => { listeners.selection.push(fn); return { dispose() {} }; },
onDidChangeActiveTextEditor: () => ({ dispose() {} }),
activeTextEditor: null,
},
languages: {
@@ -199,6 +210,27 @@ async function main() {
check('jumpToMatch cursor at close', sel.active.character === 7,
JSON.stringify(sel));
// live decorations: a bare close highlights its pair from any position.
const [matchType, mismatchType] = decorationTypes;
const editor = vscodeStub.window.activeTextEditor;
editor.decorations = new Map();
editor.setDecorations = (type, ranges) => editor.decorations.set(type, ranges);
editor.selection = new Selection(new Position(0, 7), new Position(0, 7));
listeners.selection.forEach((fn) => fn({ textEditor: editor }));
await waitFor(() => (editor.decorations.get(matchType) || []).length === 2,
'match decorations');
check('decorations: bare close boxes the pair', true);
// mismatch: red decoration type, match type cleared
setDocText('@ol x ul@\n');
editor.selection = new Selection(new Position(0, 0), new Position(0, 0));
listeners.selection.forEach((fn) => fn({ textEditor: editor }));
await waitFor(() => (editor.decorations.get(mismatchType) || []).length === 2,
'mismatch decorations');
check('decorations: mismatch in the red type',
(editor.decorations.get(matchType) || []).length === 0,
JSON.stringify([...editor.decorations.values()]));
// alignTable round-trips through workspace/applyEdit.
const src = fs.readFileSync(path.join(fixDir, 'align_mixed.kt'), 'utf8');
const exp = fs.readFileSync(path.join(fixDir, 'align_mixed_expected.kt'), 'utf8');