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

Add initial search editor implementation #85424

Merged
merged 9 commits into from Nov 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions extensions/search-result/README.md
@@ -0,0 +1,3 @@
# Language Features for Search Result files

**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled.
41 changes: 41 additions & 0 deletions extensions/search-result/package.json
@@ -0,0 +1,41 @@
{
"name": "search-result",
"displayName": "%displayName%",
"description": "%description%",
"version": "1.0.0",
"publisher": "vscode",
"license": "MIT",
"engines": {
"vscode": "^1.39.0"
},
"categories": [
"Programming Languages"
],
"main": "out/extension.js",
"activationEvents": [
"*"
],
"contributes": {
"languages": [
{
"id": "search-result",
"extensions": [
".code-search"
],
"aliases": [
"Search Result"
]
}
],
"grammars": [
{
"language": "search-result",
"scopeName": "text.searchResult",
"path": "./syntaxes/searchResult.tmLanguage.json"
}
]
},
"devDependencies": {
"vscode": "^1.1.36"
}
}
4 changes: 4 additions & 0 deletions extensions/search-result/package.nls.json
@@ -0,0 +1,4 @@
{
"displayName": "Search Result",
"description": "Provides syntax highlighting and language features for tabbed search results."
}
143 changes: 143 additions & 0 deletions extensions/search-result/src/extension.ts
@@ -0,0 +1,143 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as vscode from 'vscode';
import * as pathUtils from 'path';

const FILE_LINE_REGEX = /^(\S.*):$/;
const RESULT_LINE_REGEX = /^(\s+)(\d+):(\s+)(.*)$/;

let cachedLastParse: { version: number, parse: ParsedSearchResults } | undefined;

export function activate() {

vscode.languages.registerDefinitionProvider({ language: 'search-result' }, {
provideDefinition(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): vscode.DefinitionLink[] {
const lineResult = parseSearchResults(document, token)[position.line];
if (!lineResult) { return []; }
if (lineResult.type === 'file') {
return lineResult.allLocations.length > 1 ? lineResult.allLocations : [lineResult.location];
}

return [lineResult.location];
}
});

vscode.languages.registerDocumentLinkProvider({ language: 'search-result' }, {
async provideDocumentLinks(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<vscode.DocumentLink[]> {
return parseSearchResults(document, token)
.filter(({ type }) => type === 'file')
.map(({ location }) => ({ range: location.originSelectionRange!, target: location.targetUri }));
}
});

vscode.window.onDidChangeActiveTextEditor(e => {
if (e?.document.languageId === 'search-result') {
// Clear the parse whenever we open a new editor.
// Conservative because things like the URI might remain constant even if the contents change, and re-parsing even large files is relatively fast.
cachedLastParse = undefined;
}
});
}


function relativePathToUri(path: string, resultsUri: vscode.Uri): vscode.Uri | undefined {
if (pathUtils.isAbsolute(path)) { return vscode.Uri.file(path); }
if (path.indexOf('~/') === 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should probably be formatted with \ on windows

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ~/? That's a Mac/Linux-specific thing, the Path's don't get prefixed on Windows.

return vscode.Uri.file(pathUtils.join(process.env.HOME!, path.slice(2)));
}


if (vscode.workspace.workspaceFolders) {
const multiRootFormattedPath = /^(.*) • (.*)$/.exec(path);
if (multiRootFormattedPath) {
const [, workspaceName, workspacePath] = multiRootFormattedPath;
const folder = vscode.workspace.workspaceFolders.filter(wf => wf.name === workspaceName)[0];
if (folder) {
return vscode.Uri.file(pathUtils.join(folder.uri.fsPath, workspacePath));
}
}

else if (vscode.workspace.workspaceFolders.length === 1) {
return vscode.Uri.file(pathUtils.join(vscode.workspace.workspaceFolders[0].uri.fsPath, path));
} else if (resultsUri.scheme !== 'untitled') {
// We're in a multi-root workspace, but the path is not multi-root formatted
// Possibly a saved search from a single root session. Try checking if the search result document's URI is in a current workspace folder.
const prefixMatch = vscode.workspace.workspaceFolders.filter(wf => resultsUri.toString().startsWith(wf.uri.toString()))[0];
if (prefixMatch) { return vscode.Uri.file(pathUtils.join(prefixMatch.uri.fsPath, path)); }
}
}

console.error(`Unable to resolve path ${path}`);
return undefined;
}

type ParsedSearchResults = Array<
{ type: 'file', location: vscode.LocationLink, allLocations: vscode.LocationLink[] } |
{ type: 'result', location: vscode.LocationLink }
>;

function parseSearchResults(document: vscode.TextDocument, token: vscode.CancellationToken): ParsedSearchResults {

if (cachedLastParse && cachedLastParse.version === document.version) {
return cachedLastParse.parse;
}

const lines = document.getText().split(/\r?\n/);
const links: ParsedSearchResults = [];

let currentTarget: vscode.Uri | undefined = undefined;
let currentTargetLocations: vscode.LocationLink[] | undefined = undefined;

for (let i = 0; i < lines.length; i++) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this be slow, especially if we increase the 10000 result limit? Maybe as an optimization later this could be done more lazily.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to iterate over the entire document anyways for the file name lines for the link provider, and by parsing the result lines too we can add them as targets to the file name definition provider. So for now I think this is fine, but if yeah if we increse the limit later that could be worth looking into.

if (token.isCancellationRequested) { return []; }
const line = lines[i];

const fileLine = FILE_LINE_REGEX.exec(line);
if (fileLine) {
const [, path] = fileLine;

currentTarget = relativePathToUri(path, document.uri);
if (!currentTarget) { continue; }
currentTargetLocations = [];

const location: vscode.LocationLink = {
targetRange: new vscode.Range(0, 0, 0, 1),
targetUri: currentTarget,
originSelectionRange: new vscode.Range(i, 0, i, line.length),
};


links[i] = { type: 'file', location, allLocations: currentTargetLocations };
}

if (!currentTarget) { continue; }

const resultLine = RESULT_LINE_REGEX.exec(line);
if (resultLine) {
const [, indentation, _lineNumber, resultIndentation] = resultLine;
const lineNumber = +_lineNumber - 1;
const resultStart = (indentation + _lineNumber + ':' + resultIndentation).length;

const location: vscode.LocationLink = {
targetRange: new vscode.Range(Math.max(lineNumber - 3, 0), 0, lineNumber + 3, line.length),
targetSelectionRange: new vscode.Range(lineNumber, 0, lineNumber, line.length),
targetUri: currentTarget,
originSelectionRange: new vscode.Range(i, resultStart, i, line.length),
};

currentTargetLocations?.push(location);

links[i] = { type: 'result', location };
}
}

cachedLastParse = {
version: document.version,
parse: links
};

return links;
}
7 changes: 7 additions & 0 deletions extensions/search-result/src/typings/refs.d.ts
@@ -0,0 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

/// <reference path='../../../../src/vs/vscode.d.ts'/>
/// <reference path='../../../../src/vs/vscode.proposed.d.ts'/>
18 changes: 18 additions & 0 deletions extensions/search-result/syntaxes/searchResult.tmLanguage.json
@@ -0,0 +1,18 @@
{
"name": "Search Results",
"scopeName": "text.searchResult",
"patterns": [
{
"match": "^# (Query|Flags|Include|Exclude): .*$",
"name": "comment"
},
{
"match": "^\\S.*:$",
"name": "string path.searchResult"
},
{
"match": "^ \\d+",
"name": "constant.numeric lineNumber.searchResult"
}
]
}
9 changes: 9 additions & 0 deletions extensions/search-result/tsconfig.json
@@ -0,0 +1,9 @@
{
"extends": "../shared.tsconfig.json",
"compilerOptions": {
"outDir": "./out",
},
"include": [
"src/**/*"
]
}