diff --git a/package-lock.json b/package-lock.json index e171b60..4b989de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,9 @@ "requires": true, "packages": { "": { + "name": "quick-dim", "version": "1.1.0", + "license": "MIT", "devDependencies": { "@types/glob": "^7.1.3", "@types/mocha": "^8.0.4", @@ -655,7 +657,6 @@ "dependencies": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.3.1", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", diff --git a/package.json b/package.json index cddadbd..c60c699 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "quick-dim", "displayName": "Quick Dim", "description": "Enables you to dim your source code to mark it as read.", - "version": "1.1.0", + "version": "1.1.1", "license": "MIT", "engines": { "vscode": "^1.54.0" @@ -14,7 +14,10 @@ "url": "https://github.com/sunliangqin/VSCodeQuickDim.git" }, "activationEvents": [ - "onCommand:quickDim.dim" + "onCommand:quickDim.dim", + "onCommand:quickDim.undim", + "onCommand:quickDim.undimEntireFile", + "onCommand:quickDim.dimEntireFile" ], "publisher": "Liangqin", "main": "./out/extension.js", @@ -23,7 +26,19 @@ "commands": [ { "command": "quickDim.dim", - "title": "Dim Selected Ranges" + "title": "Quick Dim: Dim Selected Ranges" + }, + { + "command": "quickDim.undim", + "title": "Quick Dim: Undim Selected Ranges" + }, + { + "command": "quickDim.undimEntireFile", + "title": "Quick Dim: Undim Entire File" + }, + { + "command": "quickDim.dimEntireFile", + "title": "Quick Dim: Dim Entire File" } ], "configuration": [ diff --git a/src/extension.ts b/src/extension.ts index 65bf308..abac01c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,30 +1,166 @@ import * as vscode from 'vscode'; +const dimDecorationTypes = new Map>(); + +function getRangeKey(range: vscode.Range): string { + return `${range.start.line}:${range.start.character}-${range.end.line}:${range.end.character}`; +} + +function parseRangeKey(key: string): vscode.Range { + const [start, end] = key.split('-'); + const [startLine, startChar] = start.split(':').map(Number); + const [endLine, endChar] = end.split(':').map(Number); + return new vscode.Range(new vscode.Position(startLine, startChar), new vscode.Position(endLine, endChar)); +} + +function differenceRanges(original: vscode.Range, toSubtract: vscode.Range): vscode.Range[] { + const result: vscode.Range[] = []; + + // No overlap + if (!original.intersection(toSubtract)) { + result.push(original); + return result; + } + + // Subtract from the start + if (original.start.isBefore(toSubtract.start)) { + result.push(new vscode.Range(original.start, toSubtract.start)); + } + + // Subtract from the end + if (original.end.isAfter(toSubtract.end)) { + result.push(new vscode.Range(toSubtract.end, original.end)); + } + + return result; +} + export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.commands.registerCommand('quickDim.dim', () => { const editor = vscode.window.activeTextEditor; - if (editor === undefined) { + if (!editor) { return; } + if (!dimDecorationTypes.has(editor)) { + dimDecorationTypes.set(editor, new Map()); + } + const editorDimDecorations = dimDecorationTypes.get(editor)!; const opacity = vscode.workspace.getConfiguration('quickDim').get('opacity'); - const dimDecorationType = vscode.window.createTextEditorDecorationType({ - opacity: `${opacity}` - }); - const ranges: vscode.Range[] = []; - for (let i = 0; i < editor.selections.length; i++) { - const selection = editor.selections[i]; + for (let selection of editor.selections) { + let newRange = new vscode.Range( + editor.document.lineAt(selection.start.line).range.start, + editor.document.lineAt(selection.end.line).range.end + ); + + const intersectingRanges: vscode.Range[] = []; + editorDimDecorations.forEach((_, key) => { + const existingRange = parseRangeKey(key); + if (existingRange.intersection(newRange)) { + intersectingRanges.push(existingRange); + } + }); - const startLine = selection.start.line; - const startPosition = editor.document.lineAt(startLine).range.start; + for (const intersectingRange of intersectingRanges) { + newRange = newRange.union(intersectingRange); + const decoration = editorDimDecorations.get(getRangeKey(intersectingRange)); + decoration?.dispose(); + editorDimDecorations.delete(getRangeKey(intersectingRange)); + } - const endLine = selection.end.line; - const endPosition = editor.document.lineAt(endLine).range.end; + const dimDecorationType = vscode.window.createTextEditorDecorationType({ opacity: `${opacity}` }); + editorDimDecorations.set(getRangeKey(newRange), dimDecorationType); + editor.setDecorations(dimDecorationType, [newRange]); + context.subscriptions.push(dimDecorationType); + } + })); + + context.subscriptions.push(vscode.commands.registerCommand('quickDim.undim', () => { + const editor = vscode.window.activeTextEditor; + if (!editor) { + return; + } - ranges.push(new vscode.Range(startPosition, endPosition)); + const editorDimDecorations = dimDecorationTypes.get(editor); + if (!editorDimDecorations) { + return; } - editor.setDecorations(dimDecorationType, ranges); + const opacity = vscode.workspace.getConfiguration('quickDim').get('opacity'); + + for (const selection of editor.selections) { + const undimRange = new vscode.Range( + editor.document.lineAt(selection.start.line).range.start, + editor.document.lineAt(selection.end.line).range.end + ); + + const intersectingRanges: vscode.Range[] = []; + editorDimDecorations.forEach((_, key) => { + const existingRange = parseRangeKey(key); + if (existingRange.intersection(undimRange)) { + intersectingRanges.push(existingRange); + } + }); + + for (const intersectingRange of intersectingRanges) { + const decoration = editorDimDecorations.get(getRangeKey(intersectingRange)); + decoration?.dispose(); + editorDimDecorations.delete(getRangeKey(intersectingRange)); + + const newRanges = differenceRanges(intersectingRange, undimRange); + for (const newRange of newRanges) { + if (!newRange.isEmpty) { + const dimDecorationType = vscode.window.createTextEditorDecorationType({ opacity: `${opacity}` }); + editorDimDecorations.set(getRangeKey(newRange), dimDecorationType); + editor.setDecorations(dimDecorationType, [newRange]); + context.subscriptions.push(dimDecorationType); + } + } + } + } + })); + + context.subscriptions.push(vscode.commands.registerCommand('quickDim.undimEntireFile', () => { + const editor = vscode.window.activeTextEditor; + if (editor) { + const editorDimDecorations = dimDecorationTypes.get(editor); + if (editorDimDecorations) { + editorDimDecorations.forEach(decoration => decoration.dispose()); + editorDimDecorations.clear(); + } + } })); + + context.subscriptions.push(vscode.commands.registerCommand('quickDim.dimEntireFile', () => { + const editor = vscode.window.activeTextEditor; + if (!editor) { + return; + } + + if (!dimDecorationTypes.has(editor)) { + dimDecorationTypes.set(editor, new Map()); + } + const editorDimDecorations = dimDecorationTypes.get(editor)!; + const opacity = vscode.workspace.getConfiguration('quickDim').get('opacity'); + + const entireFileRange = new vscode.Range( + editor.document.positionAt(0), + editor.document.positionAt(editor.document.getText().length) + ); + + const dimDecorationType = vscode.window.createTextEditorDecorationType({ opacity: `${opacity}` }); + editorDimDecorations.set(getRangeKey(entireFileRange), dimDecorationType); + editor.setDecorations(dimDecorationType, [entireFileRange]); + context.subscriptions.push(dimDecorationType); + })); + + context.subscriptions.push({ + dispose: () => { + dimDecorationTypes.forEach(editorDecorations => { + editorDecorations.forEach(decoration => decoration.dispose()); + }); + dimDecorationTypes.clear(); + } + }); } \ No newline at end of file