Merge from vscode 3c6f6af7347d38e87bc6406024e8dcf9e9bce229 (#8962)

* Merge from vscode 3c6f6af7347d38e87bc6406024e8dcf9e9bce229

* skip failing tests

* update mac build image
This commit is contained in:
Anthony Dresser
2020-01-27 15:28:17 -08:00
committed by Karl Burtram
parent 0eaee18dc4
commit fefe1454de
481 changed files with 12764 additions and 7836 deletions

View File

@@ -110,7 +110,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
// showings until mouse is released
this.hide();
const monitor = new GlobalMouseMoveMonitor<IStandardMouseMoveEventData>();
monitor.startMonitoring(e.buttons, standardMouseMoveMerger, () => { }, () => {
monitor.startMonitoring(<HTMLElement>e.target, e.buttons, standardMouseMoveMerger, () => { }, () => {
monitor.dispose();
});
}));

View File

@@ -220,6 +220,43 @@ suite('CodeAction', () => {
}
});
test('getCodeActions no invoke a provider that has been excluded #84602', async function () {
const baseType = CodeActionKind.Refactor;
const subType = CodeActionKind.Refactor.append('sub');
disposables.add(modes.CodeActionProviderRegistry.register('fooLang', staticCodeActionProvider(
{ title: 'a', kind: baseType.value }
)));
let didInvoke = false;
disposables.add(modes.CodeActionProviderRegistry.register('fooLang', new class implements modes.CodeActionProvider {
providedCodeActionKinds = [subType.value];
provideCodeActions(): modes.ProviderResult<modes.CodeActionList> {
didInvoke = true;
return {
actions: [
{ title: 'x', kind: subType.value }
],
dispose: () => { }
};
}
}));
{
const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), {
type: modes.CodeActionTriggerType.Auto, filter: {
include: baseType,
excludes: [subType],
}
}, CancellationToken.None);
assert.strictEqual(didInvoke, false);
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'a');
}
});
test('getCodeActions should not invoke code action providers filtered out by providedCodeActionKinds', async function () {
let wasInvoked = false;
const provider = new class implements modes.CodeActionProvider {

View File

@@ -58,6 +58,12 @@ export function mayIncludeActionsOfKind(filter: CodeActionFilter, providedKind:
return false;
}
if (filter.excludes) {
if (filter.excludes.some(exclude => excludesAction(providedKind, exclude, filter.include))) {
return false;
}
}
// Don't return source actions unless they are explicitly requested
if (!filter.includeSourceActions && CodeActionKind.Source.contains(providedKind)) {
return false;
@@ -77,10 +83,7 @@ export function filtersAction(filter: CodeActionFilter, action: CodeAction): boo
}
if (filter.excludes) {
if (actionKind && filter.excludes.some(exclude => {
// Excludes are overwritten by includes
return exclude.contains(actionKind) && (!filter.include || !filter.include.contains(actionKind));
})) {
if (actionKind && filter.excludes.some(exclude => excludesAction(actionKind, exclude, filter.include))) {
return false;
}
}
@@ -101,6 +104,17 @@ export function filtersAction(filter: CodeActionFilter, action: CodeAction): boo
return true;
}
function excludesAction(providedKind: CodeActionKind, exclude: CodeActionKind, include: CodeActionKind | undefined): boolean {
if (!exclude.contains(providedKind)) {
return false;
}
if (include && exclude.contains(include)) {
// The include is more specific, don't filter out
return false;
}
return true;
}
export interface CodeActionTrigger {
readonly type: CodeActionTriggerType;
readonly filter?: CodeActionFilter;

View File

@@ -18,7 +18,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ICodeLensCache } from 'vs/editor/contrib/codelens/codeLensCache';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { createStyleSheet } from 'vs/base/browser/dom';
import * as dom from 'vs/base/browser/dom';
import { hash } from 'vs/base/common/hash';
export class CodeLensContribution implements IEditorContribution {
@@ -65,7 +65,11 @@ export class CodeLensContribution implements IEditorContribution {
this._onModelChange();
this._styleClassName = hash(this._editor.getId()).toString(16);
this._styleElement = createStyleSheet();
this._styleElement = dom.createStyleSheet(
dom.isInShadowDOM(this._editor.getContainerDomNode())
? this._editor.getContainerDomNode()
: undefined
);
this._updateLensStyle();
}
@@ -81,7 +85,13 @@ export class CodeLensContribution implements IEditorContribution {
const fontInfo = options.get(EditorOption.fontInfo);
const lineHeight = options.get(EditorOption.lineHeight);
const newStyle = `.monaco-editor .codelens-decoration.${this._styleClassName} { height: ${Math.round(lineHeight * 1.1)}px; line-height: ${lineHeight}px; font-size: ${Math.round(fontInfo.fontSize * 0.9)}px; padding-right: ${Math.round(fontInfo.fontSize * 0.45)}px;}`;
const height = Math.round(lineHeight * 1.1);
const fontSize = Math.round(fontInfo.fontSize * 0.9);
const newStyle = `
.monaco-editor .codelens-decoration.${this._styleClassName} { height: ${height}px; line-height: ${lineHeight}px; font-size: ${fontSize}px; padding-right: ${Math.round(fontInfo.fontSize * 0.45)}px;}
.monaco-editor .codelens-decoration.${this._styleClassName} > a > .codicon { line-height: ${lineHeight}px; font-size: ${fontSize}px; }
`;
this._styleElement.innerHTML = newStyle;
}

View File

@@ -27,9 +27,8 @@
}
.monaco-editor .codelens-decoration .codicon {
line-height: inherit;
font-size: 110%;
vertical-align: inherit;
vertical-align: middle;
color: currentColor !important;
}
.monaco-editor .codelens-decoration > a:hover .codicon::before {

View File

@@ -192,7 +192,7 @@ export class ColorDetector extends Disposable implements IEditorContribution {
border: 'solid 0.1em #eee'
}
}
});
}, undefined, this._editor);
}
newDecorationsTypes[key] = true;

View File

@@ -163,7 +163,7 @@ class SaturationBox extends Disposable {
this.onDidChangePosition(e.offsetX, e.offsetY);
}
this.monitor.startMonitoring(e.buttons, standardMouseMoveMerger, event => this.onDidChangePosition(event.posx - origin.left, event.posy - origin.top), () => null);
this.monitor.startMonitoring(<HTMLElement>e.target, e.buttons, standardMouseMoveMerger, event => this.onDidChangePosition(event.posx - origin.left, event.posy - origin.top), () => null);
const mouseUpListener = dom.addDisposableGenericMouseUpListner(document, () => {
this._onColorFlushed.fire();
@@ -270,7 +270,7 @@ abstract class Strip extends Disposable {
this.onDidChangeTop(e.offsetY);
}
monitor.startMonitoring(e.buttons, standardMouseMoveMerger, event => this.onDidChangeTop(event.posy - origin.top), () => null);
monitor.startMonitoring(<HTMLElement>e.target, e.buttons, standardMouseMoveMerger, event => this.onDidChangeTop(event.posy - origin.top), () => null);
const mouseUpListener = dom.addDisposableGenericMouseUpListner(document, () => {
this._onColorFlushed.fire();

View File

@@ -15,10 +15,12 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo
export class BlockCommentCommand implements ICommand {
private readonly _selection: Selection;
private readonly _insertSpace: boolean;
private _usedEndToken: string | null;
constructor(selection: Selection) {
constructor(selection: Selection, insertSpace: boolean) {
this._selection = selection;
this._insertSpace = insertSpace;
this._usedEndToken = null;
}
@@ -53,7 +55,7 @@ export class BlockCommentCommand implements ICommand {
return true;
}
private _createOperationsForBlockComment(selection: Range, startToken: string, endToken: string, model: ITextModel, builder: IEditOperationBuilder): void {
private _createOperationsForBlockComment(selection: Range, startToken: string, endToken: string, insertSpace: boolean, model: ITextModel, builder: IEditOperationBuilder): void {
const startLineNumber = selection.startLineNumber;
const startColumn = selection.startColumn;
const endLineNumber = selection.endLineNumber;
@@ -91,25 +93,21 @@ export class BlockCommentCommand implements ICommand {
if (startTokenIndex !== -1 && endTokenIndex !== -1) {
// Consider spaces as part of the comment tokens
if (startTokenIndex + startToken.length < startLineText.length) {
if (startLineText.charCodeAt(startTokenIndex + startToken.length) === CharCode.Space) {
// Pretend the start token contains a trailing space
startToken = startToken + ' ';
}
if (insertSpace && startTokenIndex + startToken.length < startLineText.length && startLineText.charCodeAt(startTokenIndex + startToken.length) === CharCode.Space) {
// Pretend the start token contains a trailing space
startToken = startToken + ' ';
}
if (endTokenIndex > 0) {
if (endLineText.charCodeAt(endTokenIndex - 1) === CharCode.Space) {
// Pretend the end token contains a leading space
endToken = ' ' + endToken;
endTokenIndex -= 1;
}
if (insertSpace && endTokenIndex > 0 && endLineText.charCodeAt(endTokenIndex - 1) === CharCode.Space) {
// Pretend the end token contains a leading space
endToken = ' ' + endToken;
endTokenIndex -= 1;
}
ops = BlockCommentCommand._createRemoveBlockCommentOperations(
new Range(startLineNumber, startTokenIndex + startToken.length + 1, endLineNumber, endTokenIndex + 1), startToken, endToken
);
} else {
ops = BlockCommentCommand._createAddBlockCommentOperations(selection, startToken, endToken);
ops = BlockCommentCommand._createAddBlockCommentOperations(selection, startToken, endToken, this._insertSpace);
this._usedEndToken = ops.length === 1 ? endToken : null;
}
@@ -144,15 +142,15 @@ export class BlockCommentCommand implements ICommand {
return res;
}
public static _createAddBlockCommentOperations(r: Range, startToken: string, endToken: string): IIdentifiedSingleEditOperation[] {
public static _createAddBlockCommentOperations(r: Range, startToken: string, endToken: string, insertSpace: boolean): IIdentifiedSingleEditOperation[] {
let res: IIdentifiedSingleEditOperation[] = [];
if (!Range.isEmpty(r)) {
// Insert block comment start
res.push(EditOperation.insert(new Position(r.startLineNumber, r.startColumn), startToken + ' '));
res.push(EditOperation.insert(new Position(r.startLineNumber, r.startColumn), startToken + (insertSpace ? ' ' : '')));
// Insert block comment end
res.push(EditOperation.insert(new Position(r.endLineNumber, r.endColumn), ' ' + endToken));
res.push(EditOperation.insert(new Position(r.endLineNumber, r.endColumn), (insertSpace ? ' ' : '') + endToken));
} else {
// Insert both continuously
res.push(EditOperation.replace(new Range(
@@ -176,7 +174,7 @@ export class BlockCommentCommand implements ICommand {
return;
}
this._createOperationsForBlockComment(this._selection, config.blockCommentStartToken, config.blockCommentEndToken, model, builder);
this._createOperationsForBlockComment(this._selection, config.blockCommentStartToken, config.blockCommentEndToken, this._insertSpace, model, builder);
}
public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection {

View File

@@ -12,6 +12,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { BlockCommentCommand } from 'vs/editor/contrib/comment/blockCommentCommand';
import { LineCommentCommand, Type } from 'vs/editor/contrib/comment/lineCommentCommand';
// import { MenuId } from 'vs/platform/actions/common/actions';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
abstract class CommentLineAction extends EditorAction {
@@ -28,13 +29,14 @@ abstract class CommentLineAction extends EditorAction {
return;
}
let model = editor.getModel();
let commands: ICommand[] = [];
let selections = editor.getSelections();
let opts = model.getOptions();
const model = editor.getModel();
const commands: ICommand[] = [];
const selections = editor.getSelections();
const modelOptions = model.getOptions();
const commentsOptions = editor.getOption(EditorOption.comments);
for (const selection of selections) {
commands.push(new LineCommentCommand(selection, opts.tabSize, this._type));
commands.push(new LineCommentCommand(selection, modelOptions.tabSize, this._type, commentsOptions.insertSpace));
}
editor.pushUndoStop();
@@ -126,10 +128,11 @@ class BlockCommentAction extends EditorAction {
return;
}
let commands: ICommand[] = [];
let selections = editor.getSelections();
const commentsOptions = editor.getOption(EditorOption.comments);
const commands: ICommand[] = [];
const selections = editor.getSelections();
for (const selection of selections) {
commands.push(new BlockCommentCommand(selection));
commands.push(new BlockCommentCommand(selection, commentsOptions.insertSpace));
}
editor.pushUndoStop();

View File

@@ -50,17 +50,19 @@ export const enum Type {
export class LineCommentCommand implements ICommand {
private readonly _selection: Selection;
private readonly _tabSize: number;
private readonly _type: Type;
private readonly _insertSpace: boolean;
private _selectionId: string | null;
private _deltaColumn: number;
private _moveEndPositionDown: boolean;
private readonly _tabSize: number;
private readonly _type: Type;
constructor(selection: Selection, tabSize: number, type: Type) {
constructor(selection: Selection, tabSize: number, type: Type, insertSpace: boolean) {
this._selection = selection;
this._selectionId = null;
this._tabSize = tabSize;
this._type = type;
this._insertSpace = insertSpace;
this._selectionId = null;
this._deltaColumn = 0;
this._moveEndPositionDown = false;
}
@@ -98,7 +100,7 @@ export class LineCommentCommand implements ICommand {
* Analyze lines and decide which lines are relevant and what the toggle should do.
* Also, build up several offsets and lengths useful in the generation of editor operations.
*/
public static _analyzeLines(type: Type, model: ISimpleModel, lines: ILinePreflightData[], startLineNumber: number): IPreflightData {
public static _analyzeLines(type: Type, insertSpace: boolean, model: ISimpleModel, lines: ILinePreflightData[], startLineNumber: number): IPreflightData {
let onlyWhitespaceLines = true;
let shouldRemoveComments: boolean;
@@ -145,7 +147,8 @@ export class LineCommentCommand implements ICommand {
}
}
if (shouldRemoveComments) {
if (shouldRemoveComments && insertSpace) {
// Remove a following space if present
const commentStrEndOffset = lineContentStartOffset + lineData.commentStrLength;
if (commentStrEndOffset < lineContent.length && lineContent.charCodeAt(commentStrEndOffset) === CharCode.Space) {
lineData.commentStrLength += 1;
@@ -173,7 +176,7 @@ export class LineCommentCommand implements ICommand {
/**
* Analyze all lines and decide exactly what to do => not supported | insert line comments | remove line comments
*/
public static _gatherPreflightData(type: Type, model: ITextModel, startLineNumber: number, endLineNumber: number): IPreflightData {
public static _gatherPreflightData(type: Type, insertSpace: boolean, model: ITextModel, startLineNumber: number, endLineNumber: number): IPreflightData {
const lines = LineCommentCommand._gatherPreflightCommentStrings(model, startLineNumber, endLineNumber);
if (lines === null) {
return {
@@ -181,7 +184,7 @@ export class LineCommentCommand implements ICommand {
};
}
return LineCommentCommand._analyzeLines(type, model, lines, startLineNumber);
return LineCommentCommand._analyzeLines(type, insertSpace, model, lines, startLineNumber);
}
/**
@@ -195,7 +198,7 @@ export class LineCommentCommand implements ICommand {
ops = LineCommentCommand._createRemoveLineCommentsOperations(data.lines, s.startLineNumber);
} else {
LineCommentCommand._normalizeInsertionPoint(model, data.lines, s.startLineNumber, this._tabSize);
ops = LineCommentCommand._createAddLineCommentsOperations(data.lines, s.startLineNumber);
ops = this._createAddLineCommentsOperations(data.lines, s.startLineNumber);
}
const cursorPosition = new Position(s.positionLineNumber, s.positionColumn);
@@ -288,11 +291,17 @@ export class LineCommentCommand implements ICommand {
firstNonWhitespaceIndex = lineContent.length;
}
ops = BlockCommentCommand._createAddBlockCommentOperations(
new Range(s.startLineNumber, firstNonWhitespaceIndex + 1, s.startLineNumber, lineContent.length + 1), startToken, endToken
new Range(s.startLineNumber, firstNonWhitespaceIndex + 1, s.startLineNumber, lineContent.length + 1),
startToken,
endToken,
this._insertSpace
);
} else {
ops = BlockCommentCommand._createAddBlockCommentOperations(
new Range(s.startLineNumber, model.getLineFirstNonWhitespaceColumn(s.startLineNumber), s.endLineNumber, model.getLineMaxColumn(s.endLineNumber)), startToken, endToken
new Range(s.startLineNumber, model.getLineFirstNonWhitespaceColumn(s.startLineNumber), s.endLineNumber, model.getLineMaxColumn(s.endLineNumber)),
startToken,
endToken,
this._insertSpace
);
}
@@ -317,7 +326,7 @@ export class LineCommentCommand implements ICommand {
s = s.setEndPosition(s.endLineNumber - 1, model.getLineMaxColumn(s.endLineNumber - 1));
}
const data = LineCommentCommand._gatherPreflightData(this._type, model, s.startLineNumber, s.endLineNumber);
const data = LineCommentCommand._gatherPreflightData(this._type, this._insertSpace, model, s.startLineNumber, s.endLineNumber);
if (data.supported) {
return this._executeLineComments(model, builder, data, s);
}
@@ -365,8 +374,10 @@ export class LineCommentCommand implements ICommand {
/**
* Generate edit operations in the add line comment case
*/
public static _createAddLineCommentsOperations(lines: ILinePreflightData[], startLineNumber: number): IIdentifiedSingleEditOperation[] {
private _createAddLineCommentsOperations(lines: ILinePreflightData[], startLineNumber: number): IIdentifiedSingleEditOperation[] {
let res: IIdentifiedSingleEditOperation[] = [];
const afterCommentStr = this._insertSpace ? ' ' : '';
for (let i = 0, len = lines.length; i < len; i++) {
const lineData = lines[i];
@@ -375,7 +386,7 @@ export class LineCommentCommand implements ICommand {
continue;
}
res.push(EditOperation.insert(new Position(startLineNumber + i, lineData.commentStrOffset + 1), lineData.commentStr + ' '));
res.push(EditOperation.insert(new Position(startLineNumber + i, lineData.commentStrOffset + 1), lineData.commentStr + afterCommentStr));
}
return res;

View File

@@ -9,7 +9,7 @@ import { CommentMode } from 'vs/editor/test/common/commentMode';
function testBlockCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
let mode = new CommentMode({ lineComment: '!@#', blockComment: ['<0', '0>'] });
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new BlockCommentCommand(sel), expectedLines, expectedSelection);
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new BlockCommentCommand(sel, true), expectedLines, expectedSelection);
mode.dispose();
}
@@ -468,4 +468,45 @@ suite('Editor Contrib - Block Comment Command', () => {
new Selection(1, 1, 1, 1)
);
});
test('', () => {
});
test('insertSpace false', () => {
function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
let mode = new CommentMode({ lineComment: '!@#', blockComment: ['<0', '0>'] });
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new BlockCommentCommand(sel, false), expectedLines, expectedSelection);
mode.dispose();
}
testLineCommentCommand(
[
'some text'
],
new Selection(1, 1, 1, 5),
[
'<0some0> text'
],
new Selection(1, 3, 1, 7)
);
});
test('insertSpace false does not remove space', () => {
function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
let mode = new CommentMode({ lineComment: '!@#', blockComment: ['<0', '0>'] });
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new BlockCommentCommand(sel, false), expectedLines, expectedSelection);
mode.dispose();
}
testLineCommentCommand(
[
'<0 some 0> text'
],
new Selection(1, 4, 1, 8),
[
' some text'
],
new Selection(1, 1, 1, 7)
);
});
});

View File

@@ -2,6 +2,7 @@
* 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 { Selection } from 'vs/editor/common/core/selection';
import { TokenizationResult2 } from 'vs/editor/common/core/token';
@@ -18,13 +19,13 @@ suite('Editor Contrib - Line Comment Command', () => {
function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
let mode = new CommentMode({ lineComment: '!@#', blockComment: ['<!@#', '#@!>'] });
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle), expectedLines, expectedSelection);
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), expectedLines, expectedSelection);
mode.dispose();
}
function testAddLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
let mode = new CommentMode({ lineComment: '!@#', blockComment: ['<!@#', '#@!>'] });
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.ForceAdd), expectedLines, expectedSelection);
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.ForceAdd, true), expectedLines, expectedSelection);
mode.dispose();
}
@@ -46,7 +47,7 @@ suite('Editor Contrib - Line Comment Command', () => {
test('case insensitive', function () {
function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
let mode = new CommentMode({ lineComment: 'rem' });
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle), expectedLines, expectedSelection);
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), expectedLines, expectedSelection);
mode.dispose();
}
@@ -85,7 +86,7 @@ suite('Editor Contrib - Line Comment Command', () => {
test('_analyzeLines', () => {
let r: IPreflightData;
r = LineCommentCommand._analyzeLines(Type.Toggle, createSimpleModel([
r = LineCommentCommand._analyzeLines(Type.Toggle, true, createSimpleModel([
'\t\t',
' ',
' c',
@@ -116,7 +117,7 @@ suite('Editor Contrib - Line Comment Command', () => {
assert.equal(r.lines[3].commentStrOffset, 2);
r = LineCommentCommand._analyzeLines(Type.Toggle, createSimpleModel([
r = LineCommentCommand._analyzeLines(Type.Toggle, true, createSimpleModel([
'\t\t',
' rem ',
' !@# c',
@@ -626,13 +627,51 @@ suite('Editor Contrib - Line Comment Command', () => {
new Selection(2, 11, 1, 1)
);
});
test('insertSpace false', () => {
function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
let mode = new CommentMode({ lineComment: '!@#' });
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, false), expectedLines, expectedSelection);
mode.dispose();
}
testLineCommentCommand(
[
'some text'
],
new Selection(1, 1, 1, 1),
[
'!@#some text'
],
new Selection(1, 4, 1, 4)
);
});
test('insertSpace false does not remove space', () => {
function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
let mode = new CommentMode({ lineComment: '!@#' });
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, false), expectedLines, expectedSelection);
mode.dispose();
}
testLineCommentCommand(
[
'!@# some text'
],
new Selection(1, 1, 1, 1),
[
' some text'
],
new Selection(1, 1, 1, 1)
);
});
});
suite('Editor Contrib - Line Comment As Block Comment', () => {
function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
let mode = new CommentMode({ lineComment: '', blockComment: ['(', ')'] });
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle), expectedLines, expectedSelection);
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), expectedLines, expectedSelection);
mode.dispose();
}
@@ -743,7 +782,7 @@ suite('Editor Contrib - Line Comment As Block Comment', () => {
suite('Editor Contrib - Line Comment As Block Comment 2', () => {
function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
let mode = new CommentMode({ lineComment: null, blockComment: ['<!@#', '#@!>'] });
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle), expectedLines, expectedSelection);
testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), expectedLines, expectedSelection);
mode.dispose();
}
@@ -984,7 +1023,7 @@ suite('Editor Contrib - Line Comment in mixed modes', () => {
lines,
outerMode.getLanguageIdentifier(),
selection,
(sel) => new LineCommentCommand(sel, 4, Type.Toggle),
(sel) => new LineCommentCommand(sel, 4, Type.Toggle, true),
expectedLines,
expectedSelection,
true

View File

@@ -9,7 +9,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { Selection } from 'vs/editor/common/core/selection';
import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
@@ -35,6 +35,14 @@ class CursorState {
}
}
class StackElement {
constructor(
public readonly cursorState: CursorState,
public readonly scrollTop: number,
public readonly scrollLeft: number
) { }
}
export class CursorUndoRedoController extends Disposable implements IEditorContribution {
public static readonly ID = 'editor.contrib.cursorUndoRedoController';
@@ -46,8 +54,8 @@ export class CursorUndoRedoController extends Disposable implements IEditorContr
private readonly _editor: ICodeEditor;
private _isCursorUndoRedo: boolean;
private _undoStack: CursorState[];
private _redoStack: CursorState[];
private _undoStack: StackElement[];
private _redoStack: StackElement[];
constructor(editor: ICodeEditor) {
super();
@@ -76,9 +84,9 @@ export class CursorUndoRedoController extends Disposable implements IEditorContr
return;
}
const prevState = new CursorState(e.oldSelections);
const isEqualToLastUndoStack = (this._undoStack.length > 0 && this._undoStack[this._undoStack.length - 1].equals(prevState));
const isEqualToLastUndoStack = (this._undoStack.length > 0 && this._undoStack[this._undoStack.length - 1].cursorState.equals(prevState));
if (!isEqualToLastUndoStack) {
this._undoStack.push(prevState);
this._undoStack.push(new StackElement(prevState, editor.getScrollTop(), editor.getScrollLeft()));
this._redoStack = [];
if (this._undoStack.length > 50) {
// keep the cursor undo stack bounded
@@ -93,7 +101,7 @@ export class CursorUndoRedoController extends Disposable implements IEditorContr
return;
}
this._redoStack.push(new CursorState(this._editor.getSelections()));
this._redoStack.push(new StackElement(new CursorState(this._editor.getSelections()), this._editor.getScrollTop(), this._editor.getScrollLeft()));
this._applyState(this._undoStack.pop()!);
}
@@ -102,14 +110,17 @@ export class CursorUndoRedoController extends Disposable implements IEditorContr
return;
}
this._undoStack.push(new CursorState(this._editor.getSelections()));
this._undoStack.push(new StackElement(new CursorState(this._editor.getSelections()), this._editor.getScrollTop(), this._editor.getScrollLeft()));
this._applyState(this._redoStack.pop()!);
}
private _applyState(state: CursorState): void {
private _applyState(stackElement: StackElement): void {
this._isCursorUndoRedo = true;
this._editor.setSelections(state.selections);
this._editor.revealRangeInCenterIfOutsideViewport(state.selections[0], ScrollType.Smooth);
this._editor.setSelections(stackElement.cursorState.selections);
this._editor.setScrollPosition({
scrollTop: stackElement.scrollTop,
scrollLeft: stackElement.scrollLeft
});
this._isCursorUndoRedo = false;
}
}

View File

@@ -191,7 +191,7 @@ class FormatOnPaste implements IEditorContribution {
return;
}
this._callOnModel.add(this.editor.onDidPaste(range => this._trigger(range)));
this._callOnModel.add(this.editor.onDidPaste(({ range }) => this._trigger(range)));
}
private _trigger(range: Range): void {

View File

@@ -27,6 +27,7 @@ import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
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';
class MarkerModel {
@@ -209,7 +210,8 @@ export class MarkerController implements IEditorContribution {
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@IThemeService private readonly _themeService: IThemeService,
@ICodeEditorService private readonly _editorService: ICodeEditorService,
@IKeybindingService private readonly _keybindingService: IKeybindingService
@IKeybindingService private readonly _keybindingService: IKeybindingService,
@IOpenerService private readonly _openerService: IOpenerService
) {
this._editor = editor;
this._widgetVisible = CONTEXT_MARKERS_NAVIGATION_VISIBLE.bindTo(this._contextKeyService);
@@ -243,7 +245,7 @@ export class MarkerController implements IEditorContribution {
new Action(NextMarkerAction.ID, NextMarkerAction.LABEL + (nextMarkerKeybinding ? ` (${nextMarkerKeybinding.getLabel()})` : ''), 'show-next-problem codicon-chevron-down', this._model.canNavigate(), async () => { if (this._model) { this._model.move(true, true); } }),
new Action(PrevMarkerAction.ID, PrevMarkerAction.LABEL + (prevMarkerKeybinding ? ` (${prevMarkerKeybinding.getLabel()})` : ''), 'show-previous-problem codicon-chevron-up', this._model.canNavigate(), async () => { if (this._model) { this._model.move(false, true); } })
];
this._widget = new MarkerNavigationWidget(this._editor, actions, this._themeService);
this._widget = new MarkerNavigationWidget(this._editor, actions, this._themeService, this._openerService);
this._widgetVisible.set(true);
this._widget.onDidClose(() => this.closeMarkersNavigation(), this, this._disposeOnClose);

View File

@@ -26,6 +26,7 @@ import { IAction } from 'vs/base/common/actions';
import { IActionBarOptions, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { IOpenerService } from 'vs/platform/opener/common/opener';
class MessageWidget {
@@ -39,7 +40,14 @@ class MessageWidget {
private readonly _relatedDiagnostics = new WeakMap<HTMLElement, IRelatedInformation>();
private readonly _disposables: DisposableStore = new DisposableStore();
constructor(parent: HTMLElement, editor: ICodeEditor, onRelatedInformation: (related: IRelatedInformation) => void) {
private _codeLink?: HTMLElement;
constructor(
parent: HTMLElement,
editor: ICodeEditor,
onRelatedInformation: (related: IRelatedInformation) => void,
private readonly _openerService: IOpenerService,
) {
this._editor = editor;
const domNode = document.createElement('div');
@@ -81,12 +89,20 @@ class MessageWidget {
}
update({ source, message, relatedInformation, code }: IMarker): void {
let sourceAndCodeLength = (source?.length || 0) + '()'.length;
if (code) {
if (typeof code === 'string') {
sourceAndCodeLength += code.length;
} else {
sourceAndCodeLength += code.value.length;
}
}
const lines = message.split(/\r\n|\r|\n/g);
this._lines = lines.length;
this._longestLineLength = 0;
for (const line of lines) {
this._longestLineLength = Math.max(line.length, this._longestLineLength);
this._longestLineLength = Math.max(line.length + sourceAndCodeLength, this._longestLineLength);
}
dom.clearNode(this._messageBlock);
@@ -111,10 +127,25 @@ class MessageWidget {
detailsElement.appendChild(sourceElement);
}
if (code) {
const codeElement = document.createElement('span');
codeElement.innerText = `(${code})`;
dom.addClass(codeElement, 'code');
detailsElement.appendChild(codeElement);
if (typeof code === 'string') {
const codeElement = document.createElement('span');
codeElement.innerText = `(${code})`;
dom.addClass(codeElement, 'code');
detailsElement.appendChild(codeElement);
} else {
this._codeLink = dom.$('a.code-link');
this._codeLink.setAttribute('href', `${code.link.toString()}`);
this._codeLink.onclick = (e) => {
this._openerService.open(code.link);
e.preventDefault();
e.stopPropagation();
};
const codeElement = dom.append(this._codeLink, dom.$('span'));
codeElement.innerText = code.value;
detailsElement.appendChild(this._codeLink);
}
}
}
@@ -180,7 +211,8 @@ export class MarkerNavigationWidget extends PeekViewWidget {
constructor(
editor: ICodeEditor,
private readonly actions: ReadonlyArray<IAction>,
private readonly _themeService: IThemeService
private readonly _themeService: IThemeService,
private readonly _openerService: IOpenerService
) {
super(editor, { showArrow: true, showFrame: true, isAccessible: true });
this._severity = MarkerSeverity.Warning;
@@ -250,7 +282,7 @@ export class MarkerNavigationWidget extends PeekViewWidget {
this._container = document.createElement('div');
container.appendChild(this._container);
this._message = new MessageWidget(this._container, this.editor, related => this._onDidSelectRelatedInformation.fire(related));
this._message = new MessageWidget(this._container, this.editor, related => this._onDidSelectRelatedInformation.fire(related), this._openerService);
this._disposables.add(this._message);
}
@@ -329,8 +361,9 @@ export const editorMarkerNavigationInfo = registerColor('editorMarkerNavigationI
export const editorMarkerNavigationBackground = registerColor('editorMarkerNavigation.background', { dark: '#2D2D30', light: Color.white, hc: '#0C141F' }, nls.localize('editorMarkerNavigationBackground', 'Editor marker navigation widget background.'));
registerThemingParticipant((theme, collector) => {
const link = theme.getColor(textLinkForeground);
if (link) {
collector.addRule(`.monaco-editor .marker-widget a { color: ${link}; }`);
const linkFg = theme.getColor(textLinkForeground);
if (linkFg) {
collector.addRule(`.monaco-editor .marker-widget a { color: ${linkFg}; }`);
collector.addRule(`.monaco-editor .marker-widget a.code-link span:hover { color: ${linkFg}; }`);
}
});

View File

@@ -45,10 +45,27 @@
}
.monaco-editor .marker-widget .descriptioncontainer .message .source,
.monaco-editor .marker-widget .descriptioncontainer .message .code {
.monaco-editor .marker-widget .descriptioncontainer .message span.code {
opacity: 0.6;
}
.monaco-editor .marker-widget .descriptioncontainer .message a.code-link {
opacity: 0.6;
color: inherit;
}
.monaco-editor .marker-widget .descriptioncontainer .message a.code-link:before {
content: '(';
}
.monaco-editor .marker-widget .descriptioncontainer .message a.code-link:after {
content: ')';
}
.monaco-editor .marker-widget .descriptioncontainer .message a.code-link > span {
text-decoration: underline;
/** Hack to force underline to show **/
border-bottom: 1px solid transparent;
text-underline-position: under;
}
.monaco-editor .marker-widget .descriptioncontainer .filename {
cursor: pointer;
}

View File

@@ -165,7 +165,7 @@ export abstract class ReferencesController implements IEditorContribution {
let selection = this._model.nearestReference(uri, pos);
if (selection) {
return this._widget.setSelection(selection).then(() => {
if (this._widget && this._editor.getOption(EditorOption.peekWidgetFocusInlineEditor)) {
if (this._widget && this._editor.getOption(EditorOption.peekWidgetDefaultFocus) === 'editor') {
this._widget.focusOnPreviewEditor();
}
});

View File

@@ -110,3 +110,20 @@
font-size: inherit;
vertical-align: middle;
}
.monaco-editor-hover .hover-contents a.code-link:before {
content: '(';
}
.monaco-editor-hover .hover-contents a.code-link:after {
content: ')';
}
.monaco-editor-hover .hover-contents a.code-link {
color: inherit;
}
.monaco-editor-hover .hover-contents a.code-link > span {
text-decoration: underline;
/** Hack to force underline to show **/
border-bottom: 1px solid transparent;
text-underline-position: under;
}

View File

@@ -22,7 +22,7 @@ import { getHover } from 'vs/editor/contrib/hover/getHover';
import { HoverOperation, HoverStartMode, IHoverComputer } from 'vs/editor/contrib/hover/hoverOperation';
import { ContentHoverWidget } from 'vs/editor/contrib/hover/hoverWidgets';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { coalesce, isNonEmptyArray, asArray } from 'vs/base/common/arrays';
import { IMarker, IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers';
import { basename } from 'vs/base/common/resources';
@@ -39,6 +39,7 @@ import { IModeService } from 'vs/editor/common/services/modeService';
import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { Constants } from 'vs/base/common/uint';
import { textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
const $ = dom.$;
@@ -204,6 +205,8 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
private _shouldFocus: boolean;
private _colorPicker: ColorPickerWidget | null;
private _codeLink?: HTMLElement;
private readonly renderDisposable = this._register(new MutableDisposable<IDisposable>());
constructor(
@@ -500,10 +503,35 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
messageElement.innerText = message;
if (source || code) {
const detailsElement = dom.append(markerElement, $('span'));
detailsElement.style.opacity = '0.6';
detailsElement.style.paddingLeft = '6px';
detailsElement.innerText = source && code ? `${source}(${code})` : source ? source : `(${code})`;
if (typeof code === 'string') {
const detailsElement = dom.append(markerElement, $('span'));
detailsElement.style.opacity = '0.6';
detailsElement.style.paddingLeft = '6px';
detailsElement.innerText = source && code ? `${source}(${code})` : source ? source : `(${code})`;
} else {
if (code) {
const sourceAndCodeElement = $('span');
if (source) {
const sourceElement = dom.append(sourceAndCodeElement, $('span'));
sourceElement.innerText = source;
}
this._codeLink = dom.append(sourceAndCodeElement, $('a.code-link'));
this._codeLink.setAttribute('href', code.link.toString());
this._codeLink.onclick = (e) => {
this._openerService.open(code.link);
e.preventDefault();
e.stopPropagation();
};
const codeElement = dom.append(this._codeLink, $('span'));
codeElement.innerText = code.value;
const detailsElement = dom.append(markerElement, sourceAndCodeElement);
detailsElement.style.opacity = '0.6';
detailsElement.style.paddingLeft = '6px';
}
}
}
if (isNonEmptyArray(relatedInformation)) {
@@ -648,3 +676,11 @@ function hoverContentsEquals(first: HoverPart[], second: HoverPart[]): boolean {
}
return true;
}
registerThemingParticipant((theme, collector) => {
const linkFg = theme.getColor(textLinkForeground);
if (linkFg) {
collector.addRule(`.monaco-editor-hover .hover-contents a.code-link span:hover { color: ${linkFg}; }`);
}
});

View File

@@ -451,7 +451,7 @@ export class AutoIndentOnPaste implements IEditorContribution {
return;
}
this.callOnModel.add(this.editor.onDidPaste((range: Range) => {
this.callOnModel.add(this.editor.onDidPaste(({ range }) => {
this.trigger(range);
}));
}

View File

@@ -260,7 +260,7 @@ export abstract class PeekViewWidget extends ZoneWidget {
export const peekViewTitleBackground = registerColor('peekViewTitle.background', { dark: '#1E1E1E', light: '#FFFFFF', hc: '#0C141F' }, nls.localize('peekViewTitleBackground', 'Background color of the peek view title area.'));
export const peekViewTitleForeground = registerColor('peekViewTitleLabel.foreground', { dark: '#FFFFFF', light: '#333333', hc: '#FFFFFF' }, nls.localize('peekViewTitleForeground', 'Color of the peek view title.'));
export const peekViewTitleInfoForeground = registerColor('peekViewTitleDescription.foreground', { dark: '#ccccccb3', light: '#6c6c6cb3', hc: '#FFFFFF99' }, nls.localize('peekViewTitleInfoForeground', 'Color of the peek view title info.'));
export const peekViewTitleInfoForeground = registerColor('peekViewTitleDescription.foreground', { dark: '#ccccccb3', light: '#616161e6', hc: '#FFFFFF99' }, nls.localize('peekViewTitleInfoForeground', 'Color of the peek view title info.'));
export const peekViewBorder = registerColor('peekView.border', { dark: '#007acc', light: '#007acc', hc: contrastBorder }, nls.localize('peekViewBorder', 'Color of the peek view borders and arrow.'));
export const peekViewResultsBackground = registerColor('peekViewResult.background', { dark: '#252526', light: '#F3F3F3', hc: Color.black }, nls.localize('peekViewResultsBackground', 'Background color of the peek view result list.'));

View File

@@ -341,8 +341,8 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfigurat
id: 'editor',
properties: {
'editor.rename.enablePreview': {
scope: ConfigurationScope.RESOURCE_LANGUAGE,
description: nls.localize('enablePreview', "Enabe/disable the ability to preview changes before renaming"),
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
description: nls.localize('enablePreview', "Enable/disable the ability to preview changes before renaming"),
default: true,
type: 'boolean'
}

View File

@@ -9,12 +9,12 @@
}
.monaco-editor .rename-box.preview {
padding: 4px;
padding: 3px 3px 0 3px;
}
.monaco-editor .rename-box .rename-input {
padding: 4px;
width: calc(100% - 8px);
padding: 3px;
width: calc(100% - 6px);
}
.monaco-editor .rename-box .rename-label {

View File

@@ -192,6 +192,7 @@ export class CompletionModel {
}
}
const textLabel = typeof item.completion.label === 'string' ? item.completion.label : item.completion.label.name;
if (wordPos >= wordLen) {
// the wordPos at which scoring starts is the whole word
// and therefore the same rules as not having a word apply
@@ -206,19 +207,19 @@ export class CompletionModel {
if (!match) {
continue; // NO match
}
if (compareIgnoreCase(item.completion.filterText, item.completion.label) === 0) {
if (compareIgnoreCase(item.completion.filterText, textLabel) === 0) {
// filterText and label are actually the same -> use good highlights
item.score = match;
} else {
// re-run the scorer on the label in the hope of a result BUT use the rank
// of the filterText-match
item.score = anyScore(word, wordLow, wordPos, item.completion.label, item.labelLow, 0);
item.score = anyScore(word, wordLow, wordPos, textLabel, item.labelLow, 0);
item.score[0] = match[0]; // use score from filterText
}
} else {
// by default match `word` against the `label`
let match = scoreFn(word, wordLow, wordPos, item.completion.label, item.labelLow, 0, false);
let match = scoreFn(word, wordLow, wordPos, textLabel, item.labelLow, 0, false);
if (!match) {
continue; // NO match
}

View File

@@ -99,20 +99,25 @@
overflow: hidden;
text-overflow: ellipsis;
white-space: pre;
justify-content: space-between;
}
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left,
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right {
display: flex;
}
.monaco-editor .suggest-widget:not(.frozen) .monaco-highlighted-label .highlight {
font-weight: bold;
}
/** Icon styles **/
/** ReadMore Icon styles **/
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .codicon-close,
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .readMore::before {
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .readMore::before {
color: inherit;
opacity: 0.6;
opacity: 1;
font-size: 14px;
margin-left: 4px;
cursor: pointer;
}
@@ -123,39 +128,110 @@
}
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .codicon-close:hover,
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .readMore:hover {
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .readMore:hover {
opacity: 1;
}
/** signature, qualifier, type/details opacity **/
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .signature-label,
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .qualifier-label,
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .details-label {
opacity: 0.7;
}
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .qualifier-label {
margin-left: 4px;
opacity: 0.4;
font-size: 90%;
text-overflow: ellipsis;
overflow: hidden;
line-height: 17px;
align-self: center;
}
/** Type Info and icon next to the label in the focused completion item **/
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .type-label {
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .details-label {
margin-left: 0.8em;
flex: 1;
text-align: right;
overflow: hidden;
text-overflow: ellipsis;
opacity: 0.7;
white-space: nowrap;
}
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .type-label > .monaco-tokenized-source {
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .details-label > .monaco-tokenized-source {
display: inline;
}
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .readMore,
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .type-label,
.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .readMore,
.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .type-label,
.monaco-editor .suggest-widget.docs-below .monaco-list .monaco-list-row.focused > .contents > .main > .readMore {
/** Details: if using CompletionItem#details, show on focus **/
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .details-label,
.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .right > .details-label {
display: none;
}
.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .readMore,
.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .type-label {
.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .right > .details-label {
display: inline;
}
/** 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 {
display: inline;
}
/** Ellipsis on hover **/
.monaco-editor .suggest-widget:not(.docs-side) .monaco-list .monaco-list-row:hover > .contents > .main > .right.can-expand-details > .details-label {
width: calc(100% - 26px);
}
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left {
flex-shrink: 1;
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 > .contents > .main > .right {
overflow: hidden;
margin-left: 16px;
flex-shrink: 0;
max-width: 45%;
}
.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .readMore {
display: inline-block;
position: absolute;
right: 10px;
width: 18px;
height: 18px;
visibility: hidden;
}
/** Do NOT display ReadMore when docs is side/below **/
.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row > .contents > .main > .right > .readMore,
.monaco-editor .suggest-widget.docs-below .monaco-list .monaco-list-row > .contents > .main > .right > .readMore {
display: none !important;
}
/** 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 {
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 {
display: inline-block;
}
.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row > .contents > .main > .right > .readMore,
.monaco-editor .suggest-widget.docs-below .monaco-list .monaco-list-row > .contents > .main > .right > .readMore {
display: none;
}
.monaco-editor .suggest-widget .monaco-list .monaco-list-row:hover > .contents > .main > .right > .readMore {
visibility: visible;
}
/** Styles for each row in the list **/
.monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label.deprecated {

View File

@@ -37,6 +37,9 @@ export class CompletionItem {
readonly editInsertEnd: IPosition;
readonly editReplaceEnd: IPosition;
//
readonly textLabel: string;
// perf
readonly labelLow: string;
readonly sortTextLow?: string;
@@ -48,9 +51,6 @@ export class CompletionItem {
idx?: number;
word?: string;
//
readonly isDetailsResolved: boolean;
constructor(
readonly position: IPosition,
readonly completion: modes.CompletionItem,
@@ -58,8 +58,13 @@ export class CompletionItem {
readonly provider: modes.CompletionItemProvider,
model: ITextModel
) {
this.textLabel = typeof completion.label === 'string'
? completion.label
: completion.label.name;
// ensure lower-variants (perf)
this.labelLow = completion.label.toLowerCase();
this.labelLow = this.textLabel.toLowerCase();
this.sortTextLow = completion.sortText && completion.sortText.toLowerCase();
this.filterTextLow = completion.filterText && completion.filterText.toLowerCase();
@@ -74,8 +79,6 @@ export class CompletionItem {
this.editReplaceEnd = new Position(completion.range.replace.endLineNumber, completion.range.replace.endColumn);
}
this.isDetailsResolved = container.isDetailsResolved || typeof provider.resolveCompletionItem === 'undefined';
// create the suggestion resolver
const { resolveCompletionItem } = provider;
if (typeof resolveCompletionItem !== 'function') {
@@ -189,7 +192,7 @@ export function provideSuggestionItems(
}
// fill in default sortText when missing
if (!suggestion.sortText) {
suggestion.sortText = suggestion.label;
suggestion.sortText = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.name;
}
allSuggestions.push(new CompletionItem(position, suggestion, container, provider, model));

View File

@@ -341,8 +341,9 @@ export class SuggestController implements IEditorContribution {
}
private _alertCompletionItem({ completion: suggestion }: CompletionItem): void {
const textLabel = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.name;
if (isNonEmptyArray(suggestion.additionalTextEdits)) {
let msg = nls.localize('arai.alert.snippet', "Accepting '{0}' made {1} additional edits", suggestion.label, suggestion.additionalTextEdits.length);
let msg = nls.localize('arai.alert.snippet', "Accepting '{0}' made {1} additional edits", textLabel, suggestion.additionalTextEdits.length);
alert(msg);
}
}

View File

@@ -142,10 +142,10 @@ export class SuggestModel implements IDisposable {
}));
let editorIsComposing = false;
this._toDispose.add(this._editor.onCompositionStart(() => {
this._toDispose.add(this._editor.onDidCompositionStart(() => {
editorIsComposing = true;
}));
this._toDispose.add(this._editor.onCompositionEnd(() => {
this._toDispose.add(this._editor.onDidCompositionEnd(() => {
// refilter when composition ends
editorIsComposing = false;
this._refilterCompletionItems();
@@ -233,7 +233,7 @@ export class SuggestModel implements IDisposable {
};
this._triggerCharacterListener.add(this._editor.onDidType(checkTriggerCharacter));
this._triggerCharacterListener.add(this._editor.onCompositionEnd(checkTriggerCharacter));
this._triggerCharacterListener.add(this._editor.onDidCompositionEnd(checkTriggerCharacter));
}
// --- trigger/retrigger/cancel suggest
@@ -286,9 +286,7 @@ export class SuggestModel implements IDisposable {
) {
// Early exit if nothing needs to be done!
// Leave some form of early exit check here if you wish to continue being a cursor position change listener ;)
if (this._state !== State.Idle) {
this.cancel();
}
this.cancel();
return;
}

View File

@@ -46,11 +46,25 @@ const expandSuggestionDocsByDefault = false;
interface ISuggestionTemplateData {
root: HTMLElement;
/**
* Flexbox
* < ------- left ------- > < -------- right -------- >
* <icon><label><signature> <qualifier><type><readmore>
*/
left: HTMLElement;
right: HTMLElement;
icon: HTMLElement;
colorspan: HTMLElement;
iconLabel: IconLabel;
iconContainer: HTMLElement;
typeLabel: HTMLElement;
signatureLabel: HTMLElement;
qualifierLabel: HTMLElement;
/**
* Showing either `CompletionItem#details` or `CompletionItemLabel#type`
*/
detailsLabel: HTMLElement;
readMore: HTMLElement;
disposables: DisposableStore;
}
@@ -66,8 +80,12 @@ export const editorSuggestWidgetHighlightForeground = registerColor('editorSugge
const colorRegExp = /^(#([\da-f]{3}){1,2}|(rgb|hsl)a\(\s*(\d{1,3}%?\s*,\s*){3}(1|0?\.\d+)\)|(rgb|hsl)\(\s*\d{1,3}%?(\s*,\s*\d{1,3}%?){2}\s*\))$/i;
function extractColor(item: CompletionItem, out: string[]): boolean {
if (item.completion.label.match(colorRegExp)) {
out[0] = item.completion.label;
const label = typeof item.completion.label === 'string'
? item.completion.label
: item.completion.label.name;
if (label.match(colorRegExp)) {
out[0] = label;
return true;
}
if (typeof item.completion.documentation === 'string' && item.completion.documentation.match(colorRegExp)) {
@@ -92,7 +110,7 @@ function getAriaId(index: number): string {
return `suggest-aria-id:${index}`;
}
class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData> {
class ItemRenderer implements IListRenderer<CompletionItem, ISuggestionTemplateData> {
constructor(
private widget: SuggestWidget,
@@ -122,14 +140,19 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
const text = append(container, $('.contents'));
const main = append(text, $('.main'));
data.iconContainer = append(main, $('.icon-label.codicon'));
data.left = append(main, $('span.left'));
data.right = append(main, $('span.right'));
data.iconLabel = new IconLabel(main, { supportHighlights: true, supportCodicons: true });
data.iconContainer = append(data.left, $('.icon-label.codicon'));
data.iconLabel = new IconLabel(data.left, { supportHighlights: true, supportCodicons: true });
data.disposables.add(data.iconLabel);
data.typeLabel = append(main, $('span.type-label'));
data.signatureLabel = append(data.left, $('span.signature-label'));
data.qualifierLabel = append(data.left, $('span.qualifier-label'));
data.detailsLabel = append(data.right, $('span.details-label'));
data.readMore = append(main, $('span.readMore.codicon.codicon-info'));
data.readMore = append(data.right, $('span.readMore.codicon.codicon-info'));
data.readMore.title = nls.localize('readMore', "Read More...{0}", this.triggerKeybindingLabel);
const configureFont = () => {
@@ -166,6 +189,7 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
renderElement(element: CompletionItem, index: number, templateData: ISuggestionTemplateData): void {
const data = <ISuggestionTemplateData>templateData;
const suggestion = (<CompletionItem>element).completion;
const textLabel = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.name;
data.root.id = getAriaId(index);
data.icon.className = 'icon ' + completionKindToCssClass(suggestion.kind);
@@ -187,7 +211,7 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
// special logic for 'file' completion items
data.icon.className = 'icon hide';
data.iconContainer.className = 'icon hide';
const labelClasses = getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.label }), FileKind.FILE);
const labelClasses = getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: textLabel }), FileKind.FILE);
const detailClasses = getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.detail }), FileKind.FILE);
labelOptions.extraClasses = labelClasses.length > detailClasses.length ? labelClasses : detailClasses;
@@ -196,7 +220,7 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
data.icon.className = 'icon hide';
data.iconContainer.className = 'icon hide';
labelOptions.extraClasses = flatten([
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.label }), FileKind.FOLDER),
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: textLabel }), FileKind.FOLDER),
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.detail }), FileKind.FOLDER)
]);
} else {
@@ -211,10 +235,21 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
labelOptions.matches = [];
}
data.iconLabel.setLabel(suggestion.label, undefined, labelOptions);
data.typeLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, '');
data.iconLabel.setLabel(textLabel, undefined, labelOptions);
if (typeof suggestion.label === 'string') {
data.signatureLabel.textContent = '';
data.qualifierLabel.textContent = '';
data.detailsLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, '');
removeClass(data.right, 'always-show-details');
} 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');
}
if (canExpandCompletionItem(element)) {
addClass(data.right, 'can-expand-details');
show(data.readMore);
data.readMore.onmousedown = e => {
e.stopPropagation();
@@ -226,6 +261,7 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
this.widget.toggleDetails();
};
} else {
removeClass(data.right, 'can-expand-details');
hide(data.readMore);
data.readMore.onmousedown = null;
data.readMore.onclick = null;
@@ -511,7 +547,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
const applyIconStyle = () => toggleClass(this.element, 'no-icons', !this.editor.getOption(EditorOption.suggest).showIcons);
applyIconStyle();
let renderer = instantiationService.createInstance(Renderer, this, this.editor, triggerKeybindingLabel);
let renderer = instantiationService.createInstance(ItemRenderer, this, this.editor, triggerKeybindingLabel);
this.list = new List('SuggestWidget', this.listElement, this, [renderer], {
useShadows: false,
@@ -519,6 +555,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
mouseSupport: false,
accessibilityProvider: {
getAriaLabel: (item: CompletionItem) => {
const textLabel = typeof item.completion.label === 'string' ? item.completion.label : item.completion.label.name;
if (item.isResolved && this.expandDocsSettingFromStorage()) {
const { documentation, detail } = item.completion;
const docs = strings.format(
@@ -526,9 +563,9 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate<Compl
detail || '',
documentation ? (typeof documentation === 'string' ? documentation : documentation.value) : '');
return nls.localize('ariaCurrenttSuggestionReadDetails', "Item {0}, docs: {1}", item.completion.label, docs);
return nls.localize('ariaCurrenttSuggestionReadDetails', "Item {0}, docs: {1}", textLabel, docs);
} else {
return item.completion.label;
return textLabel;
}
}
}
@@ -545,7 +582,7 @@ 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 => e.hasChanged(EditorOption.suggest) && applyIconStyle()));
this.toDispose.add(this.editor.onDidChangeConfiguration(e => { if (e.hasChanged(EditorOption.suggest)) { applyIconStyle(); } }));
this.suggestWidgetVisible = SuggestContext.Visible.bindTo(contextKeyService);
this.suggestWidgetMultipleSuggestions = SuggestContext.MultipleSuggestions.bindTo(contextKeyService);

View File

@@ -48,7 +48,7 @@ export abstract class WordDistance {
if (suggestion.kind === CompletionItemKind.Keyword) {
return 2 << 20;
}
let word = suggestion.label;
let word = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.name;
let wordLines = wordRanges[word];
if (isFalsyOrEmpty(wordLines)) {
return 2 << 20;