diff options
author | Nate Sesti <sestinj@gmail.com> | 2023-05-23 23:45:12 -0400 |
---|---|---|
committer | Nate Sesti <sestinj@gmail.com> | 2023-05-23 23:45:12 -0400 |
commit | f53768612b1e2268697b5444e502032ef9f3fb3c (patch) | |
tree | 4ed49b73e6bd3c2f8fceffa9643973033f87af95 /extension/src/decorations.ts | |
download | sncontinue-f53768612b1e2268697b5444e502032ef9f3fb3c.tar.gz sncontinue-f53768612b1e2268697b5444e502032ef9f3fb3c.tar.bz2 sncontinue-f53768612b1e2268697b5444e502032ef9f3fb3c.zip |
copying from old repo
Diffstat (limited to 'extension/src/decorations.ts')
-rw-r--r-- | extension/src/decorations.ts | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/extension/src/decorations.ts b/extension/src/decorations.ts new file mode 100644 index 00000000..456f0c10 --- /dev/null +++ b/extension/src/decorations.ts @@ -0,0 +1,313 @@ +import * as vscode from "vscode"; +import { getRightViewColumn, getTestFile } from "./util/vscode"; +import * as path from "path"; +import { getLanguageLibrary } from "./languages"; + +export function showAnswerInTextEditor( + filename: string, + range: vscode.Range, + answer: string +) { + vscode.workspace.openTextDocument(vscode.Uri.file(filename)).then((doc) => { + const editor = vscode.window.activeTextEditor; + if (!editor) { + return; + } + + // Open file, reveal range, show decoration + vscode.window.showTextDocument(doc).then((new_editor) => { + new_editor.revealRange( + new vscode.Range(range.end, range.end), + vscode.TextEditorRevealType.InCenter + ); + + let decorationType = vscode.window.createTextEditorDecorationType({ + after: { + contentText: answer + "\n", + color: "rgb(0, 255, 0, 0.8)", + }, + backgroundColor: "rgb(0, 255, 0, 0.2)", + }); + new_editor.setDecorations(decorationType, [range]); + vscode.window.showInformationMessage("Answer found!"); + + // Remove decoration when user moves cursor + vscode.window.onDidChangeTextEditorSelection((e) => { + if ( + e.textEditor === new_editor && + e.selections[0].active.line !== range.end.line + ) { + new_editor.setDecorations(decorationType, []); + } + }); + }); + }); +} + +type DecorationKey = { + editorUri: string; + options: vscode.DecorationOptions; + decorationType: vscode.TextEditorDecorationType; +}; + +class DecorationManager { + private editorToDecorations = new Map< + string, + Map<vscode.TextEditorDecorationType, vscode.DecorationOptions[]> + >(); + + constructor() { + vscode.window.onDidChangeVisibleTextEditors((editors) => { + for (const editor of editors) { + if (editor.document.isClosed) { + this.editorToDecorations.delete(editor.document.uri.toString()); + } + } + }); + } + + private rerenderDecorations( + editorUri: string, + decorationType: vscode.TextEditorDecorationType + ) { + const editor = vscode.window.activeTextEditor; + if (!editor) { + return; + } + + const decorationTypes = this.editorToDecorations.get(editorUri); + if (!decorationTypes) { + return; + } + + const decorations = decorationTypes.get(decorationType); + if (!decorations) { + return; + } + + editor.setDecorations(decorationType, decorations); + } + + addDecoration(key: DecorationKey) { + let decorationTypes = this.editorToDecorations.get(key.editorUri); + if (!decorationTypes) { + decorationTypes = new Map(); + decorationTypes.set(key.decorationType, [key.options]); + this.editorToDecorations.set(key.editorUri, decorationTypes); + } + + const decorations = decorationTypes.get(key.decorationType); + if (!decorations) { + decorationTypes.set(key.decorationType, [key.options]); + } else { + decorations.push(key.options); + } + this.rerenderDecorations(key.editorUri, key.decorationType); + } + + deleteDecoration(key: DecorationKey) { + let decorationTypes = this.editorToDecorations.get(key.editorUri); + if (!decorationTypes) { + return; + } + + let decorations = decorationTypes?.get(key.decorationType); + if (!decorations) { + return; + } + + decorations = decorations.filter((decOpts) => decOpts !== key.options); + decorationTypes.set(key.decorationType, decorations); + this.rerenderDecorations(key.editorUri, key.decorationType); + } + + deleteAllDecorations(editorUri: string) { + let decorationTypes = this.editorToDecorations.get(editorUri)?.keys(); + if (!decorationTypes) { + return; + } + this.editorToDecorations.delete(editorUri); + for (let decorationType of decorationTypes) { + this.rerenderDecorations(editorUri, decorationType); + } + } +} + +export const decorationManager = new DecorationManager(); + +function constructBaseKey( + editor: vscode.TextEditor, + lineno: number, + decorationType?: vscode.TextEditorDecorationType +): DecorationKey { + return { + editorUri: editor.document.uri.toString(), + options: { + range: new vscode.Range(lineno, 0, lineno, 0), + }, + decorationType: + decorationType || vscode.window.createTextEditorDecorationType({}), + }; +} + +const gutterSpinnerDecorationType = + vscode.window.createTextEditorDecorationType({ + gutterIconPath: vscode.Uri.file( + path.join(__dirname, "..", "media", "spinner.gif") + ), + gutterIconSize: "contain", + }); + +export function showGutterSpinner( + editor: vscode.TextEditor, + lineno: number +): DecorationKey { + const key = constructBaseKey(editor, lineno, gutterSpinnerDecorationType); + decorationManager.addDecoration(key); + + return key; +} + +export function showLintMessage( + editor: vscode.TextEditor, + lineno: number, + msg: string +): DecorationKey { + const key = constructBaseKey(editor, lineno); + key.decorationType = vscode.window.createTextEditorDecorationType({ + after: { + contentText: "Linting error", + color: "rgb(255, 0, 0, 0.6)", + }, + gutterIconPath: vscode.Uri.file( + path.join(__dirname, "..", "media", "error.png") + ), + gutterIconSize: "contain", + }); + key.options.hoverMessage = msg; + decorationManager.addDecoration(key); + return key; +} + +export function highlightCode( + editor: vscode.TextEditor, + range: vscode.Range, + removeOnClick: boolean = true +): DecorationKey { + const decorationType = vscode.window.createTextEditorDecorationType({ + backgroundColor: "rgb(255, 255, 0, 0.1)", + }); + const key = { + editorUri: editor.document.uri.toString(), + options: { + range, + }, + decorationType, + }; + decorationManager.addDecoration(key); + + if (removeOnClick) { + vscode.window.onDidChangeTextEditorSelection((e) => { + if (e.textEditor === editor) { + decorationManager.deleteDecoration(key); + } + }); + } + + return key; +} + +// Show unit test +const pythonImportDistinguisher = (line: string): boolean => { + if (line.startsWith("from") || line.startsWith("import")) { + return true; + } + return false; +}; +const javascriptImportDistinguisher = (line: string): boolean => { + if (line.startsWith("import")) { + return true; + } + return false; +}; +const importDistinguishersMap: { + [fileExtension: string]: (line: string) => boolean; +} = { + js: javascriptImportDistinguisher, + ts: javascriptImportDistinguisher, + py: pythonImportDistinguisher, +}; +function getImportsFromFileString( + fileString: string, + importDistinguisher: (line: string) => boolean +): Set<string> { + let importLines = new Set<string>(); + for (let line of fileString.split("\n")) { + if (importDistinguisher(line)) { + importLines.add(line); + } + } + return importLines; +} +function removeRedundantLinesFrom( + fileContents: string, + linesToRemove: Set<string> +): string { + let fileLines = fileContents.split("\n"); + fileLines = fileLines.filter((line: string) => { + return !linesToRemove.has(line); + }); + return fileLines.join("\n"); +} + +export async function writeAndShowUnitTest( + filename: string, + test: string +): Promise<DecorationKey> { + return new Promise((resolve, reject) => { + let testFilename = getTestFile(filename, true); + vscode.workspace.openTextDocument(testFilename).then((doc) => { + let fileContent = doc.getText(); + let fileEmpty = fileContent.trim() === ""; + let existingImportLines = getImportsFromFileString( + fileContent, + importDistinguishersMap[doc.fileName.split(".").at(-1) || ".py"] + ); + + // Remove redundant imports, make sure pytest is there + test = removeRedundantLinesFrom(test, existingImportLines); + test = + (fileEmpty + ? `${getLanguageLibrary(".py").writeImport( + testFilename, + filename + )}\nimport pytest\n\n` + : "\n\n") + + test.trim() + + "\n"; + + vscode.window + .showTextDocument(doc, getRightViewColumn()) + .then((editor) => { + let lastLine = editor.document.lineAt(editor.document.lineCount - 1); + let testRange = new vscode.Range( + lastLine.range.end, + new vscode.Position( + test.split("\n").length + lastLine.range.end.line, + 0 + ) + ); + editor + .edit((edit) => { + edit.insert(lastLine.range.end, test); + return true; + }) + .then((success) => { + if (!success) reject("Failed to insert test"); + let key = highlightCode(editor, testRange); + resolve(key); + }); + }); + }); + }); +} |