From bcec681c77c86bb5052a511a1f9def29cb6b6c2e Mon Sep 17 00:00:00 2001 From: Christian Hoock <9932295+wk1@users.noreply.github.com> Date: Thu, 28 Mar 2024 22:27:33 +0100 Subject: [PATCH 1/4] Add curr scope highlight --- src/extension.ts | 110 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 104 insertions(+), 6 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 147ae4e..5b3213f 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -6,7 +6,8 @@ import * as vscode from 'vscode'; export function activate(context: vscode.ExtensionContext) { // Create a decorator types that we use to decorate indent levels - let decorationTypes = []; + let decorationTypes: vscode.TextEditorDecorationType[] = []; + let activeScopeLightDecorationTypes: vscode.TextEditorDecorationType[] = []; let doIt = false; let clearMe = false; @@ -32,6 +33,8 @@ export function activate(context: vscode.ExtensionContext) { const indicatorStyle = vscode.workspace.getConfiguration('indentRainbow')['indicatorStyle'] || 'classic'; const lightIndicatorStyleLineWidth = vscode.workspace.getConfiguration('indentRainbow')['lightIndicatorStyleLineWidth'] || 1; + let activeScopeLightDecorationOptions: vscode.DecorationOptions[] = []; + // Colors will cycle through, and can be any size that you want const colors = vscode.workspace.getConfiguration('indentRainbow')['colors'] || [ "rgba(255,255,64,0.07)", @@ -53,6 +56,12 @@ export function activate(context: vscode.ExtensionContext) { borderWidth: `0 0 0 ${lightIndicatorStyleLineWidth}px` }); } + + activeScopeLightDecorationTypes[index] = vscode.window.createTextEditorDecorationType({ + borderStyle: "solid", + borderColor: color, + borderWidth: `0 0 0 ${lightIndicatorStyleLineWidth * 3}px` + }); }); // loop through ignore regex strings and convert to valid RegEx's. @@ -89,6 +98,16 @@ export function activate(context: vscode.ExtensionContext) { } }, null, context.subscriptions); + vscode.window.onDidChangeTextEditorSelection(event => { + if(activeEditor) { + indentConfig(); + } + + if (activeEditor && checkLanguage()) { + triggerUpdateDecorations(); + } + }, null, context.subscriptions); + vscode.workspace.onDidChangeTextDocument(event => { if(activeEditor) { indentConfig(); @@ -113,6 +132,50 @@ export function activate(context: vscode.ExtensionContext) { } } +function getCurrentScope() { + const editor = vscode.window.activeTextEditor; + if (!editor) { + return; // No open text editor + } + + const position = editor.selection.active; + const currentLine = position.line; + + // Get the text of the current line + const lineText = editor.document.lineAt(currentLine).text; + + // Determine the indentation of the current line + const currentIndentation = lineText.search(/\S|$/); // Index of first non-space character or end of line + + var tabSizeRaw = activeEditor.options.tabSize; + var tabSize = 4; + if(tabSizeRaw !== 'auto') { + tabSize=+tabSizeRaw; + } + + const currentIndentationLevel = currentIndentation / tabSize; + // Array to keep track of lines in the current scope + let scopeLines = []; + + // Check lines before the current line + for (let i = currentLine - 1; i >= 0; i--) { + const line = editor.document.lineAt(i).text; + if (line.search(/\S|$/) < currentIndentation) {break;} // Line is out of scope + scopeLines.unshift(i); // Prepend line number + } + + scopeLines.push(currentLine); // Add the current line to the scope + + // Check lines after the current line + for (let i = currentLine + 1; i < editor.document.lineCount; i++) { + const line = editor.document.lineAt(i).text; + if (line.search(/\S|$/) < currentIndentation) {break;} // Line is out of scope + scopeLines.push(i); // Append line number + } + + return { lines: scopeLines, indentation: currentIndentation, indentationLevel: currentIndentationLevel }; +} + function checkLanguage() { if (activeEditor) { if(currentLanguageId !== activeEditor.document.languageId) { @@ -141,6 +204,9 @@ export function activate(context: vscode.ExtensionContext) { for (let decorationType of decorationTypes) { activeEditor.setDecorations(decorationType, decor); } + for (let decorationType of activeScopeLightDecorationTypes) { + activeEditor.setDecorations(decorationType, decor); + } clearMe = false; } @@ -165,9 +231,9 @@ export function activate(context: vscode.ExtensionContext) { var regEx = /^[\t ]+/gm; var text = activeEditor.document.getText(); var tabSizeRaw = activeEditor.options.tabSize; - var tabSize = 4 + var tabSize = 4; if(tabSizeRaw !== 'auto') { - tabSize=+tabSizeRaw + tabSize=+tabSizeRaw; } var tabs = " ".repeat(tabSize); const ignoreLines = []; @@ -179,6 +245,12 @@ export function activate(context: vscode.ExtensionContext) { decorators.push(decorator); }); + let activeScopeDecoratorOptionsCurr = []; + activeScopeLightDecorationTypes.forEach(() => { + let decorator: vscode.DecorationOptions[] = []; + activeScopeDecoratorOptionsCurr.push(decorator); + }) + var match; var ignore; @@ -199,11 +271,21 @@ export function activate(context: vscode.ExtensionContext) { var re = new RegExp("\t","g"); let defaultIndentCharRegExp = null; + + // Loop over each occurance of leading whitespace in a line in the text. + // regEx is defined above as a regex that matches leading whitespace. + // text is the entire text of the document. while (match = regEx.exec(text)) { + // index of the current leading whitespace in the text. const pos = activeEditor.document.positionAt(match.index); + // get the line no from the index const line = activeEditor.document.lineAt(pos).lineNumber; + const currScope = getCurrentScope(); + const lineInCurrentScope = currScope.lines.includes(line); + // set skip to true if the lineNumber is in ignoreLines. let skip = skipAllErrors || ignoreLines.indexOf(line) !== -1; // true if the lineNumber is in ignoreLines. - var thematch = match[0]; + var thematch = match[0]; + // replace all tabs with spaces and get the length of the resulting string. Use this to check if the indent is divisible by tabSize to show/hide error decorator. var ma = (match[0].replace(re, tabs)).length; /** * Error handling. @@ -224,10 +306,13 @@ export function activate(context: vscode.ExtensionContext) { var n = 0; while(n < l) { const s = n; + // startpos wird auch immer neu gesetzt, weil man ja die range von genau einem tab haben will var startPos = activeEditor.document.positionAt(match.index + n); if(m[n] === "\t") { + // if it's a tab we just move one position n++; } else { + // if it's a space we move until the next tab stop n+=tabSize; } if (colorOnWhiteSpaceOnly && n > l) { @@ -255,7 +340,17 @@ export function activate(context: vscode.ExtensionContext) { tabmix_decorator.push(decoration); } else { let decorator_index = o % decorators.length; - decorators[decorator_index].push(decoration); + let activeScopeDecoratorOptionsCurrIndex = o % activeScopeLightDecorationTypes.length; + if (lineInCurrentScope) { + console.log("currentIndentation: " + currScope.indentation); + console.log("n: " + n); + } + if (n === currScope.indentation && lineInCurrentScope) { // ich denke das ist dann die innerste einrückung + activeScopeLightDecorationOptions.push(decoration); + activeScopeDecoratorOptionsCurr[activeScopeDecoratorOptionsCurrIndex].push(decoration); + } else { + decorators[decorator_index].push(decoration); + } } o++; } @@ -264,9 +359,12 @@ export function activate(context: vscode.ExtensionContext) { decorationTypes.forEach((decorationType, index) => { activeEditor.setDecorations(decorationType, decorators[index]); }); + activeScopeLightDecorationTypes.forEach((decorationType, index) => { + activeEditor.setDecorations(decorationType, activeScopeDecoratorOptionsCurr[index]); + }); activeEditor.setDecorations(error_decoration_type, error_decorator); tabmix_decoration_type && activeEditor.setDecorations(tabmix_decoration_type, tabmix_decorator); - clearMe = true; + clearMe = true; ////added to clear decorations when language switches away from the one we are interested in (see checkLanguage()) } /** * Listen for configuration change in indentRainbow section From 563dd26b30ce836a46c534b3e9b9b1e7679ec432 Mon Sep 17 00:00:00 2001 From: Christian Hoock <9932295+wk1@users.noreply.github.com> Date: Thu, 28 Mar 2024 22:34:45 +0100 Subject: [PATCH 2/4] Update curr scope color --- src/extension.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/extension.ts b/src/extension.ts index 5b3213f..7a529ea 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -57,9 +57,11 @@ export function activate(context: vscode.ExtensionContext) { }); } + const updatedColor = color.replace(/[\d\.]+\)$/g, "1.0)"); + activeScopeLightDecorationTypes[index] = vscode.window.createTextEditorDecorationType({ borderStyle: "solid", - borderColor: color, + borderColor: updatedColor, borderWidth: `0 0 0 ${lightIndicatorStyleLineWidth * 3}px` }); }); From 1b0de33de77f49bd0b5f02817ccc478e8b7adf2a Mon Sep 17 00:00:00 2001 From: Christian Hoock <9932295+wk1@users.noreply.github.com> Date: Fri, 29 Mar 2024 15:20:44 +0100 Subject: [PATCH 3/4] Clean up and add option to settings --- package.json | 5 ++++ src/extension.ts | 59 ++++++++++++++++++++++++++++++------------------ 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index 94d0d70..bcbab0a 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,11 @@ "type": "number", "default": 1, "description": "This property defines the indent indicator lineWidth when using light mode." + }, + "indentRainbow.highlightActiveScope": { + "type": "boolean", + "default": false, + "markdownDescription": "If enabled this will visually highlight the indent guide line for the current scope." } } } diff --git a/src/extension.ts b/src/extension.ts index 7a529ea..26c1650 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -6,8 +6,8 @@ import * as vscode from 'vscode'; export function activate(context: vscode.ExtensionContext) { // Create a decorator types that we use to decorate indent levels - let decorationTypes: vscode.TextEditorDecorationType[] = []; - let activeScopeLightDecorationTypes: vscode.TextEditorDecorationType[] = []; + let decorationTypes = []; + let activeScopeDecorationTypes = []; let doIt = false; let clearMe = false; @@ -31,10 +31,9 @@ export function activate(context: vscode.ExtensionContext) { const ignoreLinePatterns = vscode.workspace.getConfiguration('indentRainbow')['ignoreLinePatterns'] || []; const colorOnWhiteSpaceOnly = vscode.workspace.getConfiguration('indentRainbow')['colorOnWhiteSpaceOnly'] || false; const indicatorStyle = vscode.workspace.getConfiguration('indentRainbow')['indicatorStyle'] || 'classic'; + const highlightActiveScope = vscode.workspace.getConfiguration('indentRainbow')['highlightActiveScope'] || false; const lightIndicatorStyleLineWidth = vscode.workspace.getConfiguration('indentRainbow')['lightIndicatorStyleLineWidth'] || 1; - let activeScopeLightDecorationOptions: vscode.DecorationOptions[] = []; - // Colors will cycle through, and can be any size that you want const colors = vscode.workspace.getConfiguration('indentRainbow')['colors'] || [ "rgba(255,255,64,0.07)", @@ -45,25 +44,40 @@ export function activate(context: vscode.ExtensionContext) { // Loops through colors and creates decoration types for each one colors.forEach((color, index) => { + + // create a 100% alpha version of the current color for the highlight + const highlightColor = color.replace(/[\d\.]+\)$/g, "1.0)"); + if (indicatorStyle === 'classic') { decorationTypes[index] = vscode.window.createTextEditorDecorationType({ backgroundColor: color }); + if (highlightActiveScope) { + activeScopeDecorationTypes[index] = vscode.window.createTextEditorDecorationType({ + backgroundColor: highlightColor + }); + // Alternative more subtle decoration for active scope + // activeScopeDecorationTypes[index] = vscode.window.createTextEditorDecorationType({ + // backgroundColor: color, + // borderStyle: "solid", + // borderColor: highlightColor, + // borderWidth: `0 0 0 1px` + // }); + } } else if (indicatorStyle === 'light') { decorationTypes[index] = vscode.window.createTextEditorDecorationType({ borderStyle: "solid", borderColor: color, borderWidth: `0 0 0 ${lightIndicatorStyleLineWidth}px` }); + if (highlightActiveScope) { + activeScopeDecorationTypes[index] = vscode.window.createTextEditorDecorationType({ + borderStyle: "solid", + borderColor: highlightColor, + borderWidth: `0 0 0 ${lightIndicatorStyleLineWidth * 3}px` + }); + } } - - const updatedColor = color.replace(/[\d\.]+\)$/g, "1.0)"); - - activeScopeLightDecorationTypes[index] = vscode.window.createTextEditorDecorationType({ - borderStyle: "solid", - borderColor: updatedColor, - borderWidth: `0 0 0 ${lightIndicatorStyleLineWidth * 3}px` - }); }); // loop through ignore regex strings and convert to valid RegEx's. @@ -206,7 +220,7 @@ function getCurrentScope() { for (let decorationType of decorationTypes) { activeEditor.setDecorations(decorationType, decor); } - for (let decorationType of activeScopeLightDecorationTypes) { + for (let decorationType of activeScopeDecorationTypes) { activeEditor.setDecorations(decorationType, decor); } clearMe = false; @@ -248,10 +262,10 @@ function getCurrentScope() { }); let activeScopeDecoratorOptionsCurr = []; - activeScopeLightDecorationTypes.forEach(() => { + activeScopeDecorationTypes.forEach(() => { let decorator: vscode.DecorationOptions[] = []; activeScopeDecoratorOptionsCurr.push(decorator); - }) + }); var match; var ignore; @@ -342,14 +356,13 @@ function getCurrentScope() { tabmix_decorator.push(decoration); } else { let decorator_index = o % decorators.length; - let activeScopeDecoratorOptionsCurrIndex = o % activeScopeLightDecorationTypes.length; if (lineInCurrentScope) { console.log("currentIndentation: " + currScope.indentation); console.log("n: " + n); } - if (n === currScope.indentation && lineInCurrentScope) { // ich denke das ist dann die innerste einrückung - activeScopeLightDecorationOptions.push(decoration); - activeScopeDecoratorOptionsCurr[activeScopeDecoratorOptionsCurrIndex].push(decoration); + // Add to highlight scope if the indentation matches the current scopes indentation and the line in in current scope + if (n === currScope.indentation && lineInCurrentScope && highlightActiveScope) { + activeScopeDecoratorOptionsCurr[decorator_index].push(decoration); } else { decorators[decorator_index].push(decoration); } @@ -361,9 +374,11 @@ function getCurrentScope() { decorationTypes.forEach((decorationType, index) => { activeEditor.setDecorations(decorationType, decorators[index]); }); - activeScopeLightDecorationTypes.forEach((decorationType, index) => { - activeEditor.setDecorations(decorationType, activeScopeDecoratorOptionsCurr[index]); - }); + if (highlightActiveScope) { + activeScopeDecorationTypes.forEach((decorationType, index) => { + activeEditor.setDecorations(decorationType, activeScopeDecoratorOptionsCurr[index]); + }); + } activeEditor.setDecorations(error_decoration_type, error_decorator); tabmix_decoration_type && activeEditor.setDecorations(tabmix_decoration_type, tabmix_decorator); clearMe = true; ////added to clear decorations when language switches away from the one we are interested in (see checkLanguage()) From f2e879e4882e1cb756bbfc74942dea4a78e424aa Mon Sep 17 00:00:00 2001 From: Christian Hoock <9932295+wk1@users.noreply.github.com> Date: Fri, 29 Mar 2024 15:28:55 +0100 Subject: [PATCH 4/4] Add setting for highlight indicator width --- package.json | 5 +++++ src/extension.ts | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index bcbab0a..6000b78 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,11 @@ "type": "boolean", "default": false, "markdownDescription": "If enabled this will visually highlight the indent guide line for the current scope." + }, + "indentRainbow.lightIndicatorStyleLineWidthHighlight": { + "type": "number", + "default": 2, + "description": "This property defines the indent indicator lineWidth for the highlighted scope when using light mode." } } } diff --git a/src/extension.ts b/src/extension.ts index 26c1650..a720452 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -33,6 +33,7 @@ export function activate(context: vscode.ExtensionContext) { const indicatorStyle = vscode.workspace.getConfiguration('indentRainbow')['indicatorStyle'] || 'classic'; const highlightActiveScope = vscode.workspace.getConfiguration('indentRainbow')['highlightActiveScope'] || false; const lightIndicatorStyleLineWidth = vscode.workspace.getConfiguration('indentRainbow')['lightIndicatorStyleLineWidth'] || 1; + const lightIndicatorStyleLineWidthHighlight = vscode.workspace.getConfiguration('indentRainbow')['lightIndicatorStyleLineWidthHighlight'] || 2; // Colors will cycle through, and can be any size that you want const colors = vscode.workspace.getConfiguration('indentRainbow')['colors'] || [ @@ -74,7 +75,7 @@ export function activate(context: vscode.ExtensionContext) { activeScopeDecorationTypes[index] = vscode.window.createTextEditorDecorationType({ borderStyle: "solid", borderColor: highlightColor, - borderWidth: `0 0 0 ${lightIndicatorStyleLineWidth * 3}px` + borderWidth: `0 0 0 ${lightIndicatorStyleLineWidthHighlight}px` }); } }