From 2c708262a37bb64b58a881c359e362aa061fc63b Mon Sep 17 00:00:00 2001 From: Jason Fields Date: Tue, 6 Aug 2019 02:21:15 -0400 Subject: [PATCH] Implement `q/` and `q?` (#3956) These show your search history via a QuickPick, allowing you to search through and re-do these. Also implemented an alias for these, which is `` while typing a search. Refs #3949 --- src/actions/commands/actions.ts | 39 +++++++++++++++++++++++++- src/mode/modeHandler.ts | 17 +++++++++++ src/state/globalState.ts | 25 +++++++++++++++++ src/transformations/transformations.ts | 6 ++++ 4 files changed, 86 insertions(+), 1 deletion(-) diff --git a/src/actions/commands/actions.ts b/src/actions/commands/actions.ts index e71bc1033fe1..49744c790182 100644 --- a/src/actions/commands/actions.ts +++ b/src/actions/commands/actions.ts @@ -36,6 +36,7 @@ import { ReportFileInfo, ReportSearch, } from '../../util/statusBarTextUtils'; +import { globalState } from '../../state/globalState'; export class DocumentContentChangeAction extends BaseAction { contentChanges: { @@ -884,7 +885,7 @@ class CommandReplaceInReplaceMode extends BaseCommand { @RegisterAction class CommandInsertInSearchMode extends BaseCommand { modes = [ModeName.SearchInProgressMode]; - keys = [[''], [''], [''], ['']]; + keys = [[''], [''], [''], [''], ['']]; isJump = true; runsOnceForEveryCursor() { @@ -909,6 +910,11 @@ class CommandInsertInSearchMode extends BaseCommand { searchState.searchString.slice(0, vimState.statusBarCursorCharacterPos - 1) + searchState.searchString.slice(vimState.statusBarCursorCharacterPos); vimState.statusBarCursorCharacterPos = Math.max(vimState.statusBarCursorCharacterPos - 1, 0); + } else if (key === '') { + return new CommandShowSearchHistory(vimState.globalState.searchState!.searchDirection).exec( + position, + vimState + ); } else if (key === '\n') { await vimState.setCurrentMode(vimState.globalState.searchState!.previousMode); @@ -2139,6 +2145,37 @@ class CommandShowCommandHistory extends BaseCommand { } } +@RegisterAction +class CommandShowSearchHistory extends BaseCommand { + modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock]; + keys = [['q', '/'], ['q', '?']]; + + private direction = SearchDirection.Forward; + + runsOnceForEveryCursor() { + return false; + } + + public constructor(direction = SearchDirection.Forward) { + super(); + this.direction = direction; + } + + public async exec(position: Position, vimState: VimState): Promise { + if (vimState.recordedState.commandList.includes('?')) { + this.direction = SearchDirection.Backward; + } + vimState.recordedState.transformations.push({ + type: 'showSearchHistory', + direction: this.direction, + }); + + await vimState.setCurrentMode(ModeName.Normal); + + return vimState; + } +} + @RegisterAction class CommandDot extends BaseCommand { modes = [ModeName.Normal]; diff --git a/src/mode/modeHandler.ts b/src/mode/modeHandler.ts index 1d37b7afc3fd..9e422ed83298 100644 --- a/src/mode/modeHandler.ts +++ b/src/mode/modeHandler.ts @@ -32,6 +32,8 @@ import { isTextTransformation, TextTransformations, } from './../transformations/transformations'; +import { globalState } from '../state/globalState'; +import { ReportSearch } from '../util/statusBarTextUtils'; export class ModeHandler implements vscode.Disposable { private _disposables: vscode.Disposable[] = []; @@ -909,6 +911,21 @@ export class ModeHandler implements vscode.Disposable { } break; + case 'showSearchHistory': + const searchState = await globalState.showSearchHistory(); + if (searchState) { + globalState.searchState = searchState; + const nextMatch = searchState.getNextSearchMatchPosition( + vimState.cursorStartPosition, + command.direction + ); + + vimState.cursorStopPosition = nextMatch.pos; + this.updateView(this.vimState); + ReportSearch(nextMatch.index, searchState.matchRanges.length, vimState); + } + break; + case 'dot': if (!vimState.globalState.previousFullAction) { return vimState; // TODO(bell) diff --git a/src/state/globalState.ts b/src/state/globalState.ts index 03146b5c898f..7cdaa1efd436 100644 --- a/src/state/globalState.ts +++ b/src/state/globalState.ts @@ -1,3 +1,4 @@ +import * as vscode from 'vscode'; import { JumpTracker } from '../jumps/jumpTracker'; import { ModeName } from '../mode/mode'; import { Position } from '../common/motion/position'; @@ -6,6 +7,7 @@ import { SearchHistory } from '../history/historyFile'; import { SearchState, SearchDirection } from './searchState'; import { SubstituteState } from './substituteState'; import { configuration } from '../configuration/configuration'; +import { VimState } from './vimState'; /** * State which stores global state (across editors) @@ -102,6 +104,29 @@ class GlobalState { this.searchStateIndex = this.searchStatePrevious.length - 1; } + public async showSearchHistory(): Promise { + if (!vscode.window.activeTextEditor) { + return undefined; + } + + const items = this._searchStatePrevious + .slice() + .reverse() + .map(searchState => { + return { + label: searchState.searchString, + searchState: searchState, + }; + }); + + const item = await vscode.window.showQuickPick(items, { + placeHolder: 'Vim search history', + ignoreFocusOut: false, + }); + + return item ? item.searchState : undefined; + } + public get jumpTracker(): JumpTracker { return this._jumpTracker; } diff --git a/src/transformations/transformations.ts b/src/transformations/transformations.ts index 552f36028ab5..8241174c8804 100644 --- a/src/transformations/transformations.ts +++ b/src/transformations/transformations.ts @@ -188,6 +188,11 @@ export interface ShowCommandHistory { type: 'showCommandHistory'; } +export interface ShowSearchHistory { + type: 'showSearchHistory'; + direction: number; +} + /** * Represents pressing '.' */ @@ -247,6 +252,7 @@ export type Transformation = | DeleteTextTransformation | MoveCursorTransformation | ShowCommandHistory + | ShowSearchHistory | Dot | Macro | ContentChangeTransformation