mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-31 09:10:30 -04:00
233 lines
7.9 KiB
TypeScript
233 lines
7.9 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import * as nls from 'vs/nls';
|
|
import { URI } from 'vs/base/common/uri';
|
|
import { onUnexpectedError } from 'vs/base/common/errors';
|
|
import { ThrottledDelayer } from 'vs/base/common/async';
|
|
import { QuickOpenHandler, EditorQuickOpenEntry } from 'vs/workbench/browser/quickopen';
|
|
import { QuickOpenModel, QuickOpenEntry, compareEntries } from 'vs/base/parts/quickopen/browser/quickOpenModel';
|
|
import { IAutoFocus, Mode, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen';
|
|
import * as filters from 'vs/base/common/filters';
|
|
import * as strings from 'vs/base/common/strings';
|
|
import { Range } from 'vs/editor/common/core/range';
|
|
import { IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor';
|
|
import { symbolKindToCssClass } from 'vs/editor/common/modes';
|
|
import { IResourceInput } from 'vs/platform/editor/common/editor';
|
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
|
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
|
import { IWorkspaceSymbolProvider, getWorkspaceSymbols, IWorkspaceSymbol } from 'vs/workbench/contrib/search/common/search';
|
|
import { basename } from 'vs/base/common/resources';
|
|
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
|
import { ILabelService } from 'vs/platform/label/common/label';
|
|
import { CancellationToken } from 'vs/base/common/cancellation';
|
|
import { Schemas } from 'vs/base/common/network';
|
|
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
|
import { withUndefinedAsNull } from 'vs/base/common/types';
|
|
|
|
class SymbolEntry extends EditorQuickOpenEntry {
|
|
private bearingResolve: Promise<this | undefined>;
|
|
|
|
constructor(
|
|
private bearing: IWorkspaceSymbol,
|
|
private provider: IWorkspaceSymbolProvider,
|
|
@IConfigurationService private readonly configurationService: IConfigurationService,
|
|
@IEditorService editorService: IEditorService,
|
|
@ILabelService private readonly labelService: ILabelService,
|
|
@IOpenerService private readonly openerService: IOpenerService
|
|
) {
|
|
super(editorService);
|
|
}
|
|
|
|
getLabel(): string {
|
|
return this.bearing.name;
|
|
}
|
|
|
|
getAriaLabel(): string {
|
|
return nls.localize('entryAriaLabel', "{0}, symbols picker", this.getLabel());
|
|
}
|
|
|
|
getDescription(): string | null {
|
|
const containerName = this.bearing.containerName;
|
|
if (this.bearing.location.uri) {
|
|
if (containerName) {
|
|
return `${containerName} — ${basename(this.bearing.location.uri)}`;
|
|
}
|
|
|
|
return this.labelService.getUriLabel(this.bearing.location.uri, { relative: true });
|
|
}
|
|
|
|
return withUndefinedAsNull(containerName);
|
|
}
|
|
|
|
getIcon(): string {
|
|
return symbolKindToCssClass(this.bearing.kind);
|
|
}
|
|
|
|
getResource(): URI {
|
|
return this.bearing.location.uri;
|
|
}
|
|
|
|
run(mode: Mode, context: IEntryRunContext): boolean {
|
|
|
|
// resolve this type bearing if neccessary
|
|
if (!this.bearingResolve && typeof this.provider.resolveWorkspaceSymbol === 'function' && !this.bearing.location.range) {
|
|
this.bearingResolve = Promise.resolve(this.provider.resolveWorkspaceSymbol(this.bearing, CancellationToken.None)).then(result => {
|
|
this.bearing = result || this.bearing;
|
|
|
|
return this;
|
|
}, onUnexpectedError);
|
|
}
|
|
|
|
// open after resolving
|
|
Promise.resolve(this.bearingResolve).then(() => {
|
|
const scheme = this.bearing.location.uri ? this.bearing.location.uri.scheme : undefined;
|
|
if (scheme === Schemas.http || scheme === Schemas.https) {
|
|
if (mode === Mode.OPEN || mode === Mode.OPEN_IN_BACKGROUND) {
|
|
this.openerService.open(this.bearing.location.uri); // support http/https resources (https://github.com/Microsoft/vscode/issues/58924))
|
|
}
|
|
} else {
|
|
super.run(mode, context);
|
|
}
|
|
});
|
|
|
|
// hide if OPEN
|
|
return mode === Mode.OPEN;
|
|
}
|
|
|
|
getInput(): IResourceInput {
|
|
const input: IResourceInput = {
|
|
resource: this.bearing.location.uri,
|
|
options: {
|
|
pinned: !this.configurationService.getValue<IWorkbenchEditorConfiguration>().workbench.editor.enablePreviewFromQuickOpen
|
|
}
|
|
};
|
|
|
|
if (this.bearing.location.range) {
|
|
input.options!.selection = Range.collapseToStart(this.bearing.location.range);
|
|
}
|
|
|
|
return input;
|
|
}
|
|
|
|
static compare(elementA: SymbolEntry, elementB: SymbolEntry, searchValue: string): number {
|
|
|
|
// Sort by Type if name is identical
|
|
const elementAName = elementA.getLabel().toLowerCase();
|
|
const elementBName = elementB.getLabel().toLowerCase();
|
|
if (elementAName === elementBName) {
|
|
let elementAType = symbolKindToCssClass(elementA.bearing.kind);
|
|
let elementBType = symbolKindToCssClass(elementB.bearing.kind);
|
|
return elementAType.localeCompare(elementBType);
|
|
}
|
|
|
|
return compareEntries(elementA, elementB, searchValue);
|
|
}
|
|
}
|
|
|
|
export interface IOpenSymbolOptions {
|
|
skipSorting: boolean;
|
|
skipLocalSymbols: boolean;
|
|
skipDelay: boolean;
|
|
}
|
|
|
|
export class OpenSymbolHandler extends QuickOpenHandler {
|
|
|
|
static readonly ID = 'workbench.picker.symbols';
|
|
|
|
private static readonly TYPING_SEARCH_DELAY = 200; // This delay accommodates for the user typing a word and then stops typing to start searching
|
|
|
|
private delayer: ThrottledDelayer<QuickOpenEntry[]>;
|
|
private options: IOpenSymbolOptions;
|
|
|
|
constructor(@IInstantiationService private readonly instantiationService: IInstantiationService) {
|
|
super();
|
|
|
|
this.delayer = new ThrottledDelayer<QuickOpenEntry[]>(OpenSymbolHandler.TYPING_SEARCH_DELAY);
|
|
this.options = Object.create(null);
|
|
}
|
|
|
|
setOptions(options: IOpenSymbolOptions) {
|
|
this.options = options;
|
|
}
|
|
|
|
canRun(): boolean | string {
|
|
return true;
|
|
}
|
|
|
|
getResults(searchValue: string, token: CancellationToken): Promise<QuickOpenModel> {
|
|
searchValue = searchValue.trim();
|
|
|
|
let promise: Promise<QuickOpenEntry[]>;
|
|
if (!this.options.skipDelay) {
|
|
promise = this.delayer.trigger(() => {
|
|
if (token.isCancellationRequested) {
|
|
return Promise.resolve([]);
|
|
}
|
|
|
|
return this.doGetResults(searchValue, token);
|
|
});
|
|
} else {
|
|
promise = this.doGetResults(searchValue, token);
|
|
}
|
|
|
|
return promise.then(e => new QuickOpenModel(e));
|
|
}
|
|
|
|
private doGetResults(searchValue: string, token: CancellationToken): Promise<SymbolEntry[]> {
|
|
return getWorkspaceSymbols(searchValue, token).then(tuples => {
|
|
if (token.isCancellationRequested) {
|
|
return [];
|
|
}
|
|
|
|
const result: SymbolEntry[] = [];
|
|
for (let tuple of tuples) {
|
|
const [provider, bearings] = tuple;
|
|
this.fillInSymbolEntries(result, provider, bearings, searchValue);
|
|
}
|
|
|
|
// Sort (Standalone only)
|
|
if (!this.options.skipSorting) {
|
|
searchValue = searchValue ? strings.stripWildcards(searchValue.toLowerCase()) : searchValue;
|
|
return result.sort((a, b) => SymbolEntry.compare(a, b, searchValue));
|
|
}
|
|
|
|
return result;
|
|
});
|
|
}
|
|
|
|
private fillInSymbolEntries(bucket: SymbolEntry[], provider: IWorkspaceSymbolProvider, types: IWorkspaceSymbol[], searchValue: string): void {
|
|
|
|
// Convert to Entries
|
|
for (let element of types) {
|
|
if (this.options.skipLocalSymbols && !!element.containerName) {
|
|
continue; // ignore local symbols if we are told so
|
|
}
|
|
|
|
const entry = this.instantiationService.createInstance(SymbolEntry, element, provider);
|
|
entry.setHighlights(filters.matchesFuzzy2(searchValue, entry.getLabel()) || []);
|
|
bucket.push(entry);
|
|
}
|
|
}
|
|
|
|
getGroupLabel(): string {
|
|
return nls.localize('symbols', "symbol results");
|
|
}
|
|
|
|
getEmptyLabel(searchString: string): string {
|
|
if (searchString.length > 0) {
|
|
return nls.localize('noSymbolsMatching', "No symbols matching");
|
|
}
|
|
return nls.localize('noSymbolsWithoutInput', "Type to search for symbols");
|
|
}
|
|
|
|
getAutoFocus(searchValue: string): IAutoFocus {
|
|
return {
|
|
autoFocusFirstEntry: true,
|
|
autoFocusPrefixMatch: searchValue.trim()
|
|
};
|
|
}
|
|
}
|