mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-27 15:20:30 -04:00
Vscode merge (#4582)
* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd * fix issues with merges * bump node version in azpipe * replace license headers * remove duplicate launch task * fix build errors * fix build errors * fix tslint issues * working through package and linux build issues * more work * wip * fix packaged builds * working through linux build errors * wip * wip * wip * fix mac and linux file limits * iterate linux pipeline * disable editor typing * revert series to parallel * remove optimize vscode from linux * fix linting issues * revert testing change * add work round for new node * readd packaging for extensions * fix issue with angular not resolving decorator dependencies
This commit is contained in:
193
src/vs/workbench/services/search/common/replace.ts
Normal file
193
src/vs/workbench/services/search/common/replace.ts
Normal file
@@ -0,0 +1,193 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { IPatternInfo } from 'vs/workbench/services/search/common/search';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
|
||||
export class ReplacePattern {
|
||||
|
||||
private _replacePattern: string;
|
||||
private _hasParameters: boolean = false;
|
||||
private _regExp: RegExp;
|
||||
|
||||
constructor(replaceString: string, searchPatternInfo: IPatternInfo)
|
||||
constructor(replaceString: string, parseParameters: boolean, regEx: RegExp)
|
||||
constructor(replaceString: string, arg2: any, arg3?: any) {
|
||||
this._replacePattern = replaceString;
|
||||
let searchPatternInfo: IPatternInfo;
|
||||
let parseParameters: boolean;
|
||||
if (typeof arg2 === 'boolean') {
|
||||
parseParameters = arg2;
|
||||
this._regExp = arg3;
|
||||
|
||||
} else {
|
||||
searchPatternInfo = arg2;
|
||||
parseParameters = !!searchPatternInfo.isRegExp;
|
||||
this._regExp = strings.createRegExp(searchPatternInfo.pattern, !!searchPatternInfo.isRegExp, { matchCase: searchPatternInfo.isCaseSensitive, wholeWord: searchPatternInfo.isWordMatch, multiline: searchPatternInfo.isMultiline, global: false });
|
||||
}
|
||||
|
||||
if (parseParameters) {
|
||||
this.parseReplaceString(replaceString);
|
||||
}
|
||||
|
||||
if (this._regExp.global) {
|
||||
this._regExp = strings.createRegExp(this._regExp.source, true, { matchCase: !this._regExp.ignoreCase, wholeWord: false, multiline: this._regExp.multiline, global: false });
|
||||
}
|
||||
}
|
||||
|
||||
get hasParameters(): boolean {
|
||||
return this._hasParameters;
|
||||
}
|
||||
|
||||
get pattern(): string {
|
||||
return this._replacePattern;
|
||||
}
|
||||
|
||||
get regExp(): RegExp {
|
||||
return this._regExp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the replace string for the first match in the given text.
|
||||
* If text has no matches then returns null.
|
||||
*/
|
||||
getReplaceString(text: string): string | null {
|
||||
this._regExp.lastIndex = 0;
|
||||
let match = this._regExp.exec(text);
|
||||
if (match) {
|
||||
if (this.hasParameters) {
|
||||
if (match[0] === text) {
|
||||
return text.replace(this._regExp, this.pattern);
|
||||
}
|
||||
let replaceString = text.replace(this._regExp, this.pattern);
|
||||
return replaceString.substr(match.index, match[0].length - (text.length - replaceString.length));
|
||||
}
|
||||
return this.pattern;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* \n => LF
|
||||
* \t => TAB
|
||||
* \\ => \
|
||||
* $0 => $& (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_string_as_a_parameter)
|
||||
* everything else stays untouched
|
||||
*/
|
||||
private parseReplaceString(replaceString: string): void {
|
||||
if (!replaceString || replaceString.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let substrFrom = 0, result = '';
|
||||
for (let i = 0, len = replaceString.length; i < len; i++) {
|
||||
let chCode = replaceString.charCodeAt(i);
|
||||
|
||||
if (chCode === CharCode.Backslash) {
|
||||
|
||||
// move to next char
|
||||
i++;
|
||||
|
||||
if (i >= len) {
|
||||
// string ends with a \
|
||||
break;
|
||||
}
|
||||
|
||||
let nextChCode = replaceString.charCodeAt(i);
|
||||
let replaceWithCharacter: string | null = null;
|
||||
|
||||
switch (nextChCode) {
|
||||
case CharCode.Backslash:
|
||||
// \\ => \
|
||||
replaceWithCharacter = '\\';
|
||||
break;
|
||||
case CharCode.n:
|
||||
// \n => LF
|
||||
replaceWithCharacter = '\n';
|
||||
break;
|
||||
case CharCode.t:
|
||||
// \t => TAB
|
||||
replaceWithCharacter = '\t';
|
||||
break;
|
||||
}
|
||||
|
||||
if (replaceWithCharacter) {
|
||||
result += replaceString.substring(substrFrom, i - 1) + replaceWithCharacter;
|
||||
substrFrom = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (chCode === CharCode.DollarSign) {
|
||||
|
||||
// move to next char
|
||||
i++;
|
||||
|
||||
if (i >= len) {
|
||||
// string ends with a $
|
||||
break;
|
||||
}
|
||||
|
||||
let nextChCode = replaceString.charCodeAt(i);
|
||||
let replaceWithCharacter: string | null = null;
|
||||
|
||||
switch (nextChCode) {
|
||||
case CharCode.Digit0:
|
||||
// $0 => $&
|
||||
replaceWithCharacter = '$&';
|
||||
this._hasParameters = true;
|
||||
break;
|
||||
case CharCode.BackTick:
|
||||
case CharCode.SingleQuote:
|
||||
this._hasParameters = true;
|
||||
break;
|
||||
default:
|
||||
// check if it is a valid string parameter $n (0 <= n <= 99). $0 is already handled by now.
|
||||
if (!this.between(nextChCode, CharCode.Digit1, CharCode.Digit9)) {
|
||||
break;
|
||||
}
|
||||
if (i === replaceString.length - 1) {
|
||||
this._hasParameters = true;
|
||||
break;
|
||||
}
|
||||
let charCode = replaceString.charCodeAt(++i);
|
||||
if (!this.between(charCode, CharCode.Digit0, CharCode.Digit9)) {
|
||||
this._hasParameters = true;
|
||||
--i;
|
||||
break;
|
||||
}
|
||||
if (i === replaceString.length - 1) {
|
||||
this._hasParameters = true;
|
||||
break;
|
||||
}
|
||||
charCode = replaceString.charCodeAt(++i);
|
||||
if (!this.between(charCode, CharCode.Digit0, CharCode.Digit9)) {
|
||||
this._hasParameters = true;
|
||||
--i;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (replaceWithCharacter) {
|
||||
result += replaceString.substring(substrFrom, i - 1) + replaceWithCharacter;
|
||||
substrFrom = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (substrFrom === 0) {
|
||||
// no replacement occurred
|
||||
return;
|
||||
}
|
||||
|
||||
this._replacePattern = result + replaceString.substring(substrFrom);
|
||||
}
|
||||
|
||||
private between(value: number, from: number, to: number): boolean {
|
||||
return from <= value && value <= to;
|
||||
}
|
||||
}
|
||||
586
src/vs/workbench/services/search/common/search.ts
Normal file
586
src/vs/workbench/services/search/common/search.ts
Normal file
@@ -0,0 +1,586 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { mapArrayOrNot } from 'vs/base/common/arrays';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import * as glob from 'vs/base/common/glob';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import * as extpath from 'vs/base/common/extpath';
|
||||
import { getNLines } from 'vs/base/common/strings';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { IFilesConfiguration } from 'vs/platform/files/common/files';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
|
||||
export const VIEWLET_ID = 'workbench.view.search';
|
||||
export const PANEL_ID = 'workbench.view.search';
|
||||
export const VIEW_ID = 'workbench.view.search';
|
||||
/**
|
||||
* Search viewlet container.
|
||||
*/
|
||||
export const VIEW_CONTAINER: ViewContainer = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer(VIEWLET_ID, true);
|
||||
|
||||
export const ISearchService = createDecorator<ISearchService>('searchService');
|
||||
|
||||
/**
|
||||
* A service that enables to search for files or with in files.
|
||||
*/
|
||||
export interface ISearchService {
|
||||
_serviceBrand: any;
|
||||
textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): Promise<ISearchComplete>;
|
||||
fileSearch(query: IFileQuery, token?: CancellationToken): Promise<ISearchComplete>;
|
||||
clearCache(cacheKey: string): Promise<void>;
|
||||
registerSearchResultProvider(scheme: string, type: SearchProviderType, provider: ISearchResultProvider): IDisposable;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO@roblou - split text from file search entirely, or share code in a more natural way.
|
||||
*/
|
||||
export const enum SearchProviderType {
|
||||
file,
|
||||
text
|
||||
}
|
||||
|
||||
export interface ISearchResultProvider {
|
||||
textSearch(query: ITextQuery, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): Promise<ISearchComplete | undefined>;
|
||||
fileSearch(query: IFileQuery, token?: CancellationToken): Promise<ISearchComplete | undefined>;
|
||||
clearCache(cacheKey: string): Promise<void>;
|
||||
}
|
||||
|
||||
export interface IFolderQuery<U extends UriComponents=URI> {
|
||||
folder: U;
|
||||
excludePattern?: glob.IExpression;
|
||||
includePattern?: glob.IExpression;
|
||||
fileEncoding?: string;
|
||||
disregardIgnoreFiles?: boolean;
|
||||
disregardGlobalIgnoreFiles?: boolean;
|
||||
ignoreSymlinks?: boolean;
|
||||
}
|
||||
|
||||
export interface ICommonQueryProps<U extends UriComponents> {
|
||||
/** For telemetry - indicates what is triggering the source */
|
||||
_reason?: string;
|
||||
|
||||
folderQueries: IFolderQuery<U>[];
|
||||
includePattern?: glob.IExpression;
|
||||
excludePattern?: glob.IExpression;
|
||||
extraFileResources?: U[];
|
||||
|
||||
maxResults?: number;
|
||||
usingSearchPaths?: boolean;
|
||||
}
|
||||
|
||||
export interface IFileQueryProps<U extends UriComponents> extends ICommonQueryProps<U> {
|
||||
type: QueryType.File;
|
||||
filePattern?: string;
|
||||
|
||||
/**
|
||||
* If true no results will be returned. Instead `limitHit` will indicate if at least one result exists or not.
|
||||
* Currently does not work with queries including a 'siblings clause'.
|
||||
*/
|
||||
exists?: boolean;
|
||||
sortByScore?: boolean;
|
||||
cacheKey?: string;
|
||||
}
|
||||
|
||||
export interface ITextQueryProps<U extends UriComponents> extends ICommonQueryProps<U> {
|
||||
type: QueryType.Text;
|
||||
contentPattern: IPatternInfo;
|
||||
|
||||
previewOptions?: ITextSearchPreviewOptions;
|
||||
maxFileSize?: number;
|
||||
usePCRE2?: boolean;
|
||||
afterContext?: number;
|
||||
beforeContext?: number;
|
||||
|
||||
userDisabledExcludesAndIgnoreFiles?: boolean;
|
||||
}
|
||||
|
||||
export type IFileQuery = IFileQueryProps<URI>;
|
||||
export type IRawFileQuery = IFileQueryProps<UriComponents>;
|
||||
export type ITextQuery = ITextQueryProps<URI>;
|
||||
export type IRawTextQuery = ITextQueryProps<UriComponents>;
|
||||
|
||||
export type IRawQuery = IRawTextQuery | IRawFileQuery;
|
||||
export type ISearchQuery = ITextQuery | IFileQuery;
|
||||
|
||||
export const enum QueryType {
|
||||
File = 1,
|
||||
Text = 2
|
||||
}
|
||||
|
||||
/* __GDPR__FRAGMENT__
|
||||
"IPatternInfo" : {
|
||||
"pattern" : { "classification": "CustomerContent", "purpose": "FeatureInsight" },
|
||||
"isRegExp": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"isWordMatch": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"wordSeparators": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"isMultiline": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"isCaseSensitive": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"isSmartCase": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
|
||||
}
|
||||
*/
|
||||
export interface IPatternInfo {
|
||||
pattern: string;
|
||||
isRegExp?: boolean;
|
||||
isWordMatch?: boolean;
|
||||
wordSeparators?: string;
|
||||
isMultiline?: boolean;
|
||||
isCaseSensitive?: boolean;
|
||||
}
|
||||
|
||||
export interface IExtendedExtensionSearchOptions {
|
||||
usePCRE2?: boolean;
|
||||
}
|
||||
|
||||
export interface IFileMatch<U extends UriComponents = URI> {
|
||||
resource: U;
|
||||
results?: ITextSearchResult[];
|
||||
}
|
||||
|
||||
export type IRawFileMatch2 = IFileMatch<UriComponents>;
|
||||
|
||||
export interface ITextSearchPreviewOptions {
|
||||
matchLines: number;
|
||||
charsPerLine: number;
|
||||
}
|
||||
|
||||
export interface ISearchRange {
|
||||
readonly startLineNumber: number;
|
||||
readonly startColumn: number;
|
||||
readonly endLineNumber: number;
|
||||
readonly endColumn: number;
|
||||
}
|
||||
|
||||
export interface ITextSearchResultPreview {
|
||||
text: string;
|
||||
matches: ISearchRange | ISearchRange[];
|
||||
}
|
||||
|
||||
export interface ITextSearchMatch {
|
||||
uri?: URI;
|
||||
ranges: ISearchRange | ISearchRange[];
|
||||
preview: ITextSearchResultPreview;
|
||||
}
|
||||
|
||||
export interface ITextSearchContext {
|
||||
uri?: URI;
|
||||
text: string;
|
||||
lineNumber: number;
|
||||
}
|
||||
|
||||
export type ITextSearchResult = ITextSearchMatch | ITextSearchContext;
|
||||
|
||||
export function resultIsMatch(result: ITextSearchResult): result is ITextSearchMatch {
|
||||
return !!(<ITextSearchMatch>result).preview;
|
||||
}
|
||||
|
||||
export interface IProgress {
|
||||
total?: number;
|
||||
worked?: number;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export type ISearchProgressItem = IFileMatch | IProgress;
|
||||
|
||||
export interface ISearchCompleteStats {
|
||||
limitHit?: boolean;
|
||||
stats?: IFileSearchStats | ITextSearchStats;
|
||||
}
|
||||
|
||||
export interface ISearchComplete extends ISearchCompleteStats {
|
||||
results: IFileMatch[];
|
||||
}
|
||||
|
||||
export interface ITextSearchStats {
|
||||
type: 'textSearchProvider' | 'searchProcess';
|
||||
}
|
||||
|
||||
export interface IFileSearchStats {
|
||||
fromCache: boolean;
|
||||
detailStats: ISearchEngineStats | ICachedSearchStats | IFileSearchProviderStats;
|
||||
|
||||
resultCount: number;
|
||||
type: 'fileSearchProvider' | 'searchProcess';
|
||||
sortingTime?: number;
|
||||
}
|
||||
|
||||
export interface ICachedSearchStats {
|
||||
cacheWasResolved: boolean;
|
||||
cacheLookupTime: number;
|
||||
cacheFilterTime: number;
|
||||
cacheEntryCount: number;
|
||||
}
|
||||
|
||||
export interface ISearchEngineStats {
|
||||
fileWalkTime: number;
|
||||
directoriesWalked: number;
|
||||
filesWalked: number;
|
||||
cmdTime: number;
|
||||
cmdResultCount?: number;
|
||||
}
|
||||
|
||||
export interface IFileSearchProviderStats {
|
||||
providerTime: number;
|
||||
postProcessTime: number;
|
||||
}
|
||||
|
||||
export class FileMatch implements IFileMatch {
|
||||
results: ITextSearchResult[] = [];
|
||||
constructor(public resource: URI) {
|
||||
// empty
|
||||
}
|
||||
}
|
||||
|
||||
export class TextSearchMatch implements ITextSearchMatch {
|
||||
ranges: ISearchRange | ISearchRange[];
|
||||
preview: ITextSearchResultPreview;
|
||||
|
||||
constructor(text: string, range: ISearchRange | ISearchRange[], previewOptions?: ITextSearchPreviewOptions) {
|
||||
this.ranges = range;
|
||||
|
||||
if (previewOptions && previewOptions.matchLines === 1 && !Array.isArray(range)) {
|
||||
// 1 line preview requested
|
||||
text = getNLines(text, previewOptions.matchLines);
|
||||
const leadingChars = Math.floor(previewOptions.charsPerLine / 5);
|
||||
const previewStart = Math.max(range.startColumn - leadingChars, 0);
|
||||
const previewText = text.substring(previewStart, previewOptions.charsPerLine + previewStart);
|
||||
|
||||
const endColInPreview = (range.endLineNumber - range.startLineNumber + 1) <= previewOptions.matchLines ?
|
||||
Math.min(previewText.length, range.endColumn - previewStart) : // if number of match lines will not be trimmed by previewOptions
|
||||
previewText.length; // if number of lines is trimmed
|
||||
|
||||
this.preview = {
|
||||
text: previewText,
|
||||
matches: new OneLineRange(0, range.startColumn - previewStart, endColInPreview)
|
||||
};
|
||||
} else {
|
||||
const firstMatchLine = Array.isArray(range) ? range[0].startLineNumber : range.startLineNumber;
|
||||
|
||||
// n line, no preview requested, or multiple matches in the preview
|
||||
this.preview = {
|
||||
text,
|
||||
matches: mapArrayOrNot(range, r => new SearchRange(r.startLineNumber - firstMatchLine, r.startColumn, r.endLineNumber - firstMatchLine, r.endColumn))
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SearchRange implements ISearchRange {
|
||||
startLineNumber: number;
|
||||
startColumn: number;
|
||||
endLineNumber: number;
|
||||
endColumn: number;
|
||||
|
||||
constructor(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number) {
|
||||
this.startLineNumber = startLineNumber;
|
||||
this.startColumn = startColumn;
|
||||
this.endLineNumber = endLineNumber;
|
||||
this.endColumn = endColumn;
|
||||
}
|
||||
}
|
||||
|
||||
export class OneLineRange extends SearchRange {
|
||||
constructor(lineNumber: number, startColumn: number, endColumn: number) {
|
||||
super(lineNumber, startColumn, lineNumber, endColumn);
|
||||
}
|
||||
}
|
||||
|
||||
export interface ISearchConfigurationProperties {
|
||||
exclude: glob.IExpression;
|
||||
useRipgrep: boolean;
|
||||
/**
|
||||
* Use ignore file for file search.
|
||||
*/
|
||||
useIgnoreFiles: boolean;
|
||||
useGlobalIgnoreFiles: boolean;
|
||||
followSymlinks: boolean;
|
||||
smartCase: boolean;
|
||||
globalFindClipboard: boolean;
|
||||
location: 'sidebar' | 'panel';
|
||||
useReplacePreview: boolean;
|
||||
showLineNumbers: boolean;
|
||||
usePCRE2: boolean;
|
||||
actionsPosition: 'auto' | 'right';
|
||||
maintainFileSearchCache: boolean;
|
||||
collapseResults: 'auto' | 'alwaysCollapse' | 'alwaysExpand';
|
||||
}
|
||||
|
||||
export interface ISearchConfiguration extends IFilesConfiguration {
|
||||
search: ISearchConfigurationProperties;
|
||||
editor: {
|
||||
wordSeparators: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function getExcludes(configuration: ISearchConfiguration, includeSearchExcludes = true): glob.IExpression | undefined {
|
||||
const fileExcludes = configuration && configuration.files && configuration.files.exclude;
|
||||
const searchExcludes = includeSearchExcludes && configuration && configuration.search && configuration.search.exclude;
|
||||
|
||||
if (!fileExcludes && !searchExcludes) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!fileExcludes || !searchExcludes) {
|
||||
return fileExcludes || searchExcludes;
|
||||
}
|
||||
|
||||
let allExcludes: glob.IExpression = Object.create(null);
|
||||
// clone the config as it could be frozen
|
||||
allExcludes = objects.mixin(allExcludes, objects.deepClone(fileExcludes));
|
||||
allExcludes = objects.mixin(allExcludes, objects.deepClone(searchExcludes), true);
|
||||
|
||||
return allExcludes;
|
||||
}
|
||||
|
||||
export function pathIncludedInQuery(queryProps: ICommonQueryProps<URI>, fsPath: string): boolean {
|
||||
if (queryProps.excludePattern && glob.match(queryProps.excludePattern, fsPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (queryProps.includePattern && !glob.match(queryProps.includePattern, fsPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If searchPaths are being used, the extra file must be in a subfolder and match the pattern, if present
|
||||
if (queryProps.usingSearchPaths) {
|
||||
return !!queryProps.folderQueries && queryProps.folderQueries.every(fq => {
|
||||
const searchPath = fq.folder.fsPath;
|
||||
if (extpath.isEqualOrParent(fsPath, searchPath)) {
|
||||
return !fq.includePattern || !!glob.match(fq.includePattern, fsPath);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export enum SearchErrorCode {
|
||||
unknownEncoding = 1,
|
||||
regexParseError,
|
||||
globParseError,
|
||||
invalidLiteral,
|
||||
rgProcessError,
|
||||
other
|
||||
}
|
||||
|
||||
export class SearchError extends Error {
|
||||
constructor(message: string, readonly code?: SearchErrorCode) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
export function deserializeSearchError(errorMsg: string): SearchError {
|
||||
try {
|
||||
const details = JSON.parse(errorMsg);
|
||||
return new SearchError(details.message, details.code);
|
||||
} catch (e) {
|
||||
return new SearchError(errorMsg, SearchErrorCode.other);
|
||||
}
|
||||
}
|
||||
|
||||
export function serializeSearchError(searchError: SearchError): Error {
|
||||
const details = { message: searchError.message, code: searchError.code };
|
||||
return new Error(JSON.stringify(details));
|
||||
}
|
||||
export interface ITelemetryEvent {
|
||||
eventName: string;
|
||||
data: ITelemetryData;
|
||||
}
|
||||
|
||||
export interface IRawSearchService {
|
||||
fileSearch(search: IRawFileQuery): Event<ISerializedSearchProgressItem | ISerializedSearchComplete>;
|
||||
textSearch(search: IRawTextQuery): Event<ISerializedSearchProgressItem | ISerializedSearchComplete>;
|
||||
clearCache(cacheKey: string): Promise<void>;
|
||||
}
|
||||
|
||||
export interface IRawFileMatch {
|
||||
base?: string;
|
||||
relativePath: string;
|
||||
basename: string;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
export interface ISearchEngine<T> {
|
||||
search: (onResult: (matches: T) => void, onProgress: (progress: IProgress) => void, done: (error: Error | null, complete: ISearchEngineSuccess) => void) => void;
|
||||
cancel: () => void;
|
||||
}
|
||||
|
||||
export interface ISerializedSearchSuccess {
|
||||
type: 'success';
|
||||
limitHit: boolean;
|
||||
stats: IFileSearchStats | ITextSearchStats | null;
|
||||
}
|
||||
|
||||
export interface ISearchEngineSuccess {
|
||||
limitHit: boolean;
|
||||
stats: ISearchEngineStats;
|
||||
}
|
||||
|
||||
export interface ISerializedSearchError {
|
||||
type: 'error';
|
||||
error: {
|
||||
message: string,
|
||||
stack: string
|
||||
};
|
||||
}
|
||||
|
||||
export type ISerializedSearchComplete = ISerializedSearchSuccess | ISerializedSearchError;
|
||||
|
||||
export function isSerializedSearchComplete(arg: ISerializedSearchProgressItem | ISerializedSearchComplete): arg is ISerializedSearchComplete {
|
||||
if ((arg as any).type === 'error') {
|
||||
return true;
|
||||
} else if ((arg as any).type === 'success') {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function isSerializedSearchSuccess(arg: ISerializedSearchComplete): arg is ISerializedSearchSuccess {
|
||||
return arg.type === 'success';
|
||||
}
|
||||
|
||||
export function isSerializedFileMatch(arg: ISerializedSearchProgressItem): arg is ISerializedFileMatch {
|
||||
return !!(<ISerializedFileMatch>arg).path;
|
||||
}
|
||||
|
||||
export interface ISerializedFileMatch {
|
||||
path?: string;
|
||||
results?: ITextSearchResult[];
|
||||
numMatches?: number;
|
||||
}
|
||||
|
||||
// Type of the possible values for progress calls from the engine
|
||||
export type ISerializedSearchProgressItem = ISerializedFileMatch | ISerializedFileMatch[] | IProgress;
|
||||
export type IFileSearchProgressItem = IRawFileMatch | IRawFileMatch[] | IProgress;
|
||||
|
||||
|
||||
export class SerializableFileMatch implements ISerializedFileMatch {
|
||||
path: string;
|
||||
results: ITextSearchMatch[];
|
||||
|
||||
constructor(path: string) {
|
||||
this.path = path;
|
||||
this.results = [];
|
||||
}
|
||||
|
||||
addMatch(match: ITextSearchMatch): void {
|
||||
this.results.push(match);
|
||||
}
|
||||
|
||||
serialize(): ISerializedFileMatch {
|
||||
return {
|
||||
path: this.path,
|
||||
results: this.results,
|
||||
numMatches: this.results.length
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the patterns that the provider handles. Discards sibling clauses and 'false' patterns
|
||||
*/
|
||||
export function resolvePatternsForProvider(globalPattern: glob.IExpression | undefined, folderPattern: glob.IExpression | undefined): string[] {
|
||||
const merged = {
|
||||
...(globalPattern || {}),
|
||||
...(folderPattern || {})
|
||||
};
|
||||
|
||||
return Object.keys(merged)
|
||||
.filter(key => {
|
||||
const value = merged[key];
|
||||
return typeof value === 'boolean' && value;
|
||||
});
|
||||
}
|
||||
|
||||
export class QueryGlobTester {
|
||||
|
||||
private _excludeExpression: glob.IExpression;
|
||||
private _parsedExcludeExpression: glob.ParsedExpression;
|
||||
|
||||
private _parsedIncludeExpression: glob.ParsedExpression;
|
||||
|
||||
constructor(config: ISearchQuery, folderQuery: IFolderQuery) {
|
||||
this._excludeExpression = {
|
||||
...(config.excludePattern || {}),
|
||||
...(folderQuery.excludePattern || {})
|
||||
};
|
||||
this._parsedExcludeExpression = glob.parse(this._excludeExpression);
|
||||
|
||||
// Empty includeExpression means include nothing, so no {} shortcuts
|
||||
let includeExpression: glob.IExpression | undefined = config.includePattern;
|
||||
if (folderQuery.includePattern) {
|
||||
if (includeExpression) {
|
||||
includeExpression = {
|
||||
...includeExpression,
|
||||
...folderQuery.includePattern
|
||||
};
|
||||
} else {
|
||||
includeExpression = folderQuery.includePattern;
|
||||
}
|
||||
}
|
||||
|
||||
if (includeExpression) {
|
||||
this._parsedIncludeExpression = glob.parse(includeExpression);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Guaranteed sync - siblingsFn should not return a promise.
|
||||
*/
|
||||
includedInQuerySync(testPath: string, basename?: string, hasSibling?: (name: string) => boolean): boolean {
|
||||
if (this._parsedExcludeExpression && this._parsedExcludeExpression(testPath, basename, hasSibling)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._parsedIncludeExpression && !this._parsedIncludeExpression(testPath, basename, hasSibling)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Guaranteed async.
|
||||
*/
|
||||
includedInQuery(testPath: string, basename?: string, hasSibling?: (name: string) => boolean | Promise<boolean>): Promise<boolean> {
|
||||
const excludeP = this._parsedExcludeExpression ?
|
||||
Promise.resolve(this._parsedExcludeExpression(testPath, basename, hasSibling)).then(result => !!result) :
|
||||
Promise.resolve(false);
|
||||
|
||||
return excludeP.then(excluded => {
|
||||
if (excluded) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this._parsedIncludeExpression ?
|
||||
Promise.resolve(this._parsedIncludeExpression(testPath, basename, hasSibling)).then(result => !!result) :
|
||||
Promise.resolve(true);
|
||||
}).then(included => {
|
||||
return included;
|
||||
});
|
||||
}
|
||||
|
||||
hasSiblingExcludeClauses(): boolean {
|
||||
return hasSiblingClauses(this._excludeExpression);
|
||||
}
|
||||
}
|
||||
|
||||
function hasSiblingClauses(pattern: glob.IExpression): boolean {
|
||||
for (const key in pattern) {
|
||||
if (typeof pattern[key] !== 'boolean') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { FindMatch, ITextModel } from 'vs/editor/common/model';
|
||||
import { ITextSearchPreviewOptions, TextSearchMatch, ITextSearchResult, ITextSearchMatch, ITextQuery, ITextSearchContext } from 'vs/platform/search/common/search';
|
||||
import { ITextSearchPreviewOptions, TextSearchMatch, ITextSearchResult, ITextSearchMatch, ITextQuery, ITextSearchContext } from 'vs/workbench/services/search/common/search';
|
||||
|
||||
function editorMatchToTextSearchResult(matches: FindMatch[], model: ITextModel, previewOptions?: ITextSearchPreviewOptions): TextSearchMatch {
|
||||
const firstLine = matches[0].range.startLineNumber;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import * as childProcess from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { Readable } from 'stream';
|
||||
import { NodeStringDecoder, StringDecoder } from 'string_decoder';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
@@ -13,7 +13,7 @@ import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import * as glob from 'vs/base/common/glob';
|
||||
import * as normalization from 'vs/base/common/normalization';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import { isEqualOrParent } from 'vs/base/common/paths';
|
||||
import { isEqualOrParent } from 'vs/base/common/extpath';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
@@ -21,8 +21,7 @@ import * as types from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as extfs from 'vs/base/node/extfs';
|
||||
import * as flow from 'vs/base/node/flow';
|
||||
import { IFileQuery, IFolderQuery, IProgress, ISearchEngineStats } from 'vs/platform/search/common/search';
|
||||
import { IRawFileMatch, ISearchEngine, ISearchEngineSuccess } from 'vs/workbench/services/search/node/search';
|
||||
import { IFileQuery, IFolderQuery, IProgress, ISearchEngineStats, IRawFileMatch, ISearchEngine, ISearchEngineSuccess } from 'vs/workbench/services/search/common/search';
|
||||
import { spawnRipgrepCmd } from './ripgrepFileSearch';
|
||||
|
||||
interface IDirectoryEntry {
|
||||
|
||||
@@ -3,15 +3,14 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as path from 'path';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import * as glob from 'vs/base/common/glob';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IFileMatch, IFileSearchProviderStats, IFolderQuery, ISearchCompleteStats, IFileQuery } from 'vs/platform/search/common/search';
|
||||
import { QueryGlobTester, resolvePatternsForProvider } from 'vs/workbench/services/search/node/search';
|
||||
import { IFileMatch, IFileSearchProviderStats, IFolderQuery, ISearchCompleteStats, IFileQuery, QueryGlobTester, resolvePatternsForProvider } from 'vs/workbench/services/search/common/search';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export interface IInternalFileMatch {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as gracefulFs from 'graceful-fs';
|
||||
import { join, sep } from 'path';
|
||||
import { join, sep } from 'vs/base/common/path';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
@@ -16,11 +16,10 @@ import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { compareItemsByScore, IItemAccessor, prepareQuery, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer';
|
||||
import { MAX_FILE_SIZE } from 'vs/platform/files/node/files';
|
||||
import { ICachedSearchStats, IFileQuery, IFileSearchStats, IFolderQuery, IProgress, IRawFileQuery, IRawQuery, IRawTextQuery, ITextQuery } from 'vs/platform/search/common/search';
|
||||
import { MAX_FILE_SIZE } from 'vs/platform/files/node/fileConstants';
|
||||
import { ICachedSearchStats, IFileQuery, IFileSearchStats, IFolderQuery, IProgress, IRawFileQuery, IRawQuery, IRawTextQuery, ITextQuery, IFileSearchProgressItem, IRawFileMatch, IRawSearchService, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from 'vs/workbench/services/search/common/search';
|
||||
import { Engine as FileSearchEngine } from 'vs/workbench/services/search/node/fileSearch';
|
||||
import { TextSearchEngineAdapter } from 'vs/workbench/services/search/node/textSearchAdapter';
|
||||
import { IFileSearchProgressItem, IRawFileMatch, IRawSearchService, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from './search';
|
||||
|
||||
gracefulFs.gracefulify(fs);
|
||||
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as cp from 'child_process';
|
||||
import * as path from 'path';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as glob from 'vs/base/common/glob';
|
||||
import { normalizeNFD } from 'vs/base/common/normalization';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import * as extpath from 'vs/base/common/extpath';
|
||||
import { isMacintosh as isMac } from 'vs/base/common/platform';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { IFileQuery, IFolderQuery } from 'vs/platform/search/common/search';
|
||||
import { IFileQuery, IFolderQuery } from 'vs/workbench/services/search/common/search';
|
||||
import { anchorGlob } from 'vs/workbench/services/search/node/ripgrepSearchUtils';
|
||||
import { rgPath } from 'vscode-ripgrep';
|
||||
|
||||
@@ -159,7 +159,7 @@ function globExprsToRgGlobs(patterns: glob.IExpression, folder?: string, exclude
|
||||
* Exported for testing
|
||||
*/
|
||||
export function getAbsoluteGlob(folder: string, key: string): string {
|
||||
return paths.isAbsolute(key) ?
|
||||
return path.isAbsolute(key) ?
|
||||
key :
|
||||
path.join(folder, key);
|
||||
}
|
||||
@@ -170,7 +170,7 @@ function trimTrailingSlash(str: string): string {
|
||||
}
|
||||
|
||||
export function fixDriveC(path: string): string {
|
||||
const root = paths.getRoot(path);
|
||||
const root = extpath.getRoot(path);
|
||||
return root.toLowerCase() === 'c:/' ?
|
||||
path.replace(/^c:[/\\]/i, '/') :
|
||||
path;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { SearchRange, TextSearchMatch } from 'vs/platform/search/common/search';
|
||||
import { SearchRange, TextSearchMatch } from 'vs/workbench/services/search/common/search';
|
||||
import * as vscode from 'vscode';
|
||||
import { mapArrayOrNot } from 'vs/base/common/arrays';
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
|
||||
import * as cp from 'child_process';
|
||||
import { EventEmitter } from 'events';
|
||||
import * as path from 'path';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { NodeStringDecoder, StringDecoder } from 'string_decoder';
|
||||
import { createRegExp, startsWith, startsWithUTF8BOM, stripUTF8BOM, escapeRegExpCharacters, endsWith } from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IExtendedExtensionSearchOptions, SearchError, SearchErrorCode, serializeSearchError } from 'vs/platform/search/common/search';
|
||||
import { IExtendedExtensionSearchOptions, SearchError, SearchErrorCode, serializeSearchError } from 'vs/workbench/services/search/common/search';
|
||||
import * as vscode from 'vscode';
|
||||
import { rgPath } from 'vscode-ripgrep';
|
||||
import { anchorGlob, createTextSearchResult, IOutputChannel, Maybe, Range } from './ripgrepSearchUtils';
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import * as glob from 'vs/base/common/glob';
|
||||
import { IFileSearchStats, IFolderQuery, IProgress, IRawFileQuery, IRawTextQuery, ISearchEngineStats, ISearchQuery, ITextSearchMatch, ITextSearchStats, ITextSearchResult } from 'vs/platform/search/common/search';
|
||||
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
||||
export interface ITelemetryEvent {
|
||||
eventName: string;
|
||||
data: ITelemetryData;
|
||||
}
|
||||
|
||||
export interface IRawSearchService {
|
||||
fileSearch(search: IRawFileQuery): Event<ISerializedSearchProgressItem | ISerializedSearchComplete>;
|
||||
textSearch(search: IRawTextQuery): Event<ISerializedSearchProgressItem | ISerializedSearchComplete>;
|
||||
clearCache(cacheKey: string): Promise<void>;
|
||||
}
|
||||
|
||||
export interface IRawFileMatch {
|
||||
base?: string;
|
||||
relativePath: string;
|
||||
basename: string;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
export interface ISearchEngine<T> {
|
||||
search: (onResult: (matches: T) => void, onProgress: (progress: IProgress) => void, done: (error: Error | null, complete: ISearchEngineSuccess) => void) => void;
|
||||
cancel: () => void;
|
||||
}
|
||||
|
||||
export interface ISerializedSearchSuccess {
|
||||
type: 'success';
|
||||
limitHit: boolean;
|
||||
stats: IFileSearchStats | ITextSearchStats | null;
|
||||
}
|
||||
|
||||
export interface ISearchEngineSuccess {
|
||||
limitHit: boolean;
|
||||
stats: ISearchEngineStats;
|
||||
}
|
||||
|
||||
export interface ISerializedSearchError {
|
||||
type: 'error';
|
||||
error: {
|
||||
message: string,
|
||||
stack: string
|
||||
};
|
||||
}
|
||||
|
||||
export type ISerializedSearchComplete = ISerializedSearchSuccess | ISerializedSearchError;
|
||||
|
||||
export function isSerializedSearchComplete(arg: ISerializedSearchProgressItem | ISerializedSearchComplete): arg is ISerializedSearchComplete {
|
||||
if ((arg as any).type === 'error') {
|
||||
return true;
|
||||
} else if ((arg as any).type === 'success') {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function isSerializedSearchSuccess(arg: ISerializedSearchComplete): arg is ISerializedSearchSuccess {
|
||||
return arg.type === 'success';
|
||||
}
|
||||
|
||||
export function isSerializedFileMatch(arg: ISerializedSearchProgressItem): arg is ISerializedFileMatch {
|
||||
return !!(<ISerializedFileMatch>arg).path;
|
||||
}
|
||||
|
||||
export interface ISerializedFileMatch {
|
||||
path?: string;
|
||||
results?: ITextSearchResult[];
|
||||
numMatches?: number;
|
||||
}
|
||||
|
||||
// Type of the possible values for progress calls from the engine
|
||||
export type ISerializedSearchProgressItem = ISerializedFileMatch | ISerializedFileMatch[] | IProgress;
|
||||
export type IFileSearchProgressItem = IRawFileMatch | IRawFileMatch[] | IProgress;
|
||||
|
||||
|
||||
export class FileMatch implements ISerializedFileMatch {
|
||||
path: string;
|
||||
results: ITextSearchMatch[];
|
||||
|
||||
constructor(path: string) {
|
||||
this.path = path;
|
||||
this.results = [];
|
||||
}
|
||||
|
||||
addMatch(match: ITextSearchMatch): void {
|
||||
this.results.push(match);
|
||||
}
|
||||
|
||||
serialize(): ISerializedFileMatch {
|
||||
return {
|
||||
path: this.path,
|
||||
results: this.results,
|
||||
numMatches: this.results.length
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the patterns that the provider handles. Discards sibling clauses and 'false' patterns
|
||||
*/
|
||||
export function resolvePatternsForProvider(globalPattern: glob.IExpression | undefined, folderPattern: glob.IExpression | undefined): string[] {
|
||||
const merged = {
|
||||
...(globalPattern || {}),
|
||||
...(folderPattern || {})
|
||||
};
|
||||
|
||||
return Object.keys(merged)
|
||||
.filter(key => {
|
||||
const value = merged[key];
|
||||
return typeof value === 'boolean' && value;
|
||||
});
|
||||
}
|
||||
|
||||
export class QueryGlobTester {
|
||||
|
||||
private _excludeExpression: glob.IExpression;
|
||||
private _parsedExcludeExpression: glob.ParsedExpression;
|
||||
|
||||
private _parsedIncludeExpression: glob.ParsedExpression;
|
||||
|
||||
constructor(config: ISearchQuery, folderQuery: IFolderQuery) {
|
||||
this._excludeExpression = {
|
||||
...(config.excludePattern || {}),
|
||||
...(folderQuery.excludePattern || {})
|
||||
};
|
||||
this._parsedExcludeExpression = glob.parse(this._excludeExpression);
|
||||
|
||||
// Empty includeExpression means include nothing, so no {} shortcuts
|
||||
let includeExpression: glob.IExpression | undefined = config.includePattern;
|
||||
if (folderQuery.includePattern) {
|
||||
if (includeExpression) {
|
||||
includeExpression = {
|
||||
...includeExpression,
|
||||
...folderQuery.includePattern
|
||||
};
|
||||
} else {
|
||||
includeExpression = folderQuery.includePattern;
|
||||
}
|
||||
}
|
||||
|
||||
if (includeExpression) {
|
||||
this._parsedIncludeExpression = glob.parse(includeExpression);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Guaranteed sync - siblingsFn should not return a promise.
|
||||
*/
|
||||
includedInQuerySync(testPath: string, basename?: string, hasSibling?: (name: string) => boolean): boolean {
|
||||
if (this._parsedExcludeExpression && this._parsedExcludeExpression(testPath, basename, hasSibling)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._parsedIncludeExpression && !this._parsedIncludeExpression(testPath, basename, hasSibling)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Guaranteed async.
|
||||
*/
|
||||
includedInQuery(testPath: string, basename?: string, hasSibling?: (name: string) => boolean | Promise<boolean>): Promise<boolean> {
|
||||
const excludeP = this._parsedExcludeExpression ?
|
||||
Promise.resolve(this._parsedExcludeExpression(testPath, basename, hasSibling)).then(result => !!result) :
|
||||
Promise.resolve(false);
|
||||
|
||||
return excludeP.then(excluded => {
|
||||
if (excluded) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this._parsedIncludeExpression ?
|
||||
Promise.resolve(this._parsedIncludeExpression(testPath, basename, hasSibling)).then(result => !!result) :
|
||||
Promise.resolve(true);
|
||||
}).then(included => {
|
||||
return included;
|
||||
});
|
||||
}
|
||||
|
||||
hasSiblingExcludeClauses(): boolean {
|
||||
return hasSiblingClauses(this._excludeExpression);
|
||||
}
|
||||
}
|
||||
|
||||
function hasSiblingClauses(pattern: glob.IExpression): boolean {
|
||||
for (const key in pattern) {
|
||||
if (typeof pattern[key] !== 'boolean') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { ISearchHistoryValues, ISearchHistoryService } from 'vs/platform/search/common/search';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { isEmptyObject } from 'vs/base/common/types';
|
||||
|
||||
export class SearchHistoryService implements ISearchHistoryService {
|
||||
_serviceBrand: any;
|
||||
|
||||
private static readonly SEARCH_HISTORY_KEY = 'workbench.search.history';
|
||||
|
||||
private readonly _onDidClearHistory = new Emitter<void>();
|
||||
readonly onDidClearHistory: Event<void> = this._onDidClearHistory.event;
|
||||
|
||||
constructor(
|
||||
@IStorageService private readonly storageService: IStorageService
|
||||
) { }
|
||||
|
||||
clearHistory(): void {
|
||||
this.storageService.remove(SearchHistoryService.SEARCH_HISTORY_KEY, StorageScope.WORKSPACE);
|
||||
this._onDidClearHistory.fire();
|
||||
}
|
||||
|
||||
load(): ISearchHistoryValues {
|
||||
let result: ISearchHistoryValues | undefined;
|
||||
const raw = this.storageService.get(SearchHistoryService.SEARCH_HISTORY_KEY, StorageScope.WORKSPACE);
|
||||
|
||||
if (raw) {
|
||||
try {
|
||||
result = JSON.parse(raw);
|
||||
} catch (e) {
|
||||
// Invalid data
|
||||
}
|
||||
}
|
||||
|
||||
return result || {};
|
||||
}
|
||||
|
||||
save(history: ISearchHistoryValues): void {
|
||||
if (isEmptyObject(history)) {
|
||||
this.storageService.remove(SearchHistoryService.SEARCH_HISTORY_KEY, StorageScope.WORKSPACE);
|
||||
} else {
|
||||
this.storageService.store(SearchHistoryService.SEARCH_HISTORY_KEY, JSON.stringify(history), StorageScope.WORKSPACE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc';
|
||||
import { IRawFileQuery, IRawTextQuery } from 'vs/platform/search/common/search';
|
||||
import { IRawSearchService, ISerializedSearchComplete, ISerializedSearchProgressItem } from './search';
|
||||
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { IRawFileQuery, IRawTextQuery, IRawSearchService, ISerializedSearchComplete, ISerializedSearchProgressItem } from 'vs/workbench/services/search/common/search';
|
||||
|
||||
export class SearchChannel implements IServerChannel {
|
||||
|
||||
|
||||
@@ -11,25 +11,24 @@ import { Event } from 'vs/base/common/event';
|
||||
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { keys, ResourceMap, values } from 'vs/base/common/map';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import { getNextTickChannel } from 'vs/base/parts/ipc/node/ipc';
|
||||
import { Client, IIPCOptions } from 'vs/base/parts/ipc/node/ipc.cp';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IDebugParams, IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { deserializeSearchError, FileMatch, ICachedSearchStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, IProgress, ISearchComplete, ISearchConfiguration, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, ITextQuery, pathIncludedInQuery, QueryType, SearchError, SearchErrorCode, SearchProviderType } from 'vs/platform/search/common/search';
|
||||
import { deserializeSearchError, FileMatch, ICachedSearchStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, IProgress, ISearchComplete, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, ITextQuery, pathIncludedInQuery, QueryType, SearchError, SearchErrorCode, SearchProviderType, ISearchConfiguration, IRawSearchService, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess } from 'vs/workbench/services/search/common/search';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { addContextToEditorMatches, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { IRawSearchService, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess } from './search';
|
||||
import { SearchChannelClient } from './searchIpc';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
export class SearchService extends Disposable implements ISearchService {
|
||||
_serviceBrand: any;
|
||||
@@ -37,7 +36,6 @@ export class SearchService extends Disposable implements ISearchService {
|
||||
private diskSearch: DiskSearch;
|
||||
private readonly fileSearchProviders = new Map<string, ISearchResultProvider>();
|
||||
private readonly textSearchProviders = new Map<string, ISearchResultProvider>();
|
||||
private readonly fileIndexProviders = new Map<string, ISearchResultProvider>();
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@@ -46,12 +44,11 @@ export class SearchService extends Disposable implements ISearchService {
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IEnvironmentService environmentService: IEnvironmentService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IExtensionService private readonly extensionService: IExtensionService
|
||||
) {
|
||||
super();
|
||||
this.diskSearch = this.instantiationService.createInstance(DiskSearch, !environmentService.isBuilt || environmentService.verbose, /*timeout=*/undefined, environmentService.debugSearch);
|
||||
this.diskSearch = this.instantiationService.createInstance(DiskSearch, !environmentService.isBuilt || environmentService.verbose, environmentService.debugSearch);
|
||||
}
|
||||
|
||||
registerSearchResultProvider(scheme: string, type: SearchProviderType, provider: ISearchResultProvider): IDisposable {
|
||||
@@ -60,8 +57,8 @@ export class SearchService extends Disposable implements ISearchService {
|
||||
list = this.fileSearchProviders;
|
||||
} else if (type === SearchProviderType.text) {
|
||||
list = this.textSearchProviders;
|
||||
} else if (type === SearchProviderType.fileIndex) {
|
||||
list = this.fileIndexProviders;
|
||||
} else {
|
||||
throw new Error('Unknown SearchProviderType');
|
||||
}
|
||||
|
||||
list.set(scheme, provider);
|
||||
@@ -71,22 +68,6 @@ export class SearchService extends Disposable implements ISearchService {
|
||||
});
|
||||
}
|
||||
|
||||
extendQuery(query: IFileQuery): void {
|
||||
const configuration = this.configurationService.getValue<ISearchConfiguration>();
|
||||
|
||||
// Configuration: File Excludes
|
||||
if (!query.disregardExcludeSettings) {
|
||||
const fileExcludes = objects.deepClone(configuration && configuration.files && configuration.files.exclude);
|
||||
if (fileExcludes) {
|
||||
if (!query.excludePattern) {
|
||||
query.excludePattern = fileExcludes;
|
||||
} else {
|
||||
objects.mixin(query.excludePattern, fileExcludes, false /* no overwrite */);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): Promise<ISearchComplete> {
|
||||
// Get local results from dirty/untitled
|
||||
const localResults = this.getLocalResults(query);
|
||||
@@ -95,8 +76,6 @@ export class SearchService extends Disposable implements ISearchService {
|
||||
arrays.coalesce(localResults.values()).forEach(onProgress);
|
||||
}
|
||||
|
||||
this.logService.trace('SearchService#search', JSON.stringify(query));
|
||||
|
||||
const onProviderProgress = progress => {
|
||||
if (progress.resource) {
|
||||
// Match
|
||||
@@ -121,6 +100,8 @@ export class SearchService extends Disposable implements ISearchService {
|
||||
}
|
||||
|
||||
private doSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): Promise<ISearchComplete> {
|
||||
this.logService.trace('SearchService#search', JSON.stringify(query));
|
||||
|
||||
const schemesInQuery = this.getSchemesInQuery(query);
|
||||
|
||||
const providerActivations: Promise<any>[] = [Promise.resolve(null)];
|
||||
@@ -197,7 +178,7 @@ export class SearchService extends Disposable implements ISearchService {
|
||||
keys(fqs).forEach(scheme => {
|
||||
const schemeFQs = fqs.get(scheme);
|
||||
const provider = query.type === QueryType.File ?
|
||||
this.fileSearchProviders.get(scheme) || this.fileIndexProviders.get(scheme) :
|
||||
this.fileSearchProviders.get(scheme) :
|
||||
this.textSearchProviders.get(scheme);
|
||||
|
||||
if (!provider && scheme === 'file') {
|
||||
@@ -339,7 +320,7 @@ export class SearchService extends Disposable implements ISearchService {
|
||||
});
|
||||
}
|
||||
} else if (query.type === QueryType.Text) {
|
||||
let errorType: string;
|
||||
let errorType: string | undefined;
|
||||
if (err) {
|
||||
errorType = err.code === SearchErrorCode.regexParseError ? 'regex' :
|
||||
err.code === SearchErrorCode.unknownEncoding ? 'encoding' :
|
||||
@@ -436,7 +417,6 @@ export class SearchService extends Disposable implements ISearchService {
|
||||
clearCache(cacheKey: string): Promise<void> {
|
||||
const clearPs = [
|
||||
this.diskSearch,
|
||||
...values(this.fileIndexProviders),
|
||||
...values(this.fileSearchProviders)
|
||||
].map(provider => provider && provider.clearCache(cacheKey));
|
||||
|
||||
@@ -446,19 +426,21 @@ export class SearchService extends Disposable implements ISearchService {
|
||||
}
|
||||
|
||||
export class DiskSearch implements ISearchResultProvider {
|
||||
_serviceBrand: any;
|
||||
|
||||
private raw: IRawSearchService;
|
||||
|
||||
constructor(
|
||||
verboseLogging: boolean,
|
||||
timeout: number = 60 * 60 * 1000,
|
||||
searchDebug: IDebugParams | undefined,
|
||||
@ILogService private readonly logService: ILogService
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IConfigurationService private readonly configService: IConfigurationService,
|
||||
) {
|
||||
const timeout = this.configService.getValue<ISearchConfiguration>().search.maintainFileSearchCache ?
|
||||
Number.MAX_VALUE :
|
||||
60 * 60 * 1000;
|
||||
|
||||
const opts: IIPCOptions = {
|
||||
serverName: 'Search',
|
||||
timeout: timeout,
|
||||
timeout,
|
||||
args: ['--type=searchService'],
|
||||
// See https://github.com/Microsoft/vscode/issues/27665
|
||||
// Pass in fresh execArgv to the forked process such that it doesn't inherit them from `process.execArgv`.
|
||||
@@ -600,3 +582,5 @@ export class DiskSearch implements ISearchResultProvider {
|
||||
return this.raw.clearCache(cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(ISearchService, SearchService, true);
|
||||
@@ -5,10 +5,9 @@
|
||||
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import * as extfs from 'vs/base/node/extfs';
|
||||
import { IFileMatch, IProgress, ITextQuery, ITextSearchStats, ITextSearchMatch } from 'vs/platform/search/common/search';
|
||||
import { IFileMatch, IProgress, ITextQuery, ITextSearchStats, ITextSearchMatch, ISerializedFileMatch, ISerializedSearchSuccess } from 'vs/workbench/services/search/common/search';
|
||||
import { RipgrepTextSearchEngine } from 'vs/workbench/services/search/node/ripgrepTextSearchEngine';
|
||||
import { TextSearchManager } from 'vs/workbench/services/search/node/textSearchManager';
|
||||
import { ISerializedFileMatch, ISerializedSearchSuccess } from './search';
|
||||
|
||||
export class TextSearchEngineAdapter {
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as path from 'path';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { mapArrayOrNot } from 'vs/base/common/arrays';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
@@ -12,8 +12,7 @@ import * as resources from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { toCanonicalName } from 'vs/base/node/encoding';
|
||||
import * as extfs from 'vs/base/node/extfs';
|
||||
import { IExtendedExtensionSearchOptions, IFileMatch, IFolderQuery, IPatternInfo, ISearchCompleteStats, ITextQuery, ITextSearchContext, ITextSearchMatch, ITextSearchResult } from 'vs/platform/search/common/search';
|
||||
import { QueryGlobTester, resolvePatternsForProvider } from 'vs/workbench/services/search/node/search';
|
||||
import { IExtendedExtensionSearchOptions, IFileMatch, IFolderQuery, IPatternInfo, ISearchCompleteStats, ITextQuery, ITextSearchContext, ITextSearchMatch, ITextSearchResult, QueryGlobTester, resolvePatternsForProvider } from 'vs/workbench/services/search/common/search';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export class TextSearchManager {
|
||||
|
||||
218
src/vs/workbench/services/search/test/common/replace.test.ts
Normal file
218
src/vs/workbench/services/search/test/common/replace.test.ts
Normal file
@@ -0,0 +1,218 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as assert from 'assert';
|
||||
import { ReplacePattern } from 'vs/workbench/services/search/common/replace';
|
||||
|
||||
suite('Replace Pattern test', () => {
|
||||
|
||||
test('parse replace string', () => {
|
||||
let testParse = (input: string, expected: string, expectedHasParameters: boolean) => {
|
||||
let actual = new ReplacePattern(input, { pattern: 'somepattern', isRegExp: true });
|
||||
assert.equal(expected, actual.pattern);
|
||||
assert.equal(expectedHasParameters, actual.hasParameters);
|
||||
|
||||
actual = new ReplacePattern('hello' + input + 'hi', { pattern: 'sonepattern', isRegExp: true });
|
||||
assert.equal('hello' + expected + 'hi', actual.pattern);
|
||||
assert.equal(expectedHasParameters, actual.hasParameters);
|
||||
};
|
||||
|
||||
// no backslash => no treatment
|
||||
testParse('hello', 'hello', false);
|
||||
|
||||
// \t => TAB
|
||||
testParse('\\thello', '\thello', false);
|
||||
|
||||
// \n => LF
|
||||
testParse('\\nhello', '\nhello', false);
|
||||
|
||||
// \\t => \t
|
||||
testParse('\\\\thello', '\\thello', false);
|
||||
|
||||
// \\\t => \TAB
|
||||
testParse('\\\\\\thello', '\\\thello', false);
|
||||
|
||||
// \\\\t => \\t
|
||||
testParse('\\\\\\\\thello', '\\\\thello', false);
|
||||
|
||||
// \ at the end => no treatment
|
||||
testParse('hello\\', 'hello\\', false);
|
||||
|
||||
// \ with unknown char => no treatment
|
||||
testParse('hello\\x', 'hello\\x', false);
|
||||
|
||||
// \ with back reference => no treatment
|
||||
testParse('hello\\0', 'hello\\0', false);
|
||||
|
||||
|
||||
|
||||
// $1 => no treatment
|
||||
testParse('hello$1', 'hello$1', true);
|
||||
// $2 => no treatment
|
||||
testParse('hello$2', 'hello$2', true);
|
||||
// $12 => no treatment
|
||||
testParse('hello$12', 'hello$12', true);
|
||||
// $99 => no treatment
|
||||
testParse('hello$99', 'hello$99', true);
|
||||
// $99a => no treatment
|
||||
testParse('hello$99a', 'hello$99a', true);
|
||||
// $100 => no treatment
|
||||
testParse('hello$100', 'hello$100', false);
|
||||
// $100a => no treatment
|
||||
testParse('hello$100a', 'hello$100a', false);
|
||||
// $10a0 => no treatment
|
||||
testParse('hello$10a0', 'hello$10a0', true);
|
||||
// $$ => no treatment
|
||||
testParse('hello$$', 'hello$$', false);
|
||||
// $$0 => no treatment
|
||||
testParse('hello$$0', 'hello$$0', false);
|
||||
|
||||
// $0 => $&
|
||||
testParse('hello$0', 'hello$&', true);
|
||||
testParse('hello$02', 'hello$&2', true);
|
||||
|
||||
testParse('hello$`', 'hello$`', true);
|
||||
testParse('hello$\'', 'hello$\'', true);
|
||||
});
|
||||
|
||||
test('create pattern by passing regExp', () => {
|
||||
let expected = /abc/;
|
||||
let actual = new ReplacePattern('hello', false, expected).regExp;
|
||||
assert.deepEqual(expected, actual);
|
||||
|
||||
expected = /abc/;
|
||||
actual = new ReplacePattern('hello', false, /abc/g).regExp;
|
||||
assert.deepEqual(expected, actual);
|
||||
|
||||
let testObject = new ReplacePattern('hello$0', false, /abc/g);
|
||||
assert.equal(false, testObject.hasParameters);
|
||||
|
||||
testObject = new ReplacePattern('hello$0', true, /abc/g);
|
||||
assert.equal(true, testObject.hasParameters);
|
||||
});
|
||||
|
||||
test('get replace string if given text is a complete match', () => {
|
||||
let testObject = new ReplacePattern('hello', { pattern: 'bla', isRegExp: true });
|
||||
let actual = testObject.getReplaceString('bla');
|
||||
assert.equal('hello', actual);
|
||||
|
||||
testObject = new ReplacePattern('hello', { pattern: 'bla', isRegExp: false });
|
||||
actual = testObject.getReplaceString('bla');
|
||||
assert.equal('hello', actual);
|
||||
|
||||
testObject = new ReplacePattern('hello', { pattern: '(bla)', isRegExp: true });
|
||||
actual = testObject.getReplaceString('bla');
|
||||
assert.equal('hello', actual);
|
||||
|
||||
testObject = new ReplacePattern('hello$0', { pattern: '(bla)', isRegExp: true });
|
||||
actual = testObject.getReplaceString('bla');
|
||||
assert.equal('hellobla', actual);
|
||||
|
||||
testObject = new ReplacePattern('import * as $1 from \'$2\';', { pattern: 'let\\s+(\\w+)\\s*=\\s*require\\s*\\(\\s*[\'\"]([\\w\.\\-/]+)\\s*[\'\"]\\s*\\)\\s*', isRegExp: true });
|
||||
actual = testObject.getReplaceString('let fs = require(\'fs\')');
|
||||
assert.equal('import * as fs from \'fs\';', actual);
|
||||
|
||||
actual = testObject.getReplaceString('let something = require(\'fs\')');
|
||||
assert.equal('import * as something from \'fs\';', actual);
|
||||
|
||||
actual = testObject.getReplaceString('let require(\'fs\')');
|
||||
assert.equal(null, actual);
|
||||
|
||||
testObject = new ReplacePattern('import * as $1 from \'$1\';', { pattern: 'let\\s+(\\w+)\\s*=\\s*require\\s*\\(\\s*[\'\"]([\\w\.\\-/]+)\\s*[\'\"]\\s*\\)\\s*', isRegExp: true });
|
||||
actual = testObject.getReplaceString('let something = require(\'fs\')');
|
||||
assert.equal('import * as something from \'something\';', actual);
|
||||
|
||||
testObject = new ReplacePattern('import * as $2 from \'$1\';', { pattern: 'let\\s+(\\w+)\\s*=\\s*require\\s*\\(\\s*[\'\"]([\\w\.\\-/]+)\\s*[\'\"]\\s*\\)\\s*', isRegExp: true });
|
||||
actual = testObject.getReplaceString('let something = require(\'fs\')');
|
||||
assert.equal('import * as fs from \'something\';', actual);
|
||||
|
||||
testObject = new ReplacePattern('import * as $0 from \'$0\';', { pattern: 'let\\s+(\\w+)\\s*=\\s*require\\s*\\(\\s*[\'\"]([\\w\.\\-/]+)\\s*[\'\"]\\s*\\)\\s*', isRegExp: true });
|
||||
actual = testObject.getReplaceString('let something = require(\'fs\');');
|
||||
assert.equal('import * as let something = require(\'fs\') from \'let something = require(\'fs\')\';', actual);
|
||||
|
||||
testObject = new ReplacePattern('import * as $1 from \'$2\';', { pattern: 'let\\s+(\\w+)\\s*=\\s*require\\s*\\(\\s*[\'\"]([\\w\.\\-/]+)\\s*[\'\"]\\s*\\)\\s*', isRegExp: false });
|
||||
actual = testObject.getReplaceString('let fs = require(\'fs\');');
|
||||
assert.equal(null, actual);
|
||||
|
||||
testObject = new ReplacePattern('cat$1', { pattern: 'for(.*)', isRegExp: true });
|
||||
actual = testObject.getReplaceString('for ()');
|
||||
assert.equal('cat ()', actual);
|
||||
});
|
||||
|
||||
test('get replace string for no matches', () => {
|
||||
let testObject = new ReplacePattern('hello', { pattern: 'bla', isRegExp: true });
|
||||
let actual = testObject.getReplaceString('foo');
|
||||
assert.equal(null, actual);
|
||||
|
||||
testObject = new ReplacePattern('hello', { pattern: 'bla', isRegExp: false });
|
||||
actual = testObject.getReplaceString('foo');
|
||||
assert.equal(null, actual);
|
||||
});
|
||||
|
||||
test('get replace string if match is sub-string of the text', () => {
|
||||
let testObject = new ReplacePattern('hello', { pattern: 'bla', isRegExp: true });
|
||||
let actual = testObject.getReplaceString('this is a bla text');
|
||||
assert.equal('hello', actual);
|
||||
|
||||
testObject = new ReplacePattern('hello', { pattern: 'bla', isRegExp: false });
|
||||
actual = testObject.getReplaceString('this is a bla text');
|
||||
assert.equal('hello', actual);
|
||||
|
||||
testObject = new ReplacePattern('that', { pattern: 'this(?=.*bla)', isRegExp: true });
|
||||
actual = testObject.getReplaceString('this is a bla text');
|
||||
assert.equal('that', actual);
|
||||
|
||||
testObject = new ReplacePattern('$1at', { pattern: '(th)is(?=.*bla)', isRegExp: true });
|
||||
actual = testObject.getReplaceString('this is a bla text');
|
||||
assert.equal('that', actual);
|
||||
|
||||
testObject = new ReplacePattern('$1e', { pattern: '(th)is(?=.*bla)', isRegExp: true });
|
||||
actual = testObject.getReplaceString('this is a bla text');
|
||||
assert.equal('the', actual);
|
||||
|
||||
testObject = new ReplacePattern('$1ere', { pattern: '(th)is(?=.*bla)', isRegExp: true });
|
||||
actual = testObject.getReplaceString('this is a bla text');
|
||||
assert.equal('there', actual);
|
||||
|
||||
testObject = new ReplacePattern('$1', { pattern: '(th)is(?=.*bla)', isRegExp: true });
|
||||
actual = testObject.getReplaceString('this is a bla text');
|
||||
assert.equal('th', actual);
|
||||
|
||||
testObject = new ReplacePattern('ma$1', { pattern: '(th)is(?=.*bla)', isRegExp: true });
|
||||
actual = testObject.getReplaceString('this is a bla text');
|
||||
assert.equal('math', actual);
|
||||
|
||||
testObject = new ReplacePattern('ma$1s', { pattern: '(th)is(?=.*bla)', isRegExp: true });
|
||||
actual = testObject.getReplaceString('this is a bla text');
|
||||
assert.equal('maths', actual);
|
||||
|
||||
testObject = new ReplacePattern('ma$1s', { pattern: '(th)is(?=.*bla)', isRegExp: true });
|
||||
actual = testObject.getReplaceString('this is a bla text');
|
||||
assert.equal('maths', actual);
|
||||
|
||||
testObject = new ReplacePattern('$0', { pattern: '(th)is(?=.*bla)', isRegExp: true });
|
||||
actual = testObject.getReplaceString('this is a bla text');
|
||||
assert.equal('this', actual);
|
||||
|
||||
testObject = new ReplacePattern('$0$1', { pattern: '(th)is(?=.*bla)', isRegExp: true });
|
||||
actual = testObject.getReplaceString('this is a bla text');
|
||||
assert.equal('thisth', actual);
|
||||
|
||||
testObject = new ReplacePattern('foo', { pattern: 'bla(?=\\stext$)', isRegExp: true });
|
||||
actual = testObject.getReplaceString('this is a bla text');
|
||||
assert.equal('foo', actual);
|
||||
|
||||
testObject = new ReplacePattern('f$1', { pattern: 'b(la)(?=\\stext$)', isRegExp: true });
|
||||
actual = testObject.getReplaceString('this is a bla text');
|
||||
assert.equal('fla', actual);
|
||||
|
||||
testObject = new ReplacePattern('f$0', { pattern: 'b(la)(?=\\stext$)', isRegExp: true });
|
||||
actual = testObject.getReplaceString('this is a bla text');
|
||||
assert.equal('fbla', actual);
|
||||
|
||||
testObject = new ReplacePattern('$0ah', { pattern: 'b(la)(?=\\stext$)', isRegExp: true });
|
||||
actual = testObject.getReplaceString('this is a bla text');
|
||||
assert.equal('blaah', actual);
|
||||
});
|
||||
});
|
||||
105
src/vs/workbench/services/search/test/common/search.test.ts
Normal file
105
src/vs/workbench/services/search/test/common/search.test.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as assert from 'assert';
|
||||
import { ITextSearchPreviewOptions, OneLineRange, TextSearchMatch, SearchRange } from 'vs/workbench/services/search/common/search';
|
||||
|
||||
suite('TextSearchResult', () => {
|
||||
|
||||
const previewOptions1: ITextSearchPreviewOptions = {
|
||||
matchLines: 1,
|
||||
charsPerLine: 100
|
||||
};
|
||||
|
||||
function assertPreviewRangeText(text: string, result: TextSearchMatch): void {
|
||||
assert.equal(
|
||||
result.preview.text.substring((<SearchRange>result.preview.matches).startColumn, (<SearchRange>result.preview.matches).endColumn),
|
||||
text);
|
||||
}
|
||||
|
||||
test('empty without preview options', () => {
|
||||
const range = new OneLineRange(5, 0, 0);
|
||||
const result = new TextSearchMatch('', range);
|
||||
assert.deepEqual(result.ranges, range);
|
||||
assertPreviewRangeText('', result);
|
||||
});
|
||||
|
||||
test('empty with preview options', () => {
|
||||
const range = new OneLineRange(5, 0, 0);
|
||||
const result = new TextSearchMatch('', range, previewOptions1);
|
||||
assert.deepEqual(result.ranges, range);
|
||||
assertPreviewRangeText('', result);
|
||||
});
|
||||
|
||||
test('short without preview options', () => {
|
||||
const range = new OneLineRange(5, 4, 7);
|
||||
const result = new TextSearchMatch('foo bar', range);
|
||||
assert.deepEqual(result.ranges, range);
|
||||
assertPreviewRangeText('bar', result);
|
||||
});
|
||||
|
||||
test('short with preview options', () => {
|
||||
const range = new OneLineRange(5, 4, 7);
|
||||
const result = new TextSearchMatch('foo bar', range, previewOptions1);
|
||||
assert.deepEqual(result.ranges, range);
|
||||
assertPreviewRangeText('bar', result);
|
||||
});
|
||||
|
||||
test('leading', () => {
|
||||
const range = new OneLineRange(5, 25, 28);
|
||||
const result = new TextSearchMatch('long text very long text foo', range, previewOptions1);
|
||||
assert.deepEqual(result.ranges, range);
|
||||
assertPreviewRangeText('foo', result);
|
||||
});
|
||||
|
||||
test('trailing', () => {
|
||||
const range = new OneLineRange(5, 0, 3);
|
||||
const result = new TextSearchMatch('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', range, previewOptions1);
|
||||
assert.deepEqual(result.ranges, range);
|
||||
assertPreviewRangeText('foo', result);
|
||||
});
|
||||
|
||||
test('middle', () => {
|
||||
const range = new OneLineRange(5, 30, 33);
|
||||
const result = new TextSearchMatch('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', range, previewOptions1);
|
||||
assert.deepEqual(result.ranges, range);
|
||||
assertPreviewRangeText('foo', result);
|
||||
});
|
||||
|
||||
test('truncating match', () => {
|
||||
const previewOptions: ITextSearchPreviewOptions = {
|
||||
matchLines: 1,
|
||||
charsPerLine: 1
|
||||
};
|
||||
|
||||
const range = new OneLineRange(0, 4, 7);
|
||||
const result = new TextSearchMatch('foo bar', range, previewOptions);
|
||||
assert.deepEqual(result.ranges, range);
|
||||
assertPreviewRangeText('b', result);
|
||||
});
|
||||
|
||||
test('one line of multiline match', () => {
|
||||
const previewOptions: ITextSearchPreviewOptions = {
|
||||
matchLines: 1,
|
||||
charsPerLine: 10000
|
||||
};
|
||||
|
||||
const range = new SearchRange(5, 4, 6, 3);
|
||||
const result = new TextSearchMatch('foo bar\nfoo bar', range, previewOptions);
|
||||
assert.deepEqual(result.ranges, range);
|
||||
assertPreviewRangeText('bar', result);
|
||||
});
|
||||
|
||||
// test('all lines of multiline match', () => {
|
||||
// const previewOptions: ITextSearchPreviewOptions = {
|
||||
// matchLines: 5,
|
||||
// charsPerLine: 10000
|
||||
// };
|
||||
|
||||
// const range = new SearchRange(5, 4, 6, 3);
|
||||
// const result = new TextSearchResult('foo bar\nfoo bar', range, previewOptions);
|
||||
// assert.deepEqual(result.range, range);
|
||||
// assertPreviewRangeText('bar\nfoo', result);
|
||||
// });
|
||||
});
|
||||
@@ -7,7 +7,7 @@ import * as assert from 'assert';
|
||||
import { ITextModel, FindMatch } from 'vs/editor/common/model';
|
||||
import { editorMatchesToTextSearchResults, addContextToEditorMatches } from 'vs/workbench/services/search/common/searchHelpers';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ITextQuery, QueryType, ITextSearchContext } from 'vs/platform/search/common/search';
|
||||
import { ITextQuery, QueryType, ITextSearchContext } from 'vs/workbench/services/search/common/search';
|
||||
|
||||
suite('SearchHelpers', () => {
|
||||
suite('editorMatchesToTextSearchResults', () => {
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as path from 'path';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { getPathFromAmdModule } from 'vs/base/common/amd';
|
||||
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IFileQuery, IFileSearchStats, IFolderQuery, IProgress, ISearchEngineStats, QueryType } from 'vs/platform/search/common/search';
|
||||
import { IFileQuery, IFileSearchStats, IFolderQuery, IProgress, ISearchEngineStats, QueryType, IRawFileMatch, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from 'vs/workbench/services/search/common/search';
|
||||
import { SearchService as RawSearchService } from 'vs/workbench/services/search/node/rawSearchService';
|
||||
import { IRawFileMatch, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from 'vs/workbench/services/search/node/search';
|
||||
import { DiskSearch } from 'vs/workbench/services/search/node/searchService';
|
||||
|
||||
const TEST_FOLDER_QUERIES = [
|
||||
|
||||
@@ -4,15 +4,13 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as path from 'path';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { getPathFromAmdModule } from 'vs/base/common/amd';
|
||||
import { join, normalize } from 'vs/base/common/paths';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IFolderQuery, QueryType } from 'vs/platform/search/common/search';
|
||||
import { IFolderQuery, QueryType, IRawFileMatch } from 'vs/workbench/services/search/common/search';
|
||||
import { Engine as FileSearchEngine, FileWalker } from 'vs/workbench/services/search/node/fileSearch';
|
||||
import { IRawFileMatch } from 'vs/workbench/services/search/node/search';
|
||||
|
||||
const TEST_FIXTURES = path.normalize(getPathFromAmdModule(require, './fixtures'));
|
||||
const EXAMPLES_FIXTURES = URI.file(path.join(TEST_FIXTURES, 'examples'));
|
||||
@@ -188,7 +186,7 @@ suite('FileSearchEngine', () => {
|
||||
const engine = new FileSearchEngine({
|
||||
type: QueryType.File,
|
||||
folderQueries: ROOT_FOLDER_QUERY,
|
||||
filePattern: normalize(join('examples', 'com*'), true)
|
||||
filePattern: path.join('examples', 'com*')
|
||||
});
|
||||
|
||||
let count = 0;
|
||||
|
||||
@@ -4,13 +4,12 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as path from 'path';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { getPathFromAmdModule } from 'vs/base/common/amd';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import * as glob from 'vs/base/common/glob';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { deserializeSearchError, IFolderQuery, ISearchRange, ITextQuery, ITextSearchContext, ITextSearchMatch, QueryType, SearchErrorCode } from 'vs/platform/search/common/search';
|
||||
import { ISerializedFileMatch } from 'vs/workbench/services/search/node/search';
|
||||
import { deserializeSearchError, IFolderQuery, ISearchRange, ITextQuery, ITextSearchContext, ITextSearchMatch, QueryType, SearchErrorCode, ISerializedFileMatch } from 'vs/workbench/services/search/common/search';
|
||||
import { TextSearchEngineAdapter } from 'vs/workbench/services/search/node/textSearchAdapter';
|
||||
|
||||
const TEST_FIXTURES = path.normalize(getPathFromAmdModule(require, './fixtures'));
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as assert from 'assert';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ITextQuery, QueryType } from 'vs/platform/search/common/search';
|
||||
import { ITextQuery, QueryType } from 'vs/workbench/services/search/common/search';
|
||||
import { TextSearchManager } from 'vs/workbench/services/search/node/textSearchManager';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user