mirror of
https://github.com/Microsoft/vscode
synced 2024-09-19 02:26:04 +00:00
Add basic save functionality to search editor
This commit is contained in:
parent
c3c54744ab
commit
501085f224
|
@ -57,7 +57,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
|||
import { assertType } from 'vs/base/common/types';
|
||||
import { SearchViewPaneContainer } from 'vs/workbench/contrib/search/browser/searchViewlet';
|
||||
import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor';
|
||||
import { SearchEditorInput, SearchEditorInputFactory } from 'vs/workbench/contrib/search/browser/searchEditorCommands';
|
||||
import { SearchEditorInput, SearchEditorInputFactory, SearchEditorContribution } from 'vs/workbench/contrib/search/browser/searchEditorCommands';
|
||||
import { SearchEditor } from 'vs/workbench/contrib/search/browser/searchEditor';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor';
|
||||
|
@ -909,3 +909,6 @@ Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
|
|||
Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(
|
||||
SearchEditorInput.ID,
|
||||
SearchEditorInputFactory);
|
||||
|
||||
const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(SearchEditorContribution, LifecyclePhase.Starting);
|
||||
|
|
|
@ -158,6 +158,8 @@ export class SearchEditor extends BaseEditor {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._register(this.searchResultEditor.onDidChangeModel(() => this.hideHeader()));
|
||||
}
|
||||
|
||||
private async runSearch(instant = false) {
|
||||
|
@ -225,14 +227,28 @@ export class SearchEditor extends BaseEditor {
|
|||
(assertIsDefined(this._input) as SearchEditorInput).setConfig(config);
|
||||
|
||||
const labelFormatter = (uri: URI): string => this.labelService.getUriLabel(uri, { relative: true });
|
||||
const results = serializeSearchResultForEditor(searchModel.searchResult, config.includes, config.excludes, config.contextLines, labelFormatter, false);
|
||||
const results = serializeSearchResultForEditor(searchModel.searchResult, config.includes, config.excludes, config.contextLines, labelFormatter, true);
|
||||
const textModel = assertIsDefined(this.searchResultEditor.getModel());
|
||||
textModel.setValue(results.text.join(lineDelimiter));
|
||||
this.hideHeader();
|
||||
textModel.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'searchEditorFindMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } })));
|
||||
|
||||
searchModel.dispose();
|
||||
}
|
||||
|
||||
private hideHeader() {
|
||||
const headerLines =
|
||||
this.searchResultEditor
|
||||
.getModel()
|
||||
?.getValueInRange(new Range(1, 1, 6, 1))
|
||||
.split('\n')
|
||||
.filter(line => line.startsWith('#'))
|
||||
.length
|
||||
?? 0;
|
||||
|
||||
this.searchResultEditor.setHiddenAreas([new Range(1, 1, headerLines + 1, 1)]);
|
||||
}
|
||||
|
||||
layout(dimension: DOM.Dimension) {
|
||||
this.dimension = dimension;
|
||||
this.reLayout();
|
||||
|
@ -255,8 +271,7 @@ export class SearchEditor extends BaseEditor {
|
|||
if (!(newInput instanceof SearchEditorInput)) { return; }
|
||||
this.pauseSearching = true;
|
||||
// TODO: Manage model lifecycle in SearchEditorInput
|
||||
const model = this.modelService.getModel(newInput.getResource());
|
||||
|
||||
const model = this.modelService.getModel(newInput.resource);
|
||||
this.searchResultEditor.setModel(model);
|
||||
this.queryEditorWidget.setValue(newInput.config.query, true);
|
||||
this.queryEditorWidget.searchInput.setCaseSensitive(newInput.config.caseSensitive);
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
|
||||
import { coalesce, flatten } from 'vs/base/common/arrays';
|
||||
import * as network from 'vs/base/common/network';
|
||||
import { repeat } from 'vs/base/common/strings';
|
||||
import { repeat, endsWith } from 'vs/base/common/strings';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import 'vs/css!./media/searchEditor';
|
||||
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { EndOfLinePreference, TrackedRangeStickiness } from 'vs/editor/common/model';
|
||||
import { EndOfLinePreference, TrackedRangeStickiness, ITextModel } from 'vs/editor/common/model';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
@ -25,16 +25,22 @@ import { getOutOfWorkspaceEditorResources } from 'vs/workbench/contrib/search/co
|
|||
import { FileMatch, Match, searchMatchComparer, SearchModel, SearchResult } from 'vs/workbench/contrib/search/common/searchModel';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IPatternInfo, ISearchConfigurationProperties, ITextQuery } from 'vs/workbench/services/search/common/search';
|
||||
import { EditorInput, IEditorInputFactory } from 'vs/workbench/common/editor';
|
||||
import { IEditorInputFactory, GroupIdentifier, EditorInput } from 'vs/workbench/common/editor';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { SearchEditor } from 'vs/workbench/contrib/search/browser/searchEditor';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { ITextFileSaveOptions, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
|
||||
import type { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
// import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
|
||||
|
||||
|
||||
|
||||
export type SearchConfiguration = {
|
||||
query: string,
|
||||
includes: string,
|
||||
excludes: string,
|
||||
excludes: string
|
||||
contextLines: number,
|
||||
wholeWord: boolean,
|
||||
caseSensitive: boolean,
|
||||
|
@ -43,6 +49,27 @@ export type SearchConfiguration = {
|
|||
showIncludesExcludes: boolean,
|
||||
};
|
||||
|
||||
export class SearchEditorContribution implements IWorkbenchContribution {
|
||||
// constructor(
|
||||
// @IEditorService private readonly editorService: IEditorService,
|
||||
// @ITextFileService protected readonly textFileService: ITextFileService,
|
||||
// ) {
|
||||
// this.editorService.overrideOpenEditor(async (editor, options, group) => {
|
||||
// const resource = editor.getResource();
|
||||
// if (!resource || !endsWith(resource.path, '.code-search') || !(editor instanceof FileEditorInput)) {
|
||||
// return undefined;
|
||||
// }
|
||||
|
||||
// console.log('hello agian');
|
||||
// const contents = await this.textFileService.read(resource);
|
||||
// contents.value;
|
||||
|
||||
// return undefined;
|
||||
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
export class SearchEditorInputFactory implements IEditorInputFactory {
|
||||
|
||||
canSerialize() { return true; }
|
||||
|
@ -52,7 +79,7 @@ export class SearchEditorInputFactory implements IEditorInputFactory {
|
|||
}
|
||||
|
||||
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): SearchEditorInput | undefined {
|
||||
return instantiationService.createInstance(SearchEditorInput, JSON.parse(serializedEditorInput));
|
||||
return instantiationService.createInstance(SearchEditorInput, JSON.parse(serializedEditorInput), undefined);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,40 +87,56 @@ let searchEditorInputInstances = 0;
|
|||
export class SearchEditorInput extends EditorInput {
|
||||
static readonly ID: string = 'workbench.editorinputs.searchEditorInput';
|
||||
|
||||
public config: SearchConfiguration;
|
||||
private instanceNumber: number = searchEditorInputInstances++;
|
||||
private _config: SearchConfiguration;
|
||||
public get config(): Readonly<SearchConfiguration> {
|
||||
return this._config;
|
||||
}
|
||||
|
||||
private model: ITextModel;
|
||||
public readonly resource: URI;
|
||||
|
||||
constructor(
|
||||
config: SearchConfiguration | undefined,
|
||||
initialContents: string | undefined,
|
||||
@IModelService private readonly modelService: IModelService,
|
||||
@IModeService private readonly modeService: IModeService,
|
||||
@IEditorService protected readonly editorService: IEditorService,
|
||||
@IEditorGroupsService protected readonly editorGroupService: IEditorGroupsService,
|
||||
@ITextFileService protected readonly textFileService: ITextFileService,
|
||||
@IUntitledTextEditorService protected readonly untitledTextEditorService: IUntitledTextEditorService,
|
||||
) {
|
||||
super();
|
||||
this.resource = URI.from({ scheme: 'search-editor', fragment: `${searchEditorInputInstances++}` });
|
||||
|
||||
|
||||
if (config === undefined) {
|
||||
this.config = { query: '', includes: '', excludes: '', contextLines: 0, wholeWord: false, caseSensitive: false, regexp: false, useIgnores: true, showIncludesExcludes: false };
|
||||
this._config = { query: '', includes: '', excludes: '', contextLines: 0, wholeWord: false, caseSensitive: false, regexp: false, useIgnores: true, showIncludesExcludes: false };
|
||||
} else {
|
||||
this.config = config;
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
const searchResultMode = this.modeService.create('search-result');
|
||||
this.modelService.createModel('', searchResultMode, this.getResource());
|
||||
|
||||
this.model = this.modelService.createModel(initialContents ?? '', searchResultMode, this.resource);
|
||||
}
|
||||
|
||||
async save(group: GroupIdentifier, options?: ITextFileSaveOptions): Promise<boolean> {
|
||||
const path = (this.config.query.replace(/\W/g, '') || 'Untitled Search') + '.code-search';
|
||||
const untitledResource = URI.from({ scheme: 'untitled', path, fragment: this.resource.fragment });
|
||||
const a = this.untitledTextEditorService.createOrGet(untitledResource, 'search-result', this.model.getValue());
|
||||
return a.save(group, options).finally(() => a.dispose());
|
||||
}
|
||||
|
||||
getTypeId(): string {
|
||||
return SearchEditorInput.ID;
|
||||
}
|
||||
|
||||
getResource(): URI {
|
||||
return URI.from({ scheme: 'search-editor', fragment: `${this.instanceNumber}` });
|
||||
}
|
||||
|
||||
getName(): string {
|
||||
return this.config.query ? localize('searchTitle.withQuery', "Search: {0}", this.config.query) : localize('searchTitle', "Search");
|
||||
}
|
||||
|
||||
setConfig(config: SearchConfiguration) {
|
||||
this.config = config;
|
||||
this._config = config;
|
||||
this._onDidChangeLabel.fire();
|
||||
}
|
||||
|
||||
|
@ -102,9 +145,17 @@ export class SearchEditorInput extends EditorInput {
|
|||
}
|
||||
|
||||
dispose() {
|
||||
this.modelService.destroyModel(this.getResource());
|
||||
this.modelService.destroyModel(this.resource);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
matches(other: unknown) {
|
||||
if (this === other) { return true; }
|
||||
if (other instanceof UntitledTextEditorInput) {
|
||||
if (other.getResource().fragment === this.resource.fragment) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Using \r\n on Windows inserts an extra newline between results.
|
||||
|
@ -313,7 +364,7 @@ export const serializeSearchResultForEditor = (searchResult: SearchResult, rawIn
|
|||
.map(folderMatch => folderMatch.matches().sort(searchMatchComparer)
|
||||
.map(fileMatch => fileMatchToSearchResultFormat(fileMatch, labelFormatter)))));
|
||||
|
||||
return { matchRanges: allResults.matchRanges.map(translateRangeLines(header.length)), text: header.concat(allResults.text) };
|
||||
return { matchRanges: allResults.matchRanges.map(translateRangeLines(header.length)), text: header.concat(allResults.text.length ? allResults.text : ['No Results']) };
|
||||
};
|
||||
|
||||
export const refreshActiveEditorSearch =
|
||||
|
@ -373,7 +424,7 @@ export const refreshActiveEditorSearch =
|
|||
await searchModel.search(query);
|
||||
|
||||
const labelFormatter = (uri: URI): string => labelService.getUriLabel(uri, { relative: true });
|
||||
const results = serializeSearchResultForEditor(searchModel.searchResult, contentPattern.includes, contentPattern.excludes, contextLines, labelFormatter, false);
|
||||
const results = serializeSearchResultForEditor(searchModel.searchResult, contentPattern.includes, contentPattern.excludes, contextLines, labelFormatter, true);
|
||||
|
||||
textModel.setValue(results.text.join(lineDelimiter));
|
||||
textModel.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'searchEditorFindMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } })));
|
||||
|
@ -381,7 +432,7 @@ export const refreshActiveEditorSearch =
|
|||
|
||||
export const openNewSearchEditor =
|
||||
async (editorService: IEditorService, instantiationService: IInstantiationService) => {
|
||||
await editorService.openEditor(instantiationService.createInstance(SearchEditorInput, undefined), { pinned: true });
|
||||
await editorService.openEditor(instantiationService.createInstance(SearchEditorInput, undefined, undefined), { pinned: true });
|
||||
};
|
||||
|
||||
export const createEditorFromSearchResult =
|
||||
|
@ -395,7 +446,7 @@ export const createEditorFromSearchResult =
|
|||
|
||||
const labelFormatter = (uri: URI): string => labelService.getUriLabel(uri, { relative: true });
|
||||
|
||||
const results = serializeSearchResultForEditor(searchResult, rawIncludePattern, rawExcludePattern, 0, labelFormatter, false);
|
||||
const results = serializeSearchResultForEditor(searchResult, rawIncludePattern, rawExcludePattern, 0, labelFormatter, true);
|
||||
const contents = results.text.join(lineDelimiter);
|
||||
let possible = {
|
||||
contents,
|
||||
|
@ -418,24 +469,22 @@ export const createEditorFromSearchResult =
|
|||
existing = editorService.getOpened(possible);
|
||||
}
|
||||
|
||||
const editor = await editorService.openEditor(
|
||||
instantiationService.createInstance(
|
||||
SearchEditorInput,
|
||||
{
|
||||
query: searchResult.query.contentPattern.pattern,
|
||||
regexp: !!searchResult.query.contentPattern.isRegExp,
|
||||
caseSensitive: !!searchResult.query.contentPattern.isCaseSensitive,
|
||||
wholeWord: !!searchResult.query.contentPattern.isWordMatch,
|
||||
includes: rawIncludePattern,
|
||||
excludes: rawExcludePattern,
|
||||
contextLines: 0,
|
||||
useIgnores: !searchResult.query.userDisabledExcludesAndIgnoreFiles,
|
||||
showIncludesExcludes: !!(rawExcludePattern || rawExcludePattern || searchResult.query.userDisabledExcludesAndIgnoreFiles)
|
||||
}),
|
||||
{ pinned: true }) as SearchEditor;
|
||||
const input = instantiationService.createInstance(
|
||||
SearchEditorInput,
|
||||
{
|
||||
query: searchResult.query.contentPattern.pattern,
|
||||
regexp: !!searchResult.query.contentPattern.isRegExp,
|
||||
caseSensitive: !!searchResult.query.contentPattern.isCaseSensitive,
|
||||
wholeWord: !!searchResult.query.contentPattern.isWordMatch,
|
||||
includes: rawIncludePattern,
|
||||
excludes: rawExcludePattern,
|
||||
contextLines: 0,
|
||||
useIgnores: !searchResult.query.userDisabledExcludesAndIgnoreFiles,
|
||||
showIncludesExcludes: !!(rawExcludePattern || rawExcludePattern || searchResult.query.userDisabledExcludesAndIgnoreFiles)
|
||||
}, contents);
|
||||
|
||||
const editor = await editorService.openEditor(input, { pinned: true }) as SearchEditor;
|
||||
const model = assertIsDefined(editor.getModel());
|
||||
model.setValue(contents);
|
||||
model.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'searchEditorFindMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } })));
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue