mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode 1fbacccbc900bb59ba8a8f26a4128d48a1c97842
This commit is contained in:
@@ -103,7 +103,15 @@ export class OpenerService implements IOpenerService {
|
||||
// Default external opener is going through window.open()
|
||||
this._externalOpener = {
|
||||
openExternal: href => {
|
||||
dom.windowOpenNoOpener(href);
|
||||
// ensure to open HTTP/HTTPS links into new windows
|
||||
// to not trigger a navigation. Any other link is
|
||||
// safe to be set as HREF to prevent a blank window
|
||||
// from opening.
|
||||
if (matchesScheme(href, Schemas.http) || matchesScheme(href, Schemas.https)) {
|
||||
dom.windowOpenNoOpener(href);
|
||||
} else {
|
||||
window.location.href = href;
|
||||
}
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -118,7 +118,7 @@ export class View extends ViewEventHandler {
|
||||
this._context = new ViewContext(configuration, themeService.getTheme(), model, this.eventDispatcher);
|
||||
|
||||
this._register(themeService.onThemeChange(theme => {
|
||||
this._context.theme = theme;
|
||||
this._context.theme.update(theme);
|
||||
this.eventDispatcher.emit(new viewEvents.ViewThemeChangedEvent());
|
||||
this.render(true, false);
|
||||
}));
|
||||
|
||||
@@ -22,11 +22,11 @@ import { MinimapCharRenderer } from 'vs/editor/browser/viewParts/minimap/minimap
|
||||
import { Constants } from 'vs/editor/browser/viewParts/minimap/minimapCharSheet';
|
||||
import { MinimapTokensColorTracker } from 'vs/editor/common/viewModel/minimapTokensColorTracker';
|
||||
import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext';
|
||||
import { ViewContext } from 'vs/editor/common/view/viewContext';
|
||||
import { ViewContext, EditorTheme } from 'vs/editor/common/view/viewContext';
|
||||
import * as viewEvents from 'vs/editor/common/view/viewEvents';
|
||||
import { ViewLineData } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { minimapSelection, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, minimapBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { registerThemingParticipant, ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { ModelDecorationMinimapOptions } from 'vs/editor/common/model/textModel';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
@@ -109,7 +109,7 @@ class MinimapOptions {
|
||||
|
||||
public readonly backgroundColor: RGBA8;
|
||||
|
||||
constructor(configuration: IConfiguration, theme: ITheme, tokensColorTracker: MinimapTokensColorTracker) {
|
||||
constructor(configuration: IConfiguration, theme: EditorTheme, tokensColorTracker: MinimapTokensColorTracker) {
|
||||
const options = configuration.options;
|
||||
const pixelRatio = options.get(EditorOption.pixelRatio);
|
||||
const layoutInfo = options.get(EditorOption.layoutInfo);
|
||||
@@ -137,7 +137,7 @@ class MinimapOptions {
|
||||
this.backgroundColor = MinimapOptions._getMinimapBackground(theme, tokensColorTracker);
|
||||
}
|
||||
|
||||
private static _getMinimapBackground(theme: ITheme, tokensColorTracker: MinimapTokensColorTracker): RGBA8 {
|
||||
private static _getMinimapBackground(theme: EditorTheme, tokensColorTracker: MinimapTokensColorTracker): RGBA8 {
|
||||
const themeColor = theme.getColor(minimapBackground);
|
||||
if (themeColor) {
|
||||
return new RGBA8(themeColor.rgba.r, themeColor.rgba.g, themeColor.rgba.b, themeColor.rgba.a);
|
||||
|
||||
@@ -12,9 +12,8 @@ import { IConfiguration } from 'vs/editor/common/editorCommon';
|
||||
import { TokenizationRegistry } from 'vs/editor/common/modes';
|
||||
import { editorCursorForeground, editorOverviewRulerBorder } from 'vs/editor/common/view/editorColorRegistry';
|
||||
import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext';
|
||||
import { ViewContext } from 'vs/editor/common/view/viewContext';
|
||||
import { ViewContext, EditorTheme } from 'vs/editor/common/view/viewContext';
|
||||
import * as viewEvents from 'vs/editor/common/view/viewEvents';
|
||||
import { ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
|
||||
class Settings {
|
||||
@@ -42,7 +41,7 @@ class Settings {
|
||||
public readonly x: number[];
|
||||
public readonly w: number[];
|
||||
|
||||
constructor(config: IConfiguration, theme: ITheme) {
|
||||
constructor(config: IConfiguration, theme: EditorTheme) {
|
||||
const options = config.options;
|
||||
this.lineHeight = options.get(EditorOption.lineHeight);
|
||||
this.pixelRatio = options.get(EditorOption.pixelRatio);
|
||||
|
||||
@@ -29,12 +29,13 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo
|
||||
import { NULL_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/nullMode';
|
||||
import { ignoreBracketsInToken } from 'vs/editor/common/modes/supports';
|
||||
import { BracketsUtils, RichEditBracket, RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets';
|
||||
import { ITheme, ThemeColor } from 'vs/platform/theme/common/themeService';
|
||||
import { ThemeColor } from 'vs/platform/theme/common/themeService';
|
||||
import { withUndefinedAsNull } from 'vs/base/common/types';
|
||||
import { VSBufferReadableStream, VSBuffer } from 'vs/base/common/buffer';
|
||||
import { TokensStore, MultilineTokens, countEOL, MultilineTokens2, TokensStore2 } from 'vs/editor/common/model/tokensStore';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { Constants } from 'vs/base/common/uint';
|
||||
import { EditorTheme } from 'vs/editor/common/view/viewContext';
|
||||
|
||||
function createTextBufferBuilder() {
|
||||
return new PieceTreeTextBufferBuilder();
|
||||
@@ -2945,7 +2946,7 @@ export class ModelDecorationOverviewRulerOptions extends DecorationOptions {
|
||||
this.position = (typeof options.position === 'number' ? options.position : model.OverviewRulerLane.Center);
|
||||
}
|
||||
|
||||
public getColor(theme: ITheme): string {
|
||||
public getColor(theme: EditorTheme): string {
|
||||
if (!this._resolvedColor) {
|
||||
if (theme.type !== 'light' && this.darkColor) {
|
||||
this._resolvedColor = this._resolveColor(this.darkColor, theme);
|
||||
@@ -2960,7 +2961,7 @@ export class ModelDecorationOverviewRulerOptions extends DecorationOptions {
|
||||
this._resolvedColor = null;
|
||||
}
|
||||
|
||||
private _resolveColor(color: string | ThemeColor, theme: ITheme): string {
|
||||
private _resolveColor(color: string | ThemeColor, theme: EditorTheme): string {
|
||||
if (typeof color === 'string') {
|
||||
return color;
|
||||
}
|
||||
@@ -2982,7 +2983,7 @@ export class ModelDecorationMinimapOptions extends DecorationOptions {
|
||||
this.position = options.position;
|
||||
}
|
||||
|
||||
public getColor(theme: ITheme): Color | undefined {
|
||||
public getColor(theme: EditorTheme): Color | undefined {
|
||||
if (!this._resolvedColor) {
|
||||
if (theme.type !== 'light' && this.darkColor) {
|
||||
this._resolvedColor = this._resolveColor(this.darkColor, theme);
|
||||
@@ -2998,7 +2999,7 @@ export class ModelDecorationMinimapOptions extends DecorationOptions {
|
||||
this._resolvedColor = undefined;
|
||||
}
|
||||
|
||||
private _resolveColor(color: string | ThemeColor, theme: ITheme): Color | undefined {
|
||||
private _resolveColor(color: string | ThemeColor, theme: EditorTheme): Color | undefined {
|
||||
if (typeof color === 'string') {
|
||||
return Color.fromHex(color);
|
||||
}
|
||||
|
||||
@@ -615,7 +615,9 @@ export interface CodeActionProvider {
|
||||
/**
|
||||
* Optional list of CodeActionKinds that this provider returns.
|
||||
*/
|
||||
providedCodeActionKinds?: ReadonlyArray<string>;
|
||||
readonly providedCodeActionKinds?: ReadonlyArray<string>;
|
||||
|
||||
readonly documentation?: ReadonlyArray<{ readonly kind: string, readonly command: Command }>;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
|
||||
@@ -7,7 +7,30 @@ import { IConfiguration } from 'vs/editor/common/editorCommon';
|
||||
import { ViewEventDispatcher } from 'vs/editor/common/view/viewEventDispatcher';
|
||||
import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler';
|
||||
import { IViewLayout, IViewModel } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { ITheme, ThemeType } from 'vs/platform/theme/common/themeService';
|
||||
import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
export class EditorTheme {
|
||||
|
||||
private _theme: ITheme;
|
||||
|
||||
public get type(): ThemeType {
|
||||
return this._theme.type;
|
||||
}
|
||||
|
||||
constructor(theme: ITheme) {
|
||||
this._theme = theme;
|
||||
}
|
||||
|
||||
public update(theme: ITheme): void {
|
||||
this._theme = theme;
|
||||
}
|
||||
|
||||
public getColor(color: ColorIdentifier): Color | undefined {
|
||||
return this._theme.getColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
export class ViewContext {
|
||||
|
||||
@@ -15,8 +38,7 @@ export class ViewContext {
|
||||
public readonly model: IViewModel;
|
||||
public readonly viewLayout: IViewLayout;
|
||||
public readonly privateViewEventBus: ViewEventDispatcher;
|
||||
|
||||
public theme: ITheme; // will be updated
|
||||
public readonly theme: EditorTheme;
|
||||
|
||||
constructor(
|
||||
configuration: IConfiguration,
|
||||
@@ -25,7 +47,7 @@ export class ViewContext {
|
||||
privateViewEventBus: ViewEventDispatcher
|
||||
) {
|
||||
this.configuration = configuration;
|
||||
this.theme = theme;
|
||||
this.theme = new EditorTheme(theme);
|
||||
this.model = model;
|
||||
this.viewLayout = model.viewLayout;
|
||||
this.privateViewEventBus = privateViewEventBus;
|
||||
|
||||
@@ -13,9 +13,9 @@ import { ModelDecorationOptions, ModelDecorationOverviewRulerOptions } from 'vs/
|
||||
import * as viewEvents from 'vs/editor/common/view/viewEvents';
|
||||
import { PrefixSumIndexOfResult } from 'vs/editor/common/viewModel/prefixSumComputer';
|
||||
import { ICoordinatesConverter, IOverviewRulerDecorations, ViewLineData } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { FontInfo } from 'vs/editor/common/config/fontInfo';
|
||||
import { EditorTheme } from 'vs/editor/common/view/viewContext';
|
||||
|
||||
export class OutputPosition {
|
||||
outputLineIndex: number;
|
||||
@@ -131,7 +131,7 @@ export interface IViewModelLinesCollection extends IDisposable {
|
||||
getViewLineData(viewLineNumber: number): ViewLineData;
|
||||
getViewLinesData(viewStartLineNumber: number, viewEndLineNumber: number, needed: boolean[]): Array<ViewLineData | null>;
|
||||
|
||||
getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: ITheme): IOverviewRulerDecorations;
|
||||
getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: EditorTheme): IOverviewRulerDecorations;
|
||||
getDecorationsInRange(range: Range, ownerId: number, filterOutValidation: boolean): IModelDecoration[];
|
||||
}
|
||||
|
||||
@@ -940,7 +940,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection {
|
||||
return this.lines[lineIndex].getViewLineNumberOfModelPosition(deltaLineNumber, this.model.getLineMaxColumn(lineIndex + 1));
|
||||
}
|
||||
|
||||
public getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: ITheme): IOverviewRulerDecorations {
|
||||
public getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: EditorTheme): IOverviewRulerDecorations {
|
||||
const decorations = this.model.getOverviewRulerDecorations(ownerId, filterOutValidation);
|
||||
const result = new OverviewRulerDecorations();
|
||||
for (const decoration of decorations) {
|
||||
@@ -1561,7 +1561,7 @@ export class IdentityLinesCollection implements IViewModelLinesCollection {
|
||||
return result;
|
||||
}
|
||||
|
||||
public getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: ITheme): IOverviewRulerDecorations {
|
||||
public getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: EditorTheme): IOverviewRulerDecorations {
|
||||
const decorations = this.model.getOverviewRulerDecorations(ownerId, filterOutValidation);
|
||||
const result = new OverviewRulerDecorations();
|
||||
for (const decoration of decorations) {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecorationOptions, T
|
||||
import { IViewEventListener } from 'vs/editor/common/view/viewEvents';
|
||||
import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData';
|
||||
import { IEditorWhitespace, IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout';
|
||||
import { ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { EditorTheme } from 'vs/editor/common/view/viewContext';
|
||||
|
||||
export interface IViewWhitespaceViewportData {
|
||||
readonly id: string;
|
||||
@@ -127,7 +127,7 @@ export interface IViewModel {
|
||||
getLineMaxColumn(lineNumber: number): number;
|
||||
getLineFirstNonWhitespaceColumn(lineNumber: number): number;
|
||||
getLineLastNonWhitespaceColumn(lineNumber: number): number;
|
||||
getAllOverviewRulerDecorations(theme: ITheme): IOverviewRulerDecorations;
|
||||
getAllOverviewRulerDecorations(theme: EditorTheme): IOverviewRulerDecorations;
|
||||
invalidateOverviewRulerColorCache(): void;
|
||||
invalidateMinimapColorCache(): void;
|
||||
getValueInRange(range: Range, eol: EndOfLinePreference): string;
|
||||
|
||||
@@ -21,9 +21,9 @@ import { ViewLayout } from 'vs/editor/common/viewLayout/viewLayout';
|
||||
import { IViewModelLinesCollection, IdentityLinesCollection, SplitLinesCollection, ILineBreaksComputerFactory } from 'vs/editor/common/viewModel/splitLinesCollection';
|
||||
import { ICoordinatesConverter, IOverviewRulerDecorations, IViewModel, MinimapLinesRenderingData, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations';
|
||||
import { ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { EditorTheme } from 'vs/editor/common/view/viewContext';
|
||||
|
||||
const USE_IDENTITY_LINES_COLLECTION = true;
|
||||
|
||||
@@ -595,7 +595,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
);
|
||||
}
|
||||
|
||||
public getAllOverviewRulerDecorations(theme: ITheme): IOverviewRulerDecorations {
|
||||
public getAllOverviewRulerDecorations(theme: EditorTheme): IOverviewRulerDecorations {
|
||||
return this.lines.getAllOverviewRulerDecorations(this.editorId, filterValidationDecorations(this.configuration.options), theme);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { equals, flatten, isNonEmptyArray, mergeSort } from 'vs/base/common/arrays';
|
||||
import { equals, flatten, isNonEmptyArray, mergeSort, coalesce } from 'vs/base/common/arrays';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { illegalArgument, isPromiseCanceledError, onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
@@ -27,6 +27,8 @@ export interface CodeActionSet extends IDisposable {
|
||||
readonly validActions: readonly modes.CodeAction[];
|
||||
readonly allActions: readonly modes.CodeAction[];
|
||||
readonly hasAutoFix: boolean;
|
||||
|
||||
readonly documentation: readonly modes.Command[];
|
||||
}
|
||||
|
||||
class ManagedCodeActionSet extends Disposable implements CodeActionSet {
|
||||
@@ -48,7 +50,11 @@ class ManagedCodeActionSet extends Disposable implements CodeActionSet {
|
||||
public readonly validActions: readonly modes.CodeAction[];
|
||||
public readonly allActions: readonly modes.CodeAction[];
|
||||
|
||||
public constructor(actions: readonly modes.CodeAction[], disposables: DisposableStore) {
|
||||
public constructor(
|
||||
actions: readonly modes.CodeAction[],
|
||||
public readonly documentation: readonly modes.Command[],
|
||||
disposables: DisposableStore,
|
||||
) {
|
||||
super();
|
||||
this._register(disposables);
|
||||
this.allActions = mergeSort([...actions], ManagedCodeActionSet.codeActionsComparator);
|
||||
@@ -80,17 +86,23 @@ export function getCodeActions(
|
||||
const promises = providers.map(async provider => {
|
||||
try {
|
||||
const providedCodeActions = await provider.provideCodeActions(model, rangeOrSelection, codeActionContext, cts.token);
|
||||
if (cts.token.isCancellationRequested || !providedCodeActions) {
|
||||
return [];
|
||||
if (providedCodeActions) {
|
||||
disposables.add(providedCodeActions);
|
||||
}
|
||||
disposables.add(providedCodeActions);
|
||||
return providedCodeActions.actions.filter(action => action && filtersAction(filter, action));
|
||||
|
||||
if (cts.token.isCancellationRequested) {
|
||||
return { actions: [] as modes.CodeAction[], documentation: undefined };
|
||||
}
|
||||
|
||||
const filteredActions = (providedCodeActions?.actions || []).filter(action => action && filtersAction(filter, action));
|
||||
const documentation = getDocumentation(provider, filteredActions, filter.include);
|
||||
return { actions: filteredActions, documentation };
|
||||
} catch (err) {
|
||||
if (isPromiseCanceledError(err)) {
|
||||
throw err;
|
||||
}
|
||||
onUnexpectedExternalError(err);
|
||||
return [];
|
||||
return { actions: [] as modes.CodeAction[], documentation: undefined };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -101,9 +113,11 @@ export function getCodeActions(
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all(promises)
|
||||
.then(flatten)
|
||||
.then(actions => new ManagedCodeActionSet(actions, disposables))
|
||||
return Promise.all(promises).then(actions => {
|
||||
const allActions = flatten(actions.map(x => x.actions));
|
||||
const allDocumentation = coalesce(actions.map(x => x.documentation));
|
||||
return new ManagedCodeActionSet(allActions, allDocumentation, disposables);
|
||||
})
|
||||
.finally(() => {
|
||||
listener.dispose();
|
||||
cts.dispose();
|
||||
@@ -125,6 +139,52 @@ function getCodeActionProviders(
|
||||
});
|
||||
}
|
||||
|
||||
function getDocumentation(
|
||||
provider: modes.CodeActionProvider,
|
||||
providedCodeActions: readonly modes.CodeAction[],
|
||||
only?: CodeActionKind
|
||||
): modes.Command | undefined {
|
||||
if (!provider.documentation) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const documentation = provider.documentation.map(entry => ({ kind: new CodeActionKind(entry.kind), command: entry.command }));
|
||||
|
||||
if (only) {
|
||||
let currentBest: { readonly kind: CodeActionKind, readonly command: modes.Command } | undefined;
|
||||
for (const entry of documentation) {
|
||||
if (entry.kind.contains(only)) {
|
||||
if (!currentBest) {
|
||||
currentBest = entry;
|
||||
} else {
|
||||
// Take best match
|
||||
if (currentBest.kind.contains(entry.kind)) {
|
||||
currentBest = entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentBest) {
|
||||
return currentBest?.command;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, check to see if any of the provided actions match.
|
||||
for (const action of providedCodeActions) {
|
||||
if (!action.kind) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const entry of documentation) {
|
||||
if (entry.kind.contains(new CodeActionKind(action.kind))) {
|
||||
return entry.command;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
registerLanguageCommand('_executeCodeActionProvider', async function (accessor, args): Promise<ReadonlyArray<modes.CodeAction>> {
|
||||
const { resource, rangeOrSelection, kind } = args;
|
||||
if (!(resource instanceof URI)) {
|
||||
|
||||
@@ -14,9 +14,9 @@ import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IPosition, Position } from 'vs/editor/common/core/position';
|
||||
import { ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { CodeAction, CodeActionProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { CodeAction, CodeActionProviderRegistry, Command } from 'vs/editor/common/modes';
|
||||
import { codeActionCommandId, CodeActionSet, fixAllCommandId, organizeImportsCommandId, refactorCommandId, sourceActionCommandId } from 'vs/editor/contrib/codeAction/codeAction';
|
||||
import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionKind, CodeActionTrigger } from 'vs/editor/contrib/codeAction/types';
|
||||
import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionTrigger, CodeActionKind } from 'vs/editor/contrib/codeAction/types';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
|
||||
@@ -84,7 +84,7 @@ export class CodeActionMenu extends Disposable {
|
||||
this._visible = true;
|
||||
this._showingActions.value = codeActions;
|
||||
|
||||
const menuActions = this.getMenuActions(trigger, actionsToShow);
|
||||
const menuActions = this.getMenuActions(trigger, actionsToShow, codeActions.documentation);
|
||||
|
||||
const anchor = Position.isIPosition(at) ? this._toCoords(at) : at || { x: 0, y: 0 };
|
||||
const resolver = this._keybindingResolver.getResolver();
|
||||
@@ -101,28 +101,34 @@ export class CodeActionMenu extends Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
private getMenuActions(trigger: CodeActionTrigger, actionsToShow: readonly CodeAction[]): IAction[] {
|
||||
private getMenuActions(
|
||||
trigger: CodeActionTrigger,
|
||||
actionsToShow: readonly CodeAction[],
|
||||
documentation: readonly Command[]
|
||||
): IAction[] {
|
||||
const toCodeActionAction = (action: CodeAction): CodeActionAction => new CodeActionAction(action, () => this._delegate.onSelectCodeAction(action));
|
||||
|
||||
const result: IAction[] = actionsToShow
|
||||
.map(toCodeActionAction);
|
||||
|
||||
const allDocumentation: Command[] = [...documentation];
|
||||
|
||||
const model = this._editor.getModel();
|
||||
if (model && result.length) {
|
||||
for (const provider of CodeActionProviderRegistry.all(model)) {
|
||||
if (provider._getAdditionalMenuItems) {
|
||||
const items = provider._getAdditionalMenuItems({ trigger: trigger.type, only: trigger.filter?.include?.value }, actionsToShow);
|
||||
if (items.length) {
|
||||
result.push(new Separator(), ...items.map(command => toCodeActionAction({
|
||||
title: command.title,
|
||||
command: command,
|
||||
})));
|
||||
}
|
||||
allDocumentation.push(...provider._getAdditionalMenuItems({ trigger: trigger.type, only: trigger.filter?.include?.value }, actionsToShow));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allDocumentation.length) {
|
||||
result.push(new Separator(), ...allDocumentation.map(command => toCodeActionAction({
|
||||
title: command.title,
|
||||
command: command,
|
||||
})));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { flatten, coalesce } from 'vs/base/common/arrays';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import { registerModelAndPositionCommand } from 'vs/editor/browser/editorExtensions';
|
||||
@@ -28,9 +27,18 @@ function getLocationLinks<T>(
|
||||
return undefined;
|
||||
});
|
||||
});
|
||||
return Promise.all(promises)
|
||||
.then(flatten)
|
||||
.then(coalesce);
|
||||
|
||||
return Promise.all(promises).then(values => {
|
||||
const result: LocationLink[] = [];
|
||||
for (let value of values) {
|
||||
if (Array.isArray(value)) {
|
||||
result.push(...value);
|
||||
} else if (value) {
|
||||
result.push(value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -181,6 +181,8 @@ export interface SelectionEvent {
|
||||
readonly element?: Location;
|
||||
}
|
||||
|
||||
class ReferencesTree extends WorkbenchAsyncDataTree<ReferencesModel | FileReferences, TreeElement, FuzzyScore> { }
|
||||
|
||||
/**
|
||||
* ZoneWidget that is shown inside the editor
|
||||
*/
|
||||
@@ -195,7 +197,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget {
|
||||
private readonly _onDidSelectReference = new Emitter<SelectionEvent>();
|
||||
readonly onDidSelectReference = this._onDidSelectReference.event;
|
||||
|
||||
private _tree!: WorkbenchAsyncDataTree<ReferencesModel | FileReferences, TreeElement, FuzzyScore>;
|
||||
private _tree!: ReferencesTree;
|
||||
private _treeContainer!: HTMLElement;
|
||||
private _splitView!: SplitView;
|
||||
private _preview!: ICodeEditor;
|
||||
@@ -316,8 +318,8 @@ export class ReferenceWidget extends peekView.PeekViewWidget {
|
||||
listBackground: peekView.peekViewResultsBackground
|
||||
}
|
||||
};
|
||||
this._tree = this._instantiationService.createInstance<typeof WorkbenchAsyncDataTree, WorkbenchAsyncDataTree<ReferencesModel | FileReferences, TreeElement, FuzzyScore>>(
|
||||
WorkbenchAsyncDataTree,
|
||||
this._tree = this._instantiationService.createInstance(
|
||||
ReferencesTree,
|
||||
'ReferencesWidget',
|
||||
this._treeContainer,
|
||||
new Delegate(),
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
.monaco-editor .parameter-hints-widget .monaco-scrollable-element,
|
||||
.monaco-editor .parameter-hints-widget .body {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
@@ -137,8 +137,6 @@
|
||||
border-bottom-style: solid;
|
||||
|
||||
padding: 0 8px 0 4px;
|
||||
|
||||
box-shadow: 0 -.5px 3px #ddd;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.list-right.docs-side > .suggest-status-bar {
|
||||
@@ -177,6 +175,11 @@
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .signature-label {
|
||||
overflow: auto;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .qualifier-label {
|
||||
margin-left: 4px;
|
||||
opacity: 0.4;
|
||||
@@ -213,8 +216,8 @@
|
||||
|
||||
/** Details: if using CompletionItemLabel#details, always show **/
|
||||
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right.always-show-details > .details-label,
|
||||
.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .right.always-show-details > .details-label {
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row:not(.string-label) > .contents > .main > .right > .details-label,
|
||||
.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused:not(.string-label) > .contents > .main > .right > .details-label {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
@@ -228,6 +231,12 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .monaco-icon-label {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row:not(.string-label) > .contents > .main > .left > .monaco-icon-label {
|
||||
max-width: 80%;
|
||||
}
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row.string-label > .contents > .main > .left > .monaco-icon-label {
|
||||
flex-shrink: 1;
|
||||
}
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right {
|
||||
@@ -253,11 +262,11 @@
|
||||
}
|
||||
|
||||
/** Do NOT display ReadMore when using plain CompletionItemLabel (details/documentation might not be resolved) **/
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right:not(.always-show-details) > .readMore {
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row.string-label > .contents > .main > .right > .readMore {
|
||||
display: none;
|
||||
}
|
||||
/** Focused item can show ReadMore, but can't when docs is side/below **/
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .right:not(.always-show-details) > .readMore {
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused.string-label > .contents > .main > .right > .readMore {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.with-status-bar .monaco-list .monaco-list-row > .contents > .main > .right > .readMore,
|
||||
.monaco-editor .suggest-widget.with-status-bar .monaco-list .monaco-list-row.focused > .contents > .main > .right:not(.always-show-details) > .readMore {
|
||||
.monaco-editor .suggest-widget.with-status-bar .monaco-list .monaco-list-row.focused.string-label > .contents > .main > .right > .readMore {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ export const Context = {
|
||||
MakesTextEdit: new RawContextKey('suggestionMakesTextEdit', true),
|
||||
AcceptSuggestionsOnEnter: new RawContextKey<boolean>('acceptSuggestionOnEnter', true),
|
||||
HasInsertAndReplaceRange: new RawContextKey('suggestionHasInsertAndReplaceRange', false),
|
||||
CanResolve: new RawContextKey('suggestionCanResolve', false),
|
||||
};
|
||||
|
||||
export const suggestWidgetStatusbarMenu = new MenuId('suggestWidgetStatusBar');
|
||||
@@ -34,9 +35,12 @@ export class CompletionItem {
|
||||
|
||||
_brand!: 'ISuggestionItem';
|
||||
|
||||
private static readonly _defaultResolve = () => Promise.resolve();
|
||||
|
||||
readonly resolve: (token: CancellationToken) => Promise<void>;
|
||||
isResolved: boolean = false;
|
||||
|
||||
|
||||
//
|
||||
readonly editStart: IPosition;
|
||||
readonly editInsertEnd: IPosition;
|
||||
@@ -87,13 +91,13 @@ export class CompletionItem {
|
||||
// create the suggestion resolver
|
||||
const { resolveCompletionItem } = provider;
|
||||
if (typeof resolveCompletionItem !== 'function') {
|
||||
this.resolve = () => Promise.resolve();
|
||||
this.resolve = CompletionItem._defaultResolve;
|
||||
this.isResolved = true;
|
||||
} else {
|
||||
let cached: Promise<void> | undefined;
|
||||
this.resolve = (token) => {
|
||||
if (!cached) {
|
||||
cached = Promise.resolve(resolveCompletionItem.call(provider, model, position, completion, token)).then(value => {
|
||||
cached = Promise.resolve(resolveCompletionItem.call(provider, model, Position.lift(position), completion, token)).then(value => {
|
||||
assign(completion, value);
|
||||
this.isResolved = true;
|
||||
}, err => {
|
||||
|
||||
@@ -139,10 +139,12 @@ export class SuggestController implements IEditorContribution {
|
||||
// Wire up makes text edit context key
|
||||
const ctxMakesTextEdit = SuggestContext.MakesTextEdit.bindTo(this._contextKeyService);
|
||||
const ctxHasInsertAndReplace = SuggestContext.HasInsertAndReplaceRange.bindTo(this._contextKeyService);
|
||||
const ctxCanResolve = SuggestContext.CanResolve.bindTo(this._contextKeyService);
|
||||
|
||||
this._toDispose.add(toDisposable(() => {
|
||||
ctxMakesTextEdit.reset();
|
||||
ctxHasInsertAndReplace.reset();
|
||||
ctxCanResolve.reset();
|
||||
}));
|
||||
|
||||
this._toDispose.add(widget.onDidFocus(({ item }) => {
|
||||
@@ -172,6 +174,9 @@ export class SuggestController implements IEditorContribution {
|
||||
|
||||
// (ctx: hasInsertAndReplaceRange)
|
||||
ctxHasInsertAndReplace.set(!Position.equals(item.editInsertEnd, item.editReplaceEnd));
|
||||
|
||||
// (ctx: canResolve)
|
||||
ctxCanResolve.set(Boolean(item.provider.resolveCompletionItem) || Boolean(item.completion.documentation) || item.completion.detail !== item.completion.label);
|
||||
}));
|
||||
|
||||
this._toDispose.add(widget.onDetailsKeyDown(e => {
|
||||
@@ -695,13 +700,13 @@ registerEditorCommand(new SuggestCommand({
|
||||
menuId: suggestWidgetStatusbarMenu,
|
||||
group: 'right',
|
||||
order: 1,
|
||||
when: SuggestContext.DetailsVisible,
|
||||
when: ContextKeyExpr.and(SuggestContext.DetailsVisible, SuggestContext.CanResolve),
|
||||
title: nls.localize('detail.more', "show less")
|
||||
}, {
|
||||
menuId: suggestWidgetStatusbarMenu,
|
||||
group: 'right',
|
||||
order: 1,
|
||||
when: SuggestContext.DetailsVisible.toNegated(),
|
||||
when: ContextKeyExpr.and(SuggestContext.DetailsVisible.toNegated(), SuggestContext.CanResolve),
|
||||
title: nls.localize('detail.less', "show more")
|
||||
}]
|
||||
}));
|
||||
|
||||
@@ -244,12 +244,12 @@ class ItemRenderer implements IListRenderer<CompletionItem, ISuggestionTemplateD
|
||||
data.signatureLabel.textContent = '';
|
||||
data.qualifierLabel.textContent = '';
|
||||
data.detailsLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, '');
|
||||
removeClass(data.right, 'always-show-details');
|
||||
addClass(data.root, 'string-label');
|
||||
} else {
|
||||
data.signatureLabel.textContent = (suggestion.label.signature || '').replace(/\n.*$/m, '');
|
||||
data.qualifierLabel.textContent = (suggestion.label.qualifier || '').replace(/\n.*$/m, '');
|
||||
data.detailsLabel.textContent = (suggestion.label.type || '').replace(/\n.*$/m, '');
|
||||
addClass(data.right, 'always-show-details');
|
||||
removeClass(data.root, 'string-label');
|
||||
}
|
||||
|
||||
if (canExpandCompletionItem(element)) {
|
||||
|
||||
118
src/vs/editor/contrib/suggest/test/wordDistance.test.ts
Normal file
118
src/vs/editor/contrib/suggest/test/wordDistance.test.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { EditorSimpleWorker } from 'vs/editor/common/services/editorSimpleWorker';
|
||||
import { mock } from 'vs/editor/contrib/suggest/test/suggestModel.test';
|
||||
import { EditorWorkerHost, EditorWorkerServiceImpl } from 'vs/editor/common/services/editorWorkerServiceImpl';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { WordDistance } from 'vs/editor/contrib/suggest/wordDistance';
|
||||
import { createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { DEFAULT_WORD_REGEXP } from 'vs/editor/common/model/wordHelper';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { CompletionItem } from 'vs/editor/contrib/suggest/suggest';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import { MockMode } from 'vs/editor/test/common/mocks/mockMode';
|
||||
|
||||
suite('suggest, word distance', function () {
|
||||
|
||||
class BracketMode extends MockMode {
|
||||
|
||||
private static readonly _id = new modes.LanguageIdentifier('bracketMode', 3);
|
||||
|
||||
constructor() {
|
||||
super(BracketMode._id);
|
||||
this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
|
||||
brackets: [
|
||||
['{', '}'],
|
||||
['[', ']'],
|
||||
['(', ')'],
|
||||
]
|
||||
}));
|
||||
}
|
||||
}
|
||||
let distance: WordDistance;
|
||||
let disposables = new DisposableStore();
|
||||
|
||||
setup(async function () {
|
||||
|
||||
disposables.clear();
|
||||
let mode = new BracketMode();
|
||||
let model = TextModel.createFromString('function abc(aa, ab){\na\n}', undefined, mode.getLanguageIdentifier(), URI.parse('test:///some.path'));
|
||||
let editor = createTestCodeEditor({ model: model });
|
||||
editor.updateOptions({ suggest: { localityBonus: true } });
|
||||
editor.setPosition({ lineNumber: 2, column: 2 });
|
||||
|
||||
let modelService = new class extends mock<IModelService>() {
|
||||
onModelRemoved = Event.None;
|
||||
getModel(uri: URI) {
|
||||
return uri.toString() === model.uri.toString() ? model : null;
|
||||
}
|
||||
};
|
||||
|
||||
let service = new class extends EditorWorkerServiceImpl {
|
||||
|
||||
private _worker = new EditorSimpleWorker(new class extends mock<EditorWorkerHost>() { }, null);
|
||||
|
||||
constructor() {
|
||||
super(modelService, new class extends mock<ITextResourceConfigurationService>() { }, new NullLogService());
|
||||
this._worker.acceptNewModel({
|
||||
url: model.uri.toString(),
|
||||
lines: model.getLinesContent(),
|
||||
EOL: model.getEOL(),
|
||||
versionId: model.getVersionId()
|
||||
});
|
||||
model.onDidChangeContent(e => this._worker.acceptModelChanged(model.uri.toString(), e));
|
||||
}
|
||||
computeWordRanges(resource: URI, range: IRange): Promise<{ [word: string]: IRange[] } | null> {
|
||||
return this._worker.computeWordRanges(resource.toString(), range, DEFAULT_WORD_REGEXP.source, DEFAULT_WORD_REGEXP.flags);
|
||||
}
|
||||
};
|
||||
|
||||
distance = await WordDistance.create(service, editor);
|
||||
|
||||
disposables.add(mode);
|
||||
disposables.add(model);
|
||||
disposables.add(editor);
|
||||
});
|
||||
|
||||
function createSuggestItem(label: string, overwriteBefore: number, position: IPosition): CompletionItem {
|
||||
const suggestion: modes.CompletionItem = {
|
||||
label,
|
||||
range: { startLineNumber: position.lineNumber, startColumn: position.column - overwriteBefore, endLineNumber: position.lineNumber, endColumn: position.column },
|
||||
insertText: label,
|
||||
kind: 0
|
||||
};
|
||||
const container: modes.CompletionList = {
|
||||
suggestions: [suggestion]
|
||||
};
|
||||
const provider: modes.CompletionItemProvider = {
|
||||
provideCompletionItems(): any {
|
||||
return;
|
||||
}
|
||||
};
|
||||
return new CompletionItem(position, suggestion, container, provider, undefined!);
|
||||
}
|
||||
|
||||
test('Suggest locality bonus can boost current word #90515', function () {
|
||||
this.skip();
|
||||
|
||||
const pos = { lineNumber: 2, column: 2 };
|
||||
const d1 = distance.distance(pos, createSuggestItem('a', 1, pos).completion);
|
||||
const d2 = distance.distance(pos, createSuggestItem('aa', 1, pos).completion);
|
||||
const d3 = distance.distance(pos, createSuggestItem('ab', 1, pos).completion);
|
||||
|
||||
assert.ok(d1 > d2);
|
||||
assert.ok(d2 === d3);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user