diff --git a/README.md b/README.md index 632694c..1630461 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ This extension contributes the following settings: * `rgOptions`: Additional options to pass to the 'rg' command, you can view all options in your terminal via 'rg --help'. * `rgGlobExcludes`: Additional glob paths to exclude from the 'rg' search, eg: '__/dist/__'. * `addSrcPaths`: Additional source paths to include in the rg search. You may want to add this as a workspace specific setting. -* `rgMenuActions`: Create menu items which can be selected prior to any query, these items will be added to the ripgrep command to generate the results. Eg: Add `-t js` as a menu option to only show javascript files in the results. +* `rgMenuActions`: Create menu items which can be selected prior to any query, these items will be added to the ripgrep command to generate the results. Eg: Add `{ label: "JS/TS", value: "--type-add 'jsts:*.{js|ts|tsx|jsx}' -t jsts" },` as a menu option to only show js & ts files in the results. * `startFolderDisplayDepth`: The folder depth to display in the results before '...'. * `endFolderDisplayDepth`: The folder depth to display in the results after '...'. * `enableGotoNativeSearch`: If true, then swap to native vscode search if the custom suffix is entered using the current query. diff --git a/package.json b/package.json index ed93afd..f2fd30c 100644 --- a/package.json +++ b/package.json @@ -58,9 +58,20 @@ "type": "array", "default": [], "items": { - "type": "string" + "type": "object", + "properties": { + "label": { + "type": "string", + "description": "The label of the menu item to display in the menu." + }, + "value": { + "type": "string", + "description": "The value of ripgrep options you would like to include." + } + }, + "required": ["value"] }, - "description": "Create menu items which can be selected prior to any query, these items will be added to the ripgrep command to generate the results. Eg: Add `-t js` as a menu option to only show javascript files in the results." + "description": "Create menu items which can be selected prior to any query, these items will be added to the ripgrep command to generate the results. Eg: Add `{ label: \"JS/TS\", value: \"--type-add 'jsts:*.{js|ts|tsx|jsx}' -t jsts\" },` as a menu option to only show js & ts files in the results." }, "periscope.startFolderDisplayDepth": { "type": "number", diff --git a/src/periscope.ts b/src/periscope.ts index fec2908..1f4c859 100644 --- a/src/periscope.ts +++ b/src/periscope.ts @@ -5,7 +5,11 @@ import { getConfig } from './utils/getConfig'; import { getSelectedText } from './utils/getSelectedText'; import { highlightDecorationType } from './utils/decorationType'; -export interface QuickPickItemCustom extends vscode.QuickPickItem { +export interface QPItemDefault extends vscode.QuickPickItem { + _type: 'QuickPickItemDefault' +} +export interface QPItemQuery extends vscode.QuickPickItem { + _type: 'QuickPickItemQuery' // custom payload data: { filePath: string @@ -14,17 +18,26 @@ export interface QuickPickItemCustom extends vscode.QuickPickItem { rawResult: string } } +export interface QPItemRgMenuAction extends vscode.QuickPickItem { + _type: 'QuickPickItemRgMenuAction' + // custom payload + data: { + rgOption: string + } +} + +type AllQPItemVariants = QPItemDefault | QPItemQuery | QPItemRgMenuAction; export const periscope = () => { let activeEditor: vscode.TextEditor | undefined; - let quickPick: vscode.QuickPick; + let qp: vscode.QuickPick; let workspaceFolders = vscode.workspace.workspaceFolders; let query = ''; let highlightDecoration = highlightDecorationType(); - let spawnProcess: ChildProcessWithoutNullStreams | undefined; let config = getConfig(); let rgMenuActionsSelected: string[] = []; + let disposables: vscode.Disposable[] = []; function register() { setActiveContext(true); @@ -33,40 +46,56 @@ export const periscope = () => { workspaceFolders = vscode.workspace.workspaceFolders; activeEditor = vscode.window.activeTextEditor; // @see https://code.visualstudio.com/api/references/vscode-api#QuickPick - quickPick = vscode.window.createQuickPick(); - - quickPick.placeholder = '🫧'; + qp = vscode.window.createQuickPick(); // if ripgrep actions are available then open preliminary quickpick - config.rgMenuActions.length ? setupRgMenuActions(quickPick) : setupQuickPickForQuery(quickPick); + const rgMenuActionsExist = config.rgMenuActions.length > 0; + rgMenuActionsExist ? setupRgMenuActions(qp) : setupQuickPickForQuery(qp); - quickPick.onDidHide(onDidHide); - quickPick.show(); + disposables.push( + qp.onDidHide(onDidHide) + ); + qp.show(); } // when ripgrep actions are available show preliminary quickpick for those options to add to the query - function setupRgMenuActions(quickPick: vscode.QuickPick) { - quickPick.canSelectMany = true; - + function setupRgMenuActions(qp: vscode.QuickPick) { + qp.placeholder = '🫧 Rg Menu Actions (Space key to check/uncheck, Enter key to continue)'; + qp.canSelectMany = true; + // add items from the config - quickPick.items = config.rgMenuActions.map(item => ({ - label: item, - })); + qp.items = config.rgMenuActions.map(({value, label}) => ({ + _type: 'QuickPickItemRgMenuAction', + label: label || value, + description: label ? value : undefined, + data: { + rgOption: value, + } + }) + ); - quickPick.onDidAccept(() => { - rgMenuActionsSelected = quickPick.selectedItems.map(item => item.label); - setupQuickPickForQuery(quickPick); - }); + function next() { + rgMenuActionsSelected = (qp.selectedItems as QPItemRgMenuAction[]).map(item => item.data.rgOption); + setupQuickPickForQuery(qp as vscode.QuickPick); + } + + disposables.push( + qp.onDidTriggerButton(next), + qp.onDidAccept(next) + ); } // update quickpick event listeners for the query - function setupQuickPickForQuery(quickPick: vscode.QuickPick) { - quickPick.items = []; - quickPick.canSelectMany = false; - quickPick.value = getSelectedText(); - quickPick.onDidChangeValue(onDidChangeValue); - quickPick.onDidChangeActive(onDidChangeActive); - quickPick.onDidAccept(onDidAccept); + function setupQuickPickForQuery(qp: vscode.QuickPick) { + qp.placeholder = '🫧'; + qp.items = []; + qp.canSelectMany = false; + qp.value = getSelectedText(); + disposables.push( + qp.onDidChangeValue(onDidChangeValue), + qp.onDidChangeActive(onDidChangeActive), + qp.onDidAccept(onDidAccept) + ); } // create vscode context for the extension for targeted keybindings @@ -94,13 +123,13 @@ export const periscope = () => { search(value); } else { - quickPick.items = []; + qp.items = []; } } // when item is 'FOCUSSED' - function onDidChangeActive(items: readonly (vscode.QuickPickItem | QuickPickItemCustom)[]) { - peekItem(items as readonly QuickPickItemCustom[]); + function onDidChangeActive(items: readonly AllQPItemVariants[]) { + peekItem(items as readonly QPItemQuery[]); } // when item is 'SELECTED' @@ -110,7 +139,7 @@ export const periscope = () => { // when prompt is 'CANCELLED' function onDidHide() { - if (!quickPick.selectedItems[0]) { + if (!qp.selectedItems[0]) { if (activeEditor) { vscode.window.showTextDocument( activeEditor.document, @@ -123,7 +152,7 @@ export const periscope = () => { } function search(value: string) { - quickPick.busy = true; + qp.busy = true; const rgCmd = rgCommand(value); console.log('PERISCOPE: rgCmd:', rgCmd); @@ -143,7 +172,7 @@ export const periscope = () => { return; } if (code === 0 && searchResultLines.length) { - quickPick.items = searchResultLines + qp.items = searchResultLines .map(searchResult => { // break the filename via regext ':line:col:' const [filePath, linePos, colPos, ...textResult] = @@ -163,7 +192,7 @@ export const periscope = () => { searchResult ); }) - .filter(Boolean) as QuickPickItemCustom[]; + .filter(Boolean) as QPItemQuery[]; } else if (code === 127) { vscode.window.showErrorMessage( `Periscope: Exited with code ${code}, ripgrep not found.` @@ -175,7 +204,7 @@ export const periscope = () => { } else { vscode.window.showErrorMessage(`Ripgrep exited with code ${code}`); } - quickPick.busy = false; + qp.busy = false; }); } @@ -215,7 +244,7 @@ export const periscope = () => { return `rg '${value}' ${rgFlags.join(' ')}`; } - function peekItem(items: readonly QuickPickItemCustom[]) { + function peekItem(items: readonly QPItemQuery[]) { if (items.length === 0) { return; } @@ -234,13 +263,12 @@ export const periscope = () => { }) .then(editor => { setPos(editor, linePos, colPos); - }); }); } function accept() { - const currentItem = quickPick.selectedItems[0] as QuickPickItemCustom; + const currentItem = qp.selectedItems[0] as QPItemQuery; if (!currentItem.data) { return; } @@ -249,7 +277,7 @@ export const periscope = () => { vscode.workspace.openTextDocument(filePath).then(document => { vscode.window.showTextDocument(document).then(editor => { setPos(editor, linePos, colPos); - quickPick.dispose(); + qp.dispose(); }); }); } @@ -282,8 +310,9 @@ export const periscope = () => { linePos: number, colPos: number, rawResult?: string - ): QuickPickItemCustom { + ): QPItemQuery { return { + _type: 'QuickPickItemQuery', label: fileContents?.trim(), data: { filePath, @@ -345,13 +374,14 @@ export const periscope = () => { }); // close extension down - quickPick.hide(); + qp.hide(); } function finished() { checkKillProcess(); highlightDecoration.remove(); setActiveContext(false); + disposables.forEach(d => d.dispose()); console.log('PERISCOPE: finished'); } diff --git a/src/utils/getConfig.ts b/src/utils/getConfig.ts index 0baaf6e..e517468 100644 --- a/src/utils/getConfig.ts +++ b/src/utils/getConfig.ts @@ -24,7 +24,7 @@ export function getConfig() { ]), addSrcPaths: vsConfig.get('addSrcPaths', []), rgGlobExcludes: vsConfig.get('rgGlobExcludes', []), - rgMenuActions: vsConfig.get('rgMenuActions', []), + rgMenuActions: vsConfig.get<{label?: string, value: string}[]>('rgMenuActions', []), startFolderDisplayDepth: vsConfig.get('startFolderDisplayDepth', 1), endFolderDisplayDepth: vsConfig.get('endFolderDisplayDepth', 4), enableGotoNativeSearch: vsConfig.get(