Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Search result navigation with next and previous focus #12714

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,16 @@ export namespace SearchInWorkspaceCommands {
export const REPLACE_ALL_RESULTS = Command.toDefaultLocalizedCommand({
id: 'search.action.replaceAll'
});
export const NEXT_MATCH = Command.toDefaultLocalizedCommand({
id: 'search.action.nextMatchFindAction',
category: SEARCH_CATEGORY,
label: 'Focus Next Search Result',
});
export const PREVIOUS_MATCH = Command.toDefaultLocalizedCommand({
id: 'search.action.previousMatchFindAction',
category: SEARCH_CATEGORY,
label: 'Focus Previous Search Result',
});
}

@injectable()
Expand Down Expand Up @@ -262,6 +272,16 @@ export class SearchInWorkspaceFrontendContribution extends AbstractViewContribut
}
}),
});
commands.registerCommand(SearchInWorkspaceCommands.NEXT_MATCH, {
isEnabled: () => this.withWidget(undefined, widget => widget.hasResultList()),
isVisible: () => this.withWidget(undefined, widget => widget.hasResultList()),
execute: () => this.withWidget(undefined, widget => widget.resultTreeWidget.focusNextResult())
});
commands.registerCommand(SearchInWorkspaceCommands.PREVIOUS_MATCH, {
isEnabled: () => this.withWidget(undefined, widget => widget.hasResultList()),
isVisible: () => this.withWidget(undefined, widget => widget.hasResultList()),
execute: () => this.withWidget(undefined, widget => widget.resultTreeWidget.focusPreviousResult())
});
commands.registerCommand(SearchInWorkspaceCommands.COPY_ONE, {
isEnabled: () => this.withWidget(undefined, widget => {
const { selection } = this.selectionService;
Expand Down Expand Up @@ -343,6 +363,16 @@ export class SearchInWorkspaceFrontendContribution extends AbstractViewContribut
keybinding: 'shift+alt+f',
when: 'explorerResourceIsFolder'
});
keybindings.registerKeybindings({
command: SearchInWorkspaceCommands.NEXT_MATCH.id,
keybinding: 'f4',
when: 'searchViewletFocus'
});
keybindings.registerKeybinding({
command: SearchInWorkspaceCommands.PREVIOUS_MATCH.id,
keybinding: 'shift+f4',
when: 'searchViewletFocus'
});
keybindings.registerKeybinding({
command: SearchInWorkspaceCommands.DISMISS_RESULT.id,
keybinding: isOSX ? 'cmd+backspace' : 'del',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -589,16 +589,114 @@ export class SearchInWorkspaceResultTreeWidget extends TreeWidget {
});
}

/**
* Selects and focuses the first result in the result tree.
*/
focusFirstResult(): void {
if (SearchInWorkspaceRoot.is(this.model.root) && this.model.root.children.length > 0) {
const node = this.model.root.children[0];
if (SelectableTreeNode.is(node)) {
this.node.focus();
this.model.selectNode(node);
for (const rootFolderNode of this.resultTree.values()) {
for (const fileNode of rootFolderNode.children) {
for (const line of fileNode.children) {
if (SearchInWorkspaceResultLineNode.is(line)) {
this.model.selectNode(line);
this.node.focus();
return;
}
}
}
}
}

/**
* Selects and focuses the last result in the result tree.
*/
focusLastResult(): void {
for (const rootFolderNode of this.resultTree.values()) {
for (let i = rootFolderNode.children.length - 1; i >= 0; i--) {
const fileNode = rootFolderNode.children[i];
for (let j = fileNode.children.length - 1; j >= 0; j--) {
const line = fileNode.children[j];
if (SearchInWorkspaceResultLineNode.is(line)) {
this.model.selectNode(line);
this.node.focus();
return;
}
}
}
}
}

/**
* Selects and focuses the next result in the result tree.
* If there is no next result, the first result is focused.
*/
focusNextResult(): void {
const focusedResultLineNode = this.model.getFocusedNode();
if (!focusedResultLineNode) {
return this.focusFirstResult();
}

let foundFocusedResultLineNode = false;
for (const rootFolderNode of this.resultTree.values()) {
for (const fileNode of rootFolderNode.children) {
for (const line of fileNode.children) {
if (SearchInWorkspaceResultLineNode.is(line)) {
if (foundFocusedResultLineNode) {
this.expansionService.expandNode(fileNode);
this.model.selectNode(line);
this.node.focus();
return;
}
if (line === focusedResultLineNode) {
foundFocusedResultLineNode = true;
}
}
}
}
}

// the focused node is not in the result tree
// or there is no next result
this.focusFirstResult();
}

/**
* Selects and focuses the previous result in the result tree.
* If there is no previous result, the last result is focused.
*/
focusPreviousResult(): void {
const focusedResultLineNode = this.model.getFocusedNode();
if (!focusedResultLineNode) {
return this.focusLastResult();
}

let foundFocusedResultLineNode = false;
const rootFolderNodes = Array.from(this.resultTree.values());
for (let i = rootFolderNodes.length - 1; i >= 0; i--) {
const rootFolderNode = rootFolderNodes[i];
for (let j = rootFolderNode.children.length - 1; j >= 0; j--) {
const fileNode = rootFolderNode.children[j];
for (let k = fileNode.children.length - 1; k >= 0; k--) {
const line = fileNode.children[k];
if (SearchInWorkspaceResultLineNode.is(line)) {
if (foundFocusedResultLineNode) {
this.expansionService.expandNode(fileNode);
this.model.selectNode(line);
this.node.focus();
return;
}
if (line === focusedResultLineNode) {
foundFocusedResultLineNode = true;
}
}
}
}
}

// the focused node is not in the result tree
// or there is no previous result
this.focusLastResult();
}

/**
* Collapse the search-in-workspace file node
* based on the preference value.
Expand Down