提交 8c684d1b 编写于 作者: S Sandeep Somavarapu

Implement #8278

上级 db21b743
......@@ -19,7 +19,7 @@ import { LeftRightWidget, IRenderer } from 'vs/base/browser/ui/leftRightWidget/l
import { ITree, IElementCallback, IDataSource, ISorter, IAccessibilityProvider, IFilter } from 'vs/base/parts/tree/browser/tree';
import {ClickBehavior, DefaultController} from 'vs/base/parts/tree/browser/treeDefaults';
import { ContributableActionProvider } from 'vs/workbench/browser/actionBarRegistry';
import { Match, EmptyMatch, SearchResult, FileMatch, FileMatchOrMatch } from 'vs/workbench/parts/search/common/searchModel';
import { Match, SearchResult, FileMatch, FileMatchOrMatch } from 'vs/workbench/parts/search/common/searchModel';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { Range } from 'vs/editor/common/core/range';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
......@@ -104,7 +104,7 @@ class SearchActionProvider extends ContributableActionProvider {
actions.unshift(this.instantiationService.createInstance(ReplaceAllAction, tree, element, this.viewlet));
}
}
if (element instanceof Match && !(element instanceof EmptyMatch)) {
if (element instanceof Match) {
if (input.searchModel.isReplaceActive()) {
actions.unshift(this.instantiationService.createInstance(ReplaceAction, tree, element, this.viewlet), new RemoveAction(tree, element));
}
......@@ -158,12 +158,6 @@ export class SearchRenderer extends ActionsRenderer {
return widget.dispose.bind(widget);
}
// Empty
else if (element instanceof EmptyMatch) {
dom.addClass(domElement, 'linematch');
$('a.plain.label').innerHtml(nls.localize('noMatches', "no matches")).appendTo(domElement);
}
// Match
else if (element instanceof Match) {
dom.addClass(domElement, 'linematch');
......@@ -208,10 +202,6 @@ export class SearchAccessibilityProvider implements IAccessibilityProvider {
return nls.localize('fileMatchAriaLabel', "{0} matches in file {1} of folder {2}, Search result", element.count(), element.name(), paths.dirname(path));
}
if (element instanceof EmptyMatch) {
return nls.localize('emptyMatchAriaLabel', "No matches");
}
if (element instanceof Match) {
let input= <SearchResult>tree.getInput();
if (input.searchModel.isReplaceActive()) {
......@@ -254,7 +244,7 @@ export class SearchController extends DefaultController {
let result = false;
let element = tree.getFocus();
if (element instanceof FileMatch ||
(element instanceof Match && input.searchModel.isReplaceActive() && !(element instanceof EmptyMatch))) {
(element instanceof Match && input.searchModel.isReplaceActive())) {
new RemoveAction(tree, element).run().done(null, errors.onUnexpectedError);
result = true;
}
......@@ -266,7 +256,7 @@ export class SearchController extends DefaultController {
let input= <SearchResult>tree.getInput();
let result = false;
let element = tree.getFocus();
if (element instanceof Match && input.searchModel.isReplaceActive() && !(element instanceof EmptyMatch)) {
if (element instanceof Match && input.searchModel.isReplaceActive()) {
this.instantiationService.createInstance(ReplaceAction, tree, element, this.viewlet).run().done(null, errors.onUnexpectedError);
result = true;
}
......
......@@ -31,7 +31,7 @@ import {IEditorGroupService} from 'vs/workbench/services/group/common/groupServi
import {getOutOfWorkspaceEditorResources} from 'vs/workbench/common/editor';
import {FileChangeType, FileChangesEvent, EventType as FileEventType} from 'vs/platform/files/common/files';
import {Viewlet} from 'vs/workbench/browser/viewlet';
import {Match, EmptyMatch, FileMatch, SearchModel, FileMatchOrMatch} from 'vs/workbench/parts/search/common/searchModel';
import {Match, FileMatch, SearchModel, FileMatchOrMatch} from 'vs/workbench/parts/search/common/searchModel';
import {getExcludes, QueryBuilder} from 'vs/workbench/parts/search/common/searchQuery';
import {VIEWLET_ID} from 'vs/workbench/parts/search/common/constants';
import {MessageType, InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
......@@ -915,10 +915,6 @@ export class SearchViewlet extends Viewlet {
}
private getSelectionFrom(element: FileMatchOrMatch): any {
if (element instanceof EmptyMatch) {
return void 0;
}
let match: Match= null;
if (element instanceof Match) {
match= element;
......
......@@ -68,13 +68,6 @@ export class Match {
}
}
export class EmptyMatch extends Match {
constructor(parent: FileMatch) {
super(parent, null, Date.now(), Date.now(), Date.now());
}
}
export class FileMatch extends Disposable {
private static DecorationOption: IModelDecorationOptions = {
......@@ -137,13 +130,6 @@ export class FileMatch extends Disposable {
}
}));
}
this._register(this.onChange(() => {
if (this.count() === 0) {
let emptyMatch = new EmptyMatch(this);
this._matches.set(emptyMatch.id(), emptyMatch);
}
}));
}
private bindModel(model: IModel): void {
......@@ -197,7 +183,7 @@ export class FileMatch extends Disposable {
}
if (this.parent().showHighlights) {
this._modelDecorations = this._model.deltaDecorations(this._modelDecorations, this.matches().filter(match => !(match instanceof EmptyMatch)).map(match => <IModelDeltaDecoration>{
this._modelDecorations = this._model.deltaDecorations(this._modelDecorations, this.matches().map(match => <IModelDeltaDecoration>{
range: match.range(),
options: FileMatch.DecorationOption
}));
......@@ -232,13 +218,7 @@ export class FileMatch extends Disposable {
}
public count(): number {
let result = 0;
this.matches().forEach(element => {
if (!(element instanceof EmptyMatch)) {
result += 1;
}
});
return result;
return this.matches().length;
}
public resource(): URI {
......@@ -262,6 +242,8 @@ export class SearchResult extends Disposable {
public onChange: Event<any> = this._onChange.event;
private _fileMatches: SimpleMap<URI, FileMatch>;
private _unDisposedFileMatches: SimpleMap<URI, FileMatch>;
private _query: Search.IPatternInfo= null;
private _showHighlights: boolean;
private _replacingAll: boolean= false;
......@@ -269,17 +251,22 @@ export class SearchResult extends Disposable {
@IInstantiationService private instantiationService: IInstantiationService) {
super();
this._fileMatches= new SimpleMap<URI, FileMatch>();
this._unDisposedFileMatches= new SimpleMap<URI, FileMatch>();
}
public set query(query: Search.IPatternInfo) {
this._query= query;
}
public get searchModel(): SearchModel {
return this._searchModel;
}
public add(query: Search.IPatternInfo, raw: Search.IFileMatch[], silent:boolean= false): void {
public add(raw: Search.IFileMatch[], silent:boolean= false): void {
raw.forEach((rawFileMatch) => {
if (!this._fileMatches.has(rawFileMatch.resource)){
let fileMatch= this.instantiationService.createInstance(FileMatch, query, this, rawFileMatch);
this._fileMatches.set(rawFileMatch.resource, fileMatch);
let fileMatch= this.instantiationService.createInstance(FileMatch, this._query, this, rawFileMatch);
this.doAdd(fileMatch);
let disposable= fileMatch.onChange(() => this.onFileChange(fileMatch));
fileMatch.onDispose(() => disposable.dispose());
}
......@@ -290,20 +277,18 @@ export class SearchResult extends Disposable {
}
public clear(): void {
this.matches().forEach((fileMatch: FileMatch) => fileMatch.dispose());
this._fileMatches.clear();
this.disposeMatches();
this._onChange.fire(this);
}
public remove(match: FileMatch): void {
this._fileMatches.delete(match.resource());
match.dispose();
this.doRemove(match);
this._onChange.fire(this);
}
public replace(match: FileMatch, replaceText: string): TPromise<any> {
return this.replaceService.replace([match], replaceText).then(() => {
this.remove(match);
this.doRemove(match, false);
});
}
......@@ -350,11 +335,48 @@ export class SearchResult extends Disposable {
});
}
private onFileChange(file: FileMatch): void {
private onFileChange(fileMatch: FileMatch): void {
let refreshFile: boolean= true;
if (!this._fileMatches.has(fileMatch.resource())) {
this.doAdd(fileMatch);
refreshFile= false;
}
if (fileMatch.count() === 0) {
this.doRemove(fileMatch, false);
refreshFile= false;
}
if (!this._replacingAll) {
this._onChange.fire(file);
this._onChange.fire(refreshFile ? fileMatch : this);
}
}
private doAdd(fileMatch: FileMatch): void {
this._fileMatches.set(fileMatch.resource(), fileMatch);
if (this._unDisposedFileMatches.has(fileMatch.resource())) {
this._unDisposedFileMatches.delete(fileMatch.resource());
}
}
private doRemove(fileMatch: FileMatch, dispose:boolean= true): void {
this._fileMatches.delete(fileMatch.resource());
if (dispose) {
fileMatch.dispose();
} else {
this._unDisposedFileMatches.set(fileMatch.resource(), fileMatch);
}
}
private disposeMatches(): void {
this._fileMatches.values().forEach((fileMatch: FileMatch) => fileMatch.dispose());
this._unDisposedFileMatches.values().forEach((fileMatch: FileMatch) => fileMatch.dispose());
this._fileMatches.clear();
this._unDisposedFileMatches.clear();
}
public dispose():void {
this.disposeMatches();
super.dispose();
}
}
export class SearchModel extends Disposable {
......@@ -410,6 +432,7 @@ export class SearchModel extends Disposable {
this.searchResult.clear();
this._searchQuery= query;
this._searchResult.query= this._searchQuery.contentPattern;
this.progressTimer = this.telemetryService.timedPublicLog('searchResultsFirstRender');
this.doneTimer = this.telemetryService.timedPublicLog('searchResultsFinished');
this.timerEvent = timer.start(timer.Topic.WORKBENCH, 'Search');
......@@ -424,7 +447,7 @@ export class SearchModel extends Disposable {
private onSearchCompleted(completed: ISearchComplete): ISearchComplete {
this.timerEvent.stop();
this._searchResult.add(this._searchQuery.contentPattern, completed.results);
this._searchResult.add(completed.results);
this.telemetryService.publicLog('searchResultsShown', { count: this._searchResult.count(), fileCount: this._searchResult.fileCount() });
this.doneTimer.stop();
return completed;
......@@ -441,7 +464,7 @@ export class SearchModel extends Disposable {
private onSearchProgress(p: ISearchProgressItem, silent: boolean): void {
if (p.resource) {
this._searchResult.add(this._searchQuery.contentPattern, [p], silent);
this._searchResult.add([p], silent);
this.progressTimer.stop();
}
}
......
......@@ -31,7 +31,7 @@ suite('Search - Viewlet', () => {
test('Data Source', function () {
let ds = new SearchDataSource();
let result = instantiation.createInstance(SearchResult, null);
result.add(null, [{
result.add([{
resource: uri.parse('file:///c:/foo'),
lineMatches: [{ lineNumber: 1, preview: 'bar', offsetAndLengths: [[0, 1]] }]
}]);
......
......@@ -5,7 +5,7 @@
'use strict';
import * as assert from 'assert';
import {Match, FileMatch, SearchResult, EmptyMatch} from 'vs/workbench/parts/search/common/searchModel';
import {Match, FileMatch, SearchResult} from 'vs/workbench/parts/search/common/searchModel';
import URI from 'vs/base/common/uri';
import {ServiceCollection} from 'vs/platform/instantiation/common/serviceCollection';
import {InstantiationService} from 'vs/platform/instantiation/common/instantiationService';
......@@ -45,8 +45,7 @@ suite('Search - Model', () => {
}]);
let lineMatch = fileMatch.matches()[0];
fileMatch.remove(lineMatch);
assert.equal(fileMatch.matches().length, 1);
assert.ok(fileMatch.matches()[0] instanceof EmptyMatch);
assert.equal(fileMatch.matches().length, 0);
});
test('File Match', function () {
......@@ -77,7 +76,7 @@ suite('Search - Model', () => {
}]
});
}
searchResult.add(null, raw);
searchResult.add(raw);
assert.equal(searchResult.isEmpty(), false);
assert.equal(searchResult.matches().length, 10);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册