diff --git a/README.md b/README.md index 4d8edd6..80cb46c 100644 --- a/README.md +++ b/README.md @@ -13,4 +13,7 @@ Notes are saved in `$PROJECT_ROOT/.vscode/linenote` like `.vscode/linenote/path/ ![example](https://i.imgur.com/KlQtCsL.gif) -Tips: You can put hyperlinks in notes by writing `#L42` or `../foo.js#L12-L15`. +## Tips + +- You can put hyperlinks in notes by writing `#L42` or `../foo.js#L12-L15`. +- You can change the severity/color of the note with #low, #medium, #high tags diff --git a/images/gutter-high.png b/images/gutter-high.png new file mode 100644 index 0000000..963d0b6 Binary files /dev/null and b/images/gutter-high.png differ diff --git a/images/gutter-low.png b/images/gutter-low.png new file mode 100644 index 0000000..d35ed97 Binary files /dev/null and b/images/gutter-low.png differ diff --git a/images/gutter-medium.png b/images/gutter-medium.png new file mode 100644 index 0000000..3574a25 Binary files /dev/null and b/images/gutter-medium.png differ diff --git a/src/decorator.ts b/src/decorator.ts index 39f9a0d..bbf42e4 100644 --- a/src/decorator.ts +++ b/src/decorator.ts @@ -1,11 +1,13 @@ import * as vscode from "vscode"; import { filterResolved, splitArr } from "./util"; import { getCorrespondingNotes, isNotePath } from "./noteUtil"; +import { Severity } from './note'; export class Decorator { context: vscode.ExtensionContext; lineDecorator?: vscode.TextEditorDecorationType; gutterDecorator?: vscode.TextEditorDecorationType; + severity: Severity | undefined; constructor(context: vscode.ExtensionContext) { this.context = context; @@ -16,14 +18,10 @@ export class Decorator { if (this.lineDecorator) { this.lineDecorator.dispose(); } - if (this.gutterDecorator) { - this.gutterDecorator.dispose(); - } const config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration(); const lineProp: vscode.DecorationRenderOptions = {}; - const gutterProp: vscode.DecorationRenderOptions = {}; // set line color const line: string | undefined = config.get("linenote.lineColor"); @@ -38,25 +36,62 @@ export class Decorator { lineProp.overviewRulerColor = ruler.trim(); } + + this.lineDecorator = vscode.window.createTextEditorDecorationType(lineProp); + this.maybeReloadGutterDecorator(Severity.Default); + } + + maybeReloadGutterDecorator(newSeverity: Severity) { + const gutterProp: vscode.DecorationRenderOptions = {}; + const config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration(); + const showGutterIcon: boolean | undefined = config.get( "linenote.showGutterIcon" ); - if (showGutterIcon) { - let iconPath: string | undefined = config.get("linenote.gutterIconPath"); - if (iconPath) { - gutterProp.gutterIconPath = iconPath; - } else { - gutterProp.gutterIconPath = this.context.asAbsolutePath( - "images/gutter.png" - ); + + if (this.severity === undefined || this.severity !== newSeverity) { + // since we're going to switch decorators, dispose if needed + if (this.gutterDecorator) { + this.gutterDecorator.dispose(); } - gutterProp.gutterIconSize = "cover"; - } - this.lineDecorator = vscode.window.createTextEditorDecorationType(lineProp); - this.gutterDecorator = vscode.window.createTextEditorDecorationType( - gutterProp - ); + switch (newSeverity) { + case Severity.Low: + gutterProp.gutterIconSize = "cover"; + gutterProp.gutterIconPath = this.context.asAbsolutePath( + "images/gutter-low.png" + ); + break; + case Severity.Medium: + gutterProp.gutterIconSize = "cover"; + gutterProp.gutterIconPath = this.context.asAbsolutePath( + "images/gutter-medium.png" + ); + break; + case Severity.High: + gutterProp.gutterIconSize = "cover"; + gutterProp.gutterIconPath = this.context.asAbsolutePath( + "images/gutter-high.png" + ); + break; + default: + if (showGutterIcon) { + let iconPath: string | undefined = config.get("linenote.gutterIconPath"); + if (iconPath) { + gutterProp.gutterIconPath = iconPath; + } else { + gutterProp.gutterIconPath = this.context.asAbsolutePath( + "images/gutter.png" + ); + } + gutterProp.gutterIconSize = "cover"; + } + } + + this.gutterDecorator = vscode.window.createTextEditorDecorationType( + gutterProp + ); + } } async decorate() { @@ -72,6 +107,9 @@ export class Decorator { return; } + // if any note has severity marked, use the "most severe" for gutter icon color + let newSeverity = Severity.Default; + // load notes and create decoration options const notes = await getCorrespondingNotes(fsPath); const [lineProps, gutterProps] = splitArr( @@ -84,6 +122,13 @@ export class Decorator { await note.readAsMarkdown() ); markdown.isTrusted = true; + + // check if the note has a severity tag in its body + const noteSeverity = await note.readSeverity(); + if (noteSeverity > newSeverity) { + newSeverity = noteSeverity; + } + return [ { range: new vscode.Range( @@ -111,6 +156,9 @@ export class Decorator { return; } + // reset gutter icon, if necessary + this.maybeReloadGutterDecorator(newSeverity); + editor.setDecorations(this.lineDecorator!, lineProps); editor.setDecorations(this.gutterDecorator!, gutterProps); } diff --git a/src/note.ts b/src/note.ts index 2b6bd68..41cb169 100644 --- a/src/note.ts +++ b/src/note.ts @@ -10,6 +10,13 @@ export interface Props { notePath: string; } +export enum Severity { + Default = 0, + Low = 1, + Medium = 2, + High = 3 +} + export class Note implements Props { fsPath: string; from: number; @@ -26,6 +33,10 @@ export class Note implements Props { // "[" or "]" or [match, file, from] static lineLinkMatcher = /\[|\]|(?:([^\s#]*)#L?(\d+)(?:-L?(\d+))?)/g; + // extract note severity from note body + // e.g. #high #medium #low + static severityMatcher = /#(high|medium|low)/i; + constructor(props: Props) { // e.g. $PROJECT_ROOT/path/to/file.js this.fsPath = props.fsPath; @@ -122,6 +133,19 @@ export class Note implements Props { return buffer.toString(); } + async readSeverity(): Promise { + const found = (await this.read()).match(Note.severityMatcher); + if (found) { + console.log('found', found) + switch (found[0].toLowerCase()) { + case '#low': return Severity.Low; + case '#medium': return Severity.Medium; + case '#high': return Severity.High; + } + } + return Severity.Default; + } + async readAsMarkdown(): Promise { const [projectRoot] = await getRootFolders(this.fsPath);