diff --git a/package.json b/package.json index 0dced24..d74966d 100644 --- a/package.json +++ b/package.json @@ -114,6 +114,9 @@ "toggleHighlight": { "type": "boolean" }, + "toggleHighlightWithLabel": { + "type": "boolean" + }, "unhighlight": { "type": "boolean" }, @@ -142,6 +145,7 @@ "default": { "highlightUsingRegex": false, "toggleHighlight": true, + "toggleHighlightWithLabel": true, "unhighlight": false, "toggleCaseSensitivity": false, "toggleWholeMatch": false, @@ -213,6 +217,11 @@ "title": "Toggle Highlight", "category": "TextMarker" }, + { + "command": "textmarker.toggleHighlightWithLabel", + "title": "Toggle Highlight with Label", + "category": "TextMarker" + }, { "command": "textmarker.unhighlight", "title": "Unhighlight Text", @@ -272,43 +281,48 @@ "when": "editorTextFocus && config.textmarker.commandsOnContextMenu.toggleHighlight" }, { - "command": "textmarker.updateHighlight", + "command": "textmarker.toggleHighlightWithLabel", "group": "2_textmarker@2", + "when": "editorTextFocus && config.textmarker.commandsOnContextMenu.toggleHighlightWithLabel" + }, + { + "command": "textmarker.updateHighlight", + "group": "2_textmarker@3", "when": "editorTextFocus && config.textmarker.commandsOnContextMenu.updateHighlight" }, { "command": "textmarker.goToNextHighlight", - "group": "2_textmarker@3", + "group": "2_textmarker@4", "when": "editorTextFocus && config.textmarker.commandsOnContextMenu.goToNextHighlight" }, { "command": "textmarker.goToPreviousHighlight", - "group": "2_textmarker@4", + "group": "2_textmarker@5", "when": "editorTextFocus && config.textmarker.commandsOnContextMenu.goToPreviousHighlight" }, { "command": "textmarker.highlightUsingRegex", - "group": "2_textmarker@5", + "group": "2_textmarker@6", "when": "editorTextFocus && config.textmarker.commandsOnContextMenu.highlightUsingRegex" }, { "command": "textmarker.unhighlight", - "group": "2_textmarker@6", + "group": "2_textmarker@7", "when": "editorTextFocus && config.textmarker.commandsOnContextMenu.unhighlight" }, { "command": "textmarker.toggleCaseSensitivity", - "group": "2_textmarker@7", + "group": "2_textmarker@8", "when": "editorTextFocus && config.textmarker.commandsOnContextMenu.toggleCaseSensitivity" }, { "command": "textmarker.toggleWholeMatch", - "group": "2_textmarker@8", + "group": "2_textmarker@9", "when": "editorTextFocus && config.textmarker.commandsOnContextMenu.toggleWholeMatch" }, { "command": "textmarker.clearAllHighlight", - "group": "2_textmarker@9", + "group": "2_textmarker@10", "when": "editorTextFocus && config.textmarker.commandsOnContextMenu.clearAllHighlight" }, { diff --git a/src/lib/app-integrator.ts b/src/lib/app-integrator.ts index 6f39775..df191ce 100644 --- a/src/lib/app-integrator.ts +++ b/src/lib/app-integrator.ts @@ -104,6 +104,10 @@ export default class AppIntegrator { name: `${Const.EXTENSION_ID}.toggleHighlight`, command: factory.createToggleHighlightCommand(), type: 'TEXT_EDITOR' + }, { + name: `${Const.EXTENSION_ID}.toggleHighlightWithLabel`, + command: factory.createToggleHighlightWithLabelCommand(), + type: 'TEXT_EDITOR' }, { name: `${Const.EXTENSION_ID}.updateHighlight`, command: factory.createUpdateHighlightCommand(), diff --git a/src/lib/command-factory.ts b/src/lib/command-factory.ts index 7814d56..eeb43c7 100644 --- a/src/lib/command-factory.ts +++ b/src/lib/command-factory.ts @@ -15,6 +15,7 @@ import ToggleCaseSensitivityCommand from './commands/toggle-case-sensitivity'; import ToggleCaseSensitivityModeButton from './statusbar-buttons/toggle-case-sensitivity-mode'; import ToggleCaseSensitivityModeCommand from './commands/toggle-case-sensitivity-mode'; import ToggleHighlightCommand from './commands/toggle-highlight'; +import ToggleHighlightWithLabelCommand from './commands/toggle-highlight-with-label'; import ToggleWholeMatchCommand from './commands/toggle-whole-match'; import ToggleWholeMatchModeButton from './statusbar-buttons/toggle-whole-match-mode'; import ToggleWholeMatchModeCommand from './commands/toggle-whole-match-mode'; @@ -57,6 +58,10 @@ export default class CommandFactory { return new ToggleHighlightCommand(this.getMatchingModeRegistry(), this.getTextLocationRegistry(), this.getDecorationRegistry(), this.getDecorationTypeRegistry(), this.getWindowComponent()); } + createToggleHighlightWithLabelCommand() { + return new ToggleHighlightWithLabelCommand(this.getMatchingModeRegistry(), this.getTextLocationRegistry(), this.getDecorationRegistry(), this.getDecorationTypeRegistry(), this.getWindowComponent()); + } + createHighlightUsingRegex() { return new HighlightUsingRegexCommand(this.getDecorationOperatorFactory(), this.getMatchingModeRegistry(), this.getWindowComponent()); } diff --git a/src/lib/commands/toggle-highlight-with-label.ts b/src/lib/commands/toggle-highlight-with-label.ts new file mode 100644 index 0000000..903bc9b --- /dev/null +++ b/src/lib/commands/toggle-highlight-with-label.ts @@ -0,0 +1,54 @@ +import DecorationOperatorFactory from '../decoration/decoration-operator-factory'; +import PatternFactory from '../pattern/pattern-factory'; +import TextLocationRegistry from '../text-location-registry'; +import TextEditor from '../vscode/text-editor'; +import {CommandLike} from '../vscode/vscode'; +import MatchingModeRegistry from '../matching-mode-registry'; +import DecorationRegistry from '../decoration/decoration-registry'; +import WindowComponent from '../vscode/window'; +import {DecorationTypeRegistry} from '../decoration/decoration-type-registry'; +import * as O from 'fp-ts/lib/Option'; + +export default class ToggleHighlightWithLabelCommand implements CommandLike { + private readonly decorationOperatorFactory: DecorationOperatorFactory; + private readonly patternFactory: PatternFactory; + private readonly textLocationRegistry: TextLocationRegistry; + private readonly windowComponent: WindowComponent; + + constructor(matchingModeRegistry: MatchingModeRegistry, + textLocationRegistry: TextLocationRegistry, + decorationRegistry: DecorationRegistry, + decorationTypeRegistry: DecorationTypeRegistry, + windowComponent: WindowComponent) { + this.decorationOperatorFactory = new DecorationOperatorFactory(decorationRegistry, decorationTypeRegistry, textLocationRegistry, windowComponent); + this.patternFactory = new PatternFactory(matchingModeRegistry); + this.textLocationRegistry = textLocationRegistry; + this.windowComponent = windowComponent; + } + + async execute(textEditor: TextEditor) { + const existingId = this.textLocationRegistry.queryDecorationId(textEditor.id, textEditor.selection); + if (O.isSome(existingId)) { + const decorationOperator = this.decorationOperatorFactory.createForVisibleEditors(); + decorationOperator.removeDecoration(existingId.value); + return; + } + + await this.addDecorationWithLabel(textEditor); + } + + private async addDecorationWithLabel(textEditor: TextEditor) { + if (!textEditor.selectedText) return; + + const labelOption = await this.windowComponent.showInputBox({ + prompt: 'Enter a label for this highlight', + placeHolder: 'e.g. transaction_id' + })(); + + if (O.isNone(labelOption)) return; + + const pattern = this.patternFactory.create({phrase: textEditor.selectedText}); + const decorationOperator = this.decorationOperatorFactory.createForVisibleEditors(); + decorationOperator.addDecoration(pattern, undefined, labelOption.value); + } +} diff --git a/src/lib/decoration/decoration-entry-formatter.ts b/src/lib/decoration/decoration-entry-formatter.ts index da6b73e..108d0ca 100644 --- a/src/lib/decoration/decoration-entry-formatter.ts +++ b/src/lib/decoration/decoration-entry-formatter.ts @@ -6,7 +6,7 @@ export default class DecorationEntryFormatter { format(decoration: Decoration): Highlight { const pattern = decoration.pattern; - return { + const highlight: Highlight = { pattern: { type: getExternalName(pattern.type)!, expression: pattern.phrase, @@ -15,6 +15,10 @@ export default class DecorationEntryFormatter { }, color: decoration.colour }; + if (decoration.label) { + highlight.label = decoration.label; + } + return highlight; } } diff --git a/src/lib/decoration/decoration-operator.ts b/src/lib/decoration/decoration-operator.ts index d8baca3..9e6377a 100644 --- a/src/lib/decoration/decoration-operator.ts +++ b/src/lib/decoration/decoration-operator.ts @@ -19,9 +19,9 @@ export default class DecorationOperator { this.textDecorator = textDecorator; } - addDecoration(pattern: Pattern, colour?: string): void { + addDecoration(pattern: Pattern, colour?: string, label?: string): void { pipe( - this.decorationRegistry.issue(pattern, colour), + this.decorationRegistry.issue(pattern, colour, label), O.map(decoration => { this.textDecorator.decorate(this.editors, [decoration]); }) diff --git a/src/lib/decoration/decoration-registry.ts b/src/lib/decoration/decoration-registry.ts index 70a7e46..ee3ec1d 100644 --- a/src/lib/decoration/decoration-registry.ts +++ b/src/lib/decoration/decoration-registry.ts @@ -28,23 +28,23 @@ export default class DecorationRegistry { return this.map.find(isSamePattern); } - issue(pattern: Pattern, colour?: string): O.Option { + issue(pattern: Pattern, colour?: string, label?: string): O.Option { return pipe( this.inquireByPattern(pattern), O.fold( - () => O.some(this.setDecoration(this.createDecoration(pattern, colour))), + () => O.some(this.setDecoration(this.createDecoration(pattern, colour, label))), () => O.none ) ); } - private createDecoration(pattern: Pattern, colour?: string): Decoration { + private createDecoration(pattern: Pattern, colour?: string, label?: string): Decoration { const id = this.generateUuid(); if (colour) { this.colourRegistry.reserve(colour); - return new Decoration(id, pattern, colour); + return new Decoration(id, pattern, colour, label); } else { - return new Decoration(id, pattern, this.colourRegistry.issue()); + return new Decoration(id, pattern, this.colourRegistry.issue(), label); } } diff --git a/src/lib/decoration/decoration-variation-reader.ts b/src/lib/decoration/decoration-variation-reader.ts index 02bc42b..ef480aa 100644 --- a/src/lib/decoration/decoration-variation-reader.ts +++ b/src/lib/decoration/decoration-variation-reader.ts @@ -10,7 +10,8 @@ enum DecorationAction { TOGGLE_CASE_SENSITIVITY = 'toggle-case-sensitivity', TOGGLE_WHOLE_MATCH = 'toggle-whole-match', UPDATE_PHRASE = 'update-phrase', - UPDATE_COLOUR = 'update-colour' + UPDATE_COLOUR = 'update-colour', + UPDATE_LABEL = 'update-label' } interface DecorationUpdateActionQuickPickItem extends QuickPickItem { @@ -56,6 +57,15 @@ export default class DecorationVariationReader { const newPhraseOpt = this.windowComponent.showInputBox(options); return getOptionM(task).map(newPhraseOpt, newColour => currentDecoration.withColour(newColour)); } + case DecorationAction.UPDATE_LABEL: { + const options = { + value: currentDecoration.label || '', + prompt: 'Enter a label for this highlight.', + placeHolder: 'e.g. transaction_id' + }; + const newLabelOpt = this.windowComponent.showInputBox(options); + return getOptionM(task).map(newLabelOpt, newLabel => currentDecoration.withLabel(newLabel)); + } } } @@ -64,7 +74,8 @@ export default class DecorationVariationReader { this.getToggleCaseSensitivityOption(decoration), this.getToggleWholeMatchOption(decoration), this.getUpdatePhraseOption(decoration), - this.getUpdateColourOption(decoration) + this.getUpdateColourOption(decoration), + this.getUpdateLabelOption(decoration) ]; } @@ -98,4 +109,12 @@ export default class DecorationVariationReader { actionId: DecorationAction.UPDATE_COLOUR }; } + + private getUpdateLabelOption(decoration: Decoration) { + const current = decoration.label ? ` (current: ${decoration.label})` : ''; + return { + label: `Update Label${current}`, + actionId: DecorationAction.UPDATE_LABEL + }; + } } diff --git a/src/lib/decoration/text-decorator.ts b/src/lib/decoration/text-decorator.ts index 457ba64..644ad5c 100644 --- a/src/lib/decoration/text-decorator.ts +++ b/src/lib/decoration/text-decorator.ts @@ -45,7 +45,7 @@ export default class TextDecorator { private addDecoration(editor: TextEditor, decoration: Decoration): void { const ranges = decoration.pattern.locateIn(editor.wholeText); const decorationType = this.decorationTypeRegistry.provideFor(decoration); - editor.setDecorations(decorationType, ranges); + editor.setDecorations(decorationType, ranges, decoration.label); this.textLocationRegistry.register(editor.id, decoration.id, ranges); } } diff --git a/src/lib/entities/decoration.ts b/src/lib/entities/decoration.ts index 19fa171..bac97f2 100644 --- a/src/lib/entities/decoration.ts +++ b/src/lib/entities/decoration.ts @@ -4,11 +4,15 @@ export class Decoration { public readonly id: string; public readonly pattern: Pattern; public readonly colour: string; + public readonly label?: string; - constructor(id: string, pattern: Pattern, colour: string) { + constructor(id: string, pattern: Pattern, colour: string, label?: string) { this.id = id; this.pattern = pattern; this.colour = colour; + if (label !== undefined) { + this.label = label; + } } withCaseSensitivityToggled(): Decoration { @@ -24,10 +28,14 @@ export class Decoration { } private withPattern(newPattern: Pattern): Decoration { - return new Decoration(this.id, newPattern, this.colour); + return new Decoration(this.id, newPattern, this.colour, this.label); } withColour(colour: string): Decoration { - return new Decoration(this.id, this.pattern, colour); + return new Decoration(this.id, this.pattern, colour, this.label); + } + + withLabel(label: string): Decoration { + return new Decoration(this.id, this.pattern, this.colour, label); } } diff --git a/src/lib/entities/highlight.ts b/src/lib/entities/highlight.ts index fa3b27e..aec231a 100644 --- a/src/lib/entities/highlight.ts +++ b/src/lib/entities/highlight.ts @@ -12,4 +12,5 @@ export type Highlight = { wholeMatch: boolean; }, color: string; + label?: string; }; diff --git a/src/lib/saved-highlights-restorer.ts b/src/lib/saved-highlights-restorer.ts index 1869782..2df9384 100644 --- a/src/lib/saved-highlights-restorer.ts +++ b/src/lib/saved-highlights-restorer.ts @@ -41,7 +41,7 @@ export default class SavedHighlightsRestorer { private addDecoration(decorationData: Highlight, decorationOperator: DecorationOperator) { const patternData = this.decorationEntryParser.getPattern(decorationData); const pattern = this.patternFactory.create(patternData); - decorationOperator.addDecoration(pattern, decorationData.color); + decorationOperator.addDecoration(pattern, decorationData.color, decorationData.label); } } diff --git a/src/lib/vscode/text-editor.ts b/src/lib/vscode/text-editor.ts index 250d2b8..e23d35d 100644 --- a/src/lib/vscode/text-editor.ts +++ b/src/lib/vscode/text-editor.ts @@ -1,5 +1,7 @@ import SelectedTextFinder from './selected-text-finder'; import { + DecorationOptions, + MarkdownString, Position, Range, Selection, @@ -59,9 +61,15 @@ export default class TextEditor { return this.editor.document.positionAt(position); } - setDecorations(decorationType: TextEditorDecorationType, ranges: FlatRange[]) { - const vsRanges = ranges.map(range => this.getRange(range)); - this.editor.setDecorations(decorationType, vsRanges); + setDecorations(decorationType: TextEditorDecorationType, ranges: FlatRange[], label?: string) { + const vsOptions: DecorationOptions[] = ranges.map(range => { + const opt: DecorationOptions = {range: this.getRange(range)}; + if (label) { + opt.hoverMessage = new MarkdownString(`**${label}**`); + } + return opt; + }); + this.editor.setDecorations(decorationType, vsOptions); } unsetDecorations(decorationType: TextEditorDecorationType) { diff --git a/src/test/acceptance/features/highlight-selected-text.test.ts b/src/test/acceptance/features/highlight-selected-text.test.ts index abf62cf..0e1c25d 100644 --- a/src/test/acceptance/features/highlight-selected-text.test.ts +++ b/src/test/acceptance/features/highlight-selected-text.test.ts @@ -33,11 +33,11 @@ suite('Highlight command', () => { await command(editor1); verify(editor1.setDecorations('DECORATION_TYPE_1', [ - new Range(new Position(0, 2), new Position(0, 6)), - new Range(new Position(0, 9), new Position(0, 13)) + {range: new Range(new Position(0, 2), new Position(0, 6))}, + {range: new Range(new Position(0, 9), new Position(0, 13))} ])); verify(editor2.setDecorations('DECORATION_TYPE_1', [ - new Range(new Position(0, 2), new Position(0, 6)) + {range: new Range(new Position(0, 2), new Position(0, 6))} ])); }); @@ -49,8 +49,8 @@ suite('Highlight command', () => { call1: [ 'DECORATION_TYPE_2', [ - new Range(new Position(0, 2), new Position(0, 5)), - new Range(new Position(0, 9), new Position(0, 12)) + {range: new Range(new Position(0, 2), new Position(0, 5))}, + {range: new Range(new Position(0, 9), new Position(0, 12))} ] ] }); @@ -58,7 +58,7 @@ suite('Highlight command', () => { call1: [ 'DECORATION_TYPE_2', [ - new Range(new Position(0, 2), new Position(0, 5)) + {range: new Range(new Position(0, 2), new Position(0, 5))} ] ] }); diff --git a/src/test/unit/commands/go-to-next-highlight.test.ts b/src/test/unit/commands/go-to-next-highlight.test.ts index aa0872c..8d7af41 100644 --- a/src/test/unit/commands/go-to-next-highlight.test.ts +++ b/src/test/unit/commands/go-to-next-highlight.test.ts @@ -28,7 +28,7 @@ suite('Go-to-next-highlight command', function () { textLocationRegistry.register('EDITOR_ID', 'DECORATION_ID', [registeredRange1, registeredRange2, registeredRange3]); const decorationRegistry = mock(DecorationRegistry); - when(decorationRegistry.issue(newPattern, any())).thenReturn(some(mockType({pattern: newPattern}))); + when(decorationRegistry.issue(newPattern, any(), any())).thenReturn(some(mockType({pattern: newPattern}))); when(decorationRegistry.inquireById('DECORATION_ID')).thenReturn(some(mockType({id: 'DECORATION_ID'}))); const decorationTypeRegistry = mock(DecorationTypeRegistry); @@ -75,7 +75,7 @@ suite('Go-to-next-highlight command', function () { test('It highlights and take you to the next highlight', async () => { await command.execute(editor); - verify(editor.setDecorations(decorationType, [{start: 4, end: 8}, unregisteredRange, {start: 25, end: 29}])); + verify(editor.setDecorations(decorationType, [{start: 4, end: 8}, unregisteredRange, {start: 25, end: 29}], undefined)); assert.deepEqual(editor.selection, {start: 25, end: 29}); }); }); diff --git a/src/test/unit/commands/go-to-previous-highlight.test.ts b/src/test/unit/commands/go-to-previous-highlight.test.ts index 461fceb..05c2e4f 100644 --- a/src/test/unit/commands/go-to-previous-highlight.test.ts +++ b/src/test/unit/commands/go-to-previous-highlight.test.ts @@ -28,7 +28,7 @@ suite('Go-to-previous-highlight command', function () { textLocationRegistry.register('EDITOR_ID', 'DECORATION_ID', [registeredRange1, registeredRange2, registeredRange3]); const decorationRegistry = mock(DecorationRegistry); - when(decorationRegistry.issue(newPattern, any())).thenReturn(some(mockType({pattern: newPattern}))); + when(decorationRegistry.issue(newPattern, any(), any())).thenReturn(some(mockType({pattern: newPattern}))); when(decorationRegistry.inquireById('DECORATION_ID')).thenReturn(some(mockType({id: 'DECORATION_ID'}))); const decorationTypeRegistry = mock(DecorationTypeRegistry); @@ -75,7 +75,7 @@ suite('Go-to-previous-highlight command', function () { test('It highlights and take you to the previous highlight', async () => { await command.execute(editor); - verify(editor.setDecorations(decorationType, [{start: 4, end: 8}, unregisteredRange, {start: 25, end: 29}])); + verify(editor.setDecorations(decorationType, [{start: 4, end: 8}, unregisteredRange, {start: 25, end: 29}], undefined)); assert.deepEqual(editor.selection, {start: 4, end: 8}); }); }); diff --git a/src/test/unit/commands/toggle-highlight-with-label.test.ts b/src/test/unit/commands/toggle-highlight-with-label.test.ts new file mode 100644 index 0000000..51ac53f --- /dev/null +++ b/src/test/unit/commands/toggle-highlight-with-label.test.ts @@ -0,0 +1,154 @@ +import * as assert from 'assert'; +import {any, mock, mockMethods, mockType, verify, when} from '../../helpers/mock'; + +import ToggleHighlightWithLabelCommand from '../../../lib/commands/toggle-highlight-with-label'; +import TextEditor from '../../../lib/vscode/text-editor'; +import TextLocationRegistry from '../../../lib/text-location-registry'; +import MatchingModeRegistry from '../../../lib/matching-mode-registry'; +import DecorationRegistry from '../../../lib/decoration/decoration-registry'; +import WindowComponent from '../../../lib/vscode/window'; +import StringPattern from '../../../lib/pattern/string'; +import {Decoration} from '../../../lib/entities/decoration'; +import {TextEditorDecorationType} from 'vscode'; +import {none, some} from 'fp-ts/lib/Option'; +import {task} from 'fp-ts/lib/Task'; +import {DecorationTypeRegistry} from '../../../lib/decoration/decoration-type-registry'; + +suite('ToggleHighlightWithLabelCommand', () => { + + const matchingModeRegistry = mockType(); + + const registeredRange = {start: 10, end: 20}; + const unregisteredRange = {start: 0, end: 0}; + + const newPattern = new StringPattern({phrase: 'SELECTED'}); + + const decorationType = mockType(); + + const textLocationRegistry = new TextLocationRegistry(); + textLocationRegistry.register('EDITOR_ID', 'DECORATION_ID', [registeredRange]); + + const decorationRegistry = mock(DecorationRegistry); + when(decorationRegistry.issue(newPattern, undefined, 'transaction_id')) + .thenReturn(some(mockType({pattern: newPattern, label: 'transaction_id'}))); + when(decorationRegistry.inquireById('DECORATION_ID')).thenReturn(some(mockType({id: 'DECORATION_ID'}))); + + const decorationTypeRegistry = mock(DecorationTypeRegistry); + when(decorationTypeRegistry.provideFor(any())).thenReturn(decorationType); + when(decorationTypeRegistry.inquire('DECORATION_ID')).thenReturn(some(decorationType)); + + suite('When the cursor is not on a highlight and text is selected', () => { + + test('it asks for a label and decorates the selected text with it', async () => { + const editor = mockMethods(['setDecorations'], { + id: 'EDITOR_ID', + selectedText: 'SELECTED', + selection: unregisteredRange, + wholeText: 'abc SELECTED def' + }); + + const windowComponent = mockType({ + visibleTextEditors: [editor], + showInputBox: () => task.of(some('transaction_id')) + }); + + const command = new ToggleHighlightWithLabelCommand( + matchingModeRegistry, + textLocationRegistry, + decorationRegistry, + decorationTypeRegistry, + windowComponent + ); + await command.execute(editor); + + verify(editor.setDecorations(decorationType, [{start: 4, end: 12}], 'transaction_id')); + }); + + test('it does nothing if the label prompt is cancelled', async () => { + const editor = mockMethods(['setDecorations'], { + id: 'EDITOR_ID', + selectedText: 'SELECTED', + selection: unregisteredRange, + wholeText: 'abc SELECTED def' + }); + + const windowComponent = mockType({ + visibleTextEditors: [editor], + showInputBox: () => task.of(none) + }); + + const command = new ToggleHighlightWithLabelCommand( + matchingModeRegistry, + textLocationRegistry, + decorationRegistry, + decorationTypeRegistry, + windowComponent + ); + await command.execute(editor); + + verify(editor.setDecorations(any(), any(), any()), {times: 0}); + }); + }); + + suite('When the cursor is on a highlight', () => { + + test('it removes the decoration without prompting for a label', async () => { + const editor = mockMethods(['unsetDecorations'], { + id: 'EDITOR_ID', + selectedText: 'SELECTED_KNOWN', + selection: registeredRange, + wholeText: 'abc SELECTED def' + }); + + let promptShown = false; + const windowComponent = mockType({ + visibleTextEditors: [editor], + showInputBox: () => { + promptShown = true; + return task.of(some('unexpected')); + } + }); + + const command = new ToggleHighlightWithLabelCommand( + matchingModeRegistry, + textLocationRegistry, + decorationRegistry, + decorationTypeRegistry, + windowComponent + ); + await command.execute(editor); + + verify(editor.unsetDecorations(decorationType)); + assert.strictEqual(promptShown, false); + }); + }); + + suite('When text is NOT selected', () => { + + const editor = mockMethods(['setDecorations', 'unsetDecorations'], { + id: 'EDITOR_ID', + selectedText: '', + selection: unregisteredRange + }); + + const windowComponent = mockType({ + visibleTextEditors: [editor], + showInputBox: () => task.of(some('transaction_id')) + }); + + const command = new ToggleHighlightWithLabelCommand( + matchingModeRegistry, + textLocationRegistry, + decorationRegistry, + decorationTypeRegistry, + windowComponent + ); + + test('it does nothing', async () => { + await command.execute(editor); + + verify(editor.setDecorations(any(), any(), any()), {times: 0}); + verify(editor.unsetDecorations(any()), {times: 0}); + }); + }); +}); diff --git a/src/test/unit/commands/toggle-highlight.test.ts b/src/test/unit/commands/toggle-highlight.test.ts index 04f74c5..745de6a 100644 --- a/src/test/unit/commands/toggle-highlight.test.ts +++ b/src/test/unit/commands/toggle-highlight.test.ts @@ -27,7 +27,7 @@ suite('ToggleHighlightCommand', () => { textLocationRegistry.register('EDITOR_ID', 'DECORATION_ID', [registeredRange]); const decorationRegistry = mock(DecorationRegistry); - when(decorationRegistry.issue(newPattern, any())).thenReturn(some(mockType({pattern: newPattern}))); + when(decorationRegistry.issue(newPattern, any(), any())).thenReturn(some(mockType({pattern: newPattern}))); when(decorationRegistry.issue(knownPattern)).thenReturn(none); when(decorationRegistry.inquireById('DECORATION_ID')).thenReturn(some(mockType({id: 'DECORATION_ID'}))); @@ -54,7 +54,7 @@ suite('ToggleHighlightCommand', () => { ); command.execute(editor); - verify(editor.setDecorations(decorationType, [{start: 4, end: 12}])); + verify(editor.setDecorations(decorationType, [{start: 4, end: 12}], undefined)); }); test('it remove decoration if the cursor is on highlight', () => { diff --git a/src/test/unit/decoration/decoration-operator.test.ts b/src/test/unit/decoration/decoration-operator.test.ts index 497c7db..9407a17 100644 --- a/src/test/unit/decoration/decoration-operator.test.ts +++ b/src/test/unit/decoration/decoration-operator.test.ts @@ -19,7 +19,7 @@ suite('DecorationOperator', () => { test('it highlights all the strings match to the given pattern', () => { const decorationRegistry = mock(DecorationRegistry); const decoration = {} as Decoration; - when(decorationRegistry.issue(pattern, any())).thenReturn(some(decoration)); + when(decorationRegistry.issue(pattern, any(), any())).thenReturn(some(decoration)); const textDecorator = mock(TextDecorator); const operator = new DecorationOperator(editors, decorationRegistry, textDecorator); @@ -30,7 +30,7 @@ suite('DecorationOperator', () => { test('it does nothing if given pattern is already registered for highlight', () => { const decorationRegistry = mock(DecorationRegistry); - when(decorationRegistry.issue(pattern, any())).thenReturn(none); + when(decorationRegistry.issue(pattern, any(), any())).thenReturn(none); const textDecorator = mock(TextDecorator); const operator = new DecorationOperator(editors, decorationRegistry, textDecorator); operator.addDecoration(pattern); diff --git a/src/test/unit/decoration/text-decorator.test.ts b/src/test/unit/decoration/text-decorator.test.ts index 50790bc..3d265ae 100644 --- a/src/test/unit/decoration/text-decorator.test.ts +++ b/src/test/unit/decoration/text-decorator.test.ts @@ -55,8 +55,8 @@ suite('TextDecorator', () => { textDecorator.decorate(editors, [decoration0]); - verify(editors[0].setDecorations(decorationType, [{start: 7, end: 11}, {start: 12, end: 16}])); - verify(editors[1].setDecorations(decorationType, [{start: 15, end: 19}])); + verify(editors[0].setDecorations(decorationType, [{start: 7, end: 11}, {start: 12, end: 16}], undefined)); + verify(editors[1].setDecorations(decorationType, [{start: 15, end: 19}], undefined)); wrapVerify((c1, c2, c3) => verify(textLocationRegistry.register(c1(), c2(), c3())), [ [ 'EDITOR_ID_1', @@ -100,7 +100,7 @@ suite('TextDecorator', () => { }); const textDecorator = new TextDecorator(textLocationRegistry, decorationTypeRegistry); textDecorator.decorate([editor], [decoration3, decoration4]); - verify(editor.setDecorations(decorationType, [{start: 7, end: 11}, {start: 12, end: 16}])); + verify(editor.setDecorations(decorationType, [{start: 7, end: 11}, {start: 12, end: 16}], undefined)); }); function createPattern(phrase: string) { diff --git a/src/test/unit/saved-highlights-restorer.test.ts b/src/test/unit/saved-highlights-restorer.test.ts index 0c7db1f..5d12969 100644 --- a/src/test/unit/saved-highlights-restorer.test.ts +++ b/src/test/unit/saved-highlights-restorer.test.ts @@ -35,7 +35,7 @@ suite('SavedHighlightsRestorer', () => { phrase: 'PHRASE', ignoreCase: false, wholeMatch: false - }), '#F7E4B3')); + }), '#F7E4B3', undefined)); done(); });