Skip to content

Commit

Permalink
Support quick file-search with go to line
Browse files Browse the repository at this point in the history
The quick file-search text box (normally accessed via Ctrl + P)
now allows the option to append the input with ':' and a line number
to go to.

Signed-off-by: Alvaro Sanchez-Leon <alvaro.sanchez-leon@ericsson.com>
  • Loading branch information
alvsan09 committed May 13, 2021
1 parent 2eb4319 commit b8bc103
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 15 deletions.
52 changes: 52 additions & 0 deletions packages/file-search/src/browser/FileQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/********************************************************************************
* Copyright (C) 2017 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { Position } from '@theia/editor/lib/browser';
import { integer } from '@theia/core/shared/vscode-languageserver-types';
import { injectable } from '@theia/core/shared/inversify';

@injectable()
export class FileQuery {
private _filePattern: string = '';
private lineNumber: integer = 0;

/**
* Parses the query in its different components and returns the corresponding file pattern of the query
* e.g. /path/to/file:10, will parse the query and return /path/to/file.
*
* @param fileQuery a file query in the form "filePathPattern:location number".
*
* @returns the filePathPattern portion of the query.
*/
parseFileQuery(fileQuery: string): string {
const queryParts = fileQuery.split(':', 2);
this._filePattern = queryParts[0].trim();

const locationString = queryParts.length > 1 ? queryParts[1].trim() : '';
const locationNumber = Number.parseInt(locationString);
this.lineNumber = locationNumber && locationNumber > 1 ? locationNumber - 1 : 0;

return this.filePattern;
}

public get filePattern(): string {
return this._filePattern;
}

public get position(): Position {
return Position.create(this.lineNumber, 0);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { WebSocketConnectionProvider, KeybindingContribution, QuickOpenContribut
import { QuickFileOpenFrontendContribution } from './quick-file-open-contribution';
import { QuickFileOpenService } from './quick-file-open';
import { fileSearchServicePath, FileSearchService } from '../common/file-search-service';
import { FileQuery } from './FileQuery';

export default new ContainerModule((bind: interfaces.Bind) => {
bind(FileSearchService).toDynamicValue(ctx => {
Expand All @@ -33,4 +34,5 @@ export default new ContainerModule((bind: interfaces.Bind) => {
);

bind(QuickFileOpenService).toSelf().inSingletonScope();
bind(FileQuery).toSelf().inTransientScope();
});
37 changes: 22 additions & 15 deletions packages/file-search/src/browser/quick-file-open.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import { NavigationLocationService } from '@theia/editor/lib/browser/navigation/
import * as fuzzy from '@theia/core/shared/fuzzy';
import { MessageService } from '@theia/core/lib/common/message-service';
import { FileSystemPreferences } from '@theia/filesystem/lib/browser';
import { EditorOpenerOptions } from '@theia/editor/lib/browser';
import { FileQuery } from './FileQuery';

export const quickFileOpen: Command = {
id: 'file-search.openFile',
Expand Down Expand Up @@ -58,6 +60,8 @@ export class QuickFileOpenService implements QuickOpenModel, QuickOpenHandler {
protected readonly messageService: MessageService;
@inject(FileSystemPreferences)
protected readonly fsPreferences: FileSystemPreferences;
@inject(FileQuery)
protected readonly currentFileQuery: FileQuery;

/**
* Whether to hide .gitignored (and other ignored) files.
Expand All @@ -69,11 +73,6 @@ export class QuickFileOpenService implements QuickOpenModel, QuickOpenHandler {
*/
protected isOpen: boolean = false;

/**
* The current lookFor string input by the user.
*/
protected currentLookFor: string = '';

/**
* The score constants when comparing file search results.
*/
Expand All @@ -94,7 +93,7 @@ export class QuickFileOpenService implements QuickOpenModel, QuickOpenHandler {
}

getOptions(): QuickOpenOptions {
let placeholder = 'File name to search.';
let placeholder = 'File name to search (append : to go to line).';
const keybinding = this.getKeyCommand();
if (keybinding) {
placeholder += ` (Press ${keybinding} to show/hide ignored files)`;
Expand Down Expand Up @@ -126,11 +125,11 @@ export class QuickFileOpenService implements QuickOpenModel, QuickOpenHandler {
this.hideIgnoredFiles = !this.hideIgnoredFiles;
} else {
this.hideIgnoredFiles = true;
this.currentLookFor = '';
this.currentFileQuery.parseFileQuery('');
this.isOpen = true;
}

this.quickOpenService.open(this.currentLookFor);
this.quickOpenService.open(this.currentFileQuery.filePattern);
}

/**
Expand All @@ -157,20 +156,21 @@ export class QuickFileOpenService implements QuickOpenModel, QuickOpenHandler {

const roots = this.workspaceService.tryGetRoots();

this.currentLookFor = lookFor;
const filePattern = this.currentFileQuery.parseFileQuery(lookFor);

const alreadyCollected = new Set<string>();
const recentlyUsedItems: QuickOpenItem[] = [];

const locations = [...this.navigationLocationService.locations()].reverse();
for (const location of locations) {
const uriString = location.uri.toString();
if (location.uri.scheme === 'file' && !alreadyCollected.has(uriString) && fuzzy.test(lookFor, uriString)) {
if (location.uri.scheme === 'file' && !alreadyCollected.has(uriString) && fuzzy.test(filePattern, uriString)) {
const item = this.toItem(location.uri, { groupLabel: recentlyUsedItems.length === 0 ? 'recently opened' : undefined, showBorder: false });
recentlyUsedItems.push(item);
alreadyCollected.add(uriString);
}
}
if (lookFor.length > 0) {
if (filePattern.length > 0) {
const handler = async (results: string[]) => {
if (token.isCancellationRequested) {
return;
Expand Down Expand Up @@ -205,7 +205,7 @@ export class QuickFileOpenService implements QuickOpenModel, QuickOpenHandler {
acceptor([...recentlyUsedItems, ...sortedResults]);
};

this.fileSearchService.find(lookFor, {
this.fileSearchService.find(filePattern, {
rootUris: roots.map(r => r.resource.toString()),
fuzzyMatch: true,
limit: 200,
Expand Down Expand Up @@ -254,7 +254,7 @@ export class QuickFileOpenService implements QuickOpenModel, QuickOpenHandler {
}

// Normalize the user query.
const query: string = normalize(this.currentLookFor);
const query: string = normalize(this.currentFileQuery.filePattern);

/**
* Score a given string.
Expand Down Expand Up @@ -347,11 +347,18 @@ export class QuickFileOpenService implements QuickOpenModel, QuickOpenHandler {
}

openFile(uri: URI): void {
this.openerService.getOpener(uri)
.then(opener => opener.open(uri))
const options = this.buildOpenerOptions();
const resolvedOpener = this.openerService.getOpener(uri, options);
resolvedOpener
.then(opener => opener.open(uri, options))
.catch(error => this.messageService.error(error));
}

protected buildOpenerOptions(): EditorOpenerOptions {
const position = this.currentFileQuery.position;
return { selection: { start: position, end: position } };
}

private toItem(uriOrString: URI | string, group?: QuickOpenGroupItemOptions): QuickOpenItem<QuickOpenItemOptions> {
const uri = uriOrString instanceof URI ? uriOrString : new URI(uriOrString);
let description = this.labelProvider.getLongName(uri.parent);
Expand Down

0 comments on commit b8bc103

Please sign in to comment.