mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-20 12:00:24 -04:00
Merge from vscode 718331d6f3ebd1b571530ab499edb266ddd493d5
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
transition: transform 200ms linear;
|
||||
padding: 0 4px;
|
||||
box-sizing: border-box;
|
||||
transform: translateY(Calc(-100% - 10px)); /* shadow (10px) */
|
||||
transform: translateY(calc(-100% - 10px)); /* shadow (10px) */
|
||||
}
|
||||
|
||||
.monaco-editor .find-widget textarea {
|
||||
|
||||
@@ -56,7 +56,7 @@ const NLS_REPLACE_ALL_BTN_LABEL = nls.localize('label.replaceAllButton', "Replac
|
||||
const NLS_TOGGLE_REPLACE_MODE_BTN_LABEL = nls.localize('label.toggleReplaceButton', "Toggle Replace mode");
|
||||
const NLS_MATCHES_COUNT_LIMIT_TITLE = nls.localize('title.matchesCountLimit', "Only the first {0} results are highlighted, but all find operations work on the entire text.", MATCHES_LIMIT);
|
||||
const NLS_MATCHES_LOCATION = nls.localize('label.matchesLocation', "{0} of {1}");
|
||||
const NLS_NO_RESULTS = nls.localize('label.noResults', "No Results");
|
||||
const NLS_NO_RESULTS = nls.localize('label.noResults', "No results");
|
||||
|
||||
const FIND_WIDGET_INITIAL_WIDTH = 419;
|
||||
const PART_WIDTH = 275;
|
||||
@@ -415,11 +415,19 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
|
||||
if (label === NLS_NO_RESULTS) {
|
||||
return searchString === ''
|
||||
? nls.localize('ariaSearchNoResultEmpty', "{0} found", label)
|
||||
: nls.localize('ariaSearchNoResult', "{0} found for {1}", label, searchString);
|
||||
: nls.localize('ariaSearchNoResult', "{0} found for '{1}'", label, searchString);
|
||||
}
|
||||
return currentMatch
|
||||
? nls.localize('ariaSearchNoResultWithLineNum', "{0} found for {1} at {2}", label, searchString, currentMatch.startLineNumber + ':' + currentMatch.startColumn)
|
||||
: nls.localize('ariaSearchNoResultWithLineNumNoCurrentMatch', "{0} found for {1}", label, searchString);
|
||||
if (currentMatch) {
|
||||
const ariaLabel = nls.localize('ariaSearchNoResultWithLineNum', "{0} found for '{1}', at {2}", label, searchString, currentMatch.startLineNumber + ':' + currentMatch.startColumn);
|
||||
const lineContent = this._codeEditor.getModel()?.getLineContent(currentMatch.startLineNumber);
|
||||
if (lineContent) {
|
||||
return `${lineContent}, ${ariaLabel}`;
|
||||
}
|
||||
|
||||
return ariaLabel;
|
||||
}
|
||||
|
||||
return nls.localize('ariaSearchNoResultWithLineNumNoCurrentMatch', "{0} found for '{1}'", label, searchString);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -885,7 +885,7 @@ for (let i = 1; i <= 7; i++) {
|
||||
);
|
||||
}
|
||||
|
||||
export const foldBackgroundBackground = registerColor('editor.foldBackground', { light: transparent(editorSelectionBackground, 0.3), dark: transparent(editorSelectionBackground, 0.3), hc: null }, nls.localize('editorSelectionBackground', "Color of the editor selection."));
|
||||
export const foldBackgroundBackground = registerColor('editor.foldBackground', { light: transparent(editorSelectionBackground, 0.3), dark: transparent(editorSelectionBackground, 0.3), hc: null }, nls.localize('foldBackgroundBackground', "Background color behind folded ranges."));
|
||||
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
const foldBackground = theme.getColor(foldBackgroundBackground);
|
||||
|
||||
@@ -178,10 +178,8 @@ export async function formatDocumentRangeWithProvider(
|
||||
|
||||
if (isCodeEditor(editorOrModel)) {
|
||||
// use editor to apply edits
|
||||
FormattingEdit.execute(editorOrModel, edits);
|
||||
FormattingEdit.execute(editorOrModel, edits, true);
|
||||
alertFormattingEdits(edits);
|
||||
editorOrModel.pushUndoStop();
|
||||
editorOrModel.focus();
|
||||
editorOrModel.revealPositionInCenterIfOutsideViewport(editorOrModel.getPosition(), ScrollType.Immediate);
|
||||
|
||||
} else {
|
||||
@@ -266,12 +264,10 @@ export async function formatDocumentWithProvider(
|
||||
|
||||
if (isCodeEditor(editorOrModel)) {
|
||||
// use editor to apply edits
|
||||
FormattingEdit.execute(editorOrModel, edits);
|
||||
FormattingEdit.execute(editorOrModel, edits, mode !== FormattingMode.Silent);
|
||||
|
||||
if (mode !== FormattingMode.Silent) {
|
||||
alertFormattingEdits(edits);
|
||||
editorOrModel.pushUndoStop();
|
||||
editorOrModel.focus();
|
||||
editorOrModel.revealPositionInCenterIfOutsideViewport(editorOrModel.getPosition(), ScrollType.Immediate);
|
||||
}
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ class FormatOnType implements IEditorContribution {
|
||||
}
|
||||
|
||||
if (isNonEmptyArray(edits)) {
|
||||
FormattingEdit.execute(this._editor, edits);
|
||||
FormattingEdit.execute(this._editor, edits, true);
|
||||
alertFormattingEdits(edits);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,8 +43,10 @@ export class FormattingEdit {
|
||||
return fullModelRange.equalsRange(editRange);
|
||||
}
|
||||
|
||||
static execute(editor: ICodeEditor, _edits: TextEdit[]) {
|
||||
editor.pushUndoStop();
|
||||
static execute(editor: ICodeEditor, _edits: TextEdit[], addUndoStops: boolean) {
|
||||
if (addUndoStops) {
|
||||
editor.pushUndoStop();
|
||||
}
|
||||
const edits = FormattingEdit._handleEolEdits(editor, _edits);
|
||||
if (edits.length === 1 && FormattingEdit._isFullModelReplaceEdit(editor, edits[0])) {
|
||||
// We use replace semantics and hope that markers stay put...
|
||||
@@ -52,6 +54,8 @@ export class FormattingEdit {
|
||||
} else {
|
||||
editor.executeEdits('formatEditsCommand', edits.map(edit => EditOperation.replaceMove(Range.lift(edit.range), edit.text)));
|
||||
}
|
||||
editor.pushUndoStop();
|
||||
if (addUndoStops) {
|
||||
editor.pushUndoStop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import { Action } from 'vs/base/common/actions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor';
|
||||
|
||||
class MarkerModel {
|
||||
|
||||
@@ -396,7 +397,7 @@ class MarkerNavigationAction extends EditorAction {
|
||||
|
||||
return editorService.openCodeEditor({
|
||||
resource: newMarker.resource,
|
||||
options: { pinned: false, revealIfOpened: true, revealInCenterIfOutsideViewport: true, selection: newMarker }
|
||||
options: { pinned: false, revealIfOpened: true, selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport, selection: newMarker }
|
||||
}, editor).then(editor => {
|
||||
if (!editor) {
|
||||
return undefined;
|
||||
|
||||
@@ -317,6 +317,7 @@ export class MarkerNavigationWidget extends PeekViewWidget {
|
||||
this._icon.className = `codicon ${SeverityIcon.className(MarkerSeverity.toSeverity(this._severity))}`;
|
||||
|
||||
this.editor.revealPositionInCenter(position, ScrollType.Smooth);
|
||||
this.editor.focus();
|
||||
}
|
||||
|
||||
updateMarker(marker: IMarker): void {
|
||||
|
||||
@@ -36,6 +36,8 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ScrollType, IEditorAction } from 'vs/editor/common/editorCommon';
|
||||
import { assertType } from 'vs/base/common/types';
|
||||
import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget';
|
||||
import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor';
|
||||
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorContext, <ISubmenuItem>{
|
||||
@@ -129,7 +131,7 @@ abstract class SymbolNavigationAction extends EditorAction {
|
||||
private async _onResult(editorService: ICodeEditorService, symbolNavService: ISymbolNavigationService, editor: IActiveCodeEditor, model: ReferencesModel): Promise<void> {
|
||||
|
||||
const gotoLocation = this._getGoToPreference(editor);
|
||||
if (this._configuration.openInPeek || (gotoLocation === 'peek' && model.references.length > 1)) {
|
||||
if (!(editor instanceof EmbeddedCodeEditorWidget) && (this._configuration.openInPeek || (gotoLocation === 'peek' && model.references.length > 1))) {
|
||||
this._openInPeek(editor, model);
|
||||
|
||||
} else {
|
||||
@@ -165,7 +167,7 @@ abstract class SymbolNavigationAction extends EditorAction {
|
||||
resource: reference.uri,
|
||||
options: {
|
||||
selection: Range.collapseToStart(range),
|
||||
revealInCenterIfOutsideViewport: true
|
||||
selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport
|
||||
}
|
||||
}, editor, sideBySide);
|
||||
|
||||
|
||||
@@ -27,6 +27,10 @@ import { IWordAtPosition, IModelDeltaDecoration, ITextModel, IFoundBracket } fro
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { PeekContext } from 'vs/editor/contrib/peekView/peekView';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export class GotoDefinitionAtPositionEditorContribution implements IEditorContribution {
|
||||
|
||||
@@ -334,8 +338,17 @@ export class GotoDefinitionAtPositionEditorContribution implements IEditorContri
|
||||
|
||||
private gotoDefinition(position: Position, openToSide: boolean): Promise<any> {
|
||||
this.editor.setPosition(position);
|
||||
const action = new DefinitionAction({ openToSide, openInPeek: false, muteMessage: true }, { alias: '', label: '', id: '', precondition: undefined });
|
||||
return this.editor.invokeWithinContext(accessor => action.run(accessor, this.editor));
|
||||
const definitionLinkOpensInPeek = this.editor.getOption(EditorOption.definitionLinkOpensInPeek);
|
||||
return this.editor.invokeWithinContext((accessor) => {
|
||||
const canPeek = definitionLinkOpensInPeek && !this.isInPeekEditor(accessor);
|
||||
const action = new DefinitionAction({ openToSide, openInPeek: canPeek, muteMessage: true }, { alias: '', label: '', id: '', precondition: undefined });
|
||||
return action.run(accessor, this.editor);
|
||||
});
|
||||
}
|
||||
|
||||
private isInPeekEditor(accessor: ServicesAccessor): boolean | undefined {
|
||||
const contextKeyService = accessor.get(IContextKeyService);
|
||||
return PeekContext.inPeekEditor.getValue(contextKeyService);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
|
||||
@@ -216,14 +216,16 @@ export abstract class ReferencesController implements IEditorContribution {
|
||||
}
|
||||
}
|
||||
|
||||
closeWidget(): void {
|
||||
closeWidget(focusEditor = true): void {
|
||||
this._referenceSearchVisible.reset();
|
||||
this._disposables.clear();
|
||||
dispose(this._widget);
|
||||
dispose(this._model);
|
||||
this._widget = undefined;
|
||||
this._model = undefined;
|
||||
this._editor.focus();
|
||||
if (focusEditor) {
|
||||
this._editor.focus();
|
||||
}
|
||||
this._requestIdPool += 1; // Cancel pending requests
|
||||
}
|
||||
|
||||
@@ -300,7 +302,7 @@ function withController(accessor: ServicesAccessor, fn: (controller: ReferencesC
|
||||
}
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'changePeekFocus',
|
||||
id: 'togglePeekWidgetFocus',
|
||||
weight: KeybindingWeight.EditorContrib,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.F2),
|
||||
when: ContextKeyExpr.or(ctxReferenceSearchVisible, PeekContext.inPeekEditor),
|
||||
|
||||
@@ -19,6 +19,7 @@ import { localize } from 'vs/nls';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor';
|
||||
|
||||
export const ctxHasSymbols = new RawContextKey('hasSymbols', false);
|
||||
|
||||
@@ -127,7 +128,7 @@ class SymbolNavigationService implements ISymbolNavigationService {
|
||||
resource: reference.uri,
|
||||
options: {
|
||||
selection: Range.collapseToStart(reference.range),
|
||||
revealInCenterIfOutsideViewport: true
|
||||
selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport
|
||||
}
|
||||
}, source).finally(() => {
|
||||
this._ignoreEditorChange = false;
|
||||
|
||||
@@ -37,12 +37,36 @@ abstract class AbstractCopyLinesAction extends EditorAction {
|
||||
}
|
||||
|
||||
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
|
||||
if (!editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selections = editor.getSelections().map((selection, index) => ({ selection, index, ignore: false }));
|
||||
selections.sort((a, b) => Range.compareRangesUsingStarts(a.selection, b.selection));
|
||||
|
||||
// Remove selections that would result in copying the same line
|
||||
let prev = selections[0];
|
||||
for (let i = 1; i < selections.length; i++) {
|
||||
const curr = selections[i];
|
||||
if (prev.selection.endLineNumber === curr.selection.startLineNumber) {
|
||||
// these two selections would copy the same line
|
||||
if (prev.index < curr.index) {
|
||||
// prev wins
|
||||
curr.ignore = true;
|
||||
} else {
|
||||
// curr wins
|
||||
prev.ignore = true;
|
||||
prev = curr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const commands: ICommand[] = [];
|
||||
const selections = editor.getSelections() || [];
|
||||
|
||||
for (const selection of selections) {
|
||||
commands.push(new CopyLinesCommand(selection, this.down));
|
||||
if (selection.ignore) {
|
||||
continue;
|
||||
}
|
||||
commands.push(new CopyLinesCommand(selection.selection, this.down));
|
||||
}
|
||||
|
||||
editor.pushUndoStop();
|
||||
|
||||
@@ -179,7 +179,7 @@ class RenameController implements IEditorContribution {
|
||||
selectionEnd = Math.min(loc.range.endColumn, selection.endColumn) - loc.range.startColumn;
|
||||
}
|
||||
|
||||
const supportPreview = this._configService.getValue<boolean>(this.editor.getModel().uri, 'editor.rename.enablePreview');
|
||||
const supportPreview = this._bulkEditService.hasPreviewHandler() && this._configService.getValue<boolean>(this.editor.getModel().uri, 'editor.rename.enablePreview');
|
||||
const inputFieldResult = await this._renameInputField.getValue().getInput(loc.range, loc.text, selectionStart, selectionEnd, supportPreview);
|
||||
|
||||
// no result, only hint to focus the editor or not
|
||||
|
||||
@@ -111,6 +111,44 @@
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/** Status Bar **/
|
||||
|
||||
.monaco-editor .suggest-widget > .suggest-status-bar {
|
||||
visibility: hidden;
|
||||
|
||||
position: absolute;
|
||||
left: 0;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: space-between;
|
||||
|
||||
width: 100%;
|
||||
|
||||
font-size: 80%;
|
||||
|
||||
border-left-width: 1px;
|
||||
border-left-style: solid;
|
||||
border-right-width: 1px;
|
||||
border-right-style: solid;
|
||||
border-bottom-width: 1px;
|
||||
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 {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
.monaco-editor .suggest-widget.docs-side > .suggest-status-bar {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
/** ReadMore Icon styles **/
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .codicon-close,
|
||||
@@ -190,7 +228,7 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .monaco-icon-label {
|
||||
flex-shrink: 0;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right {
|
||||
overflow: hidden;
|
||||
@@ -324,6 +362,7 @@
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs.markdown-docs {
|
||||
padding: 0;
|
||||
white-space: initial;
|
||||
min-height: calc(1rem + 8px);
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs.markdown-docs > div,
|
||||
|
||||
35
src/vs/editor/contrib/suggest/media/suggestStatusBar.css
Normal file
35
src/vs/editor/contrib/suggest/media/suggestStatusBar.css
Normal file
@@ -0,0 +1,35 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-editor .suggest-widget.with-status-bar .suggest-status-bar {
|
||||
visibility: visible;
|
||||
}
|
||||
.monaco-editor .suggest-widget.with-status-bar > .tree {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.with-status-bar .suggest-status-bar .action-label {
|
||||
min-height: 18px;
|
||||
opacity: 0.5;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.with-status-bar .suggest-status-bar .action-item:not(:last-of-type) .action-label {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.with-status-bar .suggest-status-bar .action-item:not(:last-of-type) .action-label::after {
|
||||
content: ', ';
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
|
||||
.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 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget.with-status-bar:not(.docs-side) .monaco-list .monaco-list-row:hover > .contents > .main > .right.can-expand-details > .details-label {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -17,14 +17,19 @@ import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { FuzzyScore } from 'vs/base/common/filters';
|
||||
import { isDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { MenuId } from 'vs/platform/actions/common/actions';
|
||||
|
||||
export const Context = {
|
||||
Visible: new RawContextKey<boolean>('suggestWidgetVisible', false),
|
||||
DetailsVisible: new RawContextKey<boolean>('suggestWidgetDetailsVisible', false),
|
||||
MultipleSuggestions: new RawContextKey<boolean>('suggestWidgetMultipleSuggestions', false),
|
||||
MakesTextEdit: new RawContextKey('suggestionMakesTextEdit', true),
|
||||
AcceptSuggestionsOnEnter: new RawContextKey<boolean>('acceptSuggestionOnEnter', true)
|
||||
AcceptSuggestionsOnEnter: new RawContextKey<boolean>('acceptSuggestionOnEnter', true),
|
||||
HasInsertAndReplaceRange: new RawContextKey('suggestionHasInsertAndReplaceRange', false),
|
||||
};
|
||||
|
||||
export const suggestWidgetStatusbarMenu = new MenuId('suggestWidgetStatusBar');
|
||||
|
||||
export class CompletionItem {
|
||||
|
||||
_brand!: 'ISuggestionItem';
|
||||
|
||||
@@ -23,7 +23,7 @@ import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/c
|
||||
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { Context as SuggestContext, CompletionItem } from './suggest';
|
||||
import { Context as SuggestContext, CompletionItem, suggestWidgetStatusbarMenu } from './suggest';
|
||||
import { SuggestAlternatives } from './suggestAlternatives';
|
||||
import { State, SuggestModel } from './suggestModel';
|
||||
import { ISelectedSuggestion, SuggestWidget } from './suggestWidget';
|
||||
@@ -33,17 +33,16 @@ import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerServ
|
||||
import { IdleValue } from 'vs/base/common/async';
|
||||
import { isObject, assertType } from 'vs/base/common/types';
|
||||
import { CommitCharacterController } from './suggestCommitCharacters';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { IPosition, Position } from 'vs/editor/common/core/position';
|
||||
import { TrackedRangeStickiness, ITextModel } from 'vs/editor/common/model';
|
||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { SuggestRangeHighlighter } from 'vs/editor/contrib/suggest/suggestRangeHighlighter';
|
||||
import { MenuRegistry } from 'vs/platform/actions/common/actions';
|
||||
|
||||
/**
|
||||
* Stop suggest widget from disappearing when clicking into other areas
|
||||
* For development purpose only
|
||||
*/
|
||||
const _sticky = false;
|
||||
// sticky suggest widget which doesn't disappear on focus out and such
|
||||
let _sticky = false;
|
||||
// _sticky = Boolean("true"); // done "weirdly" so that a lint warning prevents you from pushing this
|
||||
|
||||
class LineSuffix {
|
||||
|
||||
@@ -138,9 +137,17 @@ export class SuggestController implements IEditorContribution {
|
||||
}));
|
||||
|
||||
// Wire up makes text edit context key
|
||||
let makesTextEdit = SuggestContext.MakesTextEdit.bindTo(this._contextKeyService);
|
||||
const ctxMakesTextEdit = SuggestContext.MakesTextEdit.bindTo(this._contextKeyService);
|
||||
const ctxHasInsertAndReplace = SuggestContext.HasInsertAndReplaceRange.bindTo(this._contextKeyService);
|
||||
|
||||
this._toDispose.add(toDisposable(() => {
|
||||
ctxMakesTextEdit.reset();
|
||||
ctxHasInsertAndReplace.reset();
|
||||
}));
|
||||
|
||||
this._toDispose.add(widget.onDidFocus(({ item }) => {
|
||||
|
||||
// (ctx: makesTextEdit)
|
||||
const position = this.editor.getPosition()!;
|
||||
const startColumn = item.editStart.column;
|
||||
const endColumn = position.column;
|
||||
@@ -161,9 +168,11 @@ export class SuggestController implements IEditorContribution {
|
||||
});
|
||||
value = oldText !== item.completion.insertText;
|
||||
}
|
||||
makesTextEdit.set(value);
|
||||
ctxMakesTextEdit.set(value);
|
||||
|
||||
// (ctx: hasInsertAndReplaceRange)
|
||||
ctxHasInsertAndReplace.set(!Position.equals(item.editInsertEnd, item.editReplaceEnd));
|
||||
}));
|
||||
this._toDispose.add(toDisposable(() => makesTextEdit.reset()));
|
||||
|
||||
this._toDispose.add(widget.onDetailsKeyDown(e => {
|
||||
// cmd + c on macOS, ctrl + c on Win / Linux
|
||||
@@ -435,7 +444,6 @@ export class SuggestController implements IEditorContribution {
|
||||
}
|
||||
this._insertSuggestion(item, flags);
|
||||
}
|
||||
|
||||
acceptNextSuggestion() {
|
||||
this._alternatives.getValue().next();
|
||||
}
|
||||
@@ -528,11 +536,8 @@ const SuggestCommand = EditorCommand.bindToContribution<SuggestController>(Sugge
|
||||
registerEditorCommand(new SuggestCommand({
|
||||
id: 'acceptSelectedSuggestion',
|
||||
precondition: SuggestContext.Visible,
|
||||
handler(x, args) {
|
||||
const alternative: boolean = typeof args === 'object' && typeof args.alternative === 'boolean'
|
||||
? args.alternative
|
||||
: false;
|
||||
x.acceptSelectedSuggestion(true, alternative);
|
||||
handler(x) {
|
||||
x.acceptSelectedSuggestion(true, false);
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -549,19 +554,55 @@ KeybindingsRegistry.registerKeybindingRule({
|
||||
id: 'acceptSelectedSuggestion',
|
||||
when: ContextKeyExpr.and(SuggestContext.Visible, EditorContextKeys.textInputFocus, SuggestContext.AcceptSuggestionsOnEnter, SuggestContext.MakesTextEdit),
|
||||
primary: KeyCode.Enter,
|
||||
weight
|
||||
weight,
|
||||
});
|
||||
|
||||
// shift+enter and shift+tab use the alternative-flag so that the suggest controller
|
||||
// is doing the opposite of the editor.suggest.overwriteOnAccept-configuration
|
||||
KeybindingsRegistry.registerKeybindingRule({
|
||||
id: 'acceptSelectedSuggestion',
|
||||
when: ContextKeyExpr.and(SuggestContext.Visible, EditorContextKeys.textInputFocus),
|
||||
primary: KeyMod.Shift | KeyCode.Tab,
|
||||
secondary: [KeyMod.Shift | KeyCode.Enter],
|
||||
args: { alternative: true },
|
||||
weight
|
||||
MenuRegistry.appendMenuItem(suggestWidgetStatusbarMenu, {
|
||||
command: { id: 'acceptSelectedSuggestion', title: nls.localize({ key: 'accept.accept', comment: ['{0} will be a keybinding, e.g "Enter to insert"'] }, "{0} to insert") },
|
||||
group: 'left',
|
||||
order: 1,
|
||||
when: SuggestContext.HasInsertAndReplaceRange.toNegated()
|
||||
});
|
||||
MenuRegistry.appendMenuItem(suggestWidgetStatusbarMenu, {
|
||||
command: { id: 'acceptSelectedSuggestion', title: nls.localize({ key: 'accept.insert', comment: ['{0} will be a keybinding, e.g "Enter to insert"'] }, "{0} to insert") },
|
||||
group: 'left',
|
||||
order: 1,
|
||||
when: ContextKeyExpr.and(SuggestContext.HasInsertAndReplaceRange, ContextKeyExpr.equals('config.editor.suggest.insertMode', 'insert'))
|
||||
});
|
||||
MenuRegistry.appendMenuItem(suggestWidgetStatusbarMenu, {
|
||||
command: { id: 'acceptSelectedSuggestion', title: nls.localize({ key: 'accept.replace', comment: ['{0} will be a keybinding, e.g "Enter to replace"'] }, "{0} to replace") },
|
||||
group: 'left',
|
||||
order: 1,
|
||||
when: ContextKeyExpr.and(SuggestContext.HasInsertAndReplaceRange, ContextKeyExpr.equals('config.editor.suggest.insertMode', 'replace'))
|
||||
});
|
||||
|
||||
registerEditorCommand(new SuggestCommand({
|
||||
id: 'acceptAlternativeSelectedSuggestion',
|
||||
precondition: ContextKeyExpr.and(SuggestContext.Visible, EditorContextKeys.textInputFocus),
|
||||
kbOpts: {
|
||||
weight: weight,
|
||||
kbExpr: EditorContextKeys.textInputFocus,
|
||||
primary: KeyMod.Shift | KeyCode.Enter,
|
||||
secondary: [KeyMod.Shift | KeyCode.Tab],
|
||||
},
|
||||
handler(x) {
|
||||
x.acceptSelectedSuggestion(false, true);
|
||||
},
|
||||
menuOpts: [{
|
||||
menuId: suggestWidgetStatusbarMenu,
|
||||
group: 'left',
|
||||
order: 2,
|
||||
when: ContextKeyExpr.and(SuggestContext.HasInsertAndReplaceRange, ContextKeyExpr.equals('config.editor.suggest.insertMode', 'insert')),
|
||||
title: nls.localize({ key: 'accept.replace', comment: ['{0} will be a keybinding, e.g "Enter to replace"'] }, "{0} to replace")
|
||||
}, {
|
||||
menuId: suggestWidgetStatusbarMenu,
|
||||
group: 'left',
|
||||
order: 2,
|
||||
when: ContextKeyExpr.and(SuggestContext.HasInsertAndReplaceRange, ContextKeyExpr.equals('config.editor.suggest.insertMode', 'replace')),
|
||||
title: nls.localize({ key: 'accept.insert', comment: ['{0} will be a keybinding, e.g "Enter to insert"'] }, "{0} to insert")
|
||||
}]
|
||||
}));
|
||||
|
||||
|
||||
// continue to support the old command
|
||||
CommandsRegistry.registerCommandAlias('acceptSelectedSuggestionOnEnter', 'acceptSelectedSuggestion');
|
||||
@@ -649,7 +690,20 @@ registerEditorCommand(new SuggestCommand({
|
||||
kbExpr: EditorContextKeys.textInputFocus,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.Space,
|
||||
mac: { primary: KeyMod.WinCtrl | KeyCode.Space }
|
||||
}
|
||||
},
|
||||
menuOpts: [{
|
||||
menuId: suggestWidgetStatusbarMenu,
|
||||
group: 'right',
|
||||
order: 1,
|
||||
when: SuggestContext.DetailsVisible,
|
||||
title: nls.localize('detail.more', "show less")
|
||||
}, {
|
||||
menuId: suggestWidgetStatusbarMenu,
|
||||
group: 'right',
|
||||
order: 1,
|
||||
when: SuggestContext.DetailsVisible.toNegated(),
|
||||
title: nls.localize('detail.less', "show more")
|
||||
}]
|
||||
}));
|
||||
|
||||
registerEditorCommand(new SuggestCommand({
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/suggest';
|
||||
import 'vs/css!./media/suggestStatusBar';
|
||||
import 'vs/base/browser/ui/codiconLabel/codiconLabel'; // The codicon symbol styles are defined here and must be loaded
|
||||
import 'vs/editor/contrib/documentSymbols/outlineTree'; // The codicon symbol colors are defined here and must be loaded
|
||||
import * as nls from 'vs/nls';
|
||||
@@ -20,7 +21,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition, IEditorMouseEvent } from 'vs/editor/browser/editorBrowser';
|
||||
import { Context as SuggestContext, CompletionItem } from './suggest';
|
||||
import { Context as SuggestContext, CompletionItem, suggestWidgetStatusbarMenu } from './suggest';
|
||||
import { CompletionModel } from './completionModel';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
@@ -39,8 +40,11 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { FileKind } from 'vs/platform/files/common/files';
|
||||
import { MarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { flatten } from 'vs/base/common/arrays';
|
||||
import { flatten, isFalsyOrEmpty } from 'vs/base/common/arrays';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { IMenuService } from 'vs/platform/actions/common/actions';
|
||||
import { ActionBar, IActionViewItemProvider, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
|
||||
const expandSuggestionDocsByDefault = false;
|
||||
|
||||
@@ -49,8 +53,8 @@ interface ISuggestionTemplateData {
|
||||
|
||||
/**
|
||||
* Flexbox
|
||||
* < ------- left ------- > < -------- right -------- >
|
||||
* <icon><label><signature> <qualifier><type><readmore>
|
||||
* < ------------- left ------------ > < --- right -- >
|
||||
* <icon><label><signature><qualifier> <type><readmore>
|
||||
*/
|
||||
left: HTMLElement;
|
||||
right: HTMLElement;
|
||||
@@ -301,7 +305,7 @@ class SuggestionDetails {
|
||||
private readonly widget: SuggestWidget,
|
||||
private readonly editor: ICodeEditor,
|
||||
private readonly markdownRenderer: MarkdownRenderer,
|
||||
private readonly triggerKeybindingLabel: string,
|
||||
private readonly kbToggleDetails: string,
|
||||
) {
|
||||
this.disposables = new DisposableStore();
|
||||
|
||||
@@ -316,7 +320,7 @@ class SuggestionDetails {
|
||||
|
||||
this.header = append(this.body, $('.header'));
|
||||
this.close = append(this.header, $('span.codicon.codicon-close'));
|
||||
this.close.title = nls.localize('readLess', "Read less...{0}", this.triggerKeybindingLabel);
|
||||
this.close.title = nls.localize('readLess', "Read less...{0}", this.kbToggleDetails);
|
||||
this.type = append(this.header, $('p.type'));
|
||||
|
||||
this.docs = append(this.body, $('p.docs'));
|
||||
@@ -340,7 +344,8 @@ class SuggestionDetails {
|
||||
}
|
||||
|
||||
renderItem(item: CompletionItem, explainMode: boolean): void {
|
||||
this.renderDisposeable = dispose(this.renderDisposeable);
|
||||
dispose(this.renderDisposeable);
|
||||
this.renderDisposeable = undefined;
|
||||
|
||||
let { documentation, detail } = item.completion;
|
||||
// --- documentation
|
||||
@@ -447,7 +452,8 @@ class SuggestionDetails {
|
||||
|
||||
dispose(): void {
|
||||
this.disposables.dispose();
|
||||
this.renderDisposeable = dispose(this.renderDisposeable);
|
||||
dispose(this.renderDisposeable);
|
||||
this.renderDisposeable = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,12 +485,14 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
private element: HTMLElement;
|
||||
private messageElement: HTMLElement;
|
||||
private listElement: HTMLElement;
|
||||
private statusBarElement: HTMLElement;
|
||||
private details: SuggestionDetails;
|
||||
private list: List<CompletionItem>;
|
||||
private listHeight?: number;
|
||||
|
||||
private readonly suggestWidgetVisible: IContextKey<boolean>;
|
||||
private readonly suggestWidgetMultipleSuggestions: IContextKey<boolean>;
|
||||
private readonly ctxSuggestWidgetVisible: IContextKey<boolean>;
|
||||
private readonly ctxSuggestWidgetDetailsVisible: IContextKey<boolean>;
|
||||
private readonly ctxSuggestWidgetMultipleSuggestions: IContextKey<boolean>;
|
||||
|
||||
private readonly showTimeout = new TimeoutTimer();
|
||||
private readonly toDispose = new DisposableStore();
|
||||
@@ -517,18 +525,19 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
constructor(
|
||||
private readonly editor: ICodeEditor,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IModeService modeService: IModeService,
|
||||
@IOpenerService openerService: IOpenerService,
|
||||
@IMenuService menuService: IMenuService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
) {
|
||||
const kb = keybindingService.lookupKeybinding('editor.action.triggerSuggest');
|
||||
const triggerKeybindingLabel = !kb ? '' : ` (${kb.getLabel()})`;
|
||||
const markdownRenderer = this.toDispose.add(new MarkdownRenderer(editor, modeService, openerService));
|
||||
|
||||
const kbToggleDetails = keybindingService.lookupKeybinding('toggleSuggestionDetails')?.getLabel() ?? '';
|
||||
|
||||
this.isAuto = false;
|
||||
this.focusedItem = null;
|
||||
this.storageService = storageService;
|
||||
@@ -542,12 +551,55 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
|
||||
this.messageElement = append(this.element, $('.message'));
|
||||
this.listElement = append(this.element, $('.tree'));
|
||||
this.details = instantiationService.createInstance(SuggestionDetails, this.element, this, this.editor, markdownRenderer, triggerKeybindingLabel);
|
||||
|
||||
const applyStatusBarStyle = () => toggleClass(this.element, 'with-status-bar', !this.editor.getOption(EditorOption.suggest).hideStatusBar);
|
||||
applyStatusBarStyle();
|
||||
|
||||
this.statusBarElement = append(this.element, $('.suggest-status-bar'));
|
||||
const actionViewItemProvider = <IActionViewItemProvider>(action => {
|
||||
const kb = keybindingService.lookupKeybindings(action.id);
|
||||
return new class extends ActionViewItem {
|
||||
constructor() {
|
||||
super(undefined, action, { label: true, icon: false });
|
||||
}
|
||||
updateLabel() {
|
||||
if (isFalsyOrEmpty(kb) || !this.label) {
|
||||
return super.updateLabel();
|
||||
}
|
||||
const { label } = this.getAction();
|
||||
this.label.textContent = /{\d}/.test(label)
|
||||
? strings.format(this.getAction().label, kb[0].getLabel())
|
||||
: `${this.getAction().label} (${kb[0].getLabel()})`;
|
||||
}
|
||||
};
|
||||
});
|
||||
const leftActions = new ActionBar(this.statusBarElement, { actionViewItemProvider });
|
||||
const rightActions = new ActionBar(this.statusBarElement, { actionViewItemProvider });
|
||||
const menu = menuService.createMenu(suggestWidgetStatusbarMenu, contextKeyService);
|
||||
const renderMenu = () => {
|
||||
const left: IAction[] = [];
|
||||
const right: IAction[] = [];
|
||||
for (let [group, actions] of menu.getActions()) {
|
||||
if (group === 'left') {
|
||||
left.push(...actions);
|
||||
} else {
|
||||
right.push(...actions);
|
||||
}
|
||||
}
|
||||
leftActions.clear();
|
||||
leftActions.push(left);
|
||||
rightActions.clear();
|
||||
rightActions.push(right);
|
||||
};
|
||||
this.toDispose.add(menu.onDidChange(() => renderMenu()));
|
||||
this.toDispose.add(menu);
|
||||
|
||||
this.details = instantiationService.createInstance(SuggestionDetails, this.element, this, this.editor, markdownRenderer, kbToggleDetails);
|
||||
|
||||
const applyIconStyle = () => toggleClass(this.element, 'no-icons', !this.editor.getOption(EditorOption.suggest).showIcons);
|
||||
applyIconStyle();
|
||||
|
||||
let renderer = instantiationService.createInstance(ItemRenderer, this, this.editor, triggerKeybindingLabel);
|
||||
let renderer = instantiationService.createInstance(ItemRenderer, this, this.editor, kbToggleDetails);
|
||||
|
||||
this.list = new List('SuggestWidget', this.listElement, this, [renderer], {
|
||||
useShadows: false,
|
||||
@@ -582,10 +634,16 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
this.toDispose.add(this.list.onSelectionChange(e => this.onListSelection(e)));
|
||||
this.toDispose.add(this.list.onFocusChange(e => this.onListFocus(e)));
|
||||
this.toDispose.add(this.editor.onDidChangeCursorSelection(() => this.onCursorSelectionChanged()));
|
||||
this.toDispose.add(this.editor.onDidChangeConfiguration(e => { if (e.hasChanged(EditorOption.suggest)) { applyIconStyle(); } }));
|
||||
this.toDispose.add(this.editor.onDidChangeConfiguration(e => {
|
||||
if (e.hasChanged(EditorOption.suggest)) {
|
||||
applyStatusBarStyle();
|
||||
applyIconStyle();
|
||||
}
|
||||
}));
|
||||
|
||||
this.suggestWidgetVisible = SuggestContext.Visible.bindTo(contextKeyService);
|
||||
this.suggestWidgetMultipleSuggestions = SuggestContext.MultipleSuggestions.bindTo(contextKeyService);
|
||||
this.ctxSuggestWidgetVisible = SuggestContext.Visible.bindTo(contextKeyService);
|
||||
this.ctxSuggestWidgetDetailsVisible = SuggestContext.DetailsVisible.bindTo(contextKeyService);
|
||||
this.ctxSuggestWidgetMultipleSuggestions = SuggestContext.MultipleSuggestions.bindTo(contextKeyService);
|
||||
|
||||
this.editor.addContentWidget(this);
|
||||
this.setState(State.Hidden);
|
||||
@@ -661,12 +719,14 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
const backgroundColor = theme.getColor(editorSuggestWidgetBackground);
|
||||
if (backgroundColor) {
|
||||
this.listElement.style.backgroundColor = backgroundColor.toString();
|
||||
this.statusBarElement.style.backgroundColor = backgroundColor.toString();
|
||||
this.details.element.style.backgroundColor = backgroundColor.toString();
|
||||
this.messageElement.style.backgroundColor = backgroundColor.toString();
|
||||
}
|
||||
const borderColor = theme.getColor(editorSuggestWidgetBorder);
|
||||
if (borderColor) {
|
||||
this.listElement.style.borderColor = borderColor.toString();
|
||||
this.statusBarElement.style.borderColor = borderColor.toString();
|
||||
this.details.element.style.borderColor = borderColor.toString();
|
||||
this.messageElement.style.borderColor = borderColor.toString();
|
||||
this.detailsBorderColor = borderColor.toString();
|
||||
@@ -704,7 +764,6 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
this.firstFocusInCurrentList = !this.focusedItem;
|
||||
if (item !== this.focusedItem) {
|
||||
|
||||
|
||||
if (this.currentSuggestionDetails) {
|
||||
this.currentSuggestionDetails.cancel();
|
||||
this.currentSuggestionDetails = null;
|
||||
@@ -759,7 +818,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
|
||||
switch (state) {
|
||||
case State.Hidden:
|
||||
hide(this.messageElement, this.details.element, this.listElement);
|
||||
hide(this.messageElement, this.details.element, this.listElement, this.statusBarElement);
|
||||
this.hide();
|
||||
this.listHeight = 0;
|
||||
if (stateChanged) {
|
||||
@@ -769,7 +828,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
break;
|
||||
case State.Loading:
|
||||
this.messageElement.textContent = SuggestWidget.LOADING_MESSAGE;
|
||||
hide(this.listElement, this.details.element);
|
||||
hide(this.listElement, this.details.element, this.statusBarElement);
|
||||
show(this.messageElement);
|
||||
removeClass(this.element, 'docs-side');
|
||||
this.show();
|
||||
@@ -777,7 +836,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
break;
|
||||
case State.Empty:
|
||||
this.messageElement.textContent = SuggestWidget.NO_SUGGESTIONS_MESSAGE;
|
||||
hide(this.listElement, this.details.element);
|
||||
hide(this.listElement, this.details.element, this.statusBarElement);
|
||||
show(this.messageElement);
|
||||
removeClass(this.element, 'docs-side');
|
||||
this.show();
|
||||
@@ -785,7 +844,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
break;
|
||||
case State.Open:
|
||||
hide(this.messageElement);
|
||||
show(this.listElement);
|
||||
show(this.listElement, this.statusBarElement);
|
||||
this.show();
|
||||
break;
|
||||
case State.Frozen:
|
||||
@@ -795,7 +854,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
break;
|
||||
case State.Details:
|
||||
hide(this.messageElement);
|
||||
show(this.details.element, this.listElement);
|
||||
show(this.details.element, this.listElement, this.statusBarElement);
|
||||
this.show();
|
||||
break;
|
||||
}
|
||||
@@ -836,7 +895,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
let visibleCount = this.completionModel.items.length;
|
||||
|
||||
const isEmpty = visibleCount === 0;
|
||||
this.suggestWidgetMultipleSuggestions.set(visibleCount > 1);
|
||||
this.ctxSuggestWidgetMultipleSuggestions.set(visibleCount > 1);
|
||||
|
||||
if (isEmpty) {
|
||||
if (isAuto) {
|
||||
@@ -1003,6 +1062,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
}
|
||||
|
||||
if (this.expandDocsSettingFromStorage()) {
|
||||
this.ctxSuggestWidgetDetailsVisible.set(false);
|
||||
this.updateExpandDocsSetting(false);
|
||||
hide(this.details.element);
|
||||
removeClass(this.element, 'docs-side');
|
||||
@@ -1014,6 +1074,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
return;
|
||||
}
|
||||
|
||||
this.ctxSuggestWidgetDetailsVisible.set(true);
|
||||
this.updateExpandDocsSetting(true);
|
||||
this.showDetails(false);
|
||||
this.telemetryService.publicLog2('suggestWidget:expandDetails');
|
||||
@@ -1021,7 +1082,10 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
}
|
||||
|
||||
showDetails(loading: boolean): void {
|
||||
this.expandSideOrBelow();
|
||||
if (!loading) {
|
||||
// When loading, don't re-layout docs, as item is not resolved yet #88731
|
||||
this.expandSideOrBelow();
|
||||
}
|
||||
|
||||
show(this.details.element);
|
||||
|
||||
@@ -1058,7 +1122,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
this.listHeight = newHeight;
|
||||
}
|
||||
|
||||
this.suggestWidgetVisible.set(true);
|
||||
this.ctxSuggestWidgetVisible.set(true);
|
||||
|
||||
this.showTimeout.cancelAndSet(() => {
|
||||
addClass(this.element, 'visible');
|
||||
@@ -1067,8 +1131,8 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
}
|
||||
|
||||
private hide(): void {
|
||||
this.suggestWidgetVisible.reset();
|
||||
this.suggestWidgetMultipleSuggestions.reset();
|
||||
this.ctxSuggestWidgetVisible.reset();
|
||||
this.ctxSuggestWidgetMultipleSuggestions.reset();
|
||||
removeClass(this.element, 'visible');
|
||||
}
|
||||
|
||||
@@ -1119,6 +1183,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
|
||||
this.element.style.lineHeight = `${this.unfocusedHeight}px`;
|
||||
this.listElement.style.height = `${height}px`;
|
||||
this.statusBarElement.style.top = `${height}px`;
|
||||
this.list.layout(height);
|
||||
return height;
|
||||
}
|
||||
@@ -1172,7 +1237,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the proper classes for positioning the docs to the side or below
|
||||
* Adds the proper classes for positioning the docs to the side or below depending on item
|
||||
*/
|
||||
private expandSideOrBelow() {
|
||||
if (!canExpandCompletionItem(this.focusedItem) && this.firstFocusInCurrentList) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { CompletionProviderRegistry, CompletionItemKind, CompletionItemInsertTextRule } from 'vs/editor/common/modes';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
|
||||
import { IMenuService, IMenu } from 'vs/platform/actions/common/actions';
|
||||
|
||||
suite('SuggestController', function () {
|
||||
|
||||
@@ -46,6 +47,13 @@ suite('SuggestController', function () {
|
||||
[ISuggestMemoryService, new class extends mock<ISuggestMemoryService>() {
|
||||
memorize(): void { }
|
||||
select(): number { return 0; }
|
||||
}],
|
||||
[IMenuService, new class extends mock<IMenuService>() {
|
||||
createMenu() {
|
||||
return new class extends mock<IMenu>() {
|
||||
onDidChange = Event.None;
|
||||
};
|
||||
}
|
||||
}]
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user