提交 07271b84 编写于 作者: R Rob Lourens

Delete search-rg in favor of internal search

上级 a32b1088
build/**
src/**
test/**
tsconfig.json
out/**
extension.webpack.config.js
yarn.lock
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const withDefaults = require('../shared.webpack.config');
module.exports = withDefaults({
context: __dirname,
entry: {
extension: './src/extension.ts'
},
externals: {
'vscode-ripgrep': 'commonjs vscode-ripgrep'
}
});
{
"name": "search-rg",
"description": "%description%",
"displayName": "%displayName%",
"version": "1.0.0",
"author": "vscode",
"publisher": "vscode",
"license": "MIT",
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
"enableProposedApi": true,
"engines": {
"vscode": "*"
},
"categories": [],
"dependencies": {
"vscode-extension-telemetry": "0.0.22",
"vscode-nls": "^4.0.0",
"vscode-ripgrep": "^1.2.2"
},
"devDependencies": {
"@types/node": "8.0.33",
"@types/semver": "5.4.0",
"vscode": "^1.1.17"
},
"scripts": {},
"activationEvents": [
"onSearch:file",
"*"
],
"main": "./out/extension",
"contributes": {
"configuration": {
"title": "Search (ripgrep)",
"properties": {
"searchRipgrep.enable": {
"type": "boolean",
"default": false,
"scope": "window",
"description": "%searchRipgrep.enable.description%"
}
}
}
}
}
{
"displayName": "Search (ripgrep)",
"description": "Provides search using Ripgrep.",
"searchRipgrep.enable.description": "(Experimental) Enable this experimental search provider extension. When enabled, it takes precedence over vscode's built-in search."
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 { RipgrepFileSearchEngine } from './ripgrepFileSearch';
export function activate(): void {
// if (vscode.workspace.getConfiguration('searchRipgrep').get('enable')) {
// const outputChannel = vscode.window.createOutputChannel('search-rg');
// const provider = new RipgrepSearchProvider(outputChannel);
// vscode.workspace.registerFileIndexProvider('file', provider);
// }
}
class RipgrepSearchProvider implements vscode.FileIndexProvider {
private inProgress: Set<vscode.CancellationTokenSource> = new Set();
constructor(private outputChannel: vscode.OutputChannel) {
process.once('exit', () => this.dispose());
}
provideFileIndex(options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable<vscode.Uri[]> {
const engine = new RipgrepFileSearchEngine(this.outputChannel);
const results: vscode.Uri[] = [];
const onResult = (relativePathMatch: string) => {
results.push(vscode.Uri.file(options.folder.fsPath + '/' + relativePathMatch));
};
return this.withToken(token, token => engine.provideFileSearchResults(options, { report: onResult }, token))
.then(() => results);
}
private async withToken<T>(token: vscode.CancellationToken, fn: (token: vscode.CancellationToken) => Thenable<T>): Promise<T> {
const merged = mergedTokenSource(token);
this.inProgress.add(merged);
const result = await fn(merged.token);
this.inProgress.delete(merged);
return result;
}
private dispose() {
this.inProgress.forEach(engine => engine.cancel());
}
}
function mergedTokenSource(token: vscode.CancellationToken): vscode.CancellationTokenSource {
const tokenSource = new vscode.CancellationTokenSource();
token.onCancellationRequested(() => tokenSource.cancel());
return tokenSource;
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/**
* The normalize() method returns the Unicode Normalization Form of a given string. The form will be
* the Normalization Form Canonical Composition.
*
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize}
*/
export const canNormalize = typeof ((<any>'').normalize) === 'function';
export function normalizeNFC(str: string): string {
return normalize(str, 'NFC');
}
export function normalizeNFD(str: string): string {
return normalize(str, 'NFD');
}
const nonAsciiCharactersPattern = /[^\u0000-\u0080]/;
function normalize(str: string, form: string): string {
if (!canNormalize || !str) {
return str;
}
let res: string;
if (nonAsciiCharactersPattern.test(str)) {
res = (<any>str).normalize(form);
} else {
res = str;
}
return res;
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { rgPath } from 'vscode-ripgrep';
export { rgPath };
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as cp from 'child_process';
import { Readable } from 'stream';
import { NodeStringDecoder, StringDecoder } from 'string_decoder';
import * as vscode from 'vscode';
import { normalizeNFC, normalizeNFD } from './normalization';
import { rgPath } from './ripgrep';
import { anchorGlob, Maybe } from './utils';
const isMac = process.platform === 'darwin';
// If vscode-ripgrep is in an .asar file, then the binary is unpacked.
const rgDiskPath = rgPath.replace(/\bnode_modules\.asar\b/, 'node_modules.asar.unpacked');
export class RipgrepFileSearchEngine {
constructor(private outputChannel: vscode.OutputChannel) { }
provideFileSearchResults(options: vscode.FileSearchOptions, progress: vscode.Progress<string>, token: vscode.CancellationToken): Thenable<void> {
this.outputChannel.appendLine(`provideFileSearchResults ${JSON.stringify({
...options,
...{
folder: options.folder.toString()
}
})}`);
return new Promise((resolve, reject) => {
let isDone = false;
const cancel = () => {
isDone = true;
if (rgProc) {
rgProc.kill();
}
};
token.onCancellationRequested(() => cancel());
const rgArgs = getRgArgs(options);
const cwd = options.folder.fsPath;
const escapedArgs = rgArgs
.map(arg => arg.match(/^-/) ? arg : `'${arg}'`)
.join(' ');
this.outputChannel.appendLine(`rg ${escapedArgs}\n - cwd: ${cwd}\n`);
let rgProc: Maybe<cp.ChildProcess> = cp.spawn(rgDiskPath, rgArgs, { cwd });
rgProc.on('error', e => {
console.log(e);
reject(e);
});
let leftover = '';
this.collectStdout(rgProc, (err, stdout, last) => {
if (err) {
reject(err);
return;
}
// Mac: uses NFD unicode form on disk, but we want NFC
const normalized = leftover + (isMac ? normalizeNFC(stdout || '') : stdout);
const relativeFiles = normalized.split('\n');
if (last) {
const n = relativeFiles.length;
relativeFiles[n - 1] = relativeFiles[n - 1].trim();
if (!relativeFiles[n - 1]) {
relativeFiles.pop();
}
} else {
leftover = <string>relativeFiles.pop();
}
if (relativeFiles.length && relativeFiles[0].indexOf('\n') !== -1) {
reject(new Error('Splitting up files failed'));
return;
}
relativeFiles.forEach(relativeFile => {
progress.report(relativeFile);
});
if (last) {
if (isDone) {
resolve();
} else {
// Trigger last result
rgProc = null;
if (err) {
reject(err);
} else {
resolve();
}
}
}
});
});
}
private collectStdout(cmd: cp.ChildProcess, cb: (err?: Error, stdout?: string, last?: boolean) => void): void {
let onData = (err?: Error, stdout?: string, last?: boolean) => {
if (err || last) {
onData = () => { };
}
cb(err, stdout, last);
};
let gotData = false;
if (cmd.stdout) {
// Should be non-null, but #38195
this.forwardData(cmd.stdout, onData);
cmd.stdout.once('data', () => gotData = true);
} else {
this.outputChannel.appendLine('stdout is null');
}
let stderr: Buffer[];
if (cmd.stderr) {
// Should be non-null, but #38195
stderr = this.collectData(cmd.stderr);
} else {
this.outputChannel.appendLine('stderr is null');
}
cmd.on('error', (err: Error) => {
onData(err);
});
cmd.on('close', (code: number) => {
// ripgrep returns code=1 when no results are found
let stderrText, displayMsg: Maybe<string>;
if (!gotData && (stderrText = this.decodeData(stderr)) && (displayMsg = rgErrorMsgForDisplay(stderrText))) {
onData(new Error(`command failed with error code ${code}: ${displayMsg}`));
} else {
onData(undefined, '', true);
}
});
}
private forwardData(stream: Readable, cb: (err?: Error, stdout?: string) => void): NodeStringDecoder {
const decoder = new StringDecoder();
stream.on('data', (data: Buffer) => {
cb(undefined, decoder.write(data));
});
return decoder;
}
private collectData(stream: Readable): Buffer[] {
const buffers: Buffer[] = [];
stream.on('data', (data: Buffer) => {
buffers.push(data);
});
return buffers;
}
private decodeData(buffers: Buffer[]): string {
const decoder = new StringDecoder();
return buffers.map(buffer => decoder.write(buffer)).join('');
}
}
function getRgArgs(options: vscode.FileSearchOptions): string[] {
const args = ['--files', '--hidden', '--case-sensitive'];
options.includes.forEach(globArg => {
const inclusion = anchorGlob(globArg);
args.push('-g', inclusion);
if (isMac) {
const normalized = normalizeNFD(inclusion);
if (normalized !== inclusion) {
args.push('-g', normalized);
}
}
});
options.excludes.forEach(globArg => {
const exclusion = `!${anchorGlob(globArg)}`;
args.push('-g', exclusion);
if (isMac) {
const normalized = normalizeNFD(exclusion);
if (normalized !== exclusion) {
args.push('-g', normalized);
}
}
});
if (options.useIgnoreFiles) {
args.push('--no-ignore-parent');
} else {
// Don't use .gitignore or .ignore
args.push('--no-ignore');
}
// Follow symlinks
if (options.followSymlinks) {
args.push('--follow');
}
args.push('--no-config');
if (!options.useGlobalIgnoreFiles) {
args.push('--no-ignore-global');
}
return args;
}
/**
* Read the first line of stderr and return an error for display or undefined, based on a whitelist.
* Ripgrep produces stderr output which is not from a fatal error, and we only want the search to be
* "failed" when a fatal error was produced.
*/
export function rgErrorMsgForDisplay(msg: string): Maybe<string> {
const firstLine = msg.split('\n')[0].trim();
if (firstLine.startsWith('Error parsing regex')) {
return firstLine;
}
if (firstLine.startsWith('error parsing glob') ||
firstLine.startsWith('unsupported encoding')) {
// Uppercase first letter
return firstLine.charAt(0).toUpperCase() + firstLine.substr(1);
}
if (firstLine === `Literal '\\n' not allowed.`) {
// I won't localize this because none of the Ripgrep error messages are localized
return `Literal '\\n' currently not supported`;
}
if (firstLine.startsWith('Literal ')) {
// Other unsupported chars
return firstLine;
}
return undefined;
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//
// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING
//
// This file is providing the test runner to use when running extension tests.
// By default the test runner in use is Mocha based.
//
// You can provide your own test runner if you want to override it by exporting
// a function run(testRoot: string, clb: (error:Error) => void) that the extension
// host can call to run the tests. The test runner is expected to use console.log
// to report the results back to the caller. When the tests are finished, return
// a possible error to the callback or null if none.
const testRunner = require('vscode/lib/testrunner');
// You can directly control Mocha options by uncommenting the following lines
// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
testRunner.configure({
ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
useColors: process.platform !== 'win32', // colored output from test results (only windows cannot handle)
timeout: 60000
});
export = testRunner;
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'mocha';
// TODO./
// import * as assert from 'assert';
// import * as vscode from 'vscode';
// import * as path from 'path';
// import { createTextSearchResult } from '../utils';
// function createOneLineRange(lineNumber: number, startCol: number, endCol: number): vscode.Range {
// return new vscode.Range(lineNumber, startCol, lineNumber, endCol);
// }
// const uri = vscode.Uri.file('/foo/bar');
suite('search-rg', () => {
// Duplicate code is tested in search.test.ts
// const previewOptions1: vscode.TextSearchPreviewOptions = {
// maxLines: 1,
// totalChars: 100
// };
// test('empty', () => {
// assert.deepEqual(
// createTextSearchResult(uri, '', createOneLineRange(5, 0, 0)),
// <vscode.TextSearchResult>{
// preview: {
// text: '',
// match: createOneLineRange(0, 0, 0)
// },
// range: createOneLineRange(5, 0, 0),
// uri
// });
// assert.deepEqual(
// createTextSearchResult(uri, '', createOneLineRange(5, 0, 0), previewOptions1),
// <vscode.TextSearchResult>{
// preview: {
// text: '',
// match: createOneLineRange(0, 0, 0)
// },
// range: createOneLineRange(5, 0, 0),
// uri
// });
// });
// test('short', () => {
// assert.deepEqual(
// createTextSearchResult(uri, 'foo bar', createOneLineRange(5, 4, 7)),
// <vscode.TextSearchResult>{
// preview: {
// text: 'foo bar',
// match: createOneLineRange(0, 4, 7)
// },
// range: createOneLineRange(5, 4, 7),
// uri
// });
// assert.deepEqual(
// createTextSearchResult(uri, 'foo bar', createOneLineRange(5, 4, 7), previewOptions1),
// <vscode.TextSearchResult>{
// preview: {
// text: 'foo bar',
// match: createOneLineRange(0, 4, 7)
// },
// range: createOneLineRange(5, 4, 7),
// uri
// });
// });
// test('leading', () => {
// assert.deepEqual(
// createTextSearchResult(uri, 'long text very long text foo', createOneLineRange(5, 25, 28), previewOptions1),
// <vscode.TextSearchResult>{
// preview: {
// text: 'long text foo',
// match: createOneLineRange(0, 10, 13)
// },
// range: createOneLineRange(5, 25, 28),
// uri
// });
// });
// test('trailing', () => {
// assert.deepEqual(
// createTextSearchResult(uri, 'foo long text very long text long text very long text long text very long text long text very long text long text very long text', createOneLineRange(5, 0, 3), previewOptions1),
// <vscode.TextSearchResult>{
// preview: {
// text: 'foo long text very long text long text very long text long text very long text long text very long t',
// match: createOneLineRange(0, 0, 3)
// },
// range: createOneLineRange(5, 0, 3),
// uri
// });
// });
// test('middle', () => {
// assert.deepEqual(
// createTextSearchResult(uri, 'long text very long text long foo text very long text long text very long text long text very long text long text very long text', createOneLineRange(5, 30, 33), previewOptions1),
// <vscode.TextSearchResult>{
// preview: {
// text: 'text long foo text very long text long text very long text long text very long text long text very l',
// match: createOneLineRange(0, 10, 13)
// },
// range: createOneLineRange(5, 30, 33),
// uri
// });
// });
// test('truncating match', () => {
// const previewOptions: vscode.TextSearchPreviewOptions = {
// leadingChars: 4,
// maxLines: 1,
// totalChars: 5
// };
// assert.deepEqual(
// createTextSearchResult(uri, 'foo bar', createOneLineRange(0, 4, 7), previewOptions),
// <vscode.TextSearchResult>{
// preview: {
// text: 'foo b',
// match: createOneLineRange(0, 4, 5)
// },
// range: createOneLineRange(0, 4, 7),
// uri
// });
// });
});
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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'/>
/// <reference types='@types/node'/>
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import * as vscode from 'vscode';
export type Maybe<T> = T | null | undefined;
export function fixDriveC(_path: string): string {
const root = path.parse(_path).root;
return root.toLowerCase() === 'c:/' ?
_path.replace(/^c:[/\\]/i, '/') :
_path;
}
export function anchorGlob(glob: string): string {
return glob.startsWith('**') || glob.startsWith('/') ? glob : `/${glob}`;
}
export function createTextSearchResult(uri: vscode.Uri, fullText: string, range: vscode.Range, previewOptions?: vscode.TextSearchPreviewOptions): vscode.TextSearchResult {
let preview: vscode.TextSearchResultPreview;
if (previewOptions) {
const leadingChars = Math.floor(previewOptions.charsPerLine / 5);
const previewStart = Math.max(range.start.character - leadingChars, 0);
const previewEnd = previewOptions.charsPerLine + previewStart;
const endOfMatchRangeInPreview = Math.min(previewEnd, range.end.character - previewStart);
preview = {
text: fullText.substring(previewStart, previewEnd),
match: new vscode.Range(0, range.start.character - previewStart, 0, endOfMatchRangeInPreview)
};
} else {
preview = {
text: fullText,
match: new vscode.Range(0, range.start.character, 0, range.end.character)
};
}
return <vscode.TextSearchResult>{
uri,
range,
preview
};
}
{
"extends": "../shared.tsconfig.json",
"compilerOptions": {
"outDir": "./out",
"experimentalDecorators": true
},
"include": [
"src/**/*"
]
}
此差异已折叠。
......@@ -17,7 +17,6 @@ cd $ROOT
./scripts/code.sh $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
./scripts/code.sh $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
./scripts/code.sh $ROOT/extensions/markdown-language-features/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
./scripts/code.sh $ROOT/extensions/search-rg/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/search-rg --extensionTestsPath=$ROOT/extensions/search-rg/out/test --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
mkdir $ROOT/extensions/emmet/test-fixtures
./scripts/code.sh $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started .
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册