Merge from vscode 1fbacccbc900bb59ba8a8f26a4128d48a1c97842

This commit is contained in:
ADS Merger
2020-02-13 02:56:02 +00:00
parent 9af1f3b0eb
commit 73ea8b79b2
229 changed files with 3192 additions and 2103 deletions

View File

@@ -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);
}
};

View File

@@ -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);
}));

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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)) {

View File

@@ -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;
}

View File

@@ -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;
});
}

View File

@@ -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(),

View File

@@ -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%;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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 => {

View File

@@ -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")
}]
}));

View File

@@ -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)) {

View 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);
});
});