diff --git a/Makefile b/Makefile index 1a594c8..4bebee9 100644 --- a/Makefile +++ b/Makefile @@ -39,9 +39,14 @@ aoctest: newt.js # Misc +# build / install old vscode extension vscode: cd newt-vscode && vsce package && code --install-extension *.vsix +# build / install new LSP vscode extension +vscode-lsp: + cd newt-vscode-lsp && vsce package && code --install-extension *.vsix + playground: .PHONY cd playground && ./build diff --git a/newt-vscode-lsp/src/extension.ts b/newt-vscode-lsp/src/extension.ts index b80292d..a95a7f7 100644 --- a/newt-vscode-lsp/src/extension.ts +++ b/newt-vscode-lsp/src/extension.ts @@ -7,6 +7,10 @@ import { TransportKind } from 'vscode-languageclient/node'; +function getIndent(text: string) { + return text.match(/\S/)?.index ?? 0 +} + // They put this at module level for deactivate below. let client: LanguageClient @@ -86,6 +90,84 @@ export function activate(context: vscode.ExtensionContext) { } } }); + + // HACK Temporarily copied from non-LSP version, until code actions are implemented + context.subscriptions.push( + vscode.languages.registerCodeActionsProvider( + { language: "newt" }, + { + provideCodeActions(document, range, context, token) { + const actions: vscode.CodeAction[] = []; + for (const diagnostic of context.diagnostics) { + let {message,range} = diagnostic; + let m = message.match(/missing cases: \[(.*)\]/); + if (m) { + // A lot of this logic would also apply to case split. + let cons = m[1].split(', '); + let line = range.start.line; + let lineText = document.lineAt(line).text; + // this isn't going to work for let. + // and I think I relaxed the indent for `|` + const indent = getIndent(lineText) + let m2 = lineText.match(/(.*=>?)/); + if (!m2) continue; + let s = range.start.character; + let e = range.end.character; + let a = lineText.slice(0,s); + let b = lineText.slice(e,m2[0].length); + let parens = a.endsWith('(') && b.startsWith(')'); + let lines = cons.map(con => + !parens && con.includes('_') + ? `${a}(${con})${b} ?` + : `${a}${con}${b} ?`); + const fix = new vscode.CodeAction( + "Add missing cases", + vscode.CodeActionKind.QuickFix + ); + fix.edit = new vscode.WorkspaceEdit(); + // skip indented lines + while (1) { + line = line + 1 + lineText = document.lineAt(line).text + if (!lineText.trim() || getIndent(lineText) <= indent) break + } + const insertPos = new vscode.Position(line, 0); + let text = lines.join('\n') + '\n'; + if (insertPos.line === document.lineCount) { + text = "\n" + text; + } + fix.edit.insert(document.uri, insertPos, text); + fix.diagnostics = [diagnostic]; + fix.isPreferred = true; + actions.push(fix); + } + m = message.match(/try importing: (.*)/); + if (m) { + let mods = m[1].split(', ') + let insertLine = 0; + for (let i = 0; i < document.lineCount; i++) { + const line = document.lineAt(i).text; + if (/^(import|module)\b/.test(line)) insertLine = i + 1; + } + const insertPos = new vscode.Position(insertLine, 0); + for (let mod of mods) { + const fix = new vscode.CodeAction( + `Import ${mod}`, + vscode.CodeActionKind.QuickFix + ); + fix.edit = new vscode.WorkspaceEdit(); + fix.edit.insert(document.uri, insertPos, `import ${mod}\n`); + fix.diagnostics = [diagnostic]; + // fix.isPreferred = true; // they're all preferred? + actions.push(fix); + } + } + } + return actions; + } + } + ) + ); return; }