mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-20 12:00:24 -04:00
Merge from vscode 073a24de05773f2261f89172987002dc0ae2f1cd (#9711)
This commit is contained in:
@@ -22,6 +22,10 @@ interface IEditorLineDecoration {
|
||||
overviewRulerDecorationId: string;
|
||||
}
|
||||
|
||||
export interface IEditorNavigationQuickAccessOptions {
|
||||
canAcceptInBackground?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* A reusable quick access provider for the editor with support
|
||||
* for adding decorations for navigating in the currently active file
|
||||
@@ -29,11 +33,16 @@ interface IEditorLineDecoration {
|
||||
*/
|
||||
export abstract class AbstractEditorNavigationQuickAccessProvider implements IQuickAccessProvider {
|
||||
|
||||
constructor(protected options?: IEditorNavigationQuickAccessOptions) { }
|
||||
|
||||
//#region Provider methods
|
||||
|
||||
provide(picker: IQuickPick<IQuickPickItem>, token: CancellationToken): IDisposable {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
// Apply options if any
|
||||
picker.canAcceptInBackground = !!this.options?.canAcceptInBackground;
|
||||
|
||||
// Disable filtering & sorting, we control the results
|
||||
picker.matchOnLabel = picker.matchOnDescription = picker.matchOnDetail = picker.sortByLabel = false;
|
||||
|
||||
@@ -71,11 +80,11 @@ export abstract class AbstractEditorNavigationQuickAccessProvider implements IQu
|
||||
lastKnownEditorViewState = withNullAsUndefined(editor.saveViewState());
|
||||
}));
|
||||
|
||||
once(token.onCancellationRequested)(() => {
|
||||
disposables.add(once(token.onCancellationRequested)(() => {
|
||||
if (lastKnownEditorViewState) {
|
||||
editor.restoreViewState(lastKnownEditorViewState);
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
// Clean up decorations on dispose
|
||||
@@ -110,10 +119,12 @@ export abstract class AbstractEditorNavigationQuickAccessProvider implements IQu
|
||||
*/
|
||||
protected abstract provideWithoutTextEditor(picker: IQuickPick<IQuickPickItem>, token: CancellationToken): IDisposable;
|
||||
|
||||
protected gotoLocation(editor: IEditor, options: { range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean }): void {
|
||||
protected gotoLocation(editor: IEditor, options: { range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean, preserveFocus?: boolean }): void {
|
||||
editor.setSelection(options.range);
|
||||
editor.revealRangeInCenter(options.range, ScrollType.Smooth);
|
||||
editor.focus();
|
||||
if (!options.preserveFocus) {
|
||||
editor.focus();
|
||||
}
|
||||
}
|
||||
|
||||
protected getModel(editor: IEditor | IDiffEditor): ITextModel | undefined {
|
||||
|
||||
@@ -6,11 +6,13 @@
|
||||
import { localize } from 'vs/nls';
|
||||
import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { DisposableStore, IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { DisposableStore, IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IEditor, ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { AbstractEditorNavigationQuickAccessProvider } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { getCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorOption, RenderLineNumbersType } from 'vs/editor/common/config/editorOptions';
|
||||
|
||||
interface IGotoLineQuickPickItem extends IQuickPickItem, Partial<IPosition> { }
|
||||
|
||||
@@ -18,6 +20,10 @@ export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditor
|
||||
|
||||
static PREFIX = ':';
|
||||
|
||||
constructor() {
|
||||
super({ canAcceptInBackground: true });
|
||||
}
|
||||
|
||||
protected provideWithoutTextEditor(picker: IQuickPick<IGotoLineQuickPickItem>): IDisposable {
|
||||
const label = localize('cannotRunGotoLine', "Open a text editor first to go to a line.");
|
||||
|
||||
@@ -31,16 +37,18 @@ export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditor
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
// Goto line once picked
|
||||
disposables.add(picker.onDidAccept(() => {
|
||||
disposables.add(picker.onDidAccept(event => {
|
||||
const [item] = picker.selectedItems;
|
||||
if (item) {
|
||||
if (!this.isValidLineNumber(editor, item.lineNumber)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.gotoLocation(editor, { range: this.toRange(item.lineNumber, item.column), keyMods: picker.keyMods });
|
||||
this.gotoLocation(editor, { range: this.toRange(item.lineNumber, item.column), keyMods: picker.keyMods, preserveFocus: event.inBackground });
|
||||
|
||||
picker.hide();
|
||||
if (!event.inBackground) {
|
||||
picker.hide();
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -75,6 +83,18 @@ export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditor
|
||||
updatePickerAndEditor();
|
||||
disposables.add(picker.onDidChangeValue(() => updatePickerAndEditor()));
|
||||
|
||||
// Adjust line number visibility as needed
|
||||
const codeEditor = getCodeEditor(editor);
|
||||
if (codeEditor) {
|
||||
const options = codeEditor.getOptions();
|
||||
const lineNumbers = options.get(EditorOption.lineNumbers);
|
||||
if (lineNumbers.renderType === RenderLineNumbersType.Relative) {
|
||||
codeEditor.updateOptions({ lineNumbers: 'on' });
|
||||
|
||||
disposables.add(toDisposable(() => codeEditor.updateOptions({ lineNumbers: 'relative' })));
|
||||
}
|
||||
}
|
||||
|
||||
return disposables;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,25 +6,26 @@
|
||||
import { localize } from 'vs/nls';
|
||||
import { IQuickPick, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { DisposableStore, IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { DisposableStore, IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IEditor, ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { AbstractEditorNavigationQuickAccessProvider } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess';
|
||||
import { AbstractEditorNavigationQuickAccessProvider, IEditorNavigationQuickAccessOptions } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess';
|
||||
import { DocumentSymbol, SymbolKinds, SymbolTag, DocumentSymbolProviderRegistry, SymbolKind } from 'vs/editor/common/modes';
|
||||
import { OutlineModel, OutlineElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
|
||||
import { values } from 'vs/base/common/collections';
|
||||
import { trim, format } from 'vs/base/common/strings';
|
||||
import { fuzzyScore, FuzzyScore, createMatches } from 'vs/base/common/filters';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
|
||||
interface IGotoSymbolQuickPickItem extends IQuickPickItem {
|
||||
export interface IGotoSymbolQuickPickItem extends IQuickPickItem {
|
||||
kind: SymbolKind,
|
||||
index: number,
|
||||
score?: FuzzyScore;
|
||||
range?: { decoration: IRange, selection: IRange },
|
||||
range?: { decoration: IRange, selection: IRange }
|
||||
}
|
||||
|
||||
export interface IGotoSymbolQuickAccessProviderOptions {
|
||||
export interface IGotoSymbolQuickAccessProviderOptions extends IEditorNavigationQuickAccessOptions {
|
||||
openSideBySideDirection: () => undefined | 'right' | 'down'
|
||||
}
|
||||
|
||||
@@ -34,8 +35,8 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit
|
||||
static SCOPE_PREFIX = ':';
|
||||
static PREFIX_BY_CATEGORY = `${AbstractGotoSymbolQuickAccessProvider.PREFIX}${AbstractGotoSymbolQuickAccessProvider.SCOPE_PREFIX}`;
|
||||
|
||||
constructor(private options?: IGotoSymbolQuickAccessProviderOptions) {
|
||||
super();
|
||||
constructor(protected options?: IGotoSymbolQuickAccessProviderOptions) {
|
||||
super(assign(options, { canAcceptInBackground: true }));
|
||||
}
|
||||
|
||||
protected provideWithoutTextEditor(picker: IQuickPick<IGotoSymbolQuickPickItem>): IDisposable {
|
||||
@@ -72,32 +73,58 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit
|
||||
picker.items = [{ label, index: 0, kind: SymbolKind.String }];
|
||||
picker.ariaLabel = label;
|
||||
|
||||
// Listen to changes to the registry and see if eventually
|
||||
// Wait for changes to the registry and see if eventually
|
||||
// we do get symbols. This can happen if the picker is opened
|
||||
// very early after the model has loaded but before the
|
||||
// language registry is ready.
|
||||
// https://github.com/microsoft/vscode/issues/70607
|
||||
(async () => {
|
||||
const result = await this.waitForLanguageSymbolRegistry(model, disposables);
|
||||
if (!result || token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
disposables.add(this.doProvideWithEditorSymbols(editor, model, picker, token));
|
||||
})();
|
||||
|
||||
return disposables;
|
||||
}
|
||||
|
||||
protected async waitForLanguageSymbolRegistry(model: ITextModel, disposables: DisposableStore): Promise<boolean> {
|
||||
if (DocumentSymbolProviderRegistry.has(model)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let symbolProviderRegistryPromiseResolve: (res: boolean) => void;
|
||||
const symbolProviderRegistryPromise = new Promise<boolean>(resolve => symbolProviderRegistryPromiseResolve = resolve);
|
||||
|
||||
// Resolve promise when registry knows model
|
||||
const symbolProviderListener = disposables.add(DocumentSymbolProviderRegistry.onDidChange(() => {
|
||||
if (DocumentSymbolProviderRegistry.has(model)) {
|
||||
symbolProviderListener.dispose();
|
||||
|
||||
disposables.add(this.doProvideWithEditorSymbols(editor, model, picker, token));
|
||||
symbolProviderRegistryPromiseResolve(true);
|
||||
}
|
||||
}));
|
||||
|
||||
return disposables;
|
||||
// Resolve promise when we get disposed too
|
||||
disposables.add(toDisposable(() => symbolProviderRegistryPromiseResolve(false)));
|
||||
|
||||
return symbolProviderRegistryPromise;
|
||||
}
|
||||
|
||||
private doProvideWithEditorSymbols(editor: IEditor, model: ITextModel, picker: IQuickPick<IGotoSymbolQuickPickItem>, token: CancellationToken): IDisposable {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
// Goto symbol once picked
|
||||
disposables.add(picker.onDidAccept(() => {
|
||||
disposables.add(picker.onDidAccept(event => {
|
||||
const [item] = picker.selectedItems;
|
||||
if (item && item.range) {
|
||||
this.gotoLocation(editor, { range: item.range.selection, keyMods: picker.keyMods });
|
||||
this.gotoLocation(editor, { range: item.range.selection, keyMods: picker.keyMods, preserveFocus: event.inBackground });
|
||||
|
||||
picker.hide();
|
||||
if (!event.inBackground) {
|
||||
picker.hide();
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -128,7 +155,7 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit
|
||||
// Collect symbol picks
|
||||
picker.busy = true;
|
||||
try {
|
||||
const items = await this.getSymbolPicks(symbolsPromise, picker.value.substr(AbstractGotoSymbolQuickAccessProvider.PREFIX.length).trim(), picksCts.token);
|
||||
const items = await this.doGetSymbolPicks(symbolsPromise, picker.value.substr(AbstractGotoSymbolQuickAccessProvider.PREFIX.length).trim(), picksCts.token);
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
@@ -167,7 +194,7 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit
|
||||
return disposables;
|
||||
}
|
||||
|
||||
private async getSymbolPicks(symbolsPromise: Promise<DocumentSymbol[]>, filter: string, token: CancellationToken): Promise<Array<IGotoSymbolQuickPickItem | IQuickPickSeparator>> {
|
||||
protected async doGetSymbolPicks(symbolsPromise: Promise<DocumentSymbol[]>, filter: string, token: CancellationToken): Promise<Array<IGotoSymbolQuickPickItem | IQuickPickSeparator>> {
|
||||
const symbols = await symbolsPromise;
|
||||
if (token.isCancellationRequested) {
|
||||
return [];
|
||||
@@ -340,7 +367,7 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit
|
||||
return result;
|
||||
}
|
||||
|
||||
private async getDocumentSymbols(document: ITextModel, flatten: boolean, token: CancellationToken): Promise<DocumentSymbol[]> {
|
||||
protected async getDocumentSymbols(document: ITextModel, flatten: boolean, token: CancellationToken): Promise<DocumentSymbol[]> {
|
||||
const model = await OutlineModel.create(document, token);
|
||||
if (token.isCancellationRequested) {
|
||||
return [];
|
||||
|
||||
@@ -50,7 +50,8 @@ suite('SmartSelect', () => {
|
||||
|
||||
setup(() => {
|
||||
const configurationService = new TestConfigurationService();
|
||||
modelService = new ModelServiceImpl(configurationService, new TestTextResourcePropertiesService(configurationService), new TestThemeService(), new NullLogService(), new UndoRedoService(new TestDialogService(), new TestNotificationService()));
|
||||
const dialogService = new TestDialogService();
|
||||
modelService = new ModelServiceImpl(configurationService, new TestTextResourcePropertiesService(configurationService), new TestThemeService(), new NullLogService(), new UndoRedoService(dialogService, new TestNotificationService()), dialogService);
|
||||
mode = new MockJSMode();
|
||||
});
|
||||
|
||||
|
||||
@@ -604,6 +604,12 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
useShadows: false,
|
||||
openController: { shouldOpen: () => false },
|
||||
mouseSupport: false,
|
||||
ariaRole: 'listbox',
|
||||
ariaProvider: {
|
||||
getRole: () => 'option',
|
||||
getSetSize: (_: CompletionItem, _index: number, listLength: number) => listLength,
|
||||
getPosInSet: (_: CompletionItem, index: number) => index,
|
||||
},
|
||||
accessibilityProvider: {
|
||||
getAriaLabel: (item: CompletionItem) => {
|
||||
const textLabel = typeof item.completion.label === 'string' ? item.completion.label : item.completion.label.name;
|
||||
|
||||
Reference in New Issue
Block a user