mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-27 07:10:30 -04:00
Refresh master with initial release/0.24 snapshot (#332)
* Initial port of release/0.24 source code * Fix additional headers * Fix a typo in launch.json
This commit is contained in:
@@ -6,8 +6,9 @@
|
||||
'use strict';
|
||||
|
||||
import { fuzzyScore } from 'vs/base/common/filters';
|
||||
import { ISuggestSupport } from 'vs/editor/common/modes';
|
||||
import { ISuggestSupport, ISuggestResult } from 'vs/editor/common/modes';
|
||||
import { ISuggestionItem, SnippetConfig } from './suggest';
|
||||
import { isDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export interface ICompletionItem extends ISuggestionItem {
|
||||
matches?: number[];
|
||||
@@ -15,6 +16,15 @@ export interface ICompletionItem extends ISuggestionItem {
|
||||
idx?: number;
|
||||
}
|
||||
|
||||
|
||||
/* __GDPR__FRAGMENT__
|
||||
"ICompletionStats" : {
|
||||
"suggestionCount" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"snippetCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"textCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
// __GDPR__TODO__: This is a dynamically extensible structure which can not be declared statically.
|
||||
export interface ICompletionStats {
|
||||
suggestionCount: number;
|
||||
snippetCount: number;
|
||||
@@ -50,6 +60,18 @@ export class CompletionModel {
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
const seen = new Set<ISuggestResult>();
|
||||
for (const { container } of this._items) {
|
||||
if (!seen.has(container)) {
|
||||
seen.add(container);
|
||||
if (isDisposable(container)) {
|
||||
container.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get lineContext(): LineContext {
|
||||
return this._lineContext;
|
||||
}
|
||||
|
||||
@@ -213,7 +213,6 @@
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .type {
|
||||
@@ -229,12 +228,26 @@
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs {
|
||||
margin: 0;
|
||||
padding: 4px 5px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs.markdown-docs {
|
||||
white-space: initial;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs .code {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > p:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-editor .suggest-widget .details code {
|
||||
border-radius: 3px;
|
||||
padding: 0 0.4em;
|
||||
}
|
||||
|
||||
/* High Contrast and Dark Theming */
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IModel, IEditorContribution, ICommonCodeEditor } from 'vs/editor/common/editorCommon';
|
||||
import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions';
|
||||
import { ISuggestResult, ISuggestSupport, ISuggestion, SuggestRegistry } from 'vs/editor/common/modes';
|
||||
import { ISuggestResult, ISuggestSupport, ISuggestion, SuggestRegistry, SuggestContext, SuggestTriggerKind } from 'vs/editor/common/modes';
|
||||
import { Position, IPosition } from 'vs/editor/common/core/position';
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
||||
@@ -42,7 +42,7 @@ export function setSnippetSuggestSupport(support: ISuggestSupport): ISuggestSupp
|
||||
return old;
|
||||
}
|
||||
|
||||
export function provideSuggestionItems(model: IModel, position: Position, snippetConfig: SnippetConfig = 'bottom', onlyFrom?: ISuggestSupport[]): TPromise<ISuggestionItem[]> {
|
||||
export function provideSuggestionItems(model: IModel, position: Position, snippetConfig: SnippetConfig = 'bottom', onlyFrom?: ISuggestSupport[], context?: SuggestContext): TPromise<ISuggestionItem[]> {
|
||||
|
||||
const allSuggestions: ISuggestionItem[] = [];
|
||||
const acceptSuggestion = createSuggesionFilter(snippetConfig);
|
||||
@@ -57,6 +57,8 @@ export function provideSuggestionItems(model: IModel, position: Position, snippe
|
||||
supports.unshift([_snippetSuggestSupport]);
|
||||
}
|
||||
|
||||
const suggestConext = context || { triggerKind: SuggestTriggerKind.Invoke };
|
||||
|
||||
// add suggestions from contributed providers - providers are ordered in groups of
|
||||
// equal score and once a group produces a result the process stops
|
||||
let hasResult = false;
|
||||
@@ -73,7 +75,7 @@ export function provideSuggestionItems(model: IModel, position: Position, snippe
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return asWinJsPromise(token => support.provideCompletionItems(model, position, token)).then(container => {
|
||||
return asWinJsPromise(token => support.provideCompletionItems(model, position, suggestConext, token)).then(container => {
|
||||
|
||||
const len = allSuggestions.length;
|
||||
|
||||
|
||||
@@ -104,10 +104,7 @@ export class SuggestController implements IEditorContribution {
|
||||
let acceptSuggestionsOnEnter = SuggestContext.AcceptSuggestionsOnEnter.bindTo(_contextKeyService);
|
||||
let updateFromConfig = () => {
|
||||
const { acceptSuggestionOnEnter } = this._editor.getConfiguration().contribInfo;
|
||||
acceptSuggestionsOnEnter.set(
|
||||
acceptSuggestionOnEnter === 'on' || acceptSuggestionOnEnter === 'smart'
|
||||
|| (<any /*migrate from old world*/>acceptSuggestionOnEnter) === true
|
||||
);
|
||||
acceptSuggestionsOnEnter.set(acceptSuggestionOnEnter === 'on' || acceptSuggestionOnEnter === 'smart');
|
||||
};
|
||||
this._toDispose.push(this._editor.onDidChangeConfiguration((e) => updateFromConfig()));
|
||||
updateFromConfig();
|
||||
@@ -173,37 +170,55 @@ export class SuggestController implements IEditorContribution {
|
||||
}
|
||||
|
||||
private _onDidSelectItem(item: ICompletionItem): void {
|
||||
if (item) {
|
||||
const { suggestion, position } = item;
|
||||
const columnDelta = this._editor.getPosition().column - position.column;
|
||||
|
||||
if (Array.isArray(suggestion.additionalTextEdits)) {
|
||||
this._editor.pushUndoStop();
|
||||
this._editor.executeEdits('suggestController.additionalTextEdits', suggestion.additionalTextEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text)));
|
||||
this._editor.pushUndoStop();
|
||||
}
|
||||
|
||||
let { insertText } = suggestion;
|
||||
if (suggestion.snippetType !== 'textmate') {
|
||||
insertText = SnippetParser.escape(insertText);
|
||||
}
|
||||
|
||||
SnippetController2.get(this._editor).insert(
|
||||
insertText,
|
||||
suggestion.overwriteBefore + columnDelta,
|
||||
suggestion.overwriteAfter
|
||||
);
|
||||
|
||||
|
||||
if (suggestion.command) {
|
||||
this._commandService.executeCommand(suggestion.command.id, ...suggestion.command.arguments).done(undefined, onUnexpectedError);
|
||||
}
|
||||
|
||||
this._alertCompletionItem(item);
|
||||
this._telemetryService.publicLog('suggestSnippetInsert', { ...this._editor.getTelemetryData(), suggestionType: suggestion.type });
|
||||
if (!item) {
|
||||
this._model.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
this._model.cancel();
|
||||
const { suggestion, position } = item;
|
||||
const columnDelta = this._editor.getPosition().column - position.column;
|
||||
|
||||
if (Array.isArray(suggestion.additionalTextEdits)) {
|
||||
this._editor.pushUndoStop();
|
||||
this._editor.executeEdits('suggestController.additionalTextEdits', suggestion.additionalTextEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text)));
|
||||
this._editor.pushUndoStop();
|
||||
}
|
||||
|
||||
let { insertText } = suggestion;
|
||||
if (suggestion.snippetType !== 'textmate') {
|
||||
insertText = SnippetParser.escape(insertText);
|
||||
}
|
||||
|
||||
SnippetController2.get(this._editor).insert(
|
||||
insertText,
|
||||
suggestion.overwriteBefore + columnDelta,
|
||||
suggestion.overwriteAfter
|
||||
);
|
||||
|
||||
if (!suggestion.command) {
|
||||
// done
|
||||
this._model.cancel();
|
||||
|
||||
} else if (suggestion.command.id === TriggerSuggestAction.id) {
|
||||
// retigger
|
||||
this._model.trigger({ auto: this._model.state === State.Auto }, true);
|
||||
|
||||
} else {
|
||||
// exec command, done
|
||||
this._commandService.executeCommand(suggestion.command.id, ...suggestion.command.arguments).done(undefined, onUnexpectedError);
|
||||
this._model.cancel();
|
||||
}
|
||||
|
||||
this._alertCompletionItem(item);
|
||||
/* __GDPR__
|
||||
"suggestSnippetInsert" : {
|
||||
"suggestionType" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"${include}": [
|
||||
"${EditorTelemetryData}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
this._telemetryService.publicLog('suggestSnippetInsert', { ...this._editor.getTelemetryData(), suggestionType: suggestion.type });
|
||||
}
|
||||
|
||||
private _alertCompletionItem({ suggestion }: ICompletionItem): void {
|
||||
@@ -212,7 +227,7 @@ export class SuggestController implements IEditorContribution {
|
||||
}
|
||||
|
||||
triggerSuggest(onlyFrom?: ISuggestSupport[]): void {
|
||||
this._model.trigger(false, false, onlyFrom);
|
||||
this._model.trigger({ auto: false }, false, onlyFrom);
|
||||
this._editor.revealLine(this._editor.getPosition().lineNumber, ScrollType.Smooth);
|
||||
this._editor.focus();
|
||||
}
|
||||
@@ -283,9 +298,11 @@ export class SuggestController implements IEditorContribution {
|
||||
@editorAction
|
||||
export class TriggerSuggestAction extends EditorAction {
|
||||
|
||||
static readonly id = 'editor.action.triggerSuggest';
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'editor.action.triggerSuggest',
|
||||
id: TriggerSuggestAction.id,
|
||||
label: nls.localize('suggest.trigger.label', "Trigger Suggest"),
|
||||
alias: 'Trigger Suggest',
|
||||
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCompletionItemProvider),
|
||||
|
||||
@@ -11,7 +11,7 @@ import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ICommonCodeEditor, IModel, IWordAtPosition } from 'vs/editor/common/editorCommon';
|
||||
import { ISuggestSupport, SuggestRegistry, StandardTokenType } from 'vs/editor/common/modes';
|
||||
import { ISuggestSupport, SuggestRegistry, StandardTokenType, SuggestTriggerKind } from 'vs/editor/common/modes';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { provideSuggestionItems, getSuggestionComparator, ISuggestionItem } from './suggest';
|
||||
import { CompletionModel } from './completionModel';
|
||||
@@ -31,6 +31,11 @@ export interface ISuggestEvent {
|
||||
auto: boolean;
|
||||
}
|
||||
|
||||
export interface SuggestTriggerContext {
|
||||
auto: boolean;
|
||||
triggerCharacter?: string;
|
||||
}
|
||||
|
||||
export class LineContext {
|
||||
|
||||
static shouldAutoTrigger(editor: ICommonCodeEditor): boolean {
|
||||
@@ -88,90 +93,94 @@ export const enum State {
|
||||
|
||||
export class SuggestModel implements IDisposable {
|
||||
|
||||
private toDispose: IDisposable[] = [];
|
||||
private quickSuggestDelay: number;
|
||||
private triggerCharacterListener: IDisposable;
|
||||
private triggerAutoSuggestPromise: TPromise<void>;
|
||||
private triggerRefilter = new TimeoutTimer();
|
||||
private _editor: ICommonCodeEditor;
|
||||
private _toDispose: IDisposable[] = [];
|
||||
private _quickSuggestDelay: number;
|
||||
private _triggerCharacterListener: IDisposable;
|
||||
private _triggerAutoSuggestPromise: TPromise<void>;
|
||||
private _triggerRefilter = new TimeoutTimer();
|
||||
private _state: State;
|
||||
|
||||
private requestPromise: TPromise<void>;
|
||||
private context: LineContext;
|
||||
private currentPosition: Position;
|
||||
|
||||
private completionModel: CompletionModel;
|
||||
private _requestPromise: TPromise<void>;
|
||||
private _context: LineContext;
|
||||
private _currentPosition: Position;
|
||||
|
||||
private _completionModel: CompletionModel;
|
||||
private _onDidCancel: Emitter<ICancelEvent> = new Emitter<ICancelEvent>();
|
||||
get onDidCancel(): Event<ICancelEvent> { return this._onDidCancel.event; }
|
||||
|
||||
private _onDidTrigger: Emitter<ITriggerEvent> = new Emitter<ITriggerEvent>();
|
||||
get onDidTrigger(): Event<ITriggerEvent> { return this._onDidTrigger.event; }
|
||||
|
||||
private _onDidSuggest: Emitter<ISuggestEvent> = new Emitter<ISuggestEvent>();
|
||||
get onDidSuggest(): Event<ISuggestEvent> { return this._onDidSuggest.event; }
|
||||
|
||||
constructor(private editor: ICommonCodeEditor) {
|
||||
readonly onDidCancel: Event<ICancelEvent> = this._onDidCancel.event;
|
||||
readonly onDidTrigger: Event<ITriggerEvent> = this._onDidTrigger.event;
|
||||
readonly onDidSuggest: Event<ISuggestEvent> = this._onDidSuggest.event;
|
||||
|
||||
constructor(editor: ICommonCodeEditor) {
|
||||
this._editor = editor;
|
||||
this._state = State.Idle;
|
||||
this.triggerAutoSuggestPromise = null;
|
||||
this.requestPromise = null;
|
||||
this.completionModel = null;
|
||||
this.context = null;
|
||||
this.currentPosition = editor.getPosition() || new Position(1, 1);
|
||||
this._triggerAutoSuggestPromise = null;
|
||||
this._requestPromise = null;
|
||||
this._completionModel = null;
|
||||
this._context = null;
|
||||
this._currentPosition = this._editor.getPosition() || new Position(1, 1);
|
||||
|
||||
// wire up various listeners
|
||||
this.toDispose.push(this.editor.onDidChangeModel(() => {
|
||||
this.updateTriggerCharacters();
|
||||
this._toDispose.push(this._editor.onDidChangeModel(() => {
|
||||
this._updateTriggerCharacters();
|
||||
this.cancel();
|
||||
}));
|
||||
this.toDispose.push(editor.onDidChangeModelLanguage(() => {
|
||||
this.updateTriggerCharacters();
|
||||
this._toDispose.push(this._editor.onDidChangeModelLanguage(() => {
|
||||
this._updateTriggerCharacters();
|
||||
this.cancel();
|
||||
}));
|
||||
this.toDispose.push(this.editor.onDidChangeConfiguration(() => {
|
||||
this.updateTriggerCharacters();
|
||||
this.updateQuickSuggest();
|
||||
this._toDispose.push(this._editor.onDidChangeConfiguration(() => {
|
||||
this._updateTriggerCharacters();
|
||||
this._updateQuickSuggest();
|
||||
}));
|
||||
this.toDispose.push(SuggestRegistry.onDidChange(() => {
|
||||
this.updateTriggerCharacters();
|
||||
this.updateActiveSuggestSession();
|
||||
this._toDispose.push(SuggestRegistry.onDidChange(() => {
|
||||
this._updateTriggerCharacters();
|
||||
this._updateActiveSuggestSession();
|
||||
}));
|
||||
this.toDispose.push(this.editor.onDidChangeCursorSelection(e => {
|
||||
this.onCursorChange(e);
|
||||
this._toDispose.push(this._editor.onDidChangeCursorSelection(e => {
|
||||
this._onCursorChange(e);
|
||||
}));
|
||||
this._toDispose.push(this._editor.onDidChangeModelContent(e => {
|
||||
this._refilterCompletionItems();
|
||||
}));
|
||||
|
||||
this.updateTriggerCharacters();
|
||||
this.updateQuickSuggest();
|
||||
this._updateTriggerCharacters();
|
||||
this._updateQuickSuggest();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
dispose([this._onDidCancel, this._onDidSuggest, this._onDidTrigger, this.triggerCharacterListener, this.triggerRefilter]);
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
dispose([this._onDidCancel, this._onDidSuggest, this._onDidTrigger, this._triggerCharacterListener, this._triggerRefilter]);
|
||||
this._toDispose = dispose(this._toDispose);
|
||||
dispose(this._completionModel);
|
||||
this.cancel();
|
||||
}
|
||||
|
||||
// --- handle configuration & precondition changes
|
||||
|
||||
private updateQuickSuggest(): void {
|
||||
this.quickSuggestDelay = this.editor.getConfiguration().contribInfo.quickSuggestionsDelay;
|
||||
private _updateQuickSuggest(): void {
|
||||
this._quickSuggestDelay = this._editor.getConfiguration().contribInfo.quickSuggestionsDelay;
|
||||
|
||||
if (isNaN(this.quickSuggestDelay) || (!this.quickSuggestDelay && this.quickSuggestDelay !== 0) || this.quickSuggestDelay < 0) {
|
||||
this.quickSuggestDelay = 10;
|
||||
if (isNaN(this._quickSuggestDelay) || (!this._quickSuggestDelay && this._quickSuggestDelay !== 0) || this._quickSuggestDelay < 0) {
|
||||
this._quickSuggestDelay = 10;
|
||||
}
|
||||
}
|
||||
|
||||
private updateTriggerCharacters(): void {
|
||||
private _updateTriggerCharacters(): void {
|
||||
|
||||
dispose(this.triggerCharacterListener);
|
||||
dispose(this._triggerCharacterListener);
|
||||
|
||||
if (this.editor.getConfiguration().readOnly
|
||||
|| !this.editor.getModel()
|
||||
|| !this.editor.getConfiguration().contribInfo.suggestOnTriggerCharacters) {
|
||||
if (this._editor.getConfiguration().readOnly
|
||||
|| !this._editor.getModel()
|
||||
|| !this._editor.getConfiguration().contribInfo.suggestOnTriggerCharacters) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const supportsByTriggerCharacter: { [ch: string]: ISuggestSupport[] } = Object.create(null);
|
||||
for (const support of SuggestRegistry.all(this.editor.getModel())) {
|
||||
for (const support of SuggestRegistry.all(this._editor.getModel())) {
|
||||
if (isFalsyOrEmpty(support.triggerCharacters)) {
|
||||
continue;
|
||||
}
|
||||
@@ -185,7 +194,7 @@ export class SuggestModel implements IDisposable {
|
||||
}
|
||||
}
|
||||
|
||||
this.triggerCharacterListener = this.editor.onDidType(text => {
|
||||
this._triggerCharacterListener = this._editor.onDidType(text => {
|
||||
const lastChar = text.charAt(text.length - 1);
|
||||
const supports = supportsByTriggerCharacter[lastChar];
|
||||
|
||||
@@ -193,14 +202,14 @@ export class SuggestModel implements IDisposable {
|
||||
// keep existing items that where not computed by the
|
||||
// supports/providers that want to trigger now
|
||||
const items: ISuggestionItem[] = [];
|
||||
if (this.completionModel) {
|
||||
for (const item of this.completionModel.items) {
|
||||
if (this._completionModel) {
|
||||
for (const item of this._completionModel.items) {
|
||||
if (supports.indexOf(item.support) < 0) {
|
||||
items.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.trigger(true, Boolean(this.completionModel), supports, items);
|
||||
this.trigger({ auto: true, triggerCharacter: lastChar }, Boolean(this._completionModel), supports, items);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -213,41 +222,43 @@ export class SuggestModel implements IDisposable {
|
||||
|
||||
cancel(retrigger: boolean = false): void {
|
||||
|
||||
if (this.triggerAutoSuggestPromise) {
|
||||
this.triggerAutoSuggestPromise.cancel();
|
||||
this.triggerAutoSuggestPromise = null;
|
||||
if (this._triggerAutoSuggestPromise) {
|
||||
this._triggerAutoSuggestPromise.cancel();
|
||||
this._triggerAutoSuggestPromise = null;
|
||||
}
|
||||
|
||||
if (this.requestPromise) {
|
||||
this.requestPromise.cancel();
|
||||
this.requestPromise = null;
|
||||
if (this._requestPromise) {
|
||||
this._requestPromise.cancel();
|
||||
this._requestPromise = null;
|
||||
}
|
||||
|
||||
this._state = State.Idle;
|
||||
this.completionModel = null;
|
||||
this.context = null;
|
||||
dispose(this._completionModel);
|
||||
this._completionModel = null;
|
||||
this._context = null;
|
||||
|
||||
this._onDidCancel.fire({ retrigger });
|
||||
}
|
||||
|
||||
private updateActiveSuggestSession(): void {
|
||||
private _updateActiveSuggestSession(): void {
|
||||
if (this._state !== State.Idle) {
|
||||
if (!SuggestRegistry.has(this.editor.getModel())) {
|
||||
if (!SuggestRegistry.has(this._editor.getModel())) {
|
||||
this.cancel();
|
||||
} else {
|
||||
this.trigger(this._state === State.Auto, true);
|
||||
this.trigger({ auto: this._state === State.Auto }, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onCursorChange(e: ICursorSelectionChangedEvent): void {
|
||||
private _onCursorChange(e: ICursorSelectionChangedEvent): void {
|
||||
|
||||
const prevPosition = this.currentPosition;
|
||||
this.currentPosition = this.editor.getPosition();
|
||||
const prevPosition = this._currentPosition;
|
||||
this._currentPosition = this._editor.getPosition();
|
||||
|
||||
if (!e.selection.isEmpty()
|
||||
|| e.source !== 'keyboard'
|
||||
|| e.reason !== CursorChangeReason.NotSet) {
|
||||
|| e.reason !== CursorChangeReason.NotSet
|
||||
) {
|
||||
|
||||
if (this._state === State.Idle) {
|
||||
// Early exit if nothing needs to be done!
|
||||
@@ -259,11 +270,11 @@ export class SuggestModel implements IDisposable {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SuggestRegistry.has(this.editor.getModel())) {
|
||||
if (!SuggestRegistry.has(this._editor.getModel())) {
|
||||
return;
|
||||
}
|
||||
|
||||
const model = this.editor.getModel();
|
||||
const model = this._editor.getModel();
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
@@ -272,33 +283,33 @@ export class SuggestModel implements IDisposable {
|
||||
|
||||
// trigger 24x7 IntelliSense when idle, enabled, when cursor
|
||||
// moved RIGHT, and when at a good position
|
||||
if (this.editor.getConfiguration().contribInfo.quickSuggestions !== false
|
||||
&& prevPosition.isBefore(this.currentPosition)
|
||||
if (this._editor.getConfiguration().contribInfo.quickSuggestions !== false
|
||||
&& prevPosition.isBefore(this._currentPosition)
|
||||
) {
|
||||
|
||||
this.cancel();
|
||||
|
||||
if (LineContext.shouldAutoTrigger(this.editor)) {
|
||||
this.triggerAutoSuggestPromise = TPromise.timeout(this.quickSuggestDelay);
|
||||
this.triggerAutoSuggestPromise.then(() => {
|
||||
const model = this.editor.getModel();
|
||||
const pos = this.editor.getPosition();
|
||||
if (LineContext.shouldAutoTrigger(this._editor)) {
|
||||
this._triggerAutoSuggestPromise = TPromise.timeout(this._quickSuggestDelay);
|
||||
this._triggerAutoSuggestPromise.then(() => {
|
||||
const model = this._editor.getModel();
|
||||
const pos = this._editor.getPosition();
|
||||
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
// validate enabled now
|
||||
const { quickSuggestions } = this.editor.getConfiguration().contribInfo;
|
||||
const { quickSuggestions } = this._editor.getConfiguration().contribInfo;
|
||||
if (quickSuggestions === false) {
|
||||
return;
|
||||
} else if (quickSuggestions === true) {
|
||||
// all good
|
||||
} else {
|
||||
// Check the type of the token that triggered this
|
||||
model.tokenizeIfCheap(pos.lineNumber);
|
||||
const { tokenType } = model
|
||||
.getLineTokens(pos.lineNumber)
|
||||
.findTokenAtOffset(pos.column - 1);
|
||||
|
||||
.findTokenAtOffset(Math.max(pos.column - 1 - 1, 0));
|
||||
const inValidScope = quickSuggestions.other && tokenType === StandardTokenType.Other
|
||||
|| quickSuggestions.comments && tokenType === StandardTokenType.Comment
|
||||
|| quickSuggestions.strings && tokenType === StandardTokenType.String;
|
||||
@@ -308,33 +319,40 @@ export class SuggestModel implements IDisposable {
|
||||
}
|
||||
}
|
||||
|
||||
this.triggerAutoSuggestPromise = null;
|
||||
this.trigger(true);
|
||||
this._triggerAutoSuggestPromise = null;
|
||||
this.trigger({ auto: true });
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
private _refilterCompletionItems(): void {
|
||||
if (this._state === State.Idle) {
|
||||
return;
|
||||
}
|
||||
const model = this._editor.getModel();
|
||||
if (model) {
|
||||
// refine active suggestion
|
||||
this.triggerRefilter.cancelAndSet(() => {
|
||||
const position = this.editor.getPosition();
|
||||
this._triggerRefilter.cancelAndSet(() => {
|
||||
const position = this._editor.getPosition();
|
||||
const ctx = new LineContext(model, position, this._state === State.Auto);
|
||||
this.onNewContext(ctx);
|
||||
this._onNewContext(ctx);
|
||||
}, 25);
|
||||
}
|
||||
}
|
||||
|
||||
public trigger(auto: boolean, retrigger: boolean = false, onlyFrom?: ISuggestSupport[], existingItems?: ISuggestionItem[]): void {
|
||||
trigger(context: SuggestTriggerContext, retrigger: boolean = false, onlyFrom?: ISuggestSupport[], existingItems?: ISuggestionItem[]): void {
|
||||
|
||||
const model = this.editor.getModel();
|
||||
const model = this._editor.getModel();
|
||||
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
const auto = context.auto;
|
||||
const ctx = new LineContext(model, this._editor.getPosition(), auto);
|
||||
|
||||
const ctx = new LineContext(model, this.editor.getPosition(), auto);
|
||||
|
||||
if (!LineContext.isInEditableRange(this.editor)) {
|
||||
if (!LineContext.isInEditableRange(this._editor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -344,92 +362,97 @@ export class SuggestModel implements IDisposable {
|
||||
this._onDidTrigger.fire({ auto });
|
||||
|
||||
// Capture context when request was sent
|
||||
this.context = ctx;
|
||||
this._context = ctx;
|
||||
|
||||
this.requestPromise = provideSuggestionItems(model, this.editor.getPosition(),
|
||||
this.editor.getConfiguration().contribInfo.snippetSuggestions,
|
||||
onlyFrom
|
||||
this._requestPromise = provideSuggestionItems(model, this._editor.getPosition(),
|
||||
this._editor.getConfiguration().contribInfo.snippetSuggestions,
|
||||
onlyFrom,
|
||||
{
|
||||
triggerCharacter: context.triggerCharacter,
|
||||
triggerKind: context.triggerCharacter ? SuggestTriggerKind.TriggerCharacter : SuggestTriggerKind.Invoke
|
||||
}
|
||||
).then(items => {
|
||||
|
||||
this.requestPromise = null;
|
||||
this._requestPromise = null;
|
||||
if (this._state === State.Idle) {
|
||||
return;
|
||||
}
|
||||
const model = this.editor.getModel();
|
||||
const model = this._editor.getModel();
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isFalsyOrEmpty(existingItems)) {
|
||||
const cmpFn = getSuggestionComparator(this.editor.getConfiguration().contribInfo.snippetSuggestions);
|
||||
const cmpFn = getSuggestionComparator(this._editor.getConfiguration().contribInfo.snippetSuggestions);
|
||||
items = items.concat(existingItems).sort(cmpFn);
|
||||
}
|
||||
|
||||
const ctx = new LineContext(model, this.editor.getPosition(), auto);
|
||||
this.completionModel = new CompletionModel(items, this.context.column, {
|
||||
const ctx = new LineContext(model, this._editor.getPosition(), auto);
|
||||
dispose(this._completionModel);
|
||||
this._completionModel = new CompletionModel(items, this._context.column, {
|
||||
leadingLineContent: ctx.leadingLineContent,
|
||||
characterCountDelta: this.context ? ctx.column - this.context.column : 0
|
||||
}, this.editor.getConfiguration().contribInfo.snippetSuggestions);
|
||||
this.onNewContext(ctx);
|
||||
characterCountDelta: this._context ? ctx.column - this._context.column : 0
|
||||
}, this._editor.getConfiguration().contribInfo.snippetSuggestions);
|
||||
this._onNewContext(ctx);
|
||||
|
||||
}).then(null, onUnexpectedError);
|
||||
}
|
||||
|
||||
private onNewContext(ctx: LineContext): void {
|
||||
private _onNewContext(ctx: LineContext): void {
|
||||
|
||||
if (!this.context) {
|
||||
if (!this._context) {
|
||||
// happens when 24x7 IntelliSense is enabled and still in its delay
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.lineNumber !== this.context.lineNumber) {
|
||||
if (ctx.lineNumber !== this._context.lineNumber) {
|
||||
// e.g. happens when pressing Enter while IntelliSense is computed
|
||||
this.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.column < this.context.column) {
|
||||
if (ctx.column < this._context.column) {
|
||||
// typed -> moved cursor LEFT -> retrigger if still on a word
|
||||
if (ctx.leadingWord.word) {
|
||||
this.trigger(this.context.auto, true);
|
||||
this.trigger({ auto: this._context.auto }, true);
|
||||
} else {
|
||||
this.cancel();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.completionModel) {
|
||||
if (!this._completionModel) {
|
||||
// happens when IntelliSense is not yet computed
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.column > this.context.column && this.completionModel.incomplete && ctx.leadingWord.word.length !== 0) {
|
||||
if (ctx.column > this._context.column && this._completionModel.incomplete && ctx.leadingWord.word.length !== 0) {
|
||||
// typed -> moved cursor RIGHT & incomple model & still on a word -> retrigger
|
||||
const { complete, incomplete } = this.completionModel.resolveIncompleteInfo();
|
||||
this.trigger(this._state === State.Auto, true, incomplete, complete);
|
||||
const { complete, incomplete } = this._completionModel.resolveIncompleteInfo();
|
||||
this.trigger({ auto: this._state === State.Auto }, true, incomplete, complete);
|
||||
|
||||
} else {
|
||||
// typed -> moved cursor RIGHT -> update UI
|
||||
let oldLineContext = this.completionModel.lineContext;
|
||||
let oldLineContext = this._completionModel.lineContext;
|
||||
let isFrozen = false;
|
||||
|
||||
this.completionModel.lineContext = {
|
||||
this._completionModel.lineContext = {
|
||||
leadingLineContent: ctx.leadingLineContent,
|
||||
characterCountDelta: ctx.column - this.context.column
|
||||
characterCountDelta: ctx.column - this._context.column
|
||||
};
|
||||
|
||||
if (this.completionModel.items.length === 0) {
|
||||
if (this._completionModel.items.length === 0) {
|
||||
|
||||
if (LineContext.shouldAutoTrigger(this.editor) && this.context.leadingWord.endColumn < ctx.leadingWord.startColumn) {
|
||||
if (LineContext.shouldAutoTrigger(this._editor) && this._context.leadingWord.endColumn < ctx.leadingWord.startColumn) {
|
||||
// retrigger when heading into a new word
|
||||
this.trigger(this.context.auto, true);
|
||||
this.trigger({ auto: this._context.auto }, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.context.auto) {
|
||||
if (!this._context.auto) {
|
||||
// freeze when IntelliSense was manually requested
|
||||
this.completionModel.lineContext = oldLineContext;
|
||||
isFrozen = this.completionModel.items.length > 0;
|
||||
this._completionModel.lineContext = oldLineContext;
|
||||
isFrozen = this._completionModel.items.length > 0;
|
||||
|
||||
if (isFrozen && ctx.leadingWord.word.length === 0) {
|
||||
// there were results before but now there aren't
|
||||
@@ -446,8 +469,8 @@ export class SuggestModel implements IDisposable {
|
||||
}
|
||||
|
||||
this._onDidSuggest.fire({
|
||||
completionModel: this.completionModel,
|
||||
auto: this.context.auto,
|
||||
completionModel: this._completionModel,
|
||||
auto: this._context.auto,
|
||||
isFrozen,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -28,10 +28,13 @@ import { alert } from 'vs/base/browser/ui/aria/aria';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService, ITheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { registerColor, editorWidgetBackground, listFocusBackground, activeContrastBorder, listHighlightForeground, editorForeground, editorWidgetBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { registerColor, editorWidgetBackground, listFocusBackground, activeContrastBorder, listHighlightForeground, editorForeground, editorWidgetBorder, focusBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/browser/markdownRenderer';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
|
||||
const sticky = false; // for development purposes
|
||||
const sticky = true; // for development purposes
|
||||
const expandSuggestionDocsByDefault = false;
|
||||
const maxSuggestionsToShow = 12;
|
||||
|
||||
@@ -142,7 +145,7 @@ class Renderer implements IRenderer<ICompletionItem, ISuggestionTemplateData> {
|
||||
data.colorspan.style.backgroundColor = '';
|
||||
|
||||
if (suggestion.type === 'color') {
|
||||
let color = matchesColor(suggestion.label) || matchesColor(suggestion.documentation);
|
||||
let color = matchesColor(suggestion.label) || typeof suggestion.documentation === 'string' && matchesColor(suggestion.documentation);
|
||||
if (color) {
|
||||
data.icon.className = 'icon customcolor';
|
||||
data.colorspan.style.backgroundColor = color;
|
||||
@@ -203,6 +206,7 @@ class SuggestionDetails {
|
||||
container: HTMLElement,
|
||||
private widget: SuggestWidget,
|
||||
private editor: ICodeEditor,
|
||||
private markdownRenderer: MarkdownRenderer,
|
||||
private triggerKeybindingLabel: string
|
||||
) {
|
||||
this.disposables = [];
|
||||
@@ -244,7 +248,14 @@ class SuggestionDetails {
|
||||
return;
|
||||
}
|
||||
removeClass(this.el, 'no-docs');
|
||||
this.docs.textContent = item.suggestion.documentation;
|
||||
if (typeof item.suggestion.documentation === 'string') {
|
||||
removeClass(this.docs, 'markdown-docs');
|
||||
this.docs.textContent = item.suggestion.documentation;
|
||||
} else {
|
||||
addClass(this.docs, 'markdown-docs');
|
||||
this.docs.innerHTML = '';
|
||||
this.docs.appendChild(this.markdownRenderer.render(item.suggestion.documentation));
|
||||
}
|
||||
|
||||
if (item.suggestion.detail) {
|
||||
this.type.innerText = item.suggestion.detail;
|
||||
@@ -382,10 +393,13 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IKeybindingService keybindingService: IKeybindingService
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IModeService modeService: IModeService,
|
||||
@IOpenerService openerService: IOpenerService
|
||||
) {
|
||||
const kb = keybindingService.lookupKeybinding('editor.action.triggerSuggest');
|
||||
const triggerKeybindingLabel = !kb ? '' : ` (${kb.getLabel()})`;
|
||||
const markdownRenderer = new MarkdownRenderer(editor, modeService, openerService);
|
||||
|
||||
this.isAuto = false;
|
||||
this.focusedItem = null;
|
||||
@@ -405,7 +419,7 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
|
||||
this.messageElement = append(this.element, $('.message'));
|
||||
this.listElement = append(this.element, $('.tree'));
|
||||
this.details = new SuggestionDetails(this.element, this, this.editor, triggerKeybindingLabel);
|
||||
this.details = new SuggestionDetails(this.element, this, this.editor, markdownRenderer, triggerKeybindingLabel);
|
||||
|
||||
let renderer = new Renderer(this, this.editor, triggerKeybindingLabel);
|
||||
|
||||
@@ -715,6 +729,15 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
} else {
|
||||
const { stats } = this.completionModel;
|
||||
stats['wasAutomaticallyTriggered'] = !!isAuto;
|
||||
/* __GDPR__
|
||||
"suggestWidget" : {
|
||||
"wasAutomaticallyTriggered" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"${include}": [
|
||||
"${ICompletionStats}",
|
||||
"${EditorTelemetryData}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('suggestWidget', { ...stats, ...this.editor.getTelemetryData() });
|
||||
|
||||
this.focusedItem = null;
|
||||
@@ -842,6 +865,13 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
this.details.element.style.borderColor = this.detailsFocusBorderColor;
|
||||
}
|
||||
}
|
||||
/* __GDPR__
|
||||
"suggestWidget:toggleDetailsFocus" : {
|
||||
"${include}": [
|
||||
"${EditorTelemetryData}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('suggestWidget:toggleDetailsFocus', this.editor.getTelemetryData());
|
||||
}
|
||||
|
||||
@@ -856,6 +886,13 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
removeClass(this.element, 'docs-side');
|
||||
removeClass(this.element, 'docs-below');
|
||||
this.editor.layoutContentWidget(this);
|
||||
/* __GDPR__
|
||||
"suggestWidget:collapseDetails" : {
|
||||
"${include}": [
|
||||
"${EditorTelemetryData}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('suggestWidget:collapseDetails', this.editor.getTelemetryData());
|
||||
} else {
|
||||
if (this.state !== State.Open && this.state !== State.Details) {
|
||||
@@ -864,6 +901,13 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
|
||||
this.updateExpandDocsSetting(true);
|
||||
this.showDetails();
|
||||
/* __GDPR__
|
||||
"suggestWidget:expandDetails" : {
|
||||
"${include}": [
|
||||
"${EditorTelemetryData}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('suggestWidget:expandDetails', this.editor.getTelemetryData());
|
||||
}
|
||||
|
||||
@@ -1062,10 +1106,20 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
let matchHighlight = theme.getColor(editorSuggestWidgetHighlightForeground);
|
||||
if (matchHighlight) {
|
||||
collector.addRule(`.monaco-editor .suggest-widget:not(.frozen) .monaco-highlighted-label .highlight { color: ${matchHighlight}; }`);
|
||||
collector.addRule(`.monaco-editor .suggest-widget:not(.frozen) .monaco-list .monaco-list-row .monaco-highlighted-label .highlight { color: ${matchHighlight}; }`);
|
||||
}
|
||||
let foreground = theme.getColor(editorSuggestWidgetForeground);
|
||||
if (foreground) {
|
||||
collector.addRule(`.monaco-editor .suggest-widget { color: ${foreground}; }`);
|
||||
}
|
||||
|
||||
const link = theme.getColor(textLinkForeground);
|
||||
if (link) {
|
||||
collector.addRule(`.monaco-editor .suggest-widget a { color: ${link}; }`);
|
||||
}
|
||||
|
||||
let codeBackground = theme.getColor(textCodeBlockBackground);
|
||||
if (codeBackground) {
|
||||
collector.addRule(`.monaco-editor .suggest-widget code { background-color: ${codeBackground}; }`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Model } from 'vs/editor/common/model/model';
|
||||
import { ICommonCodeEditor, Handler } from 'vs/editor/common/editorCommon';
|
||||
import { ISuggestSupport, ISuggestResult, SuggestRegistry } from 'vs/editor/common/modes';
|
||||
import { ISuggestSupport, ISuggestResult, SuggestRegistry, SuggestTriggerKind } from 'vs/editor/common/modes';
|
||||
import { SuggestModel, LineContext } from 'vs/editor/contrib/suggest/browser/suggestModel';
|
||||
import { MockCodeEditor, MockScopeLocation } from 'vs/editor/test/common/mocks/mockCodeEditor';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
@@ -20,6 +20,8 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
|
||||
function createMockEditor(model: Model): MockCodeEditor {
|
||||
const contextKeyService = new MockContextKeyService();
|
||||
@@ -149,25 +151,25 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
|
||||
// cancel on trigger
|
||||
assertEvent(model.onDidCancel, function () {
|
||||
model.trigger(false);
|
||||
model.trigger({ auto: false });
|
||||
}, function (event) {
|
||||
assert.equal(event.retrigger, false);
|
||||
}),
|
||||
|
||||
assertEvent(model.onDidCancel, function () {
|
||||
model.trigger(false, true);
|
||||
model.trigger({ auto: false }, true);
|
||||
}, function (event) {
|
||||
assert.equal(event.retrigger, true);
|
||||
}),
|
||||
|
||||
assertEvent(model.onDidTrigger, function () {
|
||||
model.trigger(true);
|
||||
model.trigger({ auto: true });
|
||||
}, function (event) {
|
||||
assert.equal(event.auto, true);
|
||||
}),
|
||||
|
||||
assertEvent(model.onDidTrigger, function () {
|
||||
model.trigger(false);
|
||||
model.trigger({ auto: false });
|
||||
}, function (event) {
|
||||
assert.equal(event.auto, false);
|
||||
})
|
||||
@@ -183,12 +185,12 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
return withOracle(model => {
|
||||
return TPromise.join([
|
||||
assertEvent(model.onDidCancel, function () {
|
||||
model.trigger(true);
|
||||
model.trigger({ auto: true });
|
||||
}, function (event) {
|
||||
assert.equal(event.retrigger, false);
|
||||
}),
|
||||
assertEvent(model.onDidSuggest, function () {
|
||||
model.trigger(false);
|
||||
model.trigger({ auto: false });
|
||||
}, function (event) {
|
||||
assert.equal(event.auto, false);
|
||||
assert.equal(event.isFrozen, false);
|
||||
@@ -239,7 +241,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
|
||||
return assertEvent(model.onDidSuggest, () => {
|
||||
// make sure completionModel starts here!
|
||||
model.trigger(true);
|
||||
model.trigger({ auto: true });
|
||||
}, event => {
|
||||
|
||||
return assertEvent(model.onDidSuggest, () => {
|
||||
@@ -338,7 +340,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
editor.setPosition({ lineNumber: 1, column: 3 });
|
||||
|
||||
return assertEvent(model.onDidSuggest, () => {
|
||||
model.trigger(false);
|
||||
model.trigger({ auto: false });
|
||||
}, event => {
|
||||
assert.equal(event.auto, false);
|
||||
assert.equal(event.isFrozen, false);
|
||||
@@ -363,7 +365,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
editor.setPosition({ lineNumber: 1, column: 3 });
|
||||
|
||||
return assertEvent(model.onDidSuggest, () => {
|
||||
model.trigger(false);
|
||||
model.trigger({ auto: false });
|
||||
}, event => {
|
||||
assert.equal(event.auto, false);
|
||||
assert.equal(event.isFrozen, false);
|
||||
@@ -400,7 +402,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
editor.setPosition({ lineNumber: 1, column: 4 });
|
||||
|
||||
return assertEvent(model.onDidSuggest, () => {
|
||||
model.trigger(false);
|
||||
model.trigger({ auto: false });
|
||||
}, event => {
|
||||
assert.equal(event.auto, false);
|
||||
assert.equal(event.completionModel.incomplete, true);
|
||||
@@ -437,7 +439,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
editor.setPosition({ lineNumber: 1, column: 4 });
|
||||
|
||||
return assertEvent(model.onDidSuggest, () => {
|
||||
model.trigger(false);
|
||||
model.trigger({ auto: false });
|
||||
}, event => {
|
||||
assert.equal(event.auto, false);
|
||||
assert.equal(event.completionModel.incomplete, true);
|
||||
@@ -457,4 +459,82 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Trigger character is provided in suggest context', function () {
|
||||
let triggerCharacter = '';
|
||||
disposables.push(SuggestRegistry.register({ scheme: 'test' }, {
|
||||
triggerCharacters: ['.'],
|
||||
provideCompletionItems(doc, pos, context) {
|
||||
assert.equal(context.triggerKind, SuggestTriggerKind.TriggerCharacter);
|
||||
triggerCharacter = context.triggerCharacter;
|
||||
return <ISuggestResult>{
|
||||
currentWord: '',
|
||||
incomplete: false,
|
||||
suggestions: [
|
||||
{
|
||||
label: 'foo.bar',
|
||||
type: 'property',
|
||||
insertText: 'foo.bar',
|
||||
overwriteBefore: pos.column - 1
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}));
|
||||
|
||||
model.setValue('');
|
||||
|
||||
return withOracle((model, editor) => {
|
||||
|
||||
return assertEvent(model.onDidSuggest, () => {
|
||||
editor.setPosition({ lineNumber: 1, column: 1 });
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'foo.' });
|
||||
}, event => {
|
||||
assert.equal(triggerCharacter, '.');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Mac press and hold accent character insertion does not update suggestions, #35269', function () {
|
||||
disposables.push(SuggestRegistry.register({ scheme: 'test' }, {
|
||||
provideCompletionItems(doc, pos) {
|
||||
return <ISuggestResult>{
|
||||
incomplete: true,
|
||||
suggestions: [{
|
||||
label: 'abc',
|
||||
type: 'property',
|
||||
insertText: 'abc',
|
||||
overwriteBefore: pos.column - 1
|
||||
}, {
|
||||
label: 'äbc',
|
||||
type: 'property',
|
||||
insertText: 'äbc',
|
||||
overwriteBefore: pos.column - 1
|
||||
}]
|
||||
};
|
||||
}
|
||||
}));
|
||||
|
||||
model.setValue('');
|
||||
return withOracle((model, editor) => {
|
||||
|
||||
return assertEvent(model.onDidSuggest, () => {
|
||||
editor.setPosition({ lineNumber: 1, column: 1 });
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'a' });
|
||||
}, event => {
|
||||
assert.equal(event.completionModel.items.length, 1);
|
||||
assert.equal(event.completionModel.items[0].suggestion.label, 'abc');
|
||||
|
||||
return assertEvent(model.onDidSuggest, () => {
|
||||
editor.executeEdits('test', [EditOperation.replace(new Range(1, 1, 1, 2), 'ä')]);
|
||||
|
||||
}, event => {
|
||||
// suggest model changed to äbc
|
||||
assert.equal(event.completionModel.items.length, 1);
|
||||
assert.equal(event.completionModel.items[0].suggestion.label, 'äbc');
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user