chore(vscode): update to 1.53.2

These conflicts will be resolved in the following commits. We do it this way so
that PR review is possible.
This commit is contained in:
Joe Previte
2021-02-25 11:27:27 -07:00
1900 changed files with 83066 additions and 64589 deletions

View File

@@ -12,7 +12,7 @@ import { CharWidthRequest, CharWidthRequestType, readCharWidths } from 'vs/edito
import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver';
import { CommonEditorConfiguration, IEnvConfiguration } from 'vs/editor/common/config/commonEditorConfig';
import { EditorOption, EditorFontLigatures } from 'vs/editor/common/config/editorOptions';
import { BareFontInfo, FontInfo } from 'vs/editor/common/config/fontInfo';
import { BareFontInfo, FontInfo, SERIALIZED_FONT_INFO_VERSION } from 'vs/editor/common/config/fontInfo';
import { IDimension } from 'vs/editor/common/editorCommon';
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { IEditorConstructionOptions } from 'vs/editor/browser/editorBrowser';
@@ -76,11 +76,13 @@ export function serializeFontInfo(): ISerializedFontInfo[] | null {
}
export interface ISerializedFontInfo {
readonly version: number;
readonly zoomLevel: number;
readonly pixelRatio: number;
readonly fontFamily: string;
readonly fontWeight: string;
readonly fontSize: number;
fontFeatureSettings: string;
readonly fontFeatureSettings: string;
readonly lineHeight: number;
readonly letterSpacing: number;
readonly isMonospace: boolean;
@@ -88,8 +90,8 @@ export interface ISerializedFontInfo {
readonly typicalFullwidthCharacterWidth: number;
readonly canUseHalfwidthRightwardsArrow: boolean;
readonly spaceWidth: number;
middotWidth: number;
wsmiddotWidth: number;
readonly middotWidth: number;
readonly wsmiddotWidth: number;
readonly maxDigitWidth: number;
}
@@ -138,8 +140,7 @@ class CSSBasedConfiguration extends Disposable {
private _evictUntrustedReadings(): void {
const values = this._cache.getValues();
let somethingRemoved = false;
for (let i = 0, len = values.length; i < len; i++) {
const item = values[i];
for (const item of values) {
if (!item.isTrusted) {
somethingRemoved = true;
this._cache.remove(item);
@@ -158,12 +159,11 @@ class CSSBasedConfiguration extends Disposable {
public restoreFontInfo(savedFontInfos: ISerializedFontInfo[]): void {
// Take all the saved font info and insert them in the cache without the trusted flag.
// The reason for this is that a font might have been installed on the OS in the meantime.
for (let i = 0, len = savedFontInfos.length; i < len; i++) {
const savedFontInfo = savedFontInfos[i];
// compatibility with older versions of VS Code which did not store this...
savedFontInfo.fontFeatureSettings = savedFontInfo.fontFeatureSettings || EditorFontLigatures.OFF;
savedFontInfo.middotWidth = savedFontInfo.middotWidth || savedFontInfo.spaceWidth;
savedFontInfo.wsmiddotWidth = savedFontInfo.wsmiddotWidth || savedFontInfo.spaceWidth;
for (const savedFontInfo of savedFontInfos) {
if (savedFontInfo.version !== SERIALIZED_FONT_INFO_VERSION) {
// cannot use older version
continue;
}
const fontInfo = new FontInfo(savedFontInfo, false);
this._writeToCache(fontInfo, fontInfo);
}
@@ -177,6 +177,7 @@ class CSSBasedConfiguration extends Disposable {
// Hey, it's Bug 14341 ... we couldn't read
readConfig = new FontInfo({
zoomLevel: browser.getZoomLevel(),
pixelRatio: browser.getPixelRatio(),
fontFamily: readConfig.fontFamily,
fontWeight: readConfig.fontWeight,
fontSize: readConfig.fontSize,
@@ -289,6 +290,7 @@ class CSSBasedConfiguration extends Disposable {
const canTrustBrowserZoomLevel = (browser.getTimeSinceLastZoomLevelChanged() > 2000);
return new FontInfo({
zoomLevel: browser.getZoomLevel(),
pixelRatio: browser.getPixelRatio(),
fontFamily: bareFontInfo.fontFamily,
fontWeight: bareFontInfo.fontWeight,
fontSize: bareFontInfo.fontSize,
@@ -331,15 +333,15 @@ export class Configuration extends CommonEditorConfiguration {
constructor(
isSimpleWidget: boolean,
options: IEditorConstructionOptions,
options: Readonly<IEditorConstructionOptions>,
referenceDomElement: HTMLElement | null = null,
private readonly accessibilityService: IAccessibilityService
) {
super(isSimpleWidget, options);
this._elementSizeObserver = this._register(new ElementSizeObserver(referenceDomElement, options.dimension, () => this._onReferenceDomElementSizeChanged()));
this._elementSizeObserver = this._register(new ElementSizeObserver(referenceDomElement, options.dimension, () => this._recomputeOptions()));
this._register(CSSBasedConfiguration.INSTANCE.onDidChange(() => this._onCSSBasedConfigurationChanged()));
this._register(CSSBasedConfiguration.INSTANCE.onDidChange(() => this._recomputeOptions()));
if (this._validatedOptions.get(EditorOption.automaticLayout)) {
this._elementSizeObserver.startObserving();
@@ -351,28 +353,24 @@ export class Configuration extends CommonEditorConfiguration {
this._recomputeOptions();
}
private _onReferenceDomElementSizeChanged(): void {
this._recomputeOptions();
}
private _onCSSBasedConfigurationChanged(): void {
this._recomputeOptions();
}
public observeReferenceElement(dimension?: IDimension): void {
this._elementSizeObserver.observe(dimension);
}
public dispose(): void {
super.dispose();
public updatePixelRatio(): void {
this._recomputeOptions();
}
private _getExtraEditorClassName(): string {
private static _getExtraEditorClassName(): string {
let extra = '';
if (!browser.isSafari && !browser.isWebkitWebView) {
// Use user-select: none in all browsers except Safari and native macOS WebView
extra += 'no-user-select ';
}
if (browser.isSafari) {
// See https://github.com/microsoft/vscode/issues/108822
extra += 'no-minimap-shadow ';
}
if (platform.isMacintosh) {
extra += 'mac ';
}
@@ -381,7 +379,7 @@ export class Configuration extends CommonEditorConfiguration {
protected _getEnvConfiguration(): IEnvConfiguration {
return {
extraEditorClassName: this._getExtraEditorClassName(),
extraEditorClassName: Configuration._getExtraEditorClassName(),
outerWidth: this._elementSizeObserver.getWidth(),
outerHeight: this._elementSizeObserver.getHeight(),
emptySelectionClipboard: browser.isWebKit || browser.isFirefox,

View File

@@ -110,7 +110,12 @@ export class MouseHandler extends ViewEventHandler {
return;
}
const e = new StandardWheelEvent(browserEvent);
if (e.browserEvent!.ctrlKey || e.browserEvent!.metaKey) {
const doMouseWheelZoom = (
platform.isMacintosh
? (browserEvent.metaKey && !browserEvent.ctrlKey && !browserEvent.shiftKey && !browserEvent.altKey)
: (browserEvent.ctrlKey && !browserEvent.metaKey && !browserEvent.shiftKey && !browserEvent.altKey)
);
if (doMouseWheelZoom) {
const zoomLevel: number = EditorZoom.getZoomLevel();
const delta = e.deltaY > 0 ? 1 : -1;
EditorZoom.setZoomLevel(zoomLevel + delta);

View File

@@ -269,11 +269,11 @@ export class HitTestContext {
const viewZoneWhitespace = context.viewLayout.getWhitespaceAtVerticalOffset(mouseVerticalOffset);
if (viewZoneWhitespace) {
let viewZoneMiddle = viewZoneWhitespace.verticalOffset + viewZoneWhitespace.height / 2,
lineCount = context.model.getLineCount(),
positionBefore: Position | null = null,
position: Position | null,
positionAfter: Position | null = null;
const viewZoneMiddle = viewZoneWhitespace.verticalOffset + viewZoneWhitespace.height / 2;
const lineCount = context.model.getLineCount();
let positionBefore: Position | null = null;
let position: Position | null;
let positionAfter: Position | null = null;
if (viewZoneWhitespace.afterLineNumber !== lineCount) {
// There are more lines after this view zone
@@ -767,7 +767,7 @@ export class MouseTargetFactory {
const lineWidth = ctx.getLineWidth(lineNumber);
if (request.mouseContentHorizontalOffset > lineWidth) {
if (browser.isEdge && pos.column === 1) {
if (browser.isEdgeLegacy && pos.column === 1) {
// See https://github.com/microsoft/vscode/issues/10875
const detail = createEmptyContentDataInLines(request.mouseContentHorizontalOffset - lineWidth);
return request.fulfill(MouseTargetType.CONTENT_EMPTY, new Position(lineNumber, ctx.model.getLineMaxColumn(lineNumber)), undefined, detail);
@@ -940,12 +940,16 @@ export class MouseTargetFactory {
}
}
// For inline decorations, Gecko returns the `<span>` of the line and the offset is the `<span>` with the inline decoration
// For inline decorations, Gecko sometimes returns the `<span>` of the line and the offset is the `<span>` with the inline decoration
// Some other times, it returns the `<span>` with the inline decoration
if (hitResult.offsetNode.nodeType === hitResult.offsetNode.ELEMENT_NODE) {
const parent1 = hitResult.offsetNode.parentNode; // expected to be the view line div
const parent1 = hitResult.offsetNode.parentNode;
const parent1ClassName = parent1 && parent1.nodeType === parent1.ELEMENT_NODE ? (<HTMLElement>parent1).className : null;
const parent2 = parent1 ? parent1.parentNode : null;
const parent2ClassName = parent2 && parent2.nodeType === parent2.ELEMENT_NODE ? (<HTMLElement>parent2).className : null;
if (parent1ClassName === ViewLine.CLASS_NAME) {
// it returned the `<span>` of the line and the offset is the `<span>` with the inline decoration
const tokenSpan = hitResult.offsetNode.childNodes[Math.min(hitResult.offset, hitResult.offsetNode.childNodes.length - 1)];
if (tokenSpan) {
const p = ctx.getPositionFromDOMInfo(<HTMLElement>tokenSpan, 0);
@@ -954,6 +958,13 @@ export class MouseTargetFactory {
hitTarget: null
};
}
} else if (parent2ClassName === ViewLine.CLASS_NAME) {
// it returned the `<span>` with the inline decoration
const p = ctx.getPositionFromDOMInfo(<HTMLElement>hitResult.offsetNode, 0);
return {
position: p,
hitTarget: null
};
}
}
@@ -1014,12 +1025,11 @@ export class MouseTargetFactory {
}
private static _snapToSoftTabBoundary(position: Position, viewModel: IViewModel): Position {
const minColumn = viewModel.getLineMinColumn(position.lineNumber);
const lineContent = viewModel.getLineContent(position.lineNumber);
const { tabSize } = viewModel.getTextModelOptions();
const newPosition = AtomicTabMoveOperations.atomicPosition(lineContent, position.column - minColumn, tabSize, Direction.Nearest);
const newPosition = AtomicTabMoveOperations.atomicPosition(lineContent, position.column - 1, tabSize, Direction.Nearest);
if (newPosition !== -1) {
return new Position(position.lineNumber, newPosition + minColumn);
return new Position(position.lineNumber, newPosition + 1);
}
return position;
}

View File

@@ -54,7 +54,7 @@ class VisibleTextAreaData {
}
}
const canUseZeroSizeTextarea = (browser.isEdge || browser.isFirefox);
const canUseZeroSizeTextarea = (browser.isFirefox);
export class TextAreaHandler extends ViewPart {
@@ -280,14 +280,8 @@ export class TextAreaHandler extends ViewPart {
}));
this._register(this._textAreaInput.onCompositionUpdate((e: ICompositionData) => {
if (browser.isEdge) {
// Due to isEdgeOrIE (where the textarea was not cleared initially)
// we cannot assume the text consists only of the composited text
this._visibleTextArea = this._visibleTextArea!.setWidth(0);
} else {
// adjust width by its size
this._visibleTextArea = this._visibleTextArea!.setWidth(measureText(e.data, this._fontInfo));
}
// adjust width by its size
this._visibleTextArea = this._visibleTextArea!.setWidth(measureText(e.data, this._fontInfo));
this._render();
}));

View File

@@ -13,7 +13,7 @@ import { KeyCode } from 'vs/base/common/keyCodes';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import * as platform from 'vs/base/common/platform';
import * as strings from 'vs/base/common/strings';
import { ITextAreaWrapper, ITypeData, TextAreaState } from 'vs/editor/browser/controller/textAreaState';
import { ITextAreaWrapper, ITypeData, TextAreaState, _debugComposition } from 'vs/editor/browser/controller/textAreaState';
import { Position } from 'vs/editor/common/core/position';
import { Selection } from 'vs/editor/common/core/selection';
import { BrowserFeatures } from 'vs/base/browser/canIUse';
@@ -147,6 +147,7 @@ export class TextAreaInput extends Disposable {
private readonly _host: ITextAreaInputHost;
private readonly _textArea: TextAreaWrapper;
private readonly _asyncTriggerCut: RunOnceScheduler;
private readonly _asyncFocusGainWriteScreenReaderContent: RunOnceScheduler;
private _textAreaState: TextAreaState;
private _selectionChangeListener: IDisposable | null;
@@ -160,6 +161,7 @@ export class TextAreaInput extends Disposable {
this._host = host;
this._textArea = this._register(new TextAreaWrapper(textArea));
this._asyncTriggerCut = this._register(new RunOnceScheduler(() => this._onCut.fire(), 0));
this._asyncFocusGainWriteScreenReaderContent = this._register(new RunOnceScheduler(() => this.writeScreenReaderContent('asyncFocusGain'), 0));
this._textAreaState = TextAreaState.EMPTY;
this._selectionChangeListener = null;
@@ -193,6 +195,10 @@ export class TextAreaInput extends Disposable {
}));
this._register(dom.addDisposableListener(textArea.domNode, 'compositionstart', (e: CompositionEvent) => {
if (_debugComposition) {
console.log(`[compositionstart]`, e);
}
if (this._isDoingComposition) {
return;
}
@@ -209,6 +215,9 @@ export class TextAreaInput extends Disposable {
) {
// Handling long press case on macOS + arrow key => pretend the character was selected
if (lastKeyDown.code === 'ArrowRight' || lastKeyDown.code === 'ArrowLeft') {
if (_debugComposition) {
console.log(`[compositionstart] Handling long press case on macOS + arrow key`, e);
}
moveOneCharacterLeft = true;
}
}
@@ -221,8 +230,7 @@ export class TextAreaInput extends Disposable {
this._textAreaState.selectionStartPosition ? new Position(this._textAreaState.selectionStartPosition.lineNumber, this._textAreaState.selectionStartPosition.column - 1) : null,
this._textAreaState.selectionEndPosition
);
} else if (!browser.isEdge) {
// In IE we cannot set .value when handling 'compositionstart' because the entire composition will get canceled.
} else {
this._setAndWriteTextAreaState('compositionstart', TextAreaState.EMPTY);
}
@@ -251,27 +259,10 @@ export class TextAreaInput extends Disposable {
return [newState, typeInput];
};
const compositionDataInValid = (locale: string): boolean => {
// https://github.com/microsoft/monaco-editor/issues/339
// Multi-part Japanese compositions reset cursor in Edge/IE, Chinese and Korean IME don't have this issue.
// The reason that we can't use this path for all CJK IME is IE and Edge behave differently when handling Korean IME,
// which breaks this path of code.
if (browser.isEdge && locale === 'ja') {
return true;
}
return false;
};
this._register(dom.addDisposableListener(textArea.domNode, 'compositionupdate', (e: CompositionEvent) => {
if (compositionDataInValid(e.locale)) {
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false);
this._textAreaState = newState;
this._onType.fire(typeInput);
this._onCompositionUpdate.fire(e);
return;
if (_debugComposition) {
console.log(`[compositionupdate]`, e);
}
const [newState, typeInput] = deduceComposition(e.data || '');
this._textAreaState = newState;
this._onType.fire(typeInput);
@@ -279,28 +270,23 @@ export class TextAreaInput extends Disposable {
}));
this._register(dom.addDisposableListener(textArea.domNode, 'compositionend', (e: CompositionEvent) => {
if (_debugComposition) {
console.log(`[compositionend]`, e);
}
// https://github.com/microsoft/monaco-editor/issues/1663
// On iOS 13.2, Chinese system IME randomly trigger an additional compositionend event with empty data
if (!this._isDoingComposition) {
return;
}
if (compositionDataInValid(e.locale)) {
// https://github.com/microsoft/monaco-editor/issues/339
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false);
this._textAreaState = newState;
this._onType.fire(typeInput);
} else {
const [newState, typeInput] = deduceComposition(e.data || '');
this._textAreaState = newState;
this._onType.fire(typeInput);
}
// Due to
// isEdgeOrIE (where the textarea was not cleared initially)
// and isChrome (the textarea is not updated correctly when composition ends)
// and isFirefox (the textare ais not updated correctly after inserting emojis)
// we cannot assume the text at the end consists only of the composited text
if (browser.isEdge || browser.isChrome || browser.isFirefox) {
const [newState, typeInput] = deduceComposition(e.data || '');
this._textAreaState = newState;
this._onType.fire(typeInput);
// isChrome: the textarea is not updated correctly when composition ends
// isFirefox: the textarea is not updated correctly after inserting emojis
// => we cannot assume the text at the end consists only of the composited text
if (browser.isChrome || browser.isFirefox) {
this._textAreaState = TextAreaState.readFromTextArea(this._textArea);
}
@@ -375,9 +361,31 @@ export class TextAreaInput extends Disposable {
}));
this._register(dom.addDisposableListener(textArea.domNode, 'focus', () => {
const hadFocus = this._hasFocus;
this._setHasFocus(true);
if (browser.isSafari && !hadFocus && this._hasFocus) {
// When "tabbing into" the textarea, immediately after dispatching the 'focus' event,
// Safari will always move the selection at offset 0 in the textarea
this._asyncFocusGainWriteScreenReaderContent.schedule();
}
}));
this._register(dom.addDisposableListener(textArea.domNode, 'blur', () => {
if (this._isDoingComposition) {
// See https://github.com/microsoft/vscode/issues/112621
// where compositionend is not triggered when the editor
// is taken off-dom during a composition
// Clear the flag to be able to write to the textarea
this._isDoingComposition = false;
// Clear the textarea to avoid an unwanted cursor type
this.writeScreenReaderContent('blurWithoutCompositionEnd');
// Fire artificial composition end
this._onCompositionEnd.fire();
}
this._setHasFocus(false);
}));
}
@@ -513,13 +521,7 @@ export class TextAreaInput extends Disposable {
}
if (this._hasFocus) {
if (browser.isEdge) {
// Edge has a bug where setting the selection range while the focus event
// is dispatching doesn't work. To reproduce, "tab into" the editor.
this._setAndWriteTextAreaState('focusgain', TextAreaState.EMPTY);
} else {
this.writeScreenReaderContent('focusgain');
}
this.writeScreenReaderContent('focusgain');
}
if (this._hasFocus) {

View File

@@ -8,6 +8,8 @@ import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { EndOfLinePreference } from 'vs/editor/common/model';
export const _debugComposition = false;
export interface ITextAreaWrapper {
getValue(): string;
setValue(reason: string, value: string): void;
@@ -59,7 +61,9 @@ export class TextAreaState {
}
public writeToTextArea(reason: string, textArea: ITextAreaWrapper, select: boolean): void {
// console.log(Date.now() + ': writeToTextArea ' + reason + ': ' + this.toString());
if (_debugComposition) {
console.log('writeToTextArea ' + reason + ': ' + this.toString());
}
textArea.setValue(reason, this.value);
if (select) {
textArea.setSelectionRange(reason, this.selectionStart, this.selectionEnd);
@@ -105,9 +109,11 @@ export class TextAreaState {
};
}
// console.log('------------------------deduceInput');
// console.log('PREVIOUS STATE: ' + previousState.toString());
// console.log('CURRENT STATE: ' + currentState.toString());
if (_debugComposition) {
console.log('------------------------deduceInput');
console.log('PREVIOUS STATE: ' + previousState.toString());
console.log('CURRENT STATE: ' + currentState.toString());
}
let previousValue = previousState.value;
let previousSelectionStart = previousState.selectionStart;
@@ -133,8 +139,10 @@ export class TextAreaState {
currentSelectionEnd -= prefixLength;
previousSelectionEnd -= prefixLength;
// console.log('AFTER DIFFING PREVIOUS STATE: <' + previousValue + '>, selectionStart: ' + previousSelectionStart + ', selectionEnd: ' + previousSelectionEnd);
// console.log('AFTER DIFFING CURRENT STATE: <' + currentValue + '>, selectionStart: ' + currentSelectionStart + ', selectionEnd: ' + currentSelectionEnd);
if (_debugComposition) {
console.log('AFTER DIFFING PREVIOUS STATE: <' + previousValue + '>, selectionStart: ' + previousSelectionStart + ', selectionEnd: ' + previousSelectionEnd);
console.log('AFTER DIFFING CURRENT STATE: <' + currentValue + '>, selectionStart: ' + currentSelectionStart + ', selectionEnd: ' + currentSelectionEnd);
}
if (couldBeEmojiInput && currentSelectionStart === currentSelectionEnd && previousValue.length > 0) {
// on OSX, emojis from the emoji picker are inserted at random locations
@@ -196,7 +204,9 @@ export class TextAreaState {
// no current selection
const replacePreviousCharacters = (previousPrefix.length - prefixLength);
// console.log('REMOVE PREVIOUS: ' + (previousPrefix.length - prefixLength) + ' chars');
if (_debugComposition) {
console.log('REMOVE PREVIOUS: ' + (previousPrefix.length - prefixLength) + ' chars');
}
return {
text: currentValue,

View File

@@ -88,9 +88,7 @@ export class MarkdownRenderer {
const element = document.createElement('span');
element.innerHTML = MarkdownRenderer._ttpTokenizer
? MarkdownRenderer._ttpTokenizer.createHTML(value, tokenization) as unknown as string
: tokenizeToString(value, tokenization);
element.innerHTML = (MarkdownRenderer._ttpTokenizer?.createHTML(value, tokenization) ?? tokenizeToString(value, tokenization)) as string;
// use "good" font
let fontFamily = this._options.codeBlockFontFamily;
@@ -105,7 +103,7 @@ export class MarkdownRenderer {
},
asyncRenderCallback: () => this._onDidRenderAsync.fire(),
actionHandler: {
callback: (content) => this._openerService.open(content, { fromUserGesture: true }).catch(onUnexpectedError),
callback: (content) => this._openerService.open(content, { fromUserGesture: true, allowContributedOpeners: true }).catch(onUnexpectedError),
disposeables
}
};

View File

@@ -361,6 +361,11 @@ export interface IEditorConstructionOptions extends IEditorOptions {
}
export interface IDiffEditorConstructionOptions extends IDiffEditorOptions {
/**
* The initial editor dimension (to avoid measuring the container).
*/
dimension?: editorCommon.IDimension;
/**
* Place overflow widgets inside an external DOM node.
* Defaults to an internal DOM node.
@@ -1047,7 +1052,7 @@ export interface IDiffEditor extends editorCommon.IEditor {
/**
*@internal
*/
export function isCodeEditor(thing: any): thing is ICodeEditor {
export function isCodeEditor(thing: unknown): thing is ICodeEditor {
if (thing && typeof (<ICodeEditor>thing).getEditorType === 'function') {
return (<ICodeEditor>thing).getEditorType() === editorCommon.EditorType.ICodeEditor;
} else {
@@ -1058,7 +1063,7 @@ export function isCodeEditor(thing: any): thing is ICodeEditor {
/**
*@internal
*/
export function isDiffEditor(thing: any): thing is IDiffEditor {
export function isDiffEditor(thing: unknown): thing is IDiffEditor {
if (thing && typeof (<IDiffEditor>thing).getEditorType === 'function') {
return (<IDiffEditor>thing).getEditorType() === editorCommon.EditorType.IDiffEditor;
} else {
@@ -1069,8 +1074,8 @@ export function isDiffEditor(thing: any): thing is IDiffEditor {
/**
*@internal
*/
export function isCompositeEditor(thing: any): thing is editorCommon.ICompositeCodeEditor {
return thing
export function isCompositeEditor(thing: unknown): thing is editorCommon.ICompositeCodeEditor {
return !!thing
&& typeof thing === 'object'
&& typeof (<editorCommon.ICompositeCodeEditor>thing).onDidChangeActiveEditor === 'function';
@@ -1079,7 +1084,7 @@ export function isCompositeEditor(thing: any): thing is editorCommon.ICompositeC
/**
*@internal
*/
export function getCodeEditor(thing: any): ICodeEditor | null {
export function getCodeEditor(thing: unknown): ICodeEditor | null {
if (isCodeEditor(thing)) {
return thing;
}

View File

@@ -69,11 +69,11 @@ export interface IBulkEditOptions {
progress?: IProgress<IProgressStep>;
token?: CancellationToken;
showPreview?: boolean;
suppressPreview?: boolean;
label?: string;
quotableLabel?: string;
undoRedoSource?: UndoRedoSource;
undoRedoGroupId?: number;
confirmBeforeUndo?: boolean;
}
export interface IBulkEditResult {

View File

@@ -347,6 +347,8 @@ const _CSS_MAP: { [prop: string]: string; } = {
fontStyle: 'font-style:{0};',
fontWeight: 'font-weight:{0};',
fontSize: 'font-size:{0};',
fontFamily: 'font-family:{0};',
textDecoration: 'text-decoration:{0};',
cursor: 'cursor:{0};',
letterSpacing: 'letter-spacing:{0};',
@@ -357,6 +359,7 @@ const _CSS_MAP: { [prop: string]: string; } = {
contentText: 'content:\'{0}\';',
contentIconPath: 'content:{0};',
margin: 'margin:{0};',
padding: 'padding:{0};',
width: 'width:{0};',
height: 'height:{0};'
};
@@ -529,7 +532,7 @@ class DecorationCSSRules {
cssTextArr.push(strings.format(_CSS_MAP.contentText, escaped));
}
this.collectCSSText(opts, ['fontStyle', 'fontWeight', 'textDecoration', 'color', 'opacity', 'backgroundColor', 'margin'], cssTextArr);
this.collectCSSText(opts, ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'textDecoration', 'color', 'opacity', 'backgroundColor', 'margin', 'padding'], cssTextArr);
if (this.collectCSSText(opts, ['width', 'height'], cssTextArr)) {
cssTextArr.push('display:inline-block;');
}

View File

@@ -4,18 +4,18 @@
*--------------------------------------------------------------------------------------------*/
import * as dom from 'vs/base/browser/dom';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IDisposable } from 'vs/base/common/lifecycle';
import { LinkedList } from 'vs/base/common/linkedList';
import { ResourceMap } from 'vs/base/common/map';
import { parse } from 'vs/base/common/marshalling';
import { Schemas } from 'vs/base/common/network';
import { normalizePath } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IOpener, IOpenerService, IValidator, IExternalUriResolver, OpenOptions, ResolveExternalUriOptions, IResolvedExternalUri, IExternalOpener, matchesScheme } from 'vs/platform/opener/common/opener';
import { EditorOpenContext } from 'vs/platform/editor/common/editor';
import { ResourceMap } from 'vs/base/common/map';
import { IExternalOpener, IExternalUriResolver, IOpener, IOpenerService, IResolvedExternalUri, IValidator, matchesScheme, OpenOptions, ResolveExternalUriOptions } from 'vs/platform/opener/common/opener';
class CommandOpener implements IOpener {
@@ -100,15 +100,16 @@ export class OpenerService implements IOpenerService {
private readonly _resolvers = new LinkedList<IExternalUriResolver>();
private readonly _resolvedUriTargets = new ResourceMap<URI>(uri => uri.with({ path: null, fragment: null, query: null }).toString());
private _externalOpener: IExternalOpener;
private _defaultExternalOpener: IExternalOpener;
private readonly _externalOpeners = new LinkedList<IExternalOpener>();
constructor(
@ICodeEditorService editorService: ICodeEditorService,
@ICommandService commandService: ICommandService,
) {
// Default external opener is going through window.open()
this._externalOpener = {
openExternal: href => {
this._defaultExternalOpener = {
openExternal: async href => {
// ensure to open HTTP/HTTPS links into new windows
// to not trigger a navigation. Any other link is
// safe to be set as HREF to prevent a blank window
@@ -118,11 +119,11 @@ export class OpenerService implements IOpenerService {
} else {
window.location.href = href;
}
return Promise.resolve(true);
return true;
}
};
// Default opener: maito, http(s), command, and catch-all-editors
// Default opener: any external, maito, http(s), command, and catch-all-editors
this._openers.push({
open: async (target: URI | string, options?: OpenOptions) => {
if (options?.openExternal || matchesScheme(target, Schemas.mailto) || matchesScheme(target, Schemas.http) || matchesScheme(target, Schemas.https)) {
@@ -152,8 +153,13 @@ export class OpenerService implements IOpenerService {
return { dispose: remove };
}
setExternalOpener(externalOpener: IExternalOpener): void {
this._externalOpener = externalOpener;
setDefaultExternalOpener(externalOpener: IExternalOpener): void {
this._defaultExternalOpener = externalOpener;
}
registerExternalOpener(opener: IExternalOpener): IDisposable {
const remove = this._externalOpeners.push(opener);
return { dispose: remove };
}
async open(target: URI | string, options?: OpenOptions): Promise<boolean> {
@@ -196,13 +202,29 @@ export class OpenerService implements IOpenerService {
const uri = typeof resource === 'string' ? URI.parse(resource) : resource;
const { resolved } = await this.resolveExternalUri(uri, options);
let href: string;
if (typeof resource === 'string' && uri.toString() === resolved.toString()) {
// open the url-string AS IS
return this._externalOpener.openExternal(resource);
href = resource;
} else {
// open URI using the toString(noEncode)+encodeURI-trick
return this._externalOpener.openExternal(encodeURI(resolved.toString(true)));
href = encodeURI(resolved.toString(true));
}
if (options?.allowContributedOpeners) {
const preferredOpenerId = typeof options?.allowContributedOpeners === 'string' ? options?.allowContributedOpeners : undefined;
for (const opener of this._externalOpeners) {
const didOpen = await opener.openExternal(href, {
sourceUri: uri,
preferredOpenerId,
}, CancellationToken.None);
if (didOpen) {
return true;
}
}
}
return this._defaultExternalOpener.openExternal(href, { sourceUri: uri }, CancellationToken.None);
}
dispose() {

View File

@@ -111,8 +111,8 @@ function createLineBreaks(requests: string[], fontInfo: FontInfo, tabSize: numbe
allVisibleColumns[i] = tmp[1];
}
const html = sb.build();
const trustedhtml = ttPolicy ? ttPolicy.createHTML(html) : html;
containerDomNode.innerHTML = trustedhtml as unknown as string;
const trustedhtml = ttPolicy?.createHTML(html) ?? html;
containerDomNode.innerHTML = trustedhtml as string;
containerDomNode.style.position = 'absolute';
containerDomNode.style.top = '10000';

View File

@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as dom from 'vs/base/browser/dom';
import * as browser from 'vs/base/browser/browser';
import { Selection } from 'vs/editor/common/core/selection';
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
@@ -65,6 +66,7 @@ export class View extends ViewEventHandler {
private readonly _scrollbar: EditorScrollbar;
private readonly _context: ViewContext;
private _configPixelRatio: number;
private _selections: Selection[];
// The view lines
@@ -104,6 +106,7 @@ export class View extends ViewEventHandler {
// The view context is passed on to most classes (basically to reduce param. counts in ctors)
this._context = new ViewContext(configuration, themeService.getColorTheme(), model);
this._configPixelRatio = this._configPixelRatio = this._context.configuration.options.get(EditorOption.pixelRatio);
// Ensure the view is the first event handler in order to update the layout
this._context.addEventHandler(this);
@@ -298,6 +301,7 @@ export class View extends ViewEventHandler {
this._scheduleRender();
}
public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean {
this._configPixelRatio = this._context.configuration.options.get(EditorOption.pixelRatio);
this.domNode.setClassName(this._getEditorClassName());
this._applyLayout();
return false;
@@ -330,8 +334,8 @@ export class View extends ViewEventHandler {
this._viewLines.dispose();
// Destroy view parts
for (let i = 0, len = this._viewParts.length; i < len; i++) {
this._viewParts[i].dispose();
for (const viewPart of this._viewParts) {
viewPart.dispose();
}
super.dispose();
@@ -354,8 +358,7 @@ export class View extends ViewEventHandler {
private _getViewPartsToRender(): ViewPart[] {
let result: ViewPart[] = [], resultLen = 0;
for (let i = 0, len = this._viewParts.length; i < len; i++) {
const viewPart = this._viewParts[i];
for (const viewPart of this._viewParts) {
if (viewPart.shouldRender()) {
result[resultLen++] = viewPart;
}
@@ -401,16 +404,20 @@ export class View extends ViewEventHandler {
const renderingContext = new RenderingContext(this._context.viewLayout, viewportData, this._viewLines);
// Render the rest of the parts
for (let i = 0, len = viewPartsToRender.length; i < len; i++) {
const viewPart = viewPartsToRender[i];
for (const viewPart of viewPartsToRender) {
viewPart.prepareRender(renderingContext);
}
for (let i = 0, len = viewPartsToRender.length; i < len; i++) {
const viewPart = viewPartsToRender[i];
for (const viewPart of viewPartsToRender) {
viewPart.render(renderingContext);
viewPart.onDidRender();
}
// Try to detect browser zooming and paint again if necessary
if (Math.abs(browser.getPixelRatio() - this._configPixelRatio) > 0.001) {
// looks like the pixel ratio has changed
this._context.configuration.updatePixelRatio();
}
}
// --- BEGIN CodeEditor helpers
@@ -462,8 +469,7 @@ export class View extends ViewEventHandler {
if (everything) {
// Force everything to render...
this._viewLines.forceShouldRender();
for (let i = 0, len = this._viewParts.length; i < len; i++) {
const viewPart = this._viewParts[i];
for (const viewPart of this._viewParts) {
viewPart.forceShouldRender();
}
}

View File

@@ -506,15 +506,15 @@ class ViewLayerRenderer<T extends IVisibleLine> {
ctx.lines.splice(removeIndex, removeCount);
}
private _finishRenderingNewLines(ctx: IRendererContext<T>, domNodeIsEmpty: boolean, newLinesHTML: string, wasNew: boolean[]): void {
private _finishRenderingNewLines(ctx: IRendererContext<T>, domNodeIsEmpty: boolean, newLinesHTML: string | TrustedHTML, wasNew: boolean[]): void {
if (ViewLayerRenderer._ttPolicy) {
newLinesHTML = ViewLayerRenderer._ttPolicy.createHTML(newLinesHTML) as unknown as string; // explains the ugly casts -> https://github.com/microsoft/vscode/issues/106396#issuecomment-692625393
newLinesHTML = ViewLayerRenderer._ttPolicy.createHTML(newLinesHTML as string);
}
const lastChild = <HTMLElement>this.domNode.lastChild;
if (domNodeIsEmpty || !lastChild) {
this.domNode.innerHTML = newLinesHTML;
this.domNode.innerHTML = newLinesHTML as string; // explains the ugly casts -> https://github.com/microsoft/vscode/issues/106396#issuecomment-692625393;
} else {
lastChild.insertAdjacentHTML('afterend', newLinesHTML);
lastChild.insertAdjacentHTML('afterend', newLinesHTML as string);
}
let currChild = <HTMLElement>this.domNode.lastChild;
@@ -527,13 +527,13 @@ class ViewLayerRenderer<T extends IVisibleLine> {
}
}
private _finishRenderingInvalidLines(ctx: IRendererContext<T>, invalidLinesHTML: string, wasInvalid: boolean[]): void {
private _finishRenderingInvalidLines(ctx: IRendererContext<T>, invalidLinesHTML: string | TrustedHTML, wasInvalid: boolean[]): void {
const hugeDomNode = document.createElement('div');
if (ViewLayerRenderer._ttPolicy) {
invalidLinesHTML = ViewLayerRenderer._ttPolicy.createHTML(invalidLinesHTML) as unknown as string;
invalidLinesHTML = ViewLayerRenderer._ttPolicy.createHTML(invalidLinesHTML as string);
}
hugeDomNode.innerHTML = invalidLinesHTML;
hugeDomNode.innerHTML = invalidLinesHTML as string;
for (let i = 0; i < ctx.linesLength; i++) {
const line = ctx.lines[i];

View File

@@ -42,8 +42,8 @@ export abstract class AbstractLineHighlightOverlay extends DynamicViewOverlay {
this._contentWidth = layoutInfo.contentWidth;
this._selectionIsEmpty = true;
this._focused = false;
this._cursorLineNumbers = [];
this._selections = [];
this._cursorLineNumbers = [1];
this._selections = [new Selection(1, 1, 1, 1)];
this._renderData = null;
this._context.addEventHandler(this);

View File

@@ -44,7 +44,7 @@ const canUseFastRenderedViewLine = (function () {
let monospaceAssumptionsAreValid = true;
const alwaysRenderInlineSelection = (browser.isEdge);
const alwaysRenderInlineSelection = (browser.isEdgeLegacy);
export class DomReadingContext {

View File

@@ -25,3 +25,8 @@
left: -6px;
width: 6px;
}
.monaco-editor.no-minimap-shadow .minimap-shadow-visible {
position: absolute;
left: -1px;
width: 1px;
}

View File

@@ -862,6 +862,7 @@ export class Minimap extends ViewPart implements IMinimapModel {
}
}
public onTokensColorsChanged(e: viewEvents.ViewTokensColorsChangedEvent): boolean {
this._onOptionsMaybeChanged();
return this._actual.onTokensColorsChanged();
}
public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean {

View File

@@ -60,7 +60,7 @@ function toStyled(item: LineVisibleRanges): LineVisibleRangesWithStyle {
// TODO@Alex: Remove this once IE11 fixes Bug #524217
// The problem in IE11 is that it does some sort of auto-zooming to accomodate for displays with different pixel density.
// Unfortunately, this auto-zooming is buggy around dealing with rounded borders
const isIEWithZoomingIssuesNearRoundedBorders = browser.isEdge;
const isIEWithZoomingIssuesNearRoundedBorders = browser.isEdgeLegacy;
export class SelectionsOverlay extends DynamicViewOverlay {

View File

@@ -24,7 +24,7 @@ import { ViewUserInputEvents } from 'vs/editor/browser/view/viewUserInputEvents'
import { ConfigurationChangedEvent, EditorLayoutInfo, IEditorOptions, EditorOption, IComputedEditorOptions, FindComputedEditorOptionValueById, filterValidationDecorations } from 'vs/editor/common/config/editorOptions';
import { Cursor } from 'vs/editor/common/controller/cursor';
import { CursorColumns } from 'vs/editor/common/controller/cursorCommon';
import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { CursorChangeReason, ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import { ISelection, Selection } from 'vs/editor/common/core/selection';
@@ -241,7 +241,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
constructor(
domElement: HTMLElement,
options: editorBrowser.IEditorConstructionOptions,
_options: Readonly<editorBrowser.IEditorConstructionOptions>,
codeEditorWidgetOptions: ICodeEditorWidgetOptions,
@IInstantiationService instantiationService: IInstantiationService,
@ICodeEditorService codeEditorService: ICodeEditorService,
@@ -253,10 +253,11 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
) {
super();
options = options || {};
const options = { ..._options };
this._domElement = domElement;
this._overflowWidgetsDomNode = options.overflowWidgetsDomNode;
delete options.overflowWidgetsDomNode;
this._id = (++EDITOR_ID);
this._decorationTypeKeysToIds = {};
this._decorationTypeSubtypes = {};
@@ -331,7 +332,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
this._codeEditorService.addCodeEditor(this);
}
protected _createConfiguration(options: editorBrowser.IEditorConstructionOptions, accessibilityService: IAccessibilityService): editorCommon.IConfiguration {
protected _createConfiguration(options: Readonly<editorBrowser.IEditorConstructionOptions>, accessibilityService: IAccessibilityService): editorCommon.IConfiguration {
return new Configuration(this.isSimpleWidget, options, this._domElement, accessibilityService);
}
@@ -366,7 +367,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
return this._instantiationService.invokeFunction(fn);
}
public updateOptions(newOptions: IEditorOptions): void {
public updateOptions(newOptions: Readonly<IEditorOptions>): void {
this._configuration.updateOptions(newOptions);
}
@@ -811,7 +812,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
);
}
public setSelections(ranges: readonly ISelection[], source: string = 'api'): void {
public setSelections(ranges: readonly ISelection[], source: string = 'api', reason = CursorChangeReason.NotSet): void {
if (!this._modelData) {
return;
}
@@ -823,7 +824,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
throw new Error('Invalid arguments');
}
}
this._modelData.viewModel.setSelections(source, ranges);
this._modelData.viewModel.setSelections(source, ranges, reason);
}
public getContentWidth(): number {
@@ -1834,6 +1835,7 @@ export class EditorModeContext extends Disposable {
private readonly _hasMultipleDocumentFormattingProvider: IContextKey<boolean>;
private readonly _hasMultipleDocumentSelectionFormattingProvider: IContextKey<boolean>;
private readonly _hasSignatureHelpProvider: IContextKey<boolean>;
private readonly _hasInlineHintsProvider: IContextKey<boolean>;
private readonly _isInWalkThrough: IContextKey<boolean>;
constructor(
@@ -1856,6 +1858,7 @@ export class EditorModeContext extends Disposable {
this._hasReferenceProvider = EditorContextKeys.hasReferenceProvider.bindTo(_contextKeyService);
this._hasRenameProvider = EditorContextKeys.hasRenameProvider.bindTo(_contextKeyService);
this._hasSignatureHelpProvider = EditorContextKeys.hasSignatureHelpProvider.bindTo(_contextKeyService);
this._hasInlineHintsProvider = EditorContextKeys.hasInlineHintsProvider.bindTo(_contextKeyService);
this._hasDocumentFormattingProvider = EditorContextKeys.hasDocumentFormattingProvider.bindTo(_contextKeyService);
this._hasDocumentSelectionFormattingProvider = EditorContextKeys.hasDocumentSelectionFormattingProvider.bindTo(_contextKeyService);
this._hasMultipleDocumentFormattingProvider = EditorContextKeys.hasMultipleDocumentFormattingProvider.bindTo(_contextKeyService);
@@ -1884,6 +1887,7 @@ export class EditorModeContext extends Disposable {
this._register(modes.DocumentFormattingEditProviderRegistry.onDidChange(update));
this._register(modes.DocumentRangeFormattingEditProviderRegistry.onDidChange(update));
this._register(modes.SignatureHelpProviderRegistry.onDidChange(update));
this._register(modes.InlineHintsProviderRegistry.onDidChange(update));
update();
}
@@ -1935,6 +1939,7 @@ export class EditorModeContext extends Disposable {
this._hasReferenceProvider.set(modes.ReferenceProviderRegistry.has(model));
this._hasRenameProvider.set(modes.RenameProviderRegistry.has(model));
this._hasSignatureHelpProvider.set(modes.SignatureHelpProviderRegistry.has(model));
this._hasInlineHintsProvider.set(modes.InlineHintsProviderRegistry.has(model));
this._hasDocumentFormattingProvider.set(modes.DocumentFormattingEditProviderRegistry.has(model) || modes.DocumentRangeFormattingEditProviderRegistry.has(model));
this._hasDocumentSelectionFormattingProvider.set(modes.DocumentRangeFormattingEditProviderRegistry.has(model));
this._hasMultipleDocumentFormattingProvider.set(modes.DocumentFormattingEditProviderRegistry.all(model).length + modes.DocumentRangeFormattingEditProviderRegistry.all(model).length > 1);

View File

@@ -12,15 +12,14 @@ import { RunOnceScheduler } from 'vs/base/common/async';
import { Color } from 'vs/base/common/color';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import * as objects from 'vs/base/common/objects';
import { URI } from 'vs/base/common/uri';
import { Configuration } from 'vs/editor/browser/config/configuration';
import { StableEditorScrollState } from 'vs/editor/browser/core/editorState';
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget';
import { DiffReview } from 'vs/editor/browser/widget/diffReview';
import { IDiffEditorOptions, IEditorOptions, EditorLayoutInfo, EditorOption, EditorOptions, EditorFontLigatures, stringSet as validateStringSetOption, boolean as validateBooleanOption } from 'vs/editor/common/config/editorOptions';
import { IDiffEditorOptions, EditorLayoutInfo, EditorOption, EditorOptions, EditorFontLigatures, stringSet as validateStringSetOption, boolean as validateBooleanOption } from 'vs/editor/common/config/editorOptions';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import { ISelection, Selection } from 'vs/editor/common/core/selection';
@@ -54,6 +53,11 @@ import { IViewLineTokens } from 'vs/editor/common/core/lineTokens';
import { FontInfo } from 'vs/editor/common/config/fontInfo';
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
export interface IDiffCodeEditorWidgetOptions {
originalEditor?: ICodeEditorWidgetOptions;
modifiedEditor?: ICodeEditorWidgetOptions;
}
interface IEditorDiffDecorations {
decorations: IModelDeltaDecoration[];
overviewZones: OverviewRulerZone[];
@@ -109,7 +113,7 @@ class VisualEditorState {
this._decorations = editor.deltaDecorations(this._decorations, []);
}
public apply(editor: CodeEditorWidget, overviewRuler: editorBrowser.IOverviewRuler, newDecorations: IEditorDiffDecorationsWithZones, restoreScrollState: boolean): void {
public apply(editor: CodeEditorWidget, overviewRuler: editorBrowser.IOverviewRuler | null, newDecorations: IEditorDiffDecorationsWithZones, restoreScrollState: boolean): void {
const scrollState = restoreScrollState ? StableEditorScrollState.capture(editor) : null;
@@ -212,6 +216,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
private _maxComputationTime: number;
private _renderIndicators: boolean;
private _enableSplitViewResizing: boolean;
private _renderOverviewRuler: boolean;
private _strategy!: DiffEditorWidgetStyle;
private readonly _updateDecorationsRunner: RunOnceScheduler;
@@ -226,7 +231,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
constructor(
domElement: HTMLElement,
options: editorBrowser.IDiffEditorConstructionOptions,
options: Readonly<editorBrowser.IDiffEditorConstructionOptions>,
codeEditorWidgetOptions: IDiffCodeEditorWidgetOptions,
@IClipboardService clipboardService: IClipboardService,
@IEditorWorkerService editorWorkerService: IEditorWorkerService,
@IContextKeyService contextKeyService: IContextKeyService,
@@ -287,6 +293,11 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this._contextKeyService.createKey('isInEmbeddedDiffEditor', false);
}
this._renderOverviewRuler = true;
if (typeof options.renderOverviewRuler !== 'undefined') {
this._renderOverviewRuler = Boolean(options.renderOverviewRuler);
}
this._updateDecorationsRunner = this._register(new RunOnceScheduler(() => this._updateDecorations(), 0));
this._containerDomElement = document.createElement('div');
@@ -308,7 +319,9 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this._register(dom.addStandardDisposableListener(this._overviewDomElement, 'mousedown', (e) => {
this._modifiedEditor.delegateVerticalScrollbarMouseDown(e);
}));
this._containerDomElement.appendChild(this._overviewDomElement);
if (this._renderOverviewRuler) {
this._containerDomElement.appendChild(this._overviewDomElement);
}
// Create left side
this._originalDomNode = document.createElement('div');
@@ -334,7 +347,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this._isVisible = true;
this._isHandlingScrollEvent = false;
this._elementSizeObserver = this._register(new ElementSizeObserver(this._containerDomElement, undefined, () => this._onDidContainerSizeChanged()));
this._elementSizeObserver = this._register(new ElementSizeObserver(this._containerDomElement, options.dimension, () => this._onDidContainerSizeChanged()));
if (options.automaticLayout) {
this._elementSizeObserver.startObserving();
}
@@ -353,8 +366,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
rightServices.set(IContextKeyService, rightContextKeyService);
const rightScopedInstantiationService = instantiationService.createChild(rightServices);
this._originalEditor = this._createLeftHandSideEditor(options, leftScopedInstantiationService, leftContextKeyService);
this._modifiedEditor = this._createRightHandSideEditor(options, rightScopedInstantiationService, rightContextKeyService);
this._originalEditor = this._createLeftHandSideEditor(options, codeEditorWidgetOptions.originalEditor || {}, leftScopedInstantiationService, leftContextKeyService);
this._modifiedEditor = this._createRightHandSideEditor(options, codeEditorWidgetOptions.modifiedEditor || {}, rightScopedInstantiationService, rightContextKeyService);
this._originalOverviewRuler = null;
this._modifiedOverviewRuler = null;
@@ -415,6 +428,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
return this._modifiedEditor.getContentHeight();
}
public getViewWidth(): number {
return this._elementSizeObserver.getWidth();
}
private _setState(newState: editorBrowser.DiffEditorState): void {
if (this._state === newState) {
return;
@@ -453,6 +470,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
}
private _recreateOverviewRulers(): void {
if (!this._renderOverviewRuler) {
return;
}
if (this._originalOverviewRuler) {
this._overviewDomElement.removeChild(this._originalOverviewRuler.getDomNode());
this._originalOverviewRuler.dispose();
@@ -474,8 +495,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this._layoutOverviewRulers();
}
private _createLeftHandSideEditor(options: editorBrowser.IDiffEditorConstructionOptions, instantiationService: IInstantiationService, contextKeyService: IContextKeyService): CodeEditorWidget {
const editor = this._createInnerEditor(instantiationService, this._originalDomNode, this._adjustOptionsForLeftHandSide(options));
private _createLeftHandSideEditor(options: Readonly<editorBrowser.IDiffEditorConstructionOptions>, codeEditorWidgetOptions: ICodeEditorWidgetOptions, instantiationService: IInstantiationService, contextKeyService: IContextKeyService): CodeEditorWidget {
const editor = this._createInnerEditor(instantiationService, this._originalDomNode, this._adjustOptionsForLeftHandSide(options), codeEditorWidgetOptions);
this._register(editor.onDidScrollChange((e) => {
if (this._isHandlingScrollEvent) {
@@ -536,8 +557,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
return editor;
}
private _createRightHandSideEditor(options: editorBrowser.IDiffEditorConstructionOptions, instantiationService: IInstantiationService, contextKeyService: IContextKeyService): CodeEditorWidget {
const editor = this._createInnerEditor(instantiationService, this._modifiedDomNode, this._adjustOptionsForRightHandSide(options));
private _createRightHandSideEditor(options: Readonly<editorBrowser.IDiffEditorConstructionOptions>, codeEditorWidgetOptions: ICodeEditorWidgetOptions, instantiationService: IInstantiationService, contextKeyService: IContextKeyService): CodeEditorWidget {
const editor = this._createInnerEditor(instantiationService, this._modifiedDomNode, this._adjustOptionsForRightHandSide(options), codeEditorWidgetOptions);
this._register(editor.onDidScrollChange((e) => {
if (this._isHandlingScrollEvent) {
@@ -604,8 +625,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
return editor;
}
protected _createInnerEditor(instantiationService: IInstantiationService, container: HTMLElement, options: IEditorOptions): CodeEditorWidget {
return instantiationService.createInstance(CodeEditorWidget, container, options, {});
protected _createInnerEditor(instantiationService: IInstantiationService, container: HTMLElement, options: Readonly<editorBrowser.IEditorConstructionOptions>, editorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget {
return instantiationService.createInstance(CodeEditorWidget, container, options, editorWidgetOptions);
}
public dispose(): void {
@@ -627,7 +648,9 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this._modifiedOverviewRuler.dispose();
}
this._overviewDomElement.removeChild(this._overviewViewportDomElement.domNode);
this._containerDomElement.removeChild(this._overviewDomElement);
if (this._renderOverviewRuler) {
this._containerDomElement.removeChild(this._overviewDomElement);
}
this._containerDomElement.removeChild(this._originalDomNode);
this._originalEditor.dispose();
@@ -678,7 +701,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
return this._modifiedEditor;
}
public updateOptions(newOptions: IDiffEditorOptions): void {
public updateOptions(newOptions: Readonly<IDiffEditorOptions>): void {
// Handle side by side
let renderSideBySideChanged = false;
@@ -740,6 +763,16 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
// Update class name
this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getColorTheme(), this._renderSideBySide);
}
// renderOverviewRuler
if (typeof newOptions.renderOverviewRuler !== 'undefined' && this._renderOverviewRuler !== newOptions.renderOverviewRuler) {
this._renderOverviewRuler = newOptions.renderOverviewRuler;
if (this._renderOverviewRuler) {
this._containerDomElement.appendChild(this._overviewDomElement);
} else {
this._containerDomElement.removeChild(this._overviewDomElement);
}
}
}
public getModel(): editorCommon.IDiffEditorModel {
@@ -749,7 +782,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
};
}
public setModel(model: editorCommon.IDiffEditorModel): void {
public setModel(model: editorCommon.IDiffEditorModel | null): void {
// Guard us against partial null model
if (model && (!model.original || !model.modified)) {
throw new Error(!model.original ? 'DiffEditorWidget.setModel: Original model is null' : 'DiffEditorWidget.setModel: Modified model is null');
@@ -911,7 +944,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
}
public restoreViewState(s: editorCommon.IDiffEditorViewState): void {
if (s.original && s.modified) {
if (s && s.original && s.modified) {
const diffEditorState = <editorCommon.IDiffEditorViewState>s;
this._originalEditor.restoreViewState(diffEditorState.original);
this._modifiedEditor.restoreViewState(diffEditorState.modified);
@@ -969,6 +1002,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
}
private _layoutOverviewRulers(): void {
if (!this._renderOverviewRuler) {
return;
}
if (!this._originalOverviewRuler || !this._modifiedOverviewRuler) {
return;
}
@@ -1079,9 +1116,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
}
private _updateDecorations(): void {
if (!this._originalEditor.getModel() || !this._modifiedEditor.getModel() || !this._originalOverviewRuler || !this._modifiedOverviewRuler) {
if (!this._originalEditor.getModel() || !this._modifiedEditor.getModel()) {
return;
}
const lineChanges = (this._diffComputationResult ? this._diffComputationResult.changes : []);
const foreignOriginal = this._originalEditorState.getForeignViewZones(this._originalEditor.getWhitespaces());
@@ -1098,25 +1136,24 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
}
}
private _adjustOptionsForSubEditor(options: editorBrowser.IDiffEditorConstructionOptions): editorBrowser.IDiffEditorConstructionOptions {
const clonedOptions: editorBrowser.IDiffEditorConstructionOptions = objects.deepClone(options || {});
private _adjustOptionsForSubEditor(options: Readonly<editorBrowser.IDiffEditorConstructionOptions>): editorBrowser.IEditorConstructionOptions {
const clonedOptions = { ...options };
clonedOptions.inDiffEditor = true;
clonedOptions.automaticLayout = false;
clonedOptions.scrollbar = clonedOptions.scrollbar || {};
// Clone scrollbar options before changing them
clonedOptions.scrollbar = { ...(clonedOptions.scrollbar || {}) };
clonedOptions.scrollbar.vertical = 'visible';
clonedOptions.folding = false;
clonedOptions.codeLens = this._diffCodeLens;
clonedOptions.fixedOverflowWidgets = true;
clonedOptions.overflowWidgetsDomNode = options.overflowWidgetsDomNode;
// clonedOptions.lineDecorationsWidth = '2ch';
if (!clonedOptions.minimap) {
clonedOptions.minimap = {};
}
// Clone minimap options before changing them
clonedOptions.minimap = { ...(clonedOptions.minimap || {}) };
clonedOptions.minimap.enabled = false;
return clonedOptions;
}
private _adjustOptionsForLeftHandSide(options: editorBrowser.IDiffEditorConstructionOptions): editorBrowser.IEditorConstructionOptions {
private _adjustOptionsForLeftHandSide(options: Readonly<editorBrowser.IDiffEditorConstructionOptions>): editorBrowser.IEditorConstructionOptions {
const result = this._adjustOptionsForSubEditor(options);
if (!this._renderSideBySide) {
// never wrap hidden editor
@@ -1126,16 +1163,28 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
}
result.readOnly = !this._originalIsEditable;
result.extraEditorClassName = 'original-in-monaco-diff-editor';
return result;
return {
...result,
dimension: {
height: 0,
width: 0
}
};
}
private _adjustOptionsForRightHandSide(options: editorBrowser.IDiffEditorConstructionOptions): editorBrowser.IEditorConstructionOptions {
private _adjustOptionsForRightHandSide(options: Readonly<editorBrowser.IDiffEditorConstructionOptions>): editorBrowser.IEditorConstructionOptions {
const result = this._adjustOptionsForSubEditor(options);
result.wordWrapOverride1 = this._diffWordWrap;
result.revealHorizontalRightPadding = EditorOptions.revealHorizontalRightPadding.defaultValue + DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH;
result.scrollbar!.verticalHasArrows = false;
result.extraEditorClassName = 'modified-in-monaco-diff-editor';
return result;
return {
...result,
dimension: {
height: 0,
width: 0
}
};
}
public doLayout(): void {
@@ -1164,7 +1213,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this._overviewViewportDomElement.setHeight(30);
this._originalEditor.layout({ width: splitPoint, height: (height - reviewHeight) });
this._modifiedEditor.layout({ width: width - splitPoint - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH, height: (height - reviewHeight) });
this._modifiedEditor.layout({ width: width - splitPoint - (this._renderOverviewRuler ? DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH : 0), height: (height - reviewHeight) });
if (this._originalOverviewRuler || this._modifiedOverviewRuler) {
this._layoutOverviewRulers();
@@ -1218,6 +1267,12 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
return (this._elementSizeObserver.getHeight() - this._getReviewHeight());
},
getOptions: () => {
return {
renderOverviewRuler: this._renderOverviewRuler
};
},
getContainerDomNode: () => {
return this._containerDomElement;
},
@@ -1347,6 +1402,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
interface IDataSource {
getWidth(): number;
getHeight(): number;
getOptions(): { renderOverviewRuler: boolean; };
getContainerDomNode(): HTMLElement;
relayoutEditors(): void;
@@ -1806,7 +1862,7 @@ class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IVerti
public layout(sashRatio: number | null = this._sashRatio): number {
const w = this._dataSource.getWidth();
const contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH;
const contentWidth = w - (this._dataSource.getOptions().renderOverviewRuler ? DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH : 0);
let sashPosition = Math.floor((sashRatio || 0.5) * contentWidth);
const midPoint = Math.floor(0.5 * contentWidth);
@@ -1839,7 +1895,7 @@ class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IVerti
private _onSashDrag(e: ISashEvent): void {
const w = this._dataSource.getWidth();
const contentWidth = w - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH;
const contentWidth = w - (this._dataSource.getOptions().renderOverviewRuler ? DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH : 0);
const sashPosition = this.layout((this._startSashPosition! + (e.currentX - e.startX)) / contentWidth);
this._sashRatio = sashPosition / contentWidth;
@@ -2370,7 +2426,7 @@ class InlineViewZonesComputer extends ViewZonesComputer {
const html = sb.build();
const trustedhtml = ttPolicy ? ttPolicy.createHTML(html) : html;
domNode.innerHTML = trustedhtml as unknown as string;
domNode.innerHTML = trustedhtml as string;
viewZone.minWidthInPx = (maxCharsPerLine * typicalHalfwidthCharacterWidth);
if (viewLineCounts) {

View File

@@ -80,6 +80,8 @@ const diffReviewCloseIcon = registerIcon('diff-review-close', Codicon.close, nls
export class DiffReview extends Disposable {
private static _ttPolicy = window.trustedTypes?.createPolicy('diffReview', { createHTML: value => value });
private readonly _diffEditor: DiffEditorWidget;
private _isVisible: boolean;
public readonly shadow: FastDomNode<HTMLElement>;
@@ -734,14 +736,18 @@ export class DiffReview extends Disposable {
let lineContent: string;
if (modifiedLine !== 0) {
cell.insertAdjacentHTML('beforeend',
this._renderLine(modifiedModel, modifiedOptions, modifiedModelOpts.tabSize, modifiedLine)
);
let html: string | TrustedHTML = this._renderLine(modifiedModel, modifiedOptions, modifiedModelOpts.tabSize, modifiedLine);
if (DiffReview._ttPolicy) {
html = DiffReview._ttPolicy.createHTML(html as string);
}
cell.insertAdjacentHTML('beforeend', html as string);
lineContent = modifiedModel.getLineContent(modifiedLine);
} else {
cell.insertAdjacentHTML('beforeend',
this._renderLine(originalModel, originalOptions, originalModelOpts.tabSize, originalLine)
);
let html: string | TrustedHTML = this._renderLine(originalModel, originalOptions, originalModelOpts.tabSize, originalLine);
if (DiffReview._ttPolicy) {
html = DiffReview._ttPolicy.createHTML(html as string);
}
cell.insertAdjacentHTML('beforeend', html as string);
lineContent = originalModel.getLineContent(originalLine);
}

View File

@@ -82,7 +82,7 @@ export class EmbeddedDiffEditorWidget extends DiffEditorWidget {
@IClipboardService clipboardService: IClipboardService,
@IEditorProgressService editorProgressService: IEditorProgressService,
) {
super(domElement, parentEditor.getRawOptions(), clipboardService, editorWorkerService, contextKeyService, instantiationService, codeEditorService, themeService, notificationService, contextMenuService, editorProgressService);
super(domElement, parentEditor.getRawOptions(), {}, clipboardService, editorWorkerService, contextKeyService, instantiationService, codeEditorService, themeService, notificationService, contextMenuService, editorProgressService);
this._parentEditor = parentEditor;
this._overwriteOptions = options;

View File

@@ -272,7 +272,7 @@ function migrateOptions(options: IEditorOptions): void {
}
}
function deepCloneAndMigrateOptions(_options: IEditorOptions): IEditorOptions {
function deepCloneAndMigrateOptions(_options: Readonly<IEditorOptions>): IEditorOptions {
const options = objects.deepClone(_options);
migrateOptions(options);
return options;
@@ -298,7 +298,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
private _readOptions: RawEditorOptions;
protected _validatedOptions: ValidatedEditorOptions;
constructor(isSimpleWidget: boolean, _options: IEditorOptions) {
constructor(isSimpleWidget: boolean, _options: Readonly<IEditorOptions>) {
super();
this.isSimpleWidget = isSimpleWidget;
@@ -318,8 +318,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
public observeReferenceElement(dimension?: IDimension): void {
}
public dispose(): void {
super.dispose();
public updatePixelRatio(): void {
}
protected _recomputeOptions(): void {
@@ -348,7 +347,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
private _computeInternalOptions(): ComputedEditorOptions {
const partialEnv = this._getEnvConfiguration();
const bareFontInfo = BareFontInfo.createFromValidatedSettings(this._validatedOptions, partialEnv.zoomLevel, this.isSimpleWidget);
const bareFontInfo = BareFontInfo.createFromValidatedSettings(this._validatedOptions, partialEnv.zoomLevel, partialEnv.pixelRatio, this.isSimpleWidget);
const env: IEnvironmentalOptions = {
memory: this._computeOptionsMemory,
outerWidth: partialEnv.outerWidth,
@@ -394,7 +393,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
return true;
}
public updateOptions(_newOptions: IEditorOptions): void {
public updateOptions(_newOptions: Readonly<IEditorOptions>): void {
if (typeof _newOptions === 'undefined') {
return;
}

View File

@@ -625,6 +625,10 @@ export interface IEditorOptions {
* Controls strikethrough deprecated variables.
*/
showDeprecated?: boolean;
/**
* Control the behavior and rendering of the inline hints.
*/
inlineHints?: IEditorInlineHintsOptions;
}
/**
@@ -677,6 +681,11 @@ export interface IDiffEditorOptions extends IEditorOptions {
* Defaults to false
*/
isInEmbeddedEditor?: boolean;
/**
* Is the diff editor should render overview ruler
* Defaults to true
*/
renderOverviewRuler?: boolean;
/**
* Control the wrapping of the diff editor.
*/
@@ -2364,6 +2373,74 @@ class EditorLightbulb extends BaseEditorOption<EditorOption.lightbulb, EditorLig
//#endregion
//#region inlineHints
/**
* Configuration options for editor inlineHints
*/
export interface IEditorInlineHintsOptions {
/**
* Enable the inline hints.
* Defaults to true.
*/
enabled?: boolean;
/**
* Font size of inline hints.
* Default to 90% of the editor font size.
*/
fontSize?: number;
/**
* Font family of inline hints.
* Defaults to editor font family.
*/
fontFamily?: string;
}
export type EditorInlineHintsOptions = Readonly<Required<IEditorInlineHintsOptions>>;
class EditorInlineHints extends BaseEditorOption<EditorOption.inlineHints, EditorInlineHintsOptions> {
constructor() {
const defaults: EditorInlineHintsOptions = { enabled: true, fontSize: 0, fontFamily: EDITOR_FONT_DEFAULTS.fontFamily };
super(
EditorOption.inlineHints, 'inlineHints', defaults,
{
'editor.inlineHints.enabled': {
type: 'boolean',
default: defaults.enabled,
description: nls.localize('inlineHints.enable', "Enables the inline hints in the editor.")
},
'editor.inlineHints.fontSize': {
type: 'number',
default: defaults.fontSize,
description: nls.localize('inlineHints.fontSize', "Controls font size of inline hints in the editor. When set to `0`, the 90% of `#editor.fontSize#` is used.")
},
'editor.inlineHints.fontFamily': {
type: 'string',
default: defaults.fontFamily,
description: nls.localize('inlineHints.fontFamily', "Controls font family of inline hints in the editor.")
},
}
);
}
public validate(_input: any): EditorInlineHintsOptions {
if (!_input || typeof _input !== 'object') {
return this.defaultValue;
}
const input = _input as IEditorInlineHintsOptions;
return {
enabled: boolean(input.enabled, this.defaultValue.enabled),
fontSize: EditorIntOption.clampedInt(input.fontSize, this.defaultValue.fontSize, 0, 100),
fontFamily: EditorStringOption.string(input.fontFamily, this.defaultValue.fontFamily)
};
}
}
//#endregion
//#region lineHeight
class EditorLineHeight extends EditorIntOption<EditorOption.lineHeight> {
@@ -3273,7 +3350,7 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
'editor.suggest.showInlineDetails': {
type: 'boolean',
default: defaults.showInlineDetails,
description: nls.localize('suggest.showInlineDetails', "Controls whether sugget details show inline with the label or only in the details widget")
description: nls.localize('suggest.showInlineDetails', "Controls whether suggest details show inline with the label or only in the details widget")
},
'editor.suggest.maxVisibleSuggestions': {
type: 'number',
@@ -3754,7 +3831,7 @@ export const enum EditorOption {
wrappingIndent,
wrappingStrategy,
showDeprecated,
inlineHints,
// Leave these at the end (because they have dependencies!)
editorClassName,
pixelRatio,
@@ -4258,6 +4335,7 @@ export const EditorOptions = {
EditorOption.showDeprecated, 'showDeprecated', true,
{ description: nls.localize('showDeprecated', "Controls strikethrough deprecated variables.") }
)),
inlineHints: register(new EditorInlineHints()),
snippetSuggestions: register(new EditorStringEnumOption(
EditorOption.snippetSuggestions, 'snippetSuggestions',
'inline' as 'top' | 'bottom' | 'inline' | 'none',

View File

@@ -24,33 +24,33 @@ export class BareFontInfo {
/**
* @internal
*/
public static createFromValidatedSettings(options: ValidatedEditorOptions, zoomLevel: number, ignoreEditorZoom: boolean): BareFontInfo {
public static createFromValidatedSettings(options: ValidatedEditorOptions, zoomLevel: number, pixelRatio: number, ignoreEditorZoom: boolean): BareFontInfo {
const fontFamily = options.get(EditorOption.fontFamily);
const fontWeight = options.get(EditorOption.fontWeight);
const fontSize = options.get(EditorOption.fontSize);
const fontFeatureSettings = options.get(EditorOption.fontLigatures);
const lineHeight = options.get(EditorOption.lineHeight);
const letterSpacing = options.get(EditorOption.letterSpacing);
return BareFontInfo._create(fontFamily, fontWeight, fontSize, fontFeatureSettings, lineHeight, letterSpacing, zoomLevel, ignoreEditorZoom);
return BareFontInfo._create(fontFamily, fontWeight, fontSize, fontFeatureSettings, lineHeight, letterSpacing, zoomLevel, pixelRatio, ignoreEditorZoom);
}
/**
* @internal
*/
public static createFromRawSettings(opts: { fontFamily?: string; fontWeight?: string; fontSize?: number; fontLigatures?: boolean | string; lineHeight?: number; letterSpacing?: number; }, zoomLevel: number, ignoreEditorZoom: boolean = false): BareFontInfo {
public static createFromRawSettings(opts: { fontFamily?: string; fontWeight?: string; fontSize?: number; fontLigatures?: boolean | string; lineHeight?: number; letterSpacing?: number; }, zoomLevel: number, pixelRatio: number, ignoreEditorZoom: boolean = false): BareFontInfo {
const fontFamily = EditorOptions.fontFamily.validate(opts.fontFamily);
const fontWeight = EditorOptions.fontWeight.validate(opts.fontWeight);
const fontSize = EditorOptions.fontSize.validate(opts.fontSize);
const fontFeatureSettings = EditorOptions.fontLigatures2.validate(opts.fontLigatures);
const lineHeight = EditorOptions.lineHeight.validate(opts.lineHeight);
const letterSpacing = EditorOptions.letterSpacing.validate(opts.letterSpacing);
return BareFontInfo._create(fontFamily, fontWeight, fontSize, fontFeatureSettings, lineHeight, letterSpacing, zoomLevel, ignoreEditorZoom);
return BareFontInfo._create(fontFamily, fontWeight, fontSize, fontFeatureSettings, lineHeight, letterSpacing, zoomLevel, pixelRatio, ignoreEditorZoom);
}
/**
* @internal
*/
private static _create(fontFamily: string, fontWeight: string, fontSize: number, fontFeatureSettings: string, lineHeight: number, letterSpacing: number, zoomLevel: number, ignoreEditorZoom: boolean): BareFontInfo {
private static _create(fontFamily: string, fontWeight: string, fontSize: number, fontFeatureSettings: string, lineHeight: number, letterSpacing: number, zoomLevel: number, pixelRatio: number, ignoreEditorZoom: boolean): BareFontInfo {
if (lineHeight === 0) {
lineHeight = Math.round(GOLDEN_LINE_HEIGHT_RATIO * fontSize);
} else if (lineHeight < MINIMUM_LINE_HEIGHT) {
@@ -63,6 +63,7 @@ export class BareFontInfo {
return new BareFontInfo({
zoomLevel: zoomLevel,
pixelRatio: pixelRatio,
fontFamily: fontFamily,
fontWeight: fontWeight,
fontSize: fontSize,
@@ -73,6 +74,7 @@ export class BareFontInfo {
}
readonly zoomLevel: number;
readonly pixelRatio: number;
readonly fontFamily: string;
readonly fontWeight: string;
readonly fontSize: number;
@@ -85,6 +87,7 @@ export class BareFontInfo {
*/
protected constructor(opts: {
zoomLevel: number;
pixelRatio: number;
fontFamily: string;
fontWeight: string;
fontSize: number;
@@ -93,6 +96,7 @@ export class BareFontInfo {
letterSpacing: number;
}) {
this.zoomLevel = opts.zoomLevel;
this.pixelRatio = opts.pixelRatio;
this.fontFamily = String(opts.fontFamily);
this.fontWeight = String(opts.fontWeight);
this.fontSize = opts.fontSize;
@@ -105,7 +109,7 @@ export class BareFontInfo {
* @internal
*/
public getId(): string {
return this.zoomLevel + '-' + this.fontFamily + '-' + this.fontWeight + '-' + this.fontSize + '-' + this.fontFeatureSettings + '-' + this.lineHeight + '-' + this.letterSpacing;
return this.zoomLevel + '-' + this.pixelRatio + '-' + this.fontFamily + '-' + this.fontWeight + '-' + this.fontSize + '-' + this.fontFeatureSettings + '-' + this.lineHeight + '-' + this.letterSpacing;
}
/**
@@ -125,9 +129,13 @@ export class BareFontInfo {
}
}
// change this whenever `FontInfo` members are changed
export const SERIALIZED_FONT_INFO_VERSION = 1;
export class FontInfo extends BareFontInfo {
readonly _editorStylingBrand: void;
readonly version: number = SERIALIZED_FONT_INFO_VERSION;
readonly isTrusted: boolean;
readonly isMonospace: boolean;
readonly typicalHalfwidthCharacterWidth: number;
@@ -143,6 +151,7 @@ export class FontInfo extends BareFontInfo {
*/
constructor(opts: {
zoomLevel: number;
pixelRatio: number;
fontFamily: string;
fontWeight: string;
fontSize: number;

View File

@@ -393,8 +393,8 @@ export class Cursor extends Disposable {
return this._cursors.getPrimaryCursor().modelState.position;
}
public setSelections(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, selections: readonly ISelection[]): void {
this.setStates(eventsCollector, source, CursorChangeReason.NotSet, CursorState.fromModelSelections(selections));
public setSelections(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, selections: readonly ISelection[], reason: CursorChangeReason): void {
this.setStates(eventsCollector, source, reason, CursorState.fromModelSelections(selections));
}
public getPrevEditOperationType(): EditOperationType {
@@ -584,7 +584,7 @@ export class Cursor extends Disposable {
});
if (selections) {
this._isHandling = false;
this.setSelections(eventsCollector, source, selections);
this.setSelections(eventsCollector, source, selections, CursorChangeReason.NotSet);
}
if (autoClosedCharactersRanges.length > 0) {
this._pushAutoClosedAction(autoClosedCharactersRanges, autoClosedEnclosingRanges);

View File

@@ -39,11 +39,11 @@ export class MoveOperations {
public static leftPositionAtomicSoftTabs(model: ICursorSimpleModel, lineNumber: number, column: number, tabSize: number): Position {
const minColumn = model.getLineMinColumn(lineNumber);
const lineContent = model.getLineContent(lineNumber);
const newPosition = AtomicTabMoveOperations.atomicPosition(lineContent, column - minColumn, tabSize, Direction.Left);
if (newPosition === -1) {
const newPosition = AtomicTabMoveOperations.atomicPosition(lineContent, column - 1, tabSize, Direction.Left);
if (newPosition === -1 || newPosition + 1 < minColumn) {
return this.leftPosition(model, lineNumber, column);
}
return new Position(lineNumber, minColumn + newPosition);
return new Position(lineNumber, newPosition + 1);
}
public static left(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number): CursorPosition {
@@ -81,13 +81,12 @@ export class MoveOperations {
}
public static rightPositionAtomicSoftTabs(model: ICursorSimpleModel, lineNumber: number, column: number, tabSize: number, indentSize: number): Position {
const minColumn = model.getLineMinColumn(lineNumber);
const lineContent = model.getLineContent(lineNumber);
const newPosition = AtomicTabMoveOperations.atomicPosition(lineContent, column - minColumn, tabSize, Direction.Right);
const newPosition = AtomicTabMoveOperations.atomicPosition(lineContent, column - 1, tabSize, Direction.Right);
if (newPosition === -1) {
return this.rightPosition(model, lineNumber, column);
}
return new Position(lineNumber, minColumn + newPosition);
return new Position(lineNumber, newPosition + 1);
}
public static right(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number): CursorPosition {

View File

@@ -351,13 +351,6 @@ export class TypeOperations {
if (ir) {
let oldEndViewColumn = CursorColumns.visibleColumnFromColumn2(config, model, range.getEndPosition());
const oldEndColumn = range.endColumn;
let beforeText = '\n';
if (indentation !== config.normalizeIndentation(ir.beforeEnter)) {
beforeText = config.normalizeIndentation(ir.beforeEnter) + lineText.substring(indentation.length, range.startColumn - 1) + '\n';
range = new Range(range.startLineNumber, 1, range.endLineNumber, range.endColumn);
}
const newLineContent = model.getLineContent(range.endLineNumber);
const firstNonWhitespace = strings.firstNonWhitespaceIndex(newLineContent);
if (firstNonWhitespace >= 0) {
@@ -367,7 +360,7 @@ export class TypeOperations {
}
if (keepPosition) {
return new ReplaceCommandWithoutChangingPosition(range, beforeText + config.normalizeIndentation(ir.afterEnter), true);
return new ReplaceCommandWithoutChangingPosition(range, '\n' + config.normalizeIndentation(ir.afterEnter), true);
} else {
let offset = 0;
if (oldEndColumn <= firstNonWhitespace + 1) {
@@ -376,7 +369,7 @@ export class TypeOperations {
}
offset = Math.min(oldEndViewColumn + 1 - config.normalizeIndentation(ir.afterEnter).length - 1, 0);
}
return new ReplaceCommandWithOffsetCursorState(range, beforeText + config.normalizeIndentation(ir.afterEnter), 0, offset, true);
return new ReplaceCommandWithOffsetCursorState(range, '\n' + config.normalizeIndentation(ir.afterEnter), 0, offset, true);
}
}
}

View File

@@ -313,6 +313,13 @@ export class DiffComputer {
if (this.original.lines.length === 1 && this.original.lines[0].length === 0) {
// empty original => fast path
if (this.modified.lines.length === 1 && this.modified.lines[0].length === 0) {
return {
quitEarly: false,
changes: []
};
}
return {
quitEarly: false,
changes: [{

View File

@@ -157,9 +157,10 @@ export interface IConfiguration extends IDisposable {
setMaxLineNumber(maxLineNumber: number): void;
setViewLineCount(viewLineCount: number): void;
updateOptions(newOptions: IEditorOptions): void;
updateOptions(newOptions: Readonly<IEditorOptions>): void;
getRawOptions(): IEditorOptions;
observeReferenceElement(dimension?: IDimension): void;
updatePixelRatio(): void;
setIsDominatedByLongLines(isDominatedByLongLines: boolean): void;
}
@@ -605,6 +606,7 @@ export interface IThemeDecorationRenderOptions {
fontStyle?: string;
fontWeight?: string;
fontSize?: string;
textDecoration?: string;
cursor?: string;
color?: string | ThemeColor;
@@ -629,13 +631,17 @@ export interface IContentDecorationRenderOptions {
border?: string;
borderColor?: string | ThemeColor;
borderRadius?: string;
fontStyle?: string;
fontWeight?: string;
fontSize?: string;
fontFamily?: string;
textDecoration?: string;
color?: string | ThemeColor;
backgroundColor?: string | ThemeColor;
margin?: string;
padding?: string;
width?: string;
height?: string;
}

View File

@@ -61,6 +61,7 @@ export namespace EditorContextKeys {
export const hasReferenceProvider = new RawContextKey<boolean>('editorHasReferenceProvider', false);
export const hasRenameProvider = new RawContextKey<boolean>('editorHasRenameProvider', false);
export const hasSignatureHelpProvider = new RawContextKey<boolean>('editorHasSignatureHelpProvider', false);
export const hasInlineHintsProvider = new RawContextKey<boolean>('editorHasInlineHintsProvider', false);
// -- mode context keys: formatting
export const hasDocumentFormattingProvider = new RawContextKey<boolean>('editorHasDocumentFormattingProvider', false);

View File

@@ -600,12 +600,6 @@ export interface ITextModel {
*/
setValue(newValue: string): void;
/**
* Replace the entire text buffer value contained in this model.
* @internal
*/
setValueFromTextBuffer(newValue: ITextBuffer): void;
/**
* Get the text stored in this model.
* @param eol The end of line character preference. Defaults to `EndOfLinePreference.TextDefined`.
@@ -1276,7 +1270,7 @@ export interface ITextBufferBuilder {
* @internal
*/
export interface ITextBufferFactory {
create(defaultEOL: DefaultEndOfLine): ITextBuffer;
create(defaultEOL: DefaultEndOfLine): { textBuffer: ITextBuffer; disposable: IDisposable; };
getFirstLineText(lengthLimit: number): string;
}

View File

@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { CharCode } from 'vs/base/common/charCode';
import { IDisposable } from 'vs/base/common/lifecycle';
import * as strings from 'vs/base/common/strings';
import { DefaultEndOfLine, ITextBuffer, ITextBufferBuilder, ITextBufferFactory } from 'vs/editor/common/model';
import { StringBuffer, createLineStarts, createLineStartsFast } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase';
@@ -38,7 +39,7 @@ export class PieceTreeTextBufferFactory implements ITextBufferFactory {
return '\n';
}
public create(defaultEOL: DefaultEndOfLine): ITextBuffer {
public create(defaultEOL: DefaultEndOfLine): { textBuffer: ITextBuffer; disposable: IDisposable; } {
const eol = this._getEOL(defaultEOL);
let chunks = this._chunks;
@@ -54,7 +55,8 @@ export class PieceTreeTextBufferFactory implements ITextBufferFactory {
}
}
return new PieceTreeTextBuffer(chunks, this._bom, eol, this._containsRTL, this._containsUnusualLineTerminators, this._isBasicASCII, this._normalizeEOL);
const textBuffer = new PieceTreeTextBuffer(chunks, this._bom, eol, this._containsRTL, this._containsUnusualLineTerminators, this._isBasicASCII, this._normalizeEOL);
return { textBuffer: textBuffer, disposable: textBuffer };
}
public getFirstLineText(lengthLimit: number): string {

View File

@@ -37,6 +37,7 @@ import { EditorTheme } from 'vs/editor/common/view/viewContext';
import { IUndoRedoService, ResourceEditStackSnapshot } from 'vs/platform/undoRedo/common/undoRedo';
import { TextChange } from 'vs/editor/common/model/textChange';
import { Constants } from 'vs/base/common/uint';
import { PieceTreeTextBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer';
function createTextBufferBuilder() {
return new PieceTreeTextBufferBuilder();
@@ -106,7 +107,7 @@ export function createTextBufferFactoryFromSnapshot(snapshot: model.ITextSnapsho
return builder.finish();
}
export function createTextBuffer(value: string | model.ITextBufferFactory, defaultEOL: model.DefaultEndOfLine): model.ITextBuffer {
export function createTextBuffer(value: string | model.ITextBufferFactory, defaultEOL: model.DefaultEndOfLine): { textBuffer: model.ITextBuffer; disposable: IDisposable; } {
const factory = (typeof value === 'string' ? createTextBufferFactory(value) : value);
return factory.create(defaultEOL);
}
@@ -268,6 +269,7 @@ export class TextModel extends Disposable implements model.ITextModel {
private readonly _undoRedoService: IUndoRedoService;
private _attachedEditorCount: number;
private _buffer: model.ITextBuffer;
private _bufferDisposable: IDisposable;
private _options: model.TextModelResolvedOptions;
private _isDisposed: boolean;
@@ -328,7 +330,9 @@ export class TextModel extends Disposable implements model.ITextModel {
this._undoRedoService = undoRedoService;
this._attachedEditorCount = 0;
this._buffer = createTextBuffer(source, creationOptions.defaultEOL);
const { textBuffer, disposable } = createTextBuffer(source, creationOptions.defaultEOL);
this._buffer = textBuffer;
this._bufferDisposable = disposable;
this._options = TextModel.resolveOptions(this._buffer, creationOptions);
@@ -386,10 +390,13 @@ export class TextModel extends Disposable implements model.ITextModel {
this._tokenization.dispose();
this._isDisposed = true;
super.dispose();
this._bufferDisposable.dispose();
this._isDisposing = false;
// Manually release reference to previous text buffer to avoid large leaks
// in case someone leaks a TextModel reference
this._buffer = createTextBuffer('', this._options.defaultEOL);
const emptyDisposedTextBuffer = new PieceTreeTextBuffer([], '', '\n', false, false, true, true);
emptyDisposedTextBuffer.dispose();
this._buffer = emptyDisposedTextBuffer;
}
private _assertNotDisposed(): void {
@@ -423,8 +430,8 @@ export class TextModel extends Disposable implements model.ITextModel {
return;
}
const textBuffer = createTextBuffer(value, this._options.defaultEOL);
this.setValueFromTextBuffer(textBuffer);
const { textBuffer, disposable } = createTextBuffer(value, this._options.defaultEOL);
this._setValueFromTextBuffer(textBuffer, disposable);
}
private _createContentChanged2(range: Range, rangeOffset: number, rangeLength: number, text: string, isUndoing: boolean, isRedoing: boolean, isFlush: boolean): IModelContentChangedEvent {
@@ -443,18 +450,16 @@ export class TextModel extends Disposable implements model.ITextModel {
};
}
public setValueFromTextBuffer(textBuffer: model.ITextBuffer): void {
private _setValueFromTextBuffer(textBuffer: model.ITextBuffer, textBufferDisposable: IDisposable): void {
this._assertNotDisposed();
if (textBuffer === null) {
// There's nothing to do
return;
}
const oldFullModelRange = this.getFullModelRange();
const oldModelValueLength = this.getValueLengthInRange(oldFullModelRange);
const endLineNumber = this.getLineCount();
const endColumn = this.getLineMaxColumn(endLineNumber);
this._buffer = textBuffer;
this._bufferDisposable.dispose();
this._bufferDisposable = textBufferDisposable;
this._increaseVersionId();
// Flush all tokens

View File

@@ -359,7 +359,7 @@ export class TextModelTokenization extends Disposable {
const text = this._textModel.getLineContent(lineIndex + 1);
const lineStartState = this._tokenizationStateStore.getBeginState(lineIndex);
const r = safeTokenize(languageIdentifier, this._tokenizationSupport, text, lineStartState!);
const r = safeTokenize(languageIdentifier, this._tokenizationSupport, text, true, lineStartState!);
builder.add(lineIndex + 1, r.tokens);
this._tokenizationStateStore.setEndState(linesLength, lineIndex, r.endState);
lineIndex = this._tokenizationStateStore.invalidLineStartIndex - 1; // -1 because the outer loop increments it
@@ -410,13 +410,13 @@ export class TextModelTokenization extends Disposable {
const languageIdentifier = this._textModel.getLanguageIdentifier();
let state = initialState;
for (let i = fakeLines.length - 1; i >= 0; i--) {
let r = safeTokenize(languageIdentifier, this._tokenizationSupport, fakeLines[i], state);
let r = safeTokenize(languageIdentifier, this._tokenizationSupport, fakeLines[i], false, state);
state = r.endState;
}
for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
let text = this._textModel.getLineContent(lineNumber);
let r = safeTokenize(languageIdentifier, this._tokenizationSupport, text, state);
let r = safeTokenize(languageIdentifier, this._tokenizationSupport, text, true, state);
builder.add(lineNumber, r.tokens);
this._tokenizationStateStore.setFakeTokens(lineNumber - 1);
state = r.endState;
@@ -443,12 +443,12 @@ function initializeTokenization(textModel: TextModel): [ITokenizationSupport | n
return [tokenizationSupport, initialState];
}
function safeTokenize(languageIdentifier: LanguageIdentifier, tokenizationSupport: ITokenizationSupport | null, text: string, state: IState): TokenizationResult2 {
function safeTokenize(languageIdentifier: LanguageIdentifier, tokenizationSupport: ITokenizationSupport | null, text: string, hasEOL: boolean, state: IState): TokenizationResult2 {
let r: TokenizationResult2 | null = null;
if (tokenizationSupport) {
try {
r = tokenizationSupport.tokenize2(text, state.clone(), 0);
r = tokenizationSupport.tokenize2(text, hasEOL, state.clone(), 0);
} catch (e) {
onUnexpectedError(e);
}

View File

@@ -211,9 +211,9 @@ export interface ITokenizationSupport {
getInitialState(): IState;
// add offsetDelta to each of the returned indices
tokenize(line: string, state: IState, offsetDelta: number): TokenizationResult;
tokenize(line: string, hasEOL: boolean, state: IState, offsetDelta: number): TokenizationResult;
tokenize2(line: string, state: IState, offsetDelta: number): TokenizationResult2;
tokenize2(line: string, hasEOL: boolean, state: IState, offsetDelta: number): TokenizationResult2;
}
/**
@@ -1659,6 +1659,19 @@ export interface CodeLensProvider {
resolveCodeLens?(model: model.ITextModel, codeLens: CodeLens, token: CancellationToken): ProviderResult<CodeLens>;
}
export interface InlineHint {
text: string;
range: IRange;
description?: string | IMarkdownString;
whitespaceBefore?: boolean;
whitespaceAfter?: boolean;
}
export interface InlineHintsProvider {
onDidChangeInlineHints?: Event<void> | undefined;
provideInlineHints(model: model.ITextModel, range: Range, token: CancellationToken): ProviderResult<InlineHint[]>;
}
export interface SemanticTokensLegend {
readonly tokenTypes: string[];
readonly tokenModifiers: string[];
@@ -1764,6 +1777,11 @@ export const TypeDefinitionProviderRegistry = new LanguageFeatureRegistry<TypeDe
*/
export const CodeLensProviderRegistry = new LanguageFeatureRegistry<CodeLensProvider>();
/**
* @internal
*/
export const InlineHintsProviderRegistry = new LanguageFeatureRegistry<InlineHintsProvider>();
/**
* @internal
*/
@@ -1876,3 +1894,14 @@ export interface ITokenizationRegistry {
* @internal
*/
export const TokenizationRegistry = new TokenizationRegistryImpl();
/**
* @internal
*/
export enum ExternalUriOpenerPriority {
None = 0,
Option = 1,
Default = 2,
Preferred = 3,
}

View File

@@ -150,7 +150,7 @@ export interface OnEnterRule {
/**
* This rule will only execute if the text above the this line matches this regular expression.
*/
oneLineAboveText?: RegExp;
previousLineText?: RegExp;
/**
* The action to execute.
*/

View File

@@ -101,11 +101,11 @@ export class RichEditSupport {
return this._electricCharacter;
}
public onEnter(autoIndent: EditorAutoIndentStrategy, oneLineAboveText: string, beforeEnterText: string, afterEnterText: string): EnterAction | null {
public onEnter(autoIndent: EditorAutoIndentStrategy, previousLineText: string, beforeEnterText: string, afterEnterText: string): EnterAction | null {
if (!this._onEnterSupport) {
return null;
}
return this._onEnterSupport.onEnter(autoIndent, oneLineAboveText, beforeEnterText, afterEnterText);
return this._onEnterSupport.onEnter(autoIndent, previousLineText, beforeEnterText, afterEnterText);
}
private static _mergeConf(prev: LanguageConfiguration | null, current: LanguageConfiguration): LanguageConfiguration {
@@ -700,17 +700,17 @@ export class LanguageConfigurationRegistryImpl {
afterEnterText = endScopedLineTokens.getLineContent().substr(range.endColumn - 1 - scopedLineTokens.firstCharOffset);
}
let oneLineAboveText = '';
let previousLineText = '';
if (range.startLineNumber > 1 && scopedLineTokens.firstCharOffset === 0) {
// This is not the first line and the entire line belongs to this mode
const oneLineAboveScopedLineTokens = this.getScopedLineTokens(model, range.startLineNumber - 1);
if (oneLineAboveScopedLineTokens.languageId === scopedLineTokens.languageId) {
// The line above ends with text belonging to the same mode
oneLineAboveText = oneLineAboveScopedLineTokens.getLineContent();
previousLineText = oneLineAboveScopedLineTokens.getLineContent();
}
}
const enterResult = richEditSupport.onEnter(autoIndent, oneLineAboveText, beforeEnterText, afterEnterText);
const enterResult = richEditSupport.onEnter(autoIndent, previousLineText, beforeEnterText, afterEnterText);
if (!enterResult) {
return null;
}
@@ -729,6 +729,8 @@ export class LanguageConfigurationRegistryImpl {
} else {
appendText = '';
}
} else if (indentAction === IndentAction.Indent) {
appendText = '\t' + appendText;
}
let indentation = this.getIndentationAtPosition(model, range.startLineNumber, range.startColumn);

View File

@@ -58,11 +58,12 @@ export const ModesRegistry = new EditorModesRegistry();
Registry.add(Extensions.ModesRegistry, ModesRegistry);
export const PLAINTEXT_MODE_ID = 'plaintext';
export const PLAINTEXT_EXTENSION = '.txt';
export const PLAINTEXT_LANGUAGE_IDENTIFIER = new LanguageIdentifier(PLAINTEXT_MODE_ID, LanguageId.PlainText);
ModesRegistry.registerLanguage({
id: PLAINTEXT_MODE_ID,
extensions: ['.txt'],
extensions: [PLAINTEXT_EXTENSION],
aliases: [nls.localize('plainText.alias', "Plain Text"), 'text'],
mimetypes: ['text/plain']
});

View File

@@ -49,7 +49,7 @@ export class OnEnterSupport {
this._regExpRules = opts.onEnterRules || [];
}
public onEnter(autoIndent: EditorAutoIndentStrategy, oneLineAboveText: string, beforeEnterText: string, afterEnterText: string): EnterAction | null {
public onEnter(autoIndent: EditorAutoIndentStrategy, previousLineText: string, beforeEnterText: string, afterEnterText: string): EnterAction | null {
// (1): `regExpRules`
if (autoIndent >= EditorAutoIndentStrategy.Advanced) {
for (let i = 0, len = this._regExpRules.length; i < len; i++) {
@@ -61,8 +61,8 @@ export class OnEnterSupport {
reg: rule.afterText,
text: afterEnterText
}, {
reg: rule.oneLineAboveText,
text: oneLineAboveText
reg: rule.previousLineText,
text: previousLineText
}].every((obj): boolean => {
return obj.reg ? obj.reg.test(obj.text) : true;
});

View File

@@ -12,12 +12,12 @@ import { NULL_STATE, nullTokenize2 } from 'vs/editor/common/modes/nullMode';
export interface IReducedTokenizationSupport {
getInitialState(): IState;
tokenize2(line: string, state: IState, offsetDelta: number): TokenizationResult2;
tokenize2(line: string, hasEOL: boolean, state: IState, offsetDelta: number): TokenizationResult2;
}
const fallback: IReducedTokenizationSupport = {
getInitialState: () => NULL_STATE,
tokenize2: (buffer: string, state: IState, deltaOffset: number) => nullTokenize2(LanguageId.Null, buffer, state, deltaOffset)
tokenize2: (buffer: string, hasEOL: boolean, state: IState, deltaOffset: number) => nullTokenize2(LanguageId.Null, buffer, state, deltaOffset)
};
export function tokenizeToString(text: string, tokenizationSupport: IReducedTokenizationSupport = fallback): string {
@@ -110,7 +110,7 @@ function _tokenizeToString(text: string, tokenizationSupport: IReducedTokenizati
result += `<br/>`;
}
let tokenizationResult = tokenizationSupport.tokenize2(line, currentState, 0);
let tokenizationResult = tokenizationSupport.tokenize2(line, true, currentState, 0);
LineTokens.convertToEndOffset(tokenizationResult.tokens, line.length);
let lineTokens = new LineTokens(tokenizationResult.tokens, line);
let viewLineTokens = lineTokens.inflate();

View File

@@ -0,0 +1,160 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancellationToken } from 'vs/base/common/cancellation';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { URI } from 'vs/base/common/uri';
import { ITextModel } from 'vs/editor/common/model';
import { DocumentSemanticTokensProviderRegistry, DocumentSemanticTokensProvider, SemanticTokens, SemanticTokensEdits, SemanticTokensLegend, DocumentRangeSemanticTokensProviderRegistry, DocumentRangeSemanticTokensProvider } from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
import { assertType } from 'vs/base/common/types';
import { VSBuffer } from 'vs/base/common/buffer';
import { encodeSemanticTokensDto } from 'vs/editor/common/services/semanticTokensDto';
import { Range } from 'vs/editor/common/core/range';
export function isSemanticTokens(v: SemanticTokens | SemanticTokensEdits): v is SemanticTokens {
return v && !!((<SemanticTokens>v).data);
}
export function isSemanticTokensEdits(v: SemanticTokens | SemanticTokensEdits): v is SemanticTokensEdits {
return v && Array.isArray((<SemanticTokensEdits>v).edits);
}
export interface IDocumentSemanticTokensResult {
provider: DocumentSemanticTokensProvider;
request: Promise<SemanticTokens | SemanticTokensEdits | null | undefined>;
}
export function getDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): IDocumentSemanticTokensResult | null {
const provider = _getDocumentSemanticTokensProvider(model);
if (!provider) {
return null;
}
return {
provider: provider,
request: Promise.resolve(provider.provideDocumentSemanticTokens(model, lastResultId, token))
};
}
function _getDocumentSemanticTokensProvider(model: ITextModel): DocumentSemanticTokensProvider | null {
const result = DocumentSemanticTokensProviderRegistry.ordered(model);
return (result.length > 0 ? result[0] : null);
}
export function getDocumentRangeSemanticTokensProvider(model: ITextModel): DocumentRangeSemanticTokensProvider | null {
const result = DocumentRangeSemanticTokensProviderRegistry.ordered(model);
return (result.length > 0 ? result[0] : null);
}
CommandsRegistry.registerCommand('_provideDocumentSemanticTokensLegend', async (accessor, ...args): Promise<SemanticTokensLegend | undefined> => {
const [uri] = args;
assertType(uri instanceof URI);
const model = accessor.get(IModelService).getModel(uri);
if (!model) {
return undefined;
}
const provider = _getDocumentSemanticTokensProvider(model);
if (!provider) {
// there is no provider => fall back to a document range semantic tokens provider
return accessor.get(ICommandService).executeCommand('_provideDocumentRangeSemanticTokensLegend', uri);
}
return provider.getLegend();
});
CommandsRegistry.registerCommand('_provideDocumentSemanticTokens', async (accessor, ...args): Promise<VSBuffer | undefined> => {
const [uri] = args;
assertType(uri instanceof URI);
const model = accessor.get(IModelService).getModel(uri);
if (!model) {
return undefined;
}
const r = getDocumentSemanticTokens(model, null, CancellationToken.None);
if (!r) {
// there is no provider => fall back to a document range semantic tokens provider
return accessor.get(ICommandService).executeCommand('_provideDocumentRangeSemanticTokens', uri, model.getFullModelRange());
}
const { provider, request } = r;
let result: SemanticTokens | SemanticTokensEdits | null | undefined;
try {
result = await request;
} catch (err) {
onUnexpectedExternalError(err);
return undefined;
}
if (!result || !isSemanticTokens(result)) {
return undefined;
}
const buff = encodeSemanticTokensDto({
id: 0,
type: 'full',
data: result.data
});
if (result.resultId) {
provider.releaseDocumentSemanticTokens(result.resultId);
}
return buff;
});
CommandsRegistry.registerCommand('_provideDocumentRangeSemanticTokensLegend', async (accessor, ...args): Promise<SemanticTokensLegend | undefined> => {
const [uri] = args;
assertType(uri instanceof URI);
const model = accessor.get(IModelService).getModel(uri);
if (!model) {
return undefined;
}
const provider = getDocumentRangeSemanticTokensProvider(model);
if (!provider) {
return undefined;
}
return provider.getLegend();
});
CommandsRegistry.registerCommand('_provideDocumentRangeSemanticTokens', async (accessor, ...args): Promise<VSBuffer | undefined> => {
const [uri, range] = args;
assertType(uri instanceof URI);
assertType(Range.isIRange(range));
const model = accessor.get(IModelService).getModel(uri);
if (!model) {
return undefined;
}
const provider = getDocumentRangeSemanticTokensProvider(model);
if (!provider) {
// there is no provider
return undefined;
}
let result: SemanticTokens | null | undefined;
try {
result = await provider.provideDocumentRangeSemanticTokens(model, Range.lift(range), CancellationToken.None);
} catch (err) {
onUnexpectedExternalError(err);
return undefined;
}
if (!result || !isSemanticTokens(result)) {
return undefined;
}
return encodeSemanticTokensDto({
id: 0,
type: 'full',
data: result.data
});
});

View File

@@ -87,13 +87,13 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor
this._markerDecorations.clear();
}
getMarker(model: ITextModel, decoration: IModelDecoration): IMarker | null {
const markerDecorations = this._markerDecorations.get(MODEL_ID(model.uri));
getMarker(uri: URI, decoration: IModelDecoration): IMarker | null {
const markerDecorations = this._markerDecorations.get(MODEL_ID(uri));
return markerDecorations ? (markerDecorations.getMarker(decoration) || null) : null;
}
getLiveMarkers(model: ITextModel): [Range, IMarker][] {
const markerDecorations = this._markerDecorations.get(MODEL_ID(model.uri));
getLiveMarkers(uri: URI): [Range, IMarker][] {
const markerDecorations = this._markerDecorations.get(MODEL_ID(uri));
return markerDecorations ? markerDecorations.getMarkers() : [];
}

View File

@@ -8,6 +8,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
import { IMarker } from 'vs/platform/markers/common/markers';
import { Event } from 'vs/base/common/event';
import { Range } from 'vs/editor/common/core/range';
import { URI } from 'vs/base/common/uri';
export const IMarkerDecorationsService = createDecorator<IMarkerDecorationsService>('markerDecorationsService');
@@ -16,7 +17,7 @@ export interface IMarkerDecorationsService {
onDidChangeMarker: Event<ITextModel>;
getMarker(model: ITextModel, decoration: IModelDecoration): IMarker | null;
getMarker(uri: URI, decoration: IModelDecoration): IMarker | null;
getLiveMarkers(model: ITextModel): [Range, IMarker][];
getLiveMarkers(uri: URI): [Range, IMarker][];
}

View File

@@ -50,7 +50,7 @@ export interface IModeService {
// --- instantiation
create(commaSeparatedMimetypesOrCommaSeparatedIds: string | undefined): ILanguageSelection;
createByLanguageName(languageName: string): ILanguageSelection;
createByFilepathOrFirstLine(rsource: URI | null, firstLine?: string): ILanguageSelection;
createByFilepathOrFirstLine(resource: URI | null, firstLine?: string): ILanguageSelection;
triggerMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): void;
}

View File

@@ -40,23 +40,24 @@ class LanguageSelection extends Disposable implements ILanguageSelection {
}
}
export class ModeServiceImpl implements IModeService {
export class ModeServiceImpl extends Disposable implements IModeService {
public _serviceBrand: undefined;
private readonly _instantiatedModes: { [modeId: string]: IMode; };
private readonly _registry: LanguagesRegistry;
private readonly _onDidCreateMode = new Emitter<IMode>();
private readonly _onDidCreateMode = this._register(new Emitter<IMode>());
public readonly onDidCreateMode: Event<IMode> = this._onDidCreateMode.event;
protected readonly _onLanguagesMaybeChanged = new Emitter<void>();
protected readonly _onLanguagesMaybeChanged = this._register(new Emitter<void>());
public readonly onLanguagesMaybeChanged: Event<void> = this._onLanguagesMaybeChanged.event;
constructor(warnOnOverwrite = false) {
super();
this._instantiatedModes = {};
this._registry = new LanguagesRegistry(true, warnOnOverwrite);
this._registry.onDidChange(() => this._onLanguagesMaybeChanged.fire());
this._registry = this._register(new LanguagesRegistry(true, warnOnOverwrite));
this._register(this._registry.onDidChange(() => this._onLanguagesMaybeChanged.fire()));
}
protected _onReady(): Promise<boolean> {

View File

@@ -29,6 +29,7 @@ import { StringSHA1 } from 'vs/base/common/hash';
import { EditStackElement, isEditStackElement } from 'vs/editor/common/model/editStack';
import { Schemas } from 'vs/base/common/network';
import { SemanticTokensProviderStyling, toMultilineTokens2 } from 'vs/editor/common/services/semanticTokensProviderStyling';
import { getDocumentSemanticTokens, isSemanticTokens, isSemanticTokensEdits } from 'vs/editor/common/services/getSemanticTokens';
export interface IEditorSemanticHighlightingOptions {
enabled: true | false | 'configuredByTheme';
@@ -412,10 +413,11 @@ export class ModelServiceImpl extends Disposable implements IModelService {
public updateModel(model: ITextModel, value: string | ITextBufferFactory): void {
const options = this.getCreationOptions(model.getLanguageIdentifier().language, model.uri, model.isForSimpleWidget);
const textBuffer = createTextBuffer(value, options.defaultEOL);
const { textBuffer, disposable } = createTextBuffer(value, options.defaultEOL);
// Return early if the text is already set in that form
if (model.equalsTextBuffer(textBuffer)) {
disposable.dispose();
return;
}
@@ -428,6 +430,7 @@ export class ModelServiceImpl extends Disposable implements IModelService {
() => []
);
model.pushStackElement();
disposable.dispose();
}
private static _commonPrefix(a: ILineSequence, aLen: number, aDelta: number, b: ILineSequence, bLen: number, bDelta: number): number {
@@ -513,58 +516,6 @@ export class ModelServiceImpl extends Disposable implements IModelService {
if (!modelData) {
return;
}
const model = modelData.model;
const sharesUndoRedoStack = (this._undoRedoService.getUriComparisonKey(model.uri) !== model.uri.toString());
let maintainUndoRedoStack = false;
let heapSize = 0;
if (sharesUndoRedoStack || (this._shouldRestoreUndoStack() && schemaShouldMaintainUndoRedoElements(resource))) {
const elements = this._undoRedoService.getElements(resource);
if (elements.past.length > 0 || elements.future.length > 0) {
for (const element of elements.past) {
if (isEditStackElement(element) && element.matchesResource(resource)) {
maintainUndoRedoStack = true;
heapSize += element.heapSize(resource);
element.setModel(resource); // remove reference from text buffer instance
}
}
for (const element of elements.future) {
if (isEditStackElement(element) && element.matchesResource(resource)) {
maintainUndoRedoStack = true;
heapSize += element.heapSize(resource);
element.setModel(resource); // remove reference from text buffer instance
}
}
}
}
if (!maintainUndoRedoStack) {
if (!sharesUndoRedoStack) {
const initialUndoRedoSnapshot = modelData.model.getInitialUndoRedoSnapshot();
if (initialUndoRedoSnapshot !== null) {
this._undoRedoService.restoreSnapshot(initialUndoRedoSnapshot);
}
}
modelData.model.dispose();
return;
}
const maxMemory = ModelServiceImpl.MAX_MEMORY_FOR_CLOSED_FILES_UNDO_STACK;
if (!sharesUndoRedoStack && heapSize > maxMemory) {
// the undo stack for this file would never fit in the configured memory, so don't bother with it.
const initialUndoRedoSnapshot = modelData.model.getInitialUndoRedoSnapshot();
if (initialUndoRedoSnapshot !== null) {
this._undoRedoService.restoreSnapshot(initialUndoRedoSnapshot);
}
modelData.model.dispose();
return;
}
this._ensureDisposedModelsHeapSize(maxMemory - heapSize);
// We only invalidate the elements, but they remain in the undo-redo service.
this._undoRedoService.setElementsValidFlag(resource, false, (element) => (isEditStackElement(element) && element.matchesResource(resource)));
this._insertDisposedModel(new DisposedModelInfo(resource, modelData.model.getInitialUndoRedoSnapshot(), Date.now(), sharesUndoRedoStack, heapSize, computeModelSha1(model), model.getVersionId(), model.getAlternativeVersionId()));
modelData.model.dispose();
}
@@ -599,6 +550,50 @@ export class ModelServiceImpl extends Disposable implements IModelService {
const modelId = MODEL_ID(model.uri);
const modelData = this._models[modelId];
const sharesUndoRedoStack = (this._undoRedoService.getUriComparisonKey(model.uri) !== model.uri.toString());
let maintainUndoRedoStack = false;
let heapSize = 0;
if (sharesUndoRedoStack || (this._shouldRestoreUndoStack() && schemaShouldMaintainUndoRedoElements(model.uri))) {
const elements = this._undoRedoService.getElements(model.uri);
if (elements.past.length > 0 || elements.future.length > 0) {
for (const element of elements.past) {
if (isEditStackElement(element) && element.matchesResource(model.uri)) {
maintainUndoRedoStack = true;
heapSize += element.heapSize(model.uri);
element.setModel(model.uri); // remove reference from text buffer instance
}
}
for (const element of elements.future) {
if (isEditStackElement(element) && element.matchesResource(model.uri)) {
maintainUndoRedoStack = true;
heapSize += element.heapSize(model.uri);
element.setModel(model.uri); // remove reference from text buffer instance
}
}
}
}
const maxMemory = ModelServiceImpl.MAX_MEMORY_FOR_CLOSED_FILES_UNDO_STACK;
if (!maintainUndoRedoStack) {
if (!sharesUndoRedoStack) {
const initialUndoRedoSnapshot = modelData.model.getInitialUndoRedoSnapshot();
if (initialUndoRedoSnapshot !== null) {
this._undoRedoService.restoreSnapshot(initialUndoRedoSnapshot);
}
}
} else if (!sharesUndoRedoStack && heapSize > maxMemory) {
// the undo stack for this file would never fit in the configured memory, so don't bother with it.
const initialUndoRedoSnapshot = modelData.model.getInitialUndoRedoSnapshot();
if (initialUndoRedoSnapshot !== null) {
this._undoRedoService.restoreSnapshot(initialUndoRedoSnapshot);
}
} else {
this._ensureDisposedModelsHeapSize(maxMemory - heapSize);
// We only invalidate the elements, but they remain in the undo-redo service.
this._undoRedoService.setElementsValidFlag(model.uri, false, (element) => (isEditStackElement(element) && element.matchesResource(model.uri)));
this._insertDisposedModel(new DisposedModelInfo(model.uri, modelData.model.getInitialUndoRedoSnapshot(), Date.now(), sharesUndoRedoStack, heapSize, computeModelSha1(model), model.getVersionId(), model.getAlternativeVersionId()));
}
delete this._models[modelId];
modelData.dispose();
@@ -718,7 +713,9 @@ class SemanticTokensResponse {
}
}
class ModelSemanticColoring extends Disposable {
export class ModelSemanticColoring extends Disposable {
public static FETCH_DOCUMENT_SEMANTIC_TOKENS_DELAY = 300;
private _isDisposed: boolean;
private readonly _model: ITextModel;
@@ -734,7 +731,7 @@ class ModelSemanticColoring extends Disposable {
this._isDisposed = false;
this._model = model;
this._semanticStyling = stylingProvider;
this._fetchDocumentSemanticTokens = this._register(new RunOnceScheduler(() => this._fetchDocumentSemanticTokensNow(), 300));
this._fetchDocumentSemanticTokens = this._register(new RunOnceScheduler(() => this._fetchDocumentSemanticTokensNow(), ModelSemanticColoring.FETCH_DOCUMENT_SEMANTIC_TOKENS_DELAY));
this._currentDocumentResponse = null;
this._currentDocumentRequestCancellationTokenSource = null;
this._documentProvidersChangeListeners = [];
@@ -788,15 +785,21 @@ class ModelSemanticColoring extends Disposable {
// there is already a request running, let it finish...
return;
}
const provider = this._getSemanticColoringProvider();
if (!provider) {
const cancellationTokenSource = new CancellationTokenSource();
const lastResultId = this._currentDocumentResponse ? this._currentDocumentResponse.resultId || null : null;
const r = getDocumentSemanticTokens(this._model, lastResultId, cancellationTokenSource.token);
if (!r) {
// there is no provider
if (this._currentDocumentResponse) {
// there are semantic tokens set
this._model.setSemanticTokens(null, false);
}
return;
}
this._currentDocumentRequestCancellationTokenSource = new CancellationTokenSource();
const { provider, request } = r;
this._currentDocumentRequestCancellationTokenSource = cancellationTokenSource;
const pendingChanges: IModelContentChangedEvent[] = [];
const contentChangeListener = this._model.onDidChangeContent((e) => {
@@ -805,15 +808,13 @@ class ModelSemanticColoring extends Disposable {
const styling = this._semanticStyling.get(provider);
const lastResultId = this._currentDocumentResponse ? this._currentDocumentResponse.resultId || null : null;
const request = Promise.resolve(provider.provideDocumentSemanticTokens(this._model, lastResultId, this._currentDocumentRequestCancellationTokenSource.token));
request.then((res) => {
this._currentDocumentRequestCancellationTokenSource = null;
contentChangeListener.dispose();
this._setDocumentSemanticTokens(provider, res || null, styling, pendingChanges);
}, (err) => {
if (!err || typeof err.message !== 'string' || err.message.indexOf('busy') === -1) {
const isExpectedError = err && (errors.isPromiseCanceledError(err) || (typeof err.message === 'string' && err.message.indexOf('busy') !== -1));
if (!isExpectedError) {
errors.onUnexpectedError(err);
}
@@ -831,14 +832,6 @@ class ModelSemanticColoring extends Disposable {
});
}
private static _isSemanticTokens(v: SemanticTokens | SemanticTokensEdits): v is SemanticTokens {
return v && !!((<SemanticTokens>v).data);
}
private static _isSemanticTokensEdits(v: SemanticTokens | SemanticTokensEdits): v is SemanticTokensEdits {
return v && Array.isArray((<SemanticTokensEdits>v).edits);
}
private static _copy(src: Uint32Array, srcOffset: number, dest: Uint32Array, destOffset: number, length: number): void {
for (let i = 0; i < length; i++) {
dest[destOffset + i] = src[srcOffset + i];
@@ -847,6 +840,12 @@ class ModelSemanticColoring extends Disposable {
private _setDocumentSemanticTokens(provider: DocumentSemanticTokensProvider | null, tokens: SemanticTokens | SemanticTokensEdits | null, styling: SemanticTokensProviderStyling | null, pendingChanges: IModelContentChangedEvent[]): void {
const currentResponse = this._currentDocumentResponse;
const rescheduleIfNeeded = () => {
if (pendingChanges.length > 0 && !this._fetchDocumentSemanticTokens.isScheduled()) {
this._fetchDocumentSemanticTokens.schedule();
}
};
if (this._currentDocumentResponse) {
this._currentDocumentResponse.dispose();
this._currentDocumentResponse = null;
@@ -864,10 +863,11 @@ class ModelSemanticColoring extends Disposable {
}
if (!tokens) {
this._model.setSemanticTokens(null, true);
rescheduleIfNeeded();
return;
}
if (ModelSemanticColoring._isSemanticTokensEdits(tokens)) {
if (isSemanticTokensEdits(tokens)) {
if (!currentResponse) {
// not possible!
this._model.setSemanticTokens(null, true);
@@ -918,7 +918,7 @@ class ModelSemanticColoring extends Disposable {
}
}
if (ModelSemanticColoring._isSemanticTokens(tokens)) {
if (isSemanticTokens(tokens)) {
this._currentDocumentResponse = new SemanticTokensResponse(provider, tokens.resultId, tokens.data);
@@ -937,21 +937,13 @@ class ModelSemanticColoring extends Disposable {
}
}
}
if (!this._fetchDocumentSemanticTokens.isScheduled()) {
this._fetchDocumentSemanticTokens.schedule();
}
}
this._model.setSemanticTokens(result, true);
return;
} else {
this._model.setSemanticTokens(null, true);
}
this._model.setSemanticTokens(null, true);
}
private _getSemanticColoringProvider(): DocumentSemanticTokensProvider | null {
const result = DocumentSemanticTokensProviderRegistry.ordered(this._model);
return (result.length > 0 ? result[0] : null);
rescheduleIfNeeded();
}
}

View File

@@ -0,0 +1,152 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { VSBuffer } from 'vs/base/common/buffer';
import * as platform from 'vs/base/common/platform';
export interface IFullSemanticTokensDto {
id: number;
type: 'full';
data: Uint32Array;
}
export interface IDeltaSemanticTokensDto {
id: number;
type: 'delta';
deltas: { start: number; deleteCount: number; data?: Uint32Array; }[];
}
export type ISemanticTokensDto = IFullSemanticTokensDto | IDeltaSemanticTokensDto;
const enum EncodedSemanticTokensType {
Full = 1,
Delta = 2
}
function reverseEndianness(arr: Uint8Array): void {
for (let i = 0, len = arr.length; i < len; i += 4) {
// flip bytes 0<->3 and 1<->2
const b0 = arr[i + 0];
const b1 = arr[i + 1];
const b2 = arr[i + 2];
const b3 = arr[i + 3];
arr[i + 0] = b3;
arr[i + 1] = b2;
arr[i + 2] = b1;
arr[i + 3] = b0;
}
}
function toLittleEndianBuffer(arr: Uint32Array): VSBuffer {
const uint8Arr = new Uint8Array(arr.buffer, arr.byteOffset, arr.length * 4);
if (!platform.isLittleEndian()) {
// the byte order must be changed
reverseEndianness(uint8Arr);
}
return VSBuffer.wrap(uint8Arr);
}
function fromLittleEndianBuffer(buff: VSBuffer): Uint32Array {
const uint8Arr = buff.buffer;
if (!platform.isLittleEndian()) {
// the byte order must be changed
reverseEndianness(uint8Arr);
}
if (uint8Arr.byteOffset % 4 === 0) {
return new Uint32Array(uint8Arr.buffer, uint8Arr.byteOffset, uint8Arr.length / 4);
} else {
// unaligned memory access doesn't work on all platforms
const data = new Uint8Array(uint8Arr.byteLength);
data.set(uint8Arr);
return new Uint32Array(data.buffer, data.byteOffset, data.length / 4);
}
}
export function encodeSemanticTokensDto(semanticTokens: ISemanticTokensDto): VSBuffer {
const dest = new Uint32Array(encodeSemanticTokensDtoSize(semanticTokens));
let offset = 0;
dest[offset++] = semanticTokens.id;
if (semanticTokens.type === 'full') {
dest[offset++] = EncodedSemanticTokensType.Full;
dest[offset++] = semanticTokens.data.length;
dest.set(semanticTokens.data, offset); offset += semanticTokens.data.length;
} else {
dest[offset++] = EncodedSemanticTokensType.Delta;
dest[offset++] = semanticTokens.deltas.length;
for (const delta of semanticTokens.deltas) {
dest[offset++] = delta.start;
dest[offset++] = delta.deleteCount;
if (delta.data) {
dest[offset++] = delta.data.length;
dest.set(delta.data, offset); offset += delta.data.length;
} else {
dest[offset++] = 0;
}
}
}
return toLittleEndianBuffer(dest);
}
function encodeSemanticTokensDtoSize(semanticTokens: ISemanticTokensDto): number {
let result = 0;
result += (
+ 1 // id
+ 1 // type
);
if (semanticTokens.type === 'full') {
result += (
+ 1 // data length
+ semanticTokens.data.length
);
} else {
result += (
+ 1 // delta count
);
result += (
+ 1 // start
+ 1 // deleteCount
+ 1 // data length
) * semanticTokens.deltas.length;
for (const delta of semanticTokens.deltas) {
if (delta.data) {
result += delta.data.length;
}
}
}
return result;
}
export function decodeSemanticTokensDto(_buff: VSBuffer): ISemanticTokensDto {
const src = fromLittleEndianBuffer(_buff);
let offset = 0;
const id = src[offset++];
const type: EncodedSemanticTokensType = src[offset++];
if (type === EncodedSemanticTokensType.Full) {
const length = src[offset++];
const data = src.subarray(offset, offset + length); offset += length;
return {
id: id,
type: 'full',
data: data
};
}
const deltaCount = src[offset++];
let deltas: { start: number; deleteCount: number; data?: Uint32Array; }[] = [];
for (let i = 0; i < deltaCount; i++) {
const start = src[offset++];
const deleteCount = src[offset++];
const length = src[offset++];
let data: Uint32Array | undefined;
if (length > 0) {
data = src.subarray(offset, offset + length); offset += length;
}
deltas[i] = { start, deleteCount, data };
}
return {
id: id,
type: 'delta',
deltas: deltas
};
}

View File

@@ -287,11 +287,12 @@ export enum EditorOption {
wrappingIndent = 117,
wrappingStrategy = 118,
showDeprecated = 119,
editorClassName = 120,
pixelRatio = 121,
tabFocusMode = 122,
layoutInfo = 123,
wrappingInfo = 124
inlineHints = 120,
editorClassName = 121,
pixelRatio = 122,
tabFocusMode = 123,
layoutInfo = 124,
wrappingInfo = 125
}
/**

View File

@@ -255,7 +255,7 @@ export class ViewLayout extends Disposable implements IViewLayout {
let result = this._linesLayout.getLinesTotalHeight();
if (options.get(EditorOption.scrollBeyondLastLine)) {
result += height - options.get(EditorOption.lineHeight);
result += Math.max(0, height - options.get(EditorOption.lineHeight) - options.get(EditorOption.padding).bottom);
} else {
result += this._getHorizontalScrollbarHeight(width, contentWidth);
}

View File

@@ -303,6 +303,19 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla
breakOffsetVisibleColumn = forcedBreakOffsetVisibleColumn;
}
if (breakOffset <= lastBreakingOffset) {
// Make sure that we are advancing (at least one character)
const charCode = lineText.charCodeAt(lastBreakingOffset);
if (strings.isHighSurrogate(charCode)) {
// A surrogate pair must always be considered as a single unit, so it is never to be broken
breakOffset = lastBreakingOffset + 2;
breakOffsetVisibleColumn = lastBreakingOffsetVisibleColumn + 2;
} else {
breakOffset = lastBreakingOffset + 1;
breakOffsetVisibleColumn = lastBreakingOffsetVisibleColumn + computeCharWidth(charCode, lastBreakingOffsetVisibleColumn, tabSize, columnsForFullWidthChar);
}
}
lastBreakingOffset = breakOffset;
breakingOffsets[breakingOffsetsCount] = breakOffset;
lastBreakingOffsetVisibleColumn = breakOffsetVisibleColumn;
@@ -435,6 +448,10 @@ function computeCharWidth(charCode: number, visibleColumn: number, tabSize: numb
if (strings.isFullWidthCharacter(charCode)) {
return columnsForFullWidthChar;
}
if (charCode < 32) {
// when using `editor.renderControlCharacters`, the substitutions are often wide
return columnsForFullWidthChar;
}
return 1;
}

View File

@@ -503,15 +503,8 @@ export class SplitLinesCollection implements IViewModelLinesCollection {
return null;
}
let hiddenAreas = this.getHiddenAreas();
let isInHiddenArea = false;
let testPosition = new Position(fromLineNumber, 1);
for (const hiddenArea of hiddenAreas) {
if (hiddenArea.containsPosition(testPosition)) {
isInHiddenArea = true;
break;
}
}
// cannot use this.getHiddenAreas() because those decorations have already seen the effect of this model change
const isInHiddenArea = (fromLineNumber > 2 && !this.lines[fromLineNumber - 2].isVisible());
let outputFromLineNumber = (fromLineNumber === 1 ? 1 : this.prefixSumComputer.getAccumulatedValue(fromLineNumber - 2) + 1);

View File

@@ -916,8 +916,8 @@ export class ViewModel extends Disposable implements IViewModel {
public getPosition(): Position {
return this._cursor.getPrimaryCursorState().modelState.position;
}
public setSelections(source: string | null | undefined, selections: readonly ISelection[]): void {
this._withViewEventsCollector(eventsCollector => this._cursor.setSelections(eventsCollector, source, selections));
public setSelections(source: string | null | undefined, selections: readonly ISelection[], reason = CursorChangeReason.NotSet): void {
this._withViewEventsCollector(eventsCollector => this._cursor.setSelections(eventsCollector, source, selections, reason));
}
public saveCursorState(): ICursorState[] {
return this._cursor.saveState();

View File

@@ -39,20 +39,20 @@ suite('bracket matching', () => {
// start on closing bracket
editor.setPosition(new Position(1, 20));
bracketMatchingController.jumpToBracket();
assert.deepEqual(editor.getPosition(), new Position(1, 9));
assert.deepStrictEqual(editor.getPosition(), new Position(1, 9));
bracketMatchingController.jumpToBracket();
assert.deepEqual(editor.getPosition(), new Position(1, 19));
assert.deepStrictEqual(editor.getPosition(), new Position(1, 19));
bracketMatchingController.jumpToBracket();
assert.deepEqual(editor.getPosition(), new Position(1, 9));
assert.deepStrictEqual(editor.getPosition(), new Position(1, 9));
// start on opening bracket
editor.setPosition(new Position(1, 23));
bracketMatchingController.jumpToBracket();
assert.deepEqual(editor.getPosition(), new Position(1, 31));
assert.deepStrictEqual(editor.getPosition(), new Position(1, 31));
bracketMatchingController.jumpToBracket();
assert.deepEqual(editor.getPosition(), new Position(1, 23));
assert.deepStrictEqual(editor.getPosition(), new Position(1, 23));
bracketMatchingController.jumpToBracket();
assert.deepEqual(editor.getPosition(), new Position(1, 31));
assert.deepStrictEqual(editor.getPosition(), new Position(1, 31));
bracketMatchingController.dispose();
});
@@ -71,25 +71,25 @@ suite('bracket matching', () => {
// start position between brackets
editor.setPosition(new Position(1, 16));
bracketMatchingController.jumpToBracket();
assert.deepEqual(editor.getPosition(), new Position(1, 18));
assert.deepStrictEqual(editor.getPosition(), new Position(1, 18));
bracketMatchingController.jumpToBracket();
assert.deepEqual(editor.getPosition(), new Position(1, 14));
assert.deepStrictEqual(editor.getPosition(), new Position(1, 14));
bracketMatchingController.jumpToBracket();
assert.deepEqual(editor.getPosition(), new Position(1, 18));
assert.deepStrictEqual(editor.getPosition(), new Position(1, 18));
// skip brackets in comments
editor.setPosition(new Position(1, 21));
bracketMatchingController.jumpToBracket();
assert.deepEqual(editor.getPosition(), new Position(1, 23));
assert.deepStrictEqual(editor.getPosition(), new Position(1, 23));
bracketMatchingController.jumpToBracket();
assert.deepEqual(editor.getPosition(), new Position(1, 24));
assert.deepStrictEqual(editor.getPosition(), new Position(1, 24));
bracketMatchingController.jumpToBracket();
assert.deepEqual(editor.getPosition(), new Position(1, 23));
assert.deepStrictEqual(editor.getPosition(), new Position(1, 23));
// do not break if no brackets are available
editor.setPosition(new Position(1, 26));
bracketMatchingController.jumpToBracket();
assert.deepEqual(editor.getPosition(), new Position(1, 26));
assert.deepStrictEqual(editor.getPosition(), new Position(1, 26));
bracketMatchingController.dispose();
});
@@ -109,32 +109,32 @@ suite('bracket matching', () => {
// start position in open brackets
editor.setPosition(new Position(1, 9));
bracketMatchingController.selectToBracket(true);
assert.deepEqual(editor.getPosition(), new Position(1, 20));
assert.deepEqual(editor.getSelection(), new Selection(1, 9, 1, 20));
assert.deepStrictEqual(editor.getPosition(), new Position(1, 20));
assert.deepStrictEqual(editor.getSelection(), new Selection(1, 9, 1, 20));
// start position in close brackets
editor.setPosition(new Position(1, 20));
bracketMatchingController.selectToBracket(true);
assert.deepEqual(editor.getPosition(), new Position(1, 20));
assert.deepEqual(editor.getSelection(), new Selection(1, 9, 1, 20));
assert.deepStrictEqual(editor.getPosition(), new Position(1, 20));
assert.deepStrictEqual(editor.getSelection(), new Selection(1, 9, 1, 20));
// start position between brackets
editor.setPosition(new Position(1, 16));
bracketMatchingController.selectToBracket(true);
assert.deepEqual(editor.getPosition(), new Position(1, 19));
assert.deepEqual(editor.getSelection(), new Selection(1, 14, 1, 19));
assert.deepStrictEqual(editor.getPosition(), new Position(1, 19));
assert.deepStrictEqual(editor.getSelection(), new Selection(1, 14, 1, 19));
// start position outside brackets
editor.setPosition(new Position(1, 21));
bracketMatchingController.selectToBracket(true);
assert.deepEqual(editor.getPosition(), new Position(1, 25));
assert.deepEqual(editor.getSelection(), new Selection(1, 23, 1, 25));
assert.deepStrictEqual(editor.getPosition(), new Position(1, 25));
assert.deepStrictEqual(editor.getSelection(), new Selection(1, 23, 1, 25));
// do not break if no brackets are available
editor.setPosition(new Position(1, 26));
bracketMatchingController.selectToBracket(true);
assert.deepEqual(editor.getPosition(), new Position(1, 26));
assert.deepEqual(editor.getSelection(), new Selection(1, 26, 1, 26));
assert.deepStrictEqual(editor.getPosition(), new Position(1, 26));
assert.deepStrictEqual(editor.getSelection(), new Selection(1, 26, 1, 26));
bracketMatchingController.dispose();
});
@@ -159,7 +159,7 @@ suite('bracket matching', () => {
editor.setPosition(new Position(3, 5));
bracketMatchingController.jumpToBracket();
assert.deepEqual(editor.getSelection(), new Selection(5, 1, 5, 1));
assert.deepStrictEqual(editor.getSelection(), new Selection(5, 1, 5, 1));
bracketMatchingController.dispose();
});
@@ -184,7 +184,7 @@ suite('bracket matching', () => {
editor.setPosition(new Position(3, 5));
bracketMatchingController.selectToBracket(false);
assert.deepEqual(editor.getSelection(), new Selection(1, 12, 5, 1));
assert.deepStrictEqual(editor.getSelection(), new Selection(1, 12, 5, 1));
bracketMatchingController.dispose();
});
@@ -207,7 +207,7 @@ suite('bracket matching', () => {
new Selection(1, 17, 1, 17)
]);
bracketMatchingController.selectToBracket(true);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 5),
new Selection(1, 8, 1, 13),
new Selection(1, 16, 1, 19)
@@ -220,7 +220,7 @@ suite('bracket matching', () => {
new Selection(1, 14, 1, 14)
]);
bracketMatchingController.selectToBracket(true);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 5),
new Selection(1, 8, 1, 13),
new Selection(1, 16, 1, 19)
@@ -233,7 +233,7 @@ suite('bracket matching', () => {
new Selection(1, 19, 1, 19)
]);
bracketMatchingController.selectToBracket(true);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 5),
new Selection(1, 8, 1, 13),
new Selection(1, 16, 1, 19)

View File

@@ -24,10 +24,11 @@ const CLIPBOARD_CONTEXT_MENU_GROUP = '9_cutcopypaste';
const supportsCut = (platform.isNative || document.queryCommandSupported('cut'));
const supportsCopy = (platform.isNative || document.queryCommandSupported('copy'));
// IE and Edge have trouble with setting html content in clipboard
const supportsCopyWithSyntaxHighlighting = (supportsCopy && !browser.isEdge);
const supportsCopyWithSyntaxHighlighting = (supportsCopy && !browser.isEdgeLegacy);
// Firefox only supports navigator.clipboard.readText() in browser extensions.
// See https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/readText#Browser_compatibility
const supportsPaste = (browser.isFirefox ? document.queryCommandSupported('paste') : true);
// When loading over http, navigator.clipboard can be undefined. See https://github.com/microsoft/monaco-editor/issues/2313
const supportsPaste = (typeof navigator.clipboard === 'undefined' || browser.isFirefox) ? document.queryCommandSupported('paste') : true;
function registerCommand<T extends Command>(command: T): T {
command.register();

View File

@@ -98,14 +98,17 @@ export class CodeLensContribution implements IEditorContribution {
const fontFamily = this._editor.getOption(EditorOption.codeLensFontFamily);
const editorFontInfo = this._editor.getOption(EditorOption.fontInfo);
const fontFamilyVar = `--codelens-font-family${this._styleClassName}`;
let newStyle = `
.monaco-editor .codelens-decoration.${this._styleClassName} { line-height: ${codeLensHeight}px; font-size: ${fontSize}px; padding-right: ${Math.round(fontSize * 0.5)}px; font-feature-settings: ${editorFontInfo.fontFeatureSettings} }
.monaco-editor .codelens-decoration.${this._styleClassName} span.codicon { line-height: ${codeLensHeight}px; font-size: ${fontSize}px; }
`;
if (fontFamily) {
newStyle += `.monaco-editor .codelens-decoration.${this._styleClassName} { font-family: '${fontFamily}'}`;
newStyle += `.monaco-editor .codelens-decoration.${this._styleClassName} { font-family: var(${fontFamilyVar})}`;
}
this._styleElement.textContent = newStyle;
this._editor.getDomNode()?.style.setProperty(fontFamilyVar, fontFamily ?? 'inherit');
//
this._editor.changeViewZones(accessor => {

View File

@@ -14,7 +14,7 @@ import { editorCodeLensForeground } from 'vs/editor/common/view/editorColorRegis
import { CodeLensItem } from 'vs/editor/contrib/codelens/codelens';
import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { renderCodicons } from 'vs/base/browser/codicons';
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
class CodeLensViewZone implements IViewZone {
@@ -88,7 +88,7 @@ class CodeLensContentWidget implements IContentWidget {
}
hasSymbol = true;
if (lens.command) {
const title = renderCodicons(lens.command.title.trim());
const title = renderLabelWithIcons(lens.command.title.trim());
if (lens.command.id) {
children.push(dom.$('a', { id: String(i) }, ...title));
this._commands.set(String(i), lens.command);

View File

@@ -47,7 +47,7 @@ export class ColorContribution extends Disposable implements IEditorContribution
}
const hoverController = this._editor.getContribution<ModesHoverController>(ModesHoverController.ID);
if (!hoverController.contentWidget.isColorPickerVisible()) {
if (!hoverController.isColorPickerVisible()) {
const range = new Range(mouseEvent.target.range.startLineNumber, mouseEvent.target.range.startColumn + 1, mouseEvent.target.range.endLineNumber, mouseEvent.target.range.endColumn + 1);
hoverController.showContentHover(range, HoverStartMode.Delayed, false);
}

View File

@@ -96,25 +96,25 @@ suite('Editor Contrib - Line Comment Command', () => {
throw new Error(`unexpected`);
}
assert.equal(r.shouldRemoveComments, false);
assert.strictEqual(r.shouldRemoveComments, false);
// Does not change `commentStr`
assert.equal(r.lines[0].commentStr, '//');
assert.equal(r.lines[1].commentStr, 'rem');
assert.equal(r.lines[2].commentStr, '!@#');
assert.equal(r.lines[3].commentStr, '!@#');
assert.strictEqual(r.lines[0].commentStr, '//');
assert.strictEqual(r.lines[1].commentStr, 'rem');
assert.strictEqual(r.lines[2].commentStr, '!@#');
assert.strictEqual(r.lines[3].commentStr, '!@#');
// Fills in `isWhitespace`
assert.equal(r.lines[0].ignore, true);
assert.equal(r.lines[1].ignore, true);
assert.equal(r.lines[2].ignore, false);
assert.equal(r.lines[3].ignore, false);
assert.strictEqual(r.lines[0].ignore, true);
assert.strictEqual(r.lines[1].ignore, true);
assert.strictEqual(r.lines[2].ignore, false);
assert.strictEqual(r.lines[3].ignore, false);
// Fills in `commentStrOffset`
assert.equal(r.lines[0].commentStrOffset, 2);
assert.equal(r.lines[1].commentStrOffset, 4);
assert.equal(r.lines[2].commentStrOffset, 4);
assert.equal(r.lines[3].commentStrOffset, 2);
assert.strictEqual(r.lines[0].commentStrOffset, 2);
assert.strictEqual(r.lines[1].commentStrOffset, 4);
assert.strictEqual(r.lines[2].commentStrOffset, 4);
assert.strictEqual(r.lines[3].commentStrOffset, 2);
r = LineCommentCommand._analyzeLines(Type.Toggle, true, createSimpleModel([
@@ -127,31 +127,31 @@ suite('Editor Contrib - Line Comment Command', () => {
throw new Error(`unexpected`);
}
assert.equal(r.shouldRemoveComments, true);
assert.strictEqual(r.shouldRemoveComments, true);
// Does not change `commentStr`
assert.equal(r.lines[0].commentStr, '//');
assert.equal(r.lines[1].commentStr, 'rem');
assert.equal(r.lines[2].commentStr, '!@#');
assert.equal(r.lines[3].commentStr, '!@#');
assert.strictEqual(r.lines[0].commentStr, '//');
assert.strictEqual(r.lines[1].commentStr, 'rem');
assert.strictEqual(r.lines[2].commentStr, '!@#');
assert.strictEqual(r.lines[3].commentStr, '!@#');
// Fills in `isWhitespace`
assert.equal(r.lines[0].ignore, true);
assert.equal(r.lines[1].ignore, false);
assert.equal(r.lines[2].ignore, false);
assert.equal(r.lines[3].ignore, false);
assert.strictEqual(r.lines[0].ignore, true);
assert.strictEqual(r.lines[1].ignore, false);
assert.strictEqual(r.lines[2].ignore, false);
assert.strictEqual(r.lines[3].ignore, false);
// Fills in `commentStrOffset`
assert.equal(r.lines[0].commentStrOffset, 2);
assert.equal(r.lines[1].commentStrOffset, 4);
assert.equal(r.lines[2].commentStrOffset, 4);
assert.equal(r.lines[3].commentStrOffset, 2);
assert.strictEqual(r.lines[0].commentStrOffset, 2);
assert.strictEqual(r.lines[1].commentStrOffset, 4);
assert.strictEqual(r.lines[2].commentStrOffset, 4);
assert.strictEqual(r.lines[3].commentStrOffset, 2);
// Fills in `commentStrLength`
assert.equal(r.lines[0].commentStrLength, 2);
assert.equal(r.lines[1].commentStrLength, 4);
assert.equal(r.lines[2].commentStrLength, 4);
assert.equal(r.lines[3].commentStrLength, 3);
assert.strictEqual(r.lines[0].commentStrLength, 2);
assert.strictEqual(r.lines[1].commentStrLength, 4);
assert.strictEqual(r.lines[2].commentStrLength, 4);
assert.strictEqual(r.lines[3].commentStrLength, 3);
});
test('_normalizeInsertionPoint', () => {
@@ -166,7 +166,7 @@ suite('Editor Contrib - Line Comment Command', () => {
});
LineCommentCommand._normalizeInsertionPoint(model, offsets, 1, tabSize);
const actual = offsets.map(item => item.commentStrOffset);
assert.deepEqual(actual, expected, testName);
assert.deepStrictEqual(actual, expected, testName);
};
// Bug 16696:[comment] comments not aligned in this case
@@ -1083,7 +1083,7 @@ suite('Editor Contrib - Line Comment in mixed modes', () => {
tokenize: () => {
throw new Error('not implemented');
},
tokenize2: (line: string, state: modes.IState): TokenizationResult2 => {
tokenize2: (line: string, hasEOL: boolean, state: modes.IState): TokenizationResult2 => {
let languageId = (/^ /.test(line) ? INNER_LANGUAGE_ID : OUTER_LANGUAGE_ID);
let tokens = new Uint32Array(1 << 1);

View File

@@ -29,16 +29,16 @@ suite('FindController', () => {
// press Delete
CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, {});
assert.deepEqual(editor.getValue(), 'hell');
assert.deepEqual(editor.getSelections(), [new Selection(1, 5, 1, 5)]);
assert.deepStrictEqual(editor.getValue(), 'hell');
assert.deepStrictEqual(editor.getSelections(), [new Selection(1, 5, 1, 5)]);
// press left
CoreNavigationCommands.CursorLeft.runEditorCommand(null, editor, {});
assert.deepEqual(editor.getSelections(), [new Selection(1, 4, 1, 4)]);
assert.deepStrictEqual(editor.getSelections(), [new Selection(1, 4, 1, 4)]);
// press Ctrl+U
cursorUndoAction.run(null!, editor, {});
assert.deepEqual(editor.getSelections(), [new Selection(1, 5, 1, 5)]);
assert.deepStrictEqual(editor.getSelections(), [new Selection(1, 5, 1, 5)]);
});
});
@@ -52,12 +52,12 @@ suite('FindController', () => {
// type hello
editor.trigger('test', Handler.Type, { text: 'hell' });
editor.trigger('test', Handler.Type, { text: 'o' });
assert.deepEqual(editor.getValue(), 'hello');
assert.deepEqual(editor.getSelections(), [new Selection(1, 6, 1, 6)]);
assert.deepStrictEqual(editor.getValue(), 'hello');
assert.deepStrictEqual(editor.getSelections(), [new Selection(1, 6, 1, 6)]);
// press Ctrl+U
cursorUndoAction.run(null!, editor, {});
assert.deepEqual(editor.getSelections(), [new Selection(1, 6, 1, 6)]);
assert.deepStrictEqual(editor.getSelections(), [new Selection(1, 6, 1, 6)]);
});
});
});

View File

@@ -20,6 +20,7 @@ import { IModelDeltaDecoration } from 'vs/editor/common/model';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents';
function hasTriggerModifier(e: IKeyboardEvent | IMouseEvent): boolean {
if (isMacintosh) {
@@ -176,8 +177,8 @@ export class DragAndDropController extends Disposable implements IEditorContribu
}
});
}
// Use `mouse` as the source instead of `api`.
(<CodeEditorWidget>this._editor).setSelections(newSelections || [], 'mouse');
// Use `mouse` as the source instead of `api` and setting the reason to explicit (to behave like any other mouse operation).
(<CodeEditorWidget>this._editor).setSelections(newSelections || [], 'mouse', CursorChangeReason.Explicit);
} else if (!this._dragSelection.containsPosition(newCursorPosition) ||
(
(

View File

@@ -4,62 +4,20 @@
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { Range } from 'vs/editor/common/core/range';
import { ITextModel } from 'vs/editor/common/model';
import { DocumentSymbol } from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { OutlineModel, OutlineElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
import { OutlineModel } from 'vs/editor/contrib/documentSymbols/outlineModel';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { assertType } from 'vs/base/common/types';
import { Iterable } from 'vs/base/common/iterator';
export async function getDocumentSymbols(document: ITextModel, flat: boolean, token: CancellationToken): Promise<DocumentSymbol[]> {
const model = await OutlineModel.create(document, token);
const roots: DocumentSymbol[] = [];
for (const child of model.children.values()) {
if (child instanceof OutlineElement) {
roots.push(child.symbol);
} else {
roots.push(...Iterable.map(child.children.values(), child => child.symbol));
}
}
let flatEntries: DocumentSymbol[] = [];
if (token.isCancellationRequested) {
return flatEntries;
}
if (flat) {
flatten(flatEntries, roots, '');
} else {
flatEntries = roots;
}
return flatEntries.sort(compareEntriesUsingStart);
}
function compareEntriesUsingStart(a: DocumentSymbol, b: DocumentSymbol): number {
return Range.compareRangesUsingStarts(a.range, b.range);
}
function flatten(bucket: DocumentSymbol[], entries: DocumentSymbol[], overrideContainerLabel: string): void {
for (let entry of entries) {
bucket.push({
kind: entry.kind,
tags: entry.tags,
name: entry.name,
detail: entry.detail,
containerName: entry.containerName || overrideContainerLabel,
range: entry.range,
selectionRange: entry.selectionRange,
children: undefined, // we flatten it...
});
if (entry.children) {
flatten(bucket, entry.children, entry.name);
}
}
return flat
? model.asListOfDocumentSymbols()
: model.getTopLevelSymbols();
}
CommandsRegistry.registerCommand('_executeDocumentSymbolProvider', async function (accessor, ...args) {

View File

@@ -1,46 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-list .monaco-list-row.focused.selected .outline-element .monaco-highlighted-label,
.monaco-list .monaco-list-row.focused.selected .outline-element-decoration {
/* make sure selection color wins when a label is being selected */
color: inherit !important;
}
.monaco-list .outline-element {
display: flex;
flex: 1;
flex-flow: row nowrap;
align-items: center;
}
.monaco-list .outline-element .monaco-highlighted-label {
color: var(--outline-element-color);
}
.monaco-list .outline-element .monaco-icon-label-container .monaco-highlighted-label,
.monaco-list .outline-element .monaco-icon-label-container .label-description {
white-space: nowrap;
}
.monaco-list .outline-element .outline-element-decoration {
opacity: 0.75;
font-size: 90%;
font-weight: 600;
padding: 0 12px 0 5px;
margin-left: auto;
text-align: center;
color: var(--outline-element-color);
}
.monaco-list .outline-element .outline-element-decoration.bubble {
font-family: codicon;
font-size: 14px;
opacity: 0.4;
}
.monaco-list .outline-element .outline-element-icon {
margin-right: 4px;
}

View File

@@ -1,9 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-icon-label.deprecated {
text-decoration: line-through;
opacity: 0.66;
}

View File

@@ -1,18 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
export const OutlineViewId = 'outline';
export const OutlineViewFiltered = new RawContextKey('outlineFiltered', false);
export const OutlineViewFocused = new RawContextKey('outlineFocused', false);
export const enum OutlineConfigKeys {
'icons' = 'outline.icons',
'problemsEnabled' = 'outline.problems.enabled',
'problemsColors' = 'outline.problems.colors',
'problemsBadges' = 'outline.problems.badges'
}

View File

@@ -14,8 +14,8 @@ import { ITextModel } from 'vs/editor/common/model';
import { DocumentSymbol, DocumentSymbolProvider, DocumentSymbolProviderRegistry } from 'vs/editor/common/modes';
import { MarkerSeverity } from 'vs/platform/markers/common/markers';
import { Iterable } from 'vs/base/common/iterator';
import { URI } from 'vs/base/common/uri';
import { LanguageFeatureRequestDelays } from 'vs/editor/common/modes/languageFeatureRegistry';
import { URI } from 'vs/base/common/uri';
export abstract class TreeElement {
@@ -204,8 +204,6 @@ export class OutlineGroup extends TreeElement {
}
}
export class OutlineModel extends TreeElement {
private static readonly _requestDurations = new LanguageFeatureRequestDelays(DocumentSymbolProviderRegistry, 350);
@@ -445,4 +443,43 @@ export class OutlineModel extends TreeElement {
group.updateMarker(marker.slice(0));
}
}
getTopLevelSymbols(): DocumentSymbol[] {
const roots: DocumentSymbol[] = [];
for (const child of this.children.values()) {
if (child instanceof OutlineElement) {
roots.push(child.symbol);
} else {
roots.push(...Iterable.map(child.children.values(), child => child.symbol));
}
}
return roots.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range));
}
asListOfDocumentSymbols(): DocumentSymbol[] {
const roots = this.getTopLevelSymbols();
const bucket: DocumentSymbol[] = [];
OutlineModel._flattenDocumentSymbols(bucket, roots, '');
return bucket.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range));
}
private static _flattenDocumentSymbols(bucket: DocumentSymbol[], entries: DocumentSymbol[], overrideContainerLabel: string): void {
for (const entry of entries) {
bucket.push({
kind: entry.kind,
tags: entry.tags,
name: entry.name,
detail: entry.detail,
containerName: entry.containerName || overrideContainerLabel,
range: entry.range,
selectionRange: entry.selectionRange,
children: undefined, // we flatten it...
});
// Recurse over children
if (entry.children) {
OutlineModel._flattenDocumentSymbols(bucket, entry.children, entry.name);
}
}
}
}

View File

@@ -1,725 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as dom from 'vs/base/browser/dom';
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
import { IIdentityProvider, IKeyboardNavigationLabelProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { IDataSource, ITreeNode, ITreeRenderer, ITreeSorter, ITreeFilter } from 'vs/base/browser/ui/tree/tree';
import { createMatches, FuzzyScore } from 'vs/base/common/filters';
import 'vs/css!./media/outlineTree';
import 'vs/css!./media/symbol-icons';
import { Range } from 'vs/editor/common/core/range';
import { SymbolKind, SymbolKinds, SymbolTag } from 'vs/editor/common/modes';
import { OutlineElement, OutlineGroup, OutlineModel } from 'vs/editor/contrib/documentSymbols/outlineModel';
import { localize } from 'vs/nls';
import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { OutlineConfigKeys } from 'vs/editor/contrib/documentSymbols/outline';
import { MarkerSeverity } from 'vs/platform/markers/common/markers';
import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { registerColor, listErrorForeground, listWarningForeground, foreground } from 'vs/platform/theme/common/colorRegistry';
import { IdleValue } from 'vs/base/common/async';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService';
import { URI } from 'vs/base/common/uri';
import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
import { Iterable } from 'vs/base/common/iterator';
import { Codicon } from 'vs/base/common/codicons';
export type OutlineItem = OutlineGroup | OutlineElement;
export class OutlineNavigationLabelProvider implements IKeyboardNavigationLabelProvider<OutlineItem> {
getKeyboardNavigationLabel(element: OutlineItem): { toString(): string; } {
if (element instanceof OutlineGroup) {
return element.label;
} else {
return element.symbol.name;
}
}
}
export class OutlineAccessibilityProvider implements IListAccessibilityProvider<OutlineItem> {
constructor(private readonly ariaLabel: string) { }
getWidgetAriaLabel(): string {
return this.ariaLabel;
}
getAriaLabel(element: OutlineItem): string | null {
if (element instanceof OutlineGroup) {
return element.label;
} else {
return element.symbol.name;
}
}
}
export class OutlineIdentityProvider implements IIdentityProvider<OutlineItem> {
getId(element: OutlineItem): { toString(): string; } {
return element.id;
}
}
export class OutlineGroupTemplate {
static readonly id = 'OutlineGroupTemplate';
constructor(
readonly labelContainer: HTMLElement,
readonly label: HighlightedLabel,
) { }
}
export class OutlineElementTemplate {
static readonly id = 'OutlineElementTemplate';
constructor(
readonly container: HTMLElement,
readonly iconLabel: IconLabel,
readonly iconClass: HTMLElement,
readonly decoration: HTMLElement,
) { }
}
export class OutlineVirtualDelegate implements IListVirtualDelegate<OutlineItem> {
getHeight(_element: OutlineItem): number {
return 22;
}
getTemplateId(element: OutlineItem): string {
if (element instanceof OutlineGroup) {
return OutlineGroupTemplate.id;
} else {
return OutlineElementTemplate.id;
}
}
}
export class OutlineGroupRenderer implements ITreeRenderer<OutlineGroup, FuzzyScore, OutlineGroupTemplate> {
readonly templateId: string = OutlineGroupTemplate.id;
renderTemplate(container: HTMLElement): OutlineGroupTemplate {
const labelContainer = dom.$('.outline-element-label');
container.classList.add('outline-element');
dom.append(container, labelContainer);
return new OutlineGroupTemplate(labelContainer, new HighlightedLabel(labelContainer, true));
}
renderElement(node: ITreeNode<OutlineGroup, FuzzyScore>, index: number, template: OutlineGroupTemplate): void {
template.label.set(
node.element.label,
createMatches(node.filterData)
);
}
disposeTemplate(_template: OutlineGroupTemplate): void {
// nothing
}
}
export class OutlineElementRenderer implements ITreeRenderer<OutlineElement, FuzzyScore, OutlineElementTemplate> {
readonly templateId: string = OutlineElementTemplate.id;
constructor(
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IThemeService private readonly _themeService: IThemeService,
) { }
renderTemplate(container: HTMLElement): OutlineElementTemplate {
container.classList.add('outline-element');
const iconLabel = new IconLabel(container, { supportHighlights: true });
const iconClass = dom.$('.outline-element-icon');
const decoration = dom.$('.outline-element-decoration');
container.prepend(iconClass);
container.appendChild(decoration);
return new OutlineElementTemplate(container, iconLabel, iconClass, decoration);
}
renderElement(node: ITreeNode<OutlineElement, FuzzyScore>, index: number, template: OutlineElementTemplate): void {
const { element } = node;
const options = {
matches: createMatches(node.filterData),
labelEscapeNewLines: true,
extraClasses: <string[]>[],
title: localize('title.template', "{0} ({1})", element.symbol.name, OutlineElementRenderer._symbolKindNames[element.symbol.kind])
};
if (this._configurationService.getValue(OutlineConfigKeys.icons)) {
// add styles for the icons
template.iconClass.className = '';
template.iconClass.classList.add(`outline-element-icon`, ...SymbolKinds.toCssClassName(element.symbol.kind, true).split(' '));
}
if (element.symbol.tags.indexOf(SymbolTag.Deprecated) >= 0) {
options.extraClasses.push(`deprecated`);
options.matches = [];
}
template.iconLabel.setLabel(element.symbol.name, element.symbol.detail, options);
this._renderMarkerInfo(element, template);
}
private _renderMarkerInfo(element: OutlineElement, template: OutlineElementTemplate): void {
if (!element.marker) {
dom.hide(template.decoration);
template.container.style.removeProperty('--outline-element-color');
return;
}
const { count, topSev } = element.marker;
const color = this._themeService.getColorTheme().getColor(topSev === MarkerSeverity.Error ? listErrorForeground : listWarningForeground);
const cssColor = color ? color.toString() : 'inherit';
// color of the label
if (this._configurationService.getValue(OutlineConfigKeys.problemsColors)) {
template.container.style.setProperty('--outline-element-color', cssColor);
} else {
template.container.style.removeProperty('--outline-element-color');
}
// badge with color/rollup
if (!this._configurationService.getValue(OutlineConfigKeys.problemsBadges)) {
dom.hide(template.decoration);
} else if (count > 0) {
dom.show(template.decoration);
template.decoration.classList.remove('bubble');
template.decoration.innerText = count < 10 ? count.toString() : '+9';
template.decoration.title = count === 1 ? localize('1.problem', "1 problem in this element") : localize('N.problem', "{0} problems in this element", count);
template.decoration.style.setProperty('--outline-element-color', cssColor);
} else {
dom.show(template.decoration);
template.decoration.classList.add('bubble');
template.decoration.innerText = '\uea71';
template.decoration.title = localize('deep.problem', "Contains elements with problems");
template.decoration.style.setProperty('--outline-element-color', cssColor);
}
}
private static _symbolKindNames: { [symbol: number]: string } = {
[SymbolKind.Array]: localize('Array', "array"),
[SymbolKind.Boolean]: localize('Boolean', "boolean"),
[SymbolKind.Class]: localize('Class', "class"),
[SymbolKind.Constant]: localize('Constant', "constant"),
[SymbolKind.Constructor]: localize('Constructor', "constructor"),
[SymbolKind.Enum]: localize('Enum', "enumeration"),
[SymbolKind.EnumMember]: localize('EnumMember', "enumeration member"),
[SymbolKind.Event]: localize('Event', "event"),
[SymbolKind.Field]: localize('Field', "field"),
[SymbolKind.File]: localize('File', "file"),
[SymbolKind.Function]: localize('Function', "function"),
[SymbolKind.Interface]: localize('Interface', "interface"),
[SymbolKind.Key]: localize('Key', "key"),
[SymbolKind.Method]: localize('Method', "method"),
[SymbolKind.Module]: localize('Module', "module"),
[SymbolKind.Namespace]: localize('Namespace', "namespace"),
[SymbolKind.Null]: localize('Null', "null"),
[SymbolKind.Number]: localize('Number', "number"),
[SymbolKind.Object]: localize('Object', "object"),
[SymbolKind.Operator]: localize('Operator', "operator"),
[SymbolKind.Package]: localize('Package', "package"),
[SymbolKind.Property]: localize('Property', "property"),
[SymbolKind.String]: localize('String', "string"),
[SymbolKind.Struct]: localize('Struct', "struct"),
[SymbolKind.TypeParameter]: localize('TypeParameter', "type parameter"),
[SymbolKind.Variable]: localize('Variable', "variable"),
};
disposeTemplate(_template: OutlineElementTemplate): void {
_template.iconLabel.dispose();
}
}
export const enum OutlineSortOrder {
ByPosition,
ByName,
ByKind
}
export class OutlineFilter implements ITreeFilter<OutlineItem> {
static readonly configNameToKind = Object.freeze({
['showFiles']: SymbolKind.File,
['showModules']: SymbolKind.Module,
['showNamespaces']: SymbolKind.Namespace,
['showPackages']: SymbolKind.Package,
['showClasses']: SymbolKind.Class,
['showMethods']: SymbolKind.Method,
['showProperties']: SymbolKind.Property,
['showFields']: SymbolKind.Field,
['showConstructors']: SymbolKind.Constructor,
['showEnums']: SymbolKind.Enum,
['showInterfaces']: SymbolKind.Interface,
['showFunctions']: SymbolKind.Function,
['showVariables']: SymbolKind.Variable,
['showConstants']: SymbolKind.Constant,
['showStrings']: SymbolKind.String,
['showNumbers']: SymbolKind.Number,
['showBooleans']: SymbolKind.Boolean,
['showArrays']: SymbolKind.Array,
['showObjects']: SymbolKind.Object,
['showKeys']: SymbolKind.Key,
['showNull']: SymbolKind.Null,
['showEnumMembers']: SymbolKind.EnumMember,
['showStructs']: SymbolKind.Struct,
['showEvents']: SymbolKind.Event,
['showOperators']: SymbolKind.Operator,
['showTypeParameters']: SymbolKind.TypeParameter,
});
static readonly kindToConfigName = Object.freeze({
[SymbolKind.File]: 'showFiles',
[SymbolKind.Module]: 'showModules',
[SymbolKind.Namespace]: 'showNamespaces',
[SymbolKind.Package]: 'showPackages',
[SymbolKind.Class]: 'showClasses',
[SymbolKind.Method]: 'showMethods',
[SymbolKind.Property]: 'showProperties',
[SymbolKind.Field]: 'showFields',
[SymbolKind.Constructor]: 'showConstructors',
[SymbolKind.Enum]: 'showEnums',
[SymbolKind.Interface]: 'showInterfaces',
[SymbolKind.Function]: 'showFunctions',
[SymbolKind.Variable]: 'showVariables',
[SymbolKind.Constant]: 'showConstants',
[SymbolKind.String]: 'showStrings',
[SymbolKind.Number]: 'showNumbers',
[SymbolKind.Boolean]: 'showBooleans',
[SymbolKind.Array]: 'showArrays',
[SymbolKind.Object]: 'showObjects',
[SymbolKind.Key]: 'showKeys',
[SymbolKind.Null]: 'showNull',
[SymbolKind.EnumMember]: 'showEnumMembers',
[SymbolKind.Struct]: 'showStructs',
[SymbolKind.Event]: 'showEvents',
[SymbolKind.Operator]: 'showOperators',
[SymbolKind.TypeParameter]: 'showTypeParameters',
});
constructor(
private readonly _prefix: string,
@ITextResourceConfigurationService private readonly _textResourceConfigService: ITextResourceConfigurationService,
) { }
filter(element: OutlineItem): boolean {
const outline = OutlineModel.get(element);
let uri: URI | undefined;
if (outline) {
uri = outline.uri;
}
if (!(element instanceof OutlineElement)) {
return true;
}
const configName = OutlineFilter.kindToConfigName[element.symbol.kind];
const configKey = `${this._prefix}.${configName}`;
return this._textResourceConfigService.getValue(uri, configKey);
}
}
export class OutlineItemComparator implements ITreeSorter<OutlineItem> {
private readonly _collator = new IdleValue<Intl.Collator>(() => new Intl.Collator(undefined, { numeric: true }));
constructor(
public type: OutlineSortOrder = OutlineSortOrder.ByPosition
) { }
compare(a: OutlineItem, b: OutlineItem): number {
if (a instanceof OutlineGroup && b instanceof OutlineGroup) {
return a.order - b.order;
} else if (a instanceof OutlineElement && b instanceof OutlineElement) {
if (this.type === OutlineSortOrder.ByKind) {
return a.symbol.kind - b.symbol.kind || this._collator.value.compare(a.symbol.name, b.symbol.name);
} else if (this.type === OutlineSortOrder.ByName) {
return this._collator.value.compare(a.symbol.name, b.symbol.name) || Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range);
} else if (this.type === OutlineSortOrder.ByPosition) {
return Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range) || this._collator.value.compare(a.symbol.name, b.symbol.name);
}
}
return 0;
}
}
export class OutlineDataSource implements IDataSource<OutlineModel, OutlineItem> {
getChildren(element: undefined | OutlineModel | OutlineGroup | OutlineElement) {
if (!element) {
return Iterable.empty();
}
return element.children.values();
}
}
export const SYMBOL_ICON_ARRAY_FOREGROUND = registerColor('symbolIcon.arrayForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.arrayForeground', 'The foreground color for array symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_BOOLEAN_FOREGROUND = registerColor('symbolIcon.booleanForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.booleanForeground', 'The foreground color for boolean symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_CLASS_FOREGROUND = registerColor('symbolIcon.classForeground', {
dark: '#EE9D28',
light: '#D67E00',
hc: '#EE9D28'
}, localize('symbolIcon.classForeground', 'The foreground color for class symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_COLOR_FOREGROUND = registerColor('symbolIcon.colorForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.colorForeground', 'The foreground color for color symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_CONSTANT_FOREGROUND = registerColor('symbolIcon.constantForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.constantForeground', 'The foreground color for constant symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_CONSTRUCTOR_FOREGROUND = registerColor('symbolIcon.constructorForeground', {
dark: '#B180D7',
light: '#652D90',
hc: '#B180D7'
}, localize('symbolIcon.constructorForeground', 'The foreground color for constructor symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_ENUMERATOR_FOREGROUND = registerColor('symbolIcon.enumeratorForeground', {
dark: '#EE9D28',
light: '#D67E00',
hc: '#EE9D28'
}, localize('symbolIcon.enumeratorForeground', 'The foreground color for enumerator symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_ENUMERATOR_MEMBER_FOREGROUND = registerColor('symbolIcon.enumeratorMemberForeground', {
dark: '#75BEFF',
light: '#007ACC',
hc: '#75BEFF'
}, localize('symbolIcon.enumeratorMemberForeground', 'The foreground color for enumerator member symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_EVENT_FOREGROUND = registerColor('symbolIcon.eventForeground', {
dark: '#EE9D28',
light: '#D67E00',
hc: '#EE9D28'
}, localize('symbolIcon.eventForeground', 'The foreground color for event symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_FIELD_FOREGROUND = registerColor('symbolIcon.fieldForeground', {
dark: '#75BEFF',
light: '#007ACC',
hc: '#75BEFF'
}, localize('symbolIcon.fieldForeground', 'The foreground color for field symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_FILE_FOREGROUND = registerColor('symbolIcon.fileForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.fileForeground', 'The foreground color for file symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_FOLDER_FOREGROUND = registerColor('symbolIcon.folderForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.folderForeground', 'The foreground color for folder symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_FUNCTION_FOREGROUND = registerColor('symbolIcon.functionForeground', {
dark: '#B180D7',
light: '#652D90',
hc: '#B180D7'
}, localize('symbolIcon.functionForeground', 'The foreground color for function symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_INTERFACE_FOREGROUND = registerColor('symbolIcon.interfaceForeground', {
dark: '#75BEFF',
light: '#007ACC',
hc: '#75BEFF'
}, localize('symbolIcon.interfaceForeground', 'The foreground color for interface symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_KEY_FOREGROUND = registerColor('symbolIcon.keyForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.keyForeground', 'The foreground color for key symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_KEYWORD_FOREGROUND = registerColor('symbolIcon.keywordForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.keywordForeground', 'The foreground color for keyword symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_METHOD_FOREGROUND = registerColor('symbolIcon.methodForeground', {
dark: '#B180D7',
light: '#652D90',
hc: '#B180D7'
}, localize('symbolIcon.methodForeground', 'The foreground color for method symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_MODULE_FOREGROUND = registerColor('symbolIcon.moduleForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.moduleForeground', 'The foreground color for module symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_NAMESPACE_FOREGROUND = registerColor('symbolIcon.namespaceForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.namespaceForeground', 'The foreground color for namespace symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_NULL_FOREGROUND = registerColor('symbolIcon.nullForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.nullForeground', 'The foreground color for null symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_NUMBER_FOREGROUND = registerColor('symbolIcon.numberForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.numberForeground', 'The foreground color for number symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_OBJECT_FOREGROUND = registerColor('symbolIcon.objectForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.objectForeground', 'The foreground color for object symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_OPERATOR_FOREGROUND = registerColor('symbolIcon.operatorForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.operatorForeground', 'The foreground color for operator symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_PACKAGE_FOREGROUND = registerColor('symbolIcon.packageForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.packageForeground', 'The foreground color for package symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_PROPERTY_FOREGROUND = registerColor('symbolIcon.propertyForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.propertyForeground', 'The foreground color for property symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_REFERENCE_FOREGROUND = registerColor('symbolIcon.referenceForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.referenceForeground', 'The foreground color for reference symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_SNIPPET_FOREGROUND = registerColor('symbolIcon.snippetForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.snippetForeground', 'The foreground color for snippet symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_STRING_FOREGROUND = registerColor('symbolIcon.stringForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.stringForeground', 'The foreground color for string symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_STRUCT_FOREGROUND = registerColor('symbolIcon.structForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.structForeground', 'The foreground color for struct symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_TEXT_FOREGROUND = registerColor('symbolIcon.textForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.textForeground', 'The foreground color for text symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_TYPEPARAMETER_FOREGROUND = registerColor('symbolIcon.typeParameterForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.typeParameterForeground', 'The foreground color for type parameter symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_UNIT_FOREGROUND = registerColor('symbolIcon.unitForeground', {
dark: foreground,
light: foreground,
hc: foreground
}, localize('symbolIcon.unitForeground', 'The foreground color for unit symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
export const SYMBOL_ICON_VARIABLE_FOREGROUND = registerColor('symbolIcon.variableForeground', {
dark: '#75BEFF',
light: '#007ACC',
hc: '#75BEFF'
}, localize('symbolIcon.variableForeground', 'The foreground color for variable symbols. These symbols appear in the outline, breadcrumb, and suggest widget.'));
registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {
const symbolIconArrayColor = theme.getColor(SYMBOL_ICON_ARRAY_FOREGROUND);
if (symbolIconArrayColor) {
collector.addRule(`${Codicon.symbolArray.cssSelector} { color: ${symbolIconArrayColor}; }`);
}
const symbolIconBooleanColor = theme.getColor(SYMBOL_ICON_BOOLEAN_FOREGROUND);
if (symbolIconBooleanColor) {
collector.addRule(`${Codicon.symbolBoolean.cssSelector} { color: ${symbolIconBooleanColor}; }`);
}
const symbolIconClassColor = theme.getColor(SYMBOL_ICON_CLASS_FOREGROUND);
if (symbolIconClassColor) {
collector.addRule(`${Codicon.symbolClass.cssSelector} { color: ${symbolIconClassColor}; }`);
}
const symbolIconMethodColor = theme.getColor(SYMBOL_ICON_METHOD_FOREGROUND);
if (symbolIconMethodColor) {
collector.addRule(`${Codicon.symbolMethod.cssSelector} { color: ${symbolIconMethodColor}; }`);
}
const symbolIconColorColor = theme.getColor(SYMBOL_ICON_COLOR_FOREGROUND);
if (symbolIconColorColor) {
collector.addRule(`${Codicon.symbolColor.cssSelector} { color: ${symbolIconColorColor}; }`);
}
const symbolIconConstantColor = theme.getColor(SYMBOL_ICON_CONSTANT_FOREGROUND);
if (symbolIconConstantColor) {
collector.addRule(`${Codicon.symbolConstant.cssSelector} { color: ${symbolIconConstantColor}; }`);
}
const symbolIconConstructorColor = theme.getColor(SYMBOL_ICON_CONSTRUCTOR_FOREGROUND);
if (symbolIconConstructorColor) {
collector.addRule(`${Codicon.symbolConstructor.cssSelector} { color: ${symbolIconConstructorColor}; }`);
}
const symbolIconEnumeratorColor = theme.getColor(SYMBOL_ICON_ENUMERATOR_FOREGROUND);
if (symbolIconEnumeratorColor) {
collector.addRule(`
${Codicon.symbolValue.cssSelector},${Codicon.symbolEnum.cssSelector} { color: ${symbolIconEnumeratorColor}; }`);
}
const symbolIconEnumeratorMemberColor = theme.getColor(SYMBOL_ICON_ENUMERATOR_MEMBER_FOREGROUND);
if (symbolIconEnumeratorMemberColor) {
collector.addRule(`${Codicon.symbolEnumMember.cssSelector} { color: ${symbolIconEnumeratorMemberColor}; }`);
}
const symbolIconEventColor = theme.getColor(SYMBOL_ICON_EVENT_FOREGROUND);
if (symbolIconEventColor) {
collector.addRule(`${Codicon.symbolEvent.cssSelector} { color: ${symbolIconEventColor}; }`);
}
const symbolIconFieldColor = theme.getColor(SYMBOL_ICON_FIELD_FOREGROUND);
if (symbolIconFieldColor) {
collector.addRule(`${Codicon.symbolField.cssSelector} { color: ${symbolIconFieldColor}; }`);
}
const symbolIconFileColor = theme.getColor(SYMBOL_ICON_FILE_FOREGROUND);
if (symbolIconFileColor) {
collector.addRule(`${Codicon.symbolFile.cssSelector} { color: ${symbolIconFileColor}; }`);
}
const symbolIconFolderColor = theme.getColor(SYMBOL_ICON_FOLDER_FOREGROUND);
if (symbolIconFolderColor) {
collector.addRule(`${Codicon.symbolFolder.cssSelector} { color: ${symbolIconFolderColor}; }`);
}
const symbolIconFunctionColor = theme.getColor(SYMBOL_ICON_FUNCTION_FOREGROUND);
if (symbolIconFunctionColor) {
collector.addRule(`${Codicon.symbolFunction.cssSelector} { color: ${symbolIconFunctionColor}; }`);
}
const symbolIconInterfaceColor = theme.getColor(SYMBOL_ICON_INTERFACE_FOREGROUND);
if (symbolIconInterfaceColor) {
collector.addRule(`${Codicon.symbolInterface.cssSelector} { color: ${symbolIconInterfaceColor}; }`);
}
const symbolIconKeyColor = theme.getColor(SYMBOL_ICON_KEY_FOREGROUND);
if (symbolIconKeyColor) {
collector.addRule(`${Codicon.symbolKey.cssSelector} { color: ${symbolIconKeyColor}; }`);
}
const symbolIconKeywordColor = theme.getColor(SYMBOL_ICON_KEYWORD_FOREGROUND);
if (symbolIconKeywordColor) {
collector.addRule(`${Codicon.symbolKeyword.cssSelector} { color: ${symbolIconKeywordColor}; }`);
}
const symbolIconModuleColor = theme.getColor(SYMBOL_ICON_MODULE_FOREGROUND);
if (symbolIconModuleColor) {
collector.addRule(`${Codicon.symbolModule.cssSelector} { color: ${symbolIconModuleColor}; }`);
}
const outlineNamespaceColor = theme.getColor(SYMBOL_ICON_NAMESPACE_FOREGROUND);
if (outlineNamespaceColor) {
collector.addRule(`${Codicon.symbolNamespace.cssSelector} { color: ${outlineNamespaceColor}; }`);
}
const symbolIconNullColor = theme.getColor(SYMBOL_ICON_NULL_FOREGROUND);
if (symbolIconNullColor) {
collector.addRule(`${Codicon.symbolNull.cssSelector} { color: ${symbolIconNullColor}; }`);
}
const symbolIconNumberColor = theme.getColor(SYMBOL_ICON_NUMBER_FOREGROUND);
if (symbolIconNumberColor) {
collector.addRule(`${Codicon.symbolNumber.cssSelector} { color: ${symbolIconNumberColor}; }`);
}
const symbolIconObjectColor = theme.getColor(SYMBOL_ICON_OBJECT_FOREGROUND);
if (symbolIconObjectColor) {
collector.addRule(`${Codicon.symbolObject.cssSelector} { color: ${symbolIconObjectColor}; }`);
}
const symbolIconOperatorColor = theme.getColor(SYMBOL_ICON_OPERATOR_FOREGROUND);
if (symbolIconOperatorColor) {
collector.addRule(`${Codicon.symbolOperator.cssSelector} { color: ${symbolIconOperatorColor}; }`);
}
const symbolIconPackageColor = theme.getColor(SYMBOL_ICON_PACKAGE_FOREGROUND);
if (symbolIconPackageColor) {
collector.addRule(`${Codicon.symbolPackage.cssSelector} { color: ${symbolIconPackageColor}; }`);
}
const symbolIconPropertyColor = theme.getColor(SYMBOL_ICON_PROPERTY_FOREGROUND);
if (symbolIconPropertyColor) {
collector.addRule(`${Codicon.symbolProperty.cssSelector} { color: ${symbolIconPropertyColor}; }`);
}
const symbolIconReferenceColor = theme.getColor(SYMBOL_ICON_REFERENCE_FOREGROUND);
if (symbolIconReferenceColor) {
collector.addRule(`${Codicon.symbolReference.cssSelector} { color: ${symbolIconReferenceColor}; }`);
}
const symbolIconSnippetColor = theme.getColor(SYMBOL_ICON_SNIPPET_FOREGROUND);
if (symbolIconSnippetColor) {
collector.addRule(`${Codicon.symbolSnippet.cssSelector} { color: ${symbolIconSnippetColor}; }`);
}
const symbolIconStringColor = theme.getColor(SYMBOL_ICON_STRING_FOREGROUND);
if (symbolIconStringColor) {
collector.addRule(`${Codicon.symbolString.cssSelector} { color: ${symbolIconStringColor}; }`);
}
const symbolIconStructColor = theme.getColor(SYMBOL_ICON_STRUCT_FOREGROUND);
if (symbolIconStructColor) {
collector.addRule(`${Codicon.symbolStruct.cssSelector} { color: ${symbolIconStructColor}; }`);
}
const symbolIconTextColor = theme.getColor(SYMBOL_ICON_TEXT_FOREGROUND);
if (symbolIconTextColor) {
collector.addRule(`${Codicon.symbolText.cssSelector} { color: ${symbolIconTextColor}; }`);
}
const symbolIconTypeParameterColor = theme.getColor(SYMBOL_ICON_TYPEPARAMETER_FOREGROUND);
if (symbolIconTypeParameterColor) {
collector.addRule(`${Codicon.symbolTypeParameter.cssSelector} { color: ${symbolIconTypeParameterColor}; }`);
}
const symbolIconUnitColor = theme.getColor(SYMBOL_ICON_UNIT_FOREGROUND);
if (symbolIconUnitColor) {
collector.addRule(`${Codicon.symbolUnit.cssSelector} { color: ${symbolIconUnitColor}; }`);
}
const symbolIconVariableColor = theme.getColor(SYMBOL_ICON_VARIABLE_FOREGROUND);
if (symbolIconVariableColor) {
collector.addRule(`${Codicon.symbolVariable.cssSelector} { color: ${symbolIconVariableColor}; }`);
}
});

View File

@@ -60,14 +60,14 @@
}
.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .mirror {
.monaco-editor .find-widget > .replace-part .monaco-inputbox > .ibwrapper > .mirror {
padding-right: 22px;
}
.monaco-editor .find-widget > .find-part .monaco-inputbox > .wrapper > .input,
.monaco-editor .find-widget > .find-part .monaco-inputbox > .wrapper > .mirror,
.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .input,
.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .mirror {
.monaco-editor .find-widget > .find-part .monaco-inputbox > .ibwrapper > .input,
.monaco-editor .find-widget > .find-part .monaco-inputbox > .ibwrapper > .mirror,
.monaco-editor .find-widget > .replace-part .monaco-inputbox > .ibwrapper > .input,
.monaco-editor .find-widget > .replace-part .monaco-inputbox > .ibwrapper > .mirror {
padding-top: 2px;
padding-bottom: 2px;
}

View File

@@ -1044,7 +1044,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL
// Toggle selection button
this._toggleSelectionFind = this._register(new Checkbox({
icon: ThemeIcon.asCSSIcon(findSelectionIcon),
icon: findSelectionIcon,
title: NLS_TOGGLE_SELECTION_FIND_TITLE + this._keybindingLabelFor(FIND_IDS.ToggleSearchScopeCommand),
isChecked: false
}));

View File

@@ -20,17 +20,17 @@ suite('Find', () => {
// The cursor is at the very top, of the file, at the first ABC
let searchStringAtTop = getSelectionSearchString(editor);
assert.equal(searchStringAtTop, 'ABC');
assert.strictEqual(searchStringAtTop, 'ABC');
// Move cursor to the end of ABC
editor.setPosition(new Position(1, 3));
let searchStringAfterABC = getSelectionSearchString(editor);
assert.equal(searchStringAfterABC, 'ABC');
assert.strictEqual(searchStringAfterABC, 'ABC');
// Move cursor to DEF
editor.setPosition(new Position(1, 5));
let searchStringInsideDEF = getSelectionSearchString(editor);
assert.equal(searchStringInsideDEF, 'DEF');
assert.strictEqual(searchStringInsideDEF, 'DEF');
});
});
@@ -44,17 +44,17 @@ suite('Find', () => {
// Select A of ABC
editor.setSelection(new Range(1, 1, 1, 2));
let searchStringSelectionA = getSelectionSearchString(editor);
assert.equal(searchStringSelectionA, 'A');
assert.strictEqual(searchStringSelectionA, 'A');
// Select BC of ABC
editor.setSelection(new Range(1, 2, 1, 4));
let searchStringSelectionBC = getSelectionSearchString(editor);
assert.equal(searchStringSelectionBC, 'BC');
assert.strictEqual(searchStringSelectionBC, 'BC');
// Select BC DE
editor.setSelection(new Range(1, 2, 1, 7));
let searchStringSelectionBCDE = getSelectionSearchString(editor);
assert.equal(searchStringSelectionBCDE, 'BC DE');
assert.strictEqual(searchStringSelectionBCDE, 'BC DE');
});
});
@@ -68,17 +68,17 @@ suite('Find', () => {
// Select first line and newline
editor.setSelection(new Range(1, 1, 2, 1));
let searchStringSelectionWholeLine = getSelectionSearchString(editor);
assert.equal(searchStringSelectionWholeLine, null);
assert.strictEqual(searchStringSelectionWholeLine, null);
// Select first line and chunk of second
editor.setSelection(new Range(1, 1, 2, 4));
let searchStringSelectionTwoLines = getSelectionSearchString(editor);
assert.equal(searchStringSelectionTwoLines, null);
assert.strictEqual(searchStringSelectionTwoLines, null);
// Select end of first line newline and chunk of second
editor.setSelection(new Range(1, 7, 2, 4));
let searchStringSelectionSpanLines = getSelectionSearchString(editor);
assert.equal(searchStringSelectionSpanLines, null);
assert.strictEqual(searchStringSelectionSpanLines, null);
});
});

View File

@@ -102,7 +102,7 @@ suite('FindController', async () => {
// I hit Ctrl+F to show the Find dialog
startFindAction.run(null, editor);
assert.deepEqual(findController.getGlobalBufferTerm(), findController.getState().searchString);
assert.deepStrictEqual(findController.getGlobalBufferTerm(), findController.getState().searchString);
findController.dispose();
});
});
@@ -126,9 +126,9 @@ suite('FindController', async () => {
let nextMatchFindAction = new NextMatchFindAction();
nextMatchFindAction.run(null, editor);
assert.equal(findState.searchString, 'ABC');
assert.strictEqual(findState.searchString, 'ABC');
assert.deepEqual(fromSelection(editor.getSelection()!), [1, 1, 1, 4]);
assert.deepStrictEqual(fromSelection(editor.getSelection()!), [1, 1, 1, 4]);
findController.dispose();
});
@@ -152,7 +152,7 @@ suite('FindController', async () => {
findState.change({ searchString: 'ABC' }, true);
assert.deepEqual(findController.getGlobalBufferTerm(), 'ABC');
assert.deepStrictEqual(findController.getGlobalBufferTerm(), 'ABC');
findController.dispose();
});
@@ -181,14 +181,14 @@ suite('FindController', async () => {
findState.change({ searchString: 'ABC' }, true);
// The first ABC is highlighted.
assert.deepEqual(fromSelection(editor.getSelection()!), [1, 1, 1, 4]);
assert.deepStrictEqual(fromSelection(editor.getSelection()!), [1, 1, 1, 4]);
// I hit Esc to exit the Find dialog.
findController.closeFindWidget();
findController.hasFocus = false;
// The cursor is now at end of the first line, with ABC on that line highlighted.
assert.deepEqual(fromSelection(editor.getSelection()!), [1, 1, 1, 4]);
assert.deepStrictEqual(fromSelection(editor.getSelection()!), [1, 1, 1, 4]);
// I hit delete to remove it and change the text to XYZ.
editor.pushUndoStop();
@@ -201,16 +201,16 @@ suite('FindController', async () => {
// ABC
// XYZ
// ABC
assert.equal(editor.getModel()!.getLineContent(1), 'XYZ');
assert.strictEqual(editor.getModel()!.getLineContent(1), 'XYZ');
// The cursor is at end of the first line.
assert.deepEqual(fromSelection(editor.getSelection()!), [1, 4, 1, 4]);
assert.deepStrictEqual(fromSelection(editor.getSelection()!), [1, 4, 1, 4]);
// I hit F3 to "Find Next" to find the next occurrence of ABC, but instead it searches for XYZ.
await nextMatchFindAction.run(null, editor);
assert.equal(findState.searchString, 'ABC');
assert.equal(findController.hasFocus, false);
assert.strictEqual(findState.searchString, 'ABC');
assert.strictEqual(findController.hasFocus, false);
findController.dispose();
});
@@ -230,10 +230,10 @@ suite('FindController', async () => {
});
await nextMatchFindAction.run(null, editor);
assert.deepEqual(fromSelection(editor.getSelection()!), [1, 26, 1, 29]);
assert.deepStrictEqual(fromSelection(editor.getSelection()!), [1, 26, 1, 29]);
await nextMatchFindAction.run(null, editor);
assert.deepEqual(fromSelection(editor.getSelection()!), [1, 8, 1, 11]);
assert.deepStrictEqual(fromSelection(editor.getSelection()!), [1, 8, 1, 11]);
findController.dispose();
});
@@ -256,10 +256,10 @@ suite('FindController', async () => {
await startFindAction.run(null, editor);
await nextMatchFindAction.run(null, editor);
assert.deepEqual(fromSelection(editor.getSelection()!), [2, 9, 2, 13]);
assert.deepStrictEqual(fromSelection(editor.getSelection()!), [2, 9, 2, 13]);
await nextMatchFindAction.run(null, editor);
assert.deepEqual(fromSelection(editor.getSelection()!), [1, 9, 1, 13]);
assert.deepStrictEqual(fromSelection(editor.getSelection()!), [1, 9, 1, 13]);
findController.dispose();
});
@@ -288,7 +288,7 @@ suite('FindController', async () => {
await nextMatchFindAction.run(null, editor);
await startFindReplaceAction.run(null, editor);
assert.equal(findController.getState().searchString, testRegexString);
assert.strictEqual(findController.getState().searchString, testRegexString);
findController.dispose();
});
@@ -312,16 +312,16 @@ suite('FindController', async () => {
loop: true
});
assert.equal(findController.getState().searchScope, null);
assert.strictEqual(findController.getState().searchScope, null);
findController.getState().change({
searchScope: [new Range(1, 1, 1, 5)]
}, false);
assert.deepEqual(findController.getState().searchScope, [new Range(1, 1, 1, 5)]);
assert.deepStrictEqual(findController.getState().searchScope, [new Range(1, 1, 1, 5)]);
findController.closeFindWidget();
assert.equal(findController.getState().searchScope, null);
assert.strictEqual(findController.getState().searchScope, null);
});
});
@@ -338,13 +338,13 @@ suite('FindController', async () => {
findController.getState().change({ searchString: '\\b\\s{3}\\b', replaceString: ' ', isRegex: true }, false);
findController.moveToNextMatch();
assert.deepEqual(editor.getSelections()!.map(fromSelection), [
assert.deepStrictEqual(editor.getSelections()!.map(fromSelection), [
[1, 39, 1, 42]
]);
findController.replace();
assert.deepEqual(editor.getValue(), 'HRESULT OnAmbientPropertyChange(DISPID dispid);');
assert.deepStrictEqual(editor.getValue(), 'HRESULT OnAmbientPropertyChange(DISPID dispid);');
findController.dispose();
});
@@ -365,13 +365,13 @@ suite('FindController', async () => {
findController.getState().change({ searchString: '^', replaceString: 'x', isRegex: true }, false);
findController.moveToNextMatch();
assert.deepEqual(editor.getSelections()!.map(fromSelection), [
assert.deepStrictEqual(editor.getSelections()!.map(fromSelection), [
[2, 1, 2, 1]
]);
findController.replace();
assert.deepEqual(editor.getValue(), '\nxline2\nline3');
assert.deepStrictEqual(editor.getValue(), '\nxline2\nline3');
findController.dispose();
});
@@ -396,7 +396,7 @@ suite('FindController', async () => {
// cmd+f3
await nextSelectionMatchFindAction.run(null, editor);
assert.deepEqual(editor.getSelections()!.map(fromSelection), [
assert.deepStrictEqual(editor.getSelections()!.map(fromSelection), [
[3, 1, 3, 9]
]);
@@ -427,7 +427,7 @@ suite('FindController', async () => {
// cmd+f3
await nextSelectionMatchFindAction.run(null, editor);
assert.deepEqual(editor.getSelections()!.map(fromSelection), [
assert.deepStrictEqual(editor.getSelections()!.map(fromSelection), [
[3, 1, 3, 9]
]);
@@ -458,7 +458,7 @@ suite('FindController', async () => {
await startFindWithSelectionAction.run(null, editor);
let findState = findController.getState();
assert.deepEqual(findState.searchString.split(/\r\n|\r|\n/g), ['ABC', 'ABC']);
assert.deepStrictEqual(findState.searchString.split(/\r\n|\r|\n/g), ['ABC', 'ABC']);
editor.setSelection(new Selection(3, 1, 3, 1));
await startFindWithSelectionAction.run(null, editor);
@@ -483,7 +483,7 @@ suite('FindController', async () => {
startFindWithSelectionAction.run(null, editor);
let findState = findController.getState();
assert.deepEqual(findState.searchString, 'ABC');
assert.deepStrictEqual(findState.searchString, 'ABC');
findController.dispose();
});
});
@@ -531,7 +531,7 @@ suite('FindController query options persistence', async () => {
// I type ABC.
findState.change({ searchString: 'ABC' }, true);
// The second ABC is highlighted as matchCase is true.
assert.deepEqual(fromSelection(editor.getSelection()!), [2, 1, 2, 4]);
assert.deepStrictEqual(fromSelection(editor.getSelection()!), [2, 1, 2, 4]);
findController.dispose();
});
@@ -558,7 +558,7 @@ suite('FindController query options persistence', async () => {
// I type AB.
findState.change({ searchString: 'AB' }, true);
// The second AB is highlighted as wholeWord is true.
assert.deepEqual(fromSelection(editor.getSelection()!), [2, 1, 2, 3]);
assert.deepStrictEqual(fromSelection(editor.getSelection()!), [2, 1, 2, 3]);
findController.dispose();
});
@@ -575,7 +575,7 @@ suite('FindController query options persistence', async () => {
// The cursor is at the very top, of the file, at the first ABC
let findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
findController.toggleRegex();
assert.equal(queryState['editor.isRegex'], true);
assert.strictEqual(queryState['editor.isRegex'], true);
findController.dispose();
});
@@ -601,13 +601,13 @@ suite('FindController query options persistence', async () => {
editor.setSelection(new Range(1, 1, 2, 1));
findController.start(findConfig);
assert.deepEqual(findController.getState().searchScope, [new Selection(1, 1, 2, 1)]);
assert.deepStrictEqual(findController.getState().searchScope, [new Selection(1, 1, 2, 1)]);
findController.closeFindWidget();
editor.setSelections([new Selection(1, 1, 2, 1), new Selection(2, 1, 2, 5)]);
findController.start(findConfig);
assert.deepEqual(findController.getState().searchScope, [new Selection(1, 1, 2, 1), new Selection(2, 1, 2, 5)]);
assert.deepStrictEqual(findController.getState().searchScope, [new Selection(1, 1, 2, 1), new Selection(2, 1, 2, 5)]);
});
});
@@ -631,7 +631,7 @@ suite('FindController query options persistence', async () => {
loop: true
});
assert.deepEqual(findController.getState().searchScope, null);
assert.deepStrictEqual(findController.getState().searchScope, null);
});
});
@@ -655,7 +655,7 @@ suite('FindController query options persistence', async () => {
loop: true
});
assert.deepEqual(findController.getState().searchScope, [new Selection(1, 2, 1, 3)]);
assert.deepStrictEqual(findController.getState().searchScope, [new Selection(1, 2, 1, 3)]);
});
});
@@ -680,7 +680,7 @@ suite('FindController query options persistence', async () => {
loop: true
});
assert.deepEqual(findController.getState().searchScope, [new Selection(1, 6, 2, 1)]);
assert.deepStrictEqual(findController.getState().searchScope, [new Selection(1, 6, 2, 1)]);
});
});
});

View File

@@ -81,13 +81,13 @@ suite('FindModel', () => {
}
function assertFindState(editor: ICodeEditor, cursor: number[], highlighted: number[] | null, findDecorations: number[][]): void {
assert.deepEqual(fromRange(editor.getSelection()!), cursor, 'cursor');
assert.deepStrictEqual(fromRange(editor.getSelection()!), cursor, 'cursor');
let expectedState = {
highlighted: highlighted ? [highlighted] : [],
findDecorations: findDecorations
};
assert.deepEqual(_getFindState(editor), expectedState, 'state');
assert.deepStrictEqual(_getFindState(editor), expectedState, 'state');
}
findTest('incremental find from beginning of file', (editor) => {
@@ -245,7 +245,7 @@ suite('FindModel', () => {
findState.change({ searchString: 'hello' }, false);
let findModel = new FindModelBoundToEditorModel(editor, findState);
assert.equal(findState.matchesCount, 5);
assert.strictEqual(findState.matchesCount, 5);
assertFindState(
editor,
[1, 1, 1, 1],
@@ -275,7 +275,7 @@ suite('FindModel', () => {
findState.change({ searchString: 'hello' }, false);
let findModel = new FindModelBoundToEditorModel(editor, findState);
assert.equal(findState.matchesCount, 5);
assert.strictEqual(findState.matchesCount, 5);
assertFindState(
editor,
[1, 1, 1, 1],
@@ -290,7 +290,7 @@ suite('FindModel', () => {
);
findState.change({ searchString: 'helloo' }, false);
assert.equal(findState.matchesCount, 0);
assert.strictEqual(findState.matchesCount, 0);
assertFindState(
editor,
[1, 1, 1, 1],
@@ -1306,7 +1306,7 @@ suite('FindModel', () => {
[8, 14, 8, 19]
]
);
assert.equal(editor.getModel()!.getLineContent(6), ' cout << "hello world, Hello!" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(6), ' cout << "hello world, Hello!" << endl;');
findModel.replace();
assertFindState(
@@ -1320,7 +1320,7 @@ suite('FindModel', () => {
[8, 14, 8, 19]
]
);
assert.equal(editor.getModel()!.getLineContent(6), ' cout << "hello world, Hello!" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(6), ' cout << "hello world, Hello!" << endl;');
findModel.replace();
assertFindState(
@@ -1333,7 +1333,7 @@ suite('FindModel', () => {
[8, 14, 8, 19]
]
);
assert.equal(editor.getModel()!.getLineContent(6), ' cout << "hello world, hi!" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(6), ' cout << "hello world, hi!" << endl;');
findModel.replace();
assertFindState(
@@ -1345,7 +1345,7 @@ suite('FindModel', () => {
[8, 14, 8, 19]
]
);
assert.equal(editor.getModel()!.getLineContent(7), ' cout << "hi world again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(7), ' cout << "hi world again" << endl;');
findModel.replace();
assertFindState(
@@ -1356,7 +1356,7 @@ suite('FindModel', () => {
[6, 14, 6, 19]
]
);
assert.equal(editor.getModel()!.getLineContent(8), ' cout << "hi world again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(8), ' cout << "hi world again" << endl;');
findModel.replace();
assertFindState(
@@ -1365,7 +1365,7 @@ suite('FindModel', () => {
null,
[]
);
assert.equal(editor.getModel()!.getLineContent(6), ' cout << "hi world, hi!" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(6), ' cout << "hi world, hi!" << endl;');
findModel.dispose();
findState.dispose();
@@ -1398,7 +1398,7 @@ suite('FindModel', () => {
[11, 10, 11, 13]
]
);
assert.equal(editor.getModel()!.getLineContent(11), '// blablablaciao');
assert.strictEqual(editor.getModel()!.getLineContent(11), '// blablablaciao');
findModel.replace();
assertFindState(
@@ -1410,7 +1410,7 @@ suite('FindModel', () => {
[11, 11, 11, 14]
]
);
assert.equal(editor.getModel()!.getLineContent(11), '// ciaoblablaciao');
assert.strictEqual(editor.getModel()!.getLineContent(11), '// ciaoblablaciao');
findModel.replace();
assertFindState(
@@ -1421,7 +1421,7 @@ suite('FindModel', () => {
[11, 12, 11, 15]
]
);
assert.equal(editor.getModel()!.getLineContent(11), '// ciaociaoblaciao');
assert.strictEqual(editor.getModel()!.getLineContent(11), '// ciaociaoblaciao');
findModel.replace();
assertFindState(
@@ -1430,7 +1430,7 @@ suite('FindModel', () => {
null,
[]
);
assert.equal(editor.getModel()!.getLineContent(11), '// ciaociaociaociao');
assert.strictEqual(editor.getModel()!.getLineContent(11), '// ciaociaociaociao');
findModel.dispose();
findState.dispose();
@@ -1467,7 +1467,7 @@ suite('FindModel', () => {
[8, 14, 8, 19]
]
);
assert.equal(editor.getModel()!.getLineContent(6), ' cout << "hello world, Hello!" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(6), ' cout << "hello world, Hello!" << endl;');
findModel.replaceAll();
assertFindState(
@@ -1476,9 +1476,9 @@ suite('FindModel', () => {
null,
[]
);
assert.equal(editor.getModel()!.getLineContent(6), ' cout << "hi world, hi!" << endl;');
assert.equal(editor.getModel()!.getLineContent(7), ' cout << "hi world again" << endl;');
assert.equal(editor.getModel()!.getLineContent(8), ' cout << "hi world again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(6), ' cout << "hi world, hi!" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(7), ' cout << "hi world again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(8), ' cout << "hi world again" << endl;');
findModel.dispose();
findState.dispose();
@@ -1517,10 +1517,10 @@ suite('FindModel', () => {
[9, 1, 9, 3]
]
);
assert.equal(editor.getModel()!.getLineContent(6), ' cout << "hello world, Hello!" << endl;');
assert.equal(editor.getModel()!.getLineContent(7), ' cout << "hello world again" << endl;');
assert.equal(editor.getModel()!.getLineContent(8), ' cout << "Hello world again" << endl;');
assert.equal(editor.getModel()!.getLineContent(9), ' cout << "helloworld again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(6), ' cout << "hello world, Hello!" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(7), ' cout << "hello world again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(8), ' cout << "Hello world again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(9), ' cout << "helloworld again" << endl;');
findModel.dispose();
findState.dispose();
@@ -1549,7 +1549,7 @@ suite('FindModel', () => {
null,
[]
);
assert.equal(editor.getModel()!.getLineContent(11), '// ciaociaociaociao');
assert.strictEqual(editor.getModel()!.getLineContent(11), '// ciaociaociaociao');
findModel.dispose();
findState.dispose();
@@ -1578,10 +1578,10 @@ suite('FindModel', () => {
null,
[]
);
assert.equal(editor.getModel()!.getLineContent(11), '// <');
assert.equal(editor.getModel()!.getLineContent(12), '\t><');
assert.equal(editor.getModel()!.getLineContent(13), '\t><');
assert.equal(editor.getModel()!.getLineContent(14), '\t>ciao');
assert.strictEqual(editor.getModel()!.getLineContent(11), '// <');
assert.strictEqual(editor.getModel()!.getLineContent(12), '\t><');
assert.strictEqual(editor.getModel()!.getLineContent(13), '\t><');
assert.strictEqual(editor.getModel()!.getLineContent(14), '\t>ciao');
findModel.dispose();
findState.dispose();
@@ -1610,8 +1610,8 @@ suite('FindModel', () => {
[]
);
assert.equal(editor.getModel()!.getLineContent(2), '#bar "cool.h"');
assert.equal(editor.getModel()!.getLineContent(3), '#bar <iostream>');
assert.strictEqual(editor.getModel()!.getLineContent(2), '#bar "cool.h"');
assert.strictEqual(editor.getModel()!.getLineContent(3), '#bar <iostream>');
findModel.dispose();
findState.dispose();
@@ -1665,7 +1665,7 @@ suite('FindModel', () => {
findModel.selectAllMatches();
assert.deepEqual(editor!.getSelections()!.map(s => s.toString()), [
assert.deepStrictEqual(editor!.getSelections()!.map(s => s.toString()), [
new Selection(6, 14, 6, 19),
new Selection(6, 27, 6, 32),
new Selection(7, 14, 7, 19),
@@ -1709,14 +1709,14 @@ suite('FindModel', () => {
findModel.selectAllMatches();
assert.deepEqual(editor!.getSelections()!.map(s => s.toString()), [
assert.deepStrictEqual(editor!.getSelections()!.map(s => s.toString()), [
new Selection(7, 14, 7, 19),
new Selection(6, 14, 6, 19),
new Selection(6, 27, 6, 32),
new Selection(8, 14, 8, 19)
].map(s => s.toString()));
assert.deepEqual(editor!.getSelection()!.toString(), new Selection(7, 14, 7, 19).toString());
assert.deepStrictEqual(editor!.getSelection()!.toString(), new Selection(7, 14, 7, 19).toString());
assertFindState(
editor,
@@ -1800,7 +1800,7 @@ suite('FindModel', () => {
[8, 14, 8, 19]
]
);
assert.equal(editor.getModel()!.getLineContent(6), ' cout << "hello world, Hello!" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(6), ' cout << "hello world, Hello!" << endl;');
findModel.replace();
assertFindState(
@@ -1812,7 +1812,7 @@ suite('FindModel', () => {
[8, 14, 8, 19]
]
);
assert.equal(editor.getModel()!.getLineContent(6), ' cout << "hi world, Hello!" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(6), ' cout << "hi world, Hello!" << endl;');
findModel.replace();
assertFindState(
@@ -1823,7 +1823,7 @@ suite('FindModel', () => {
[8, 14, 8, 19]
]
);
assert.equal(editor.getModel()!.getLineContent(7), ' cout << "hi world again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(7), ' cout << "hi world again" << endl;');
findModel.replace();
assertFindState(
@@ -1832,7 +1832,7 @@ suite('FindModel', () => {
null,
[]
);
assert.equal(editor.getModel()!.getLineContent(8), ' cout << "hi world again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(8), ' cout << "hi world again" << endl;');
findModel.dispose();
findState.dispose();
@@ -1871,7 +1871,7 @@ suite('FindModel', () => {
]
);
assert.equal(editor.getModel()!.getLineContent(8), ' cout << "Hello world again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(8), ' cout << "Hello world again" << endl;');
findModel.replace();
assertFindState(
@@ -1883,7 +1883,7 @@ suite('FindModel', () => {
[7, 14, 7, 19],
]
);
assert.equal(editor.getModel()!.getLineContent(8), ' cout << "hi world again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(8), ' cout << "hi world again" << endl;');
findModel.replace();
assertFindState(
@@ -1894,7 +1894,7 @@ suite('FindModel', () => {
[7, 14, 7, 19]
]
);
assert.equal(editor.getModel()!.getLineContent(6), ' cout << "hi world, Hello!" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(6), ' cout << "hi world, Hello!" << endl;');
findModel.replace();
assertFindState(
@@ -1903,7 +1903,7 @@ suite('FindModel', () => {
null,
[]
);
assert.equal(editor.getModel()!.getLineContent(7), ' cout << "hi world again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(7), ' cout << "hi world again" << endl;');
findModel.dispose();
findState.dispose();
@@ -1927,9 +1927,9 @@ suite('FindModel', () => {
findModel.replaceAll();
assert.equal(editor.getModel()!.getLineContent(6), ' cout << "hi world, Hello!" << endl;');
assert.equal(editor.getModel()!.getLineContent(7), ' cout << "hi world again" << endl;');
assert.equal(editor.getModel()!.getLineContent(8), ' cout << "hi world again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(6), ' cout << "hi world, Hello!" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(7), ' cout << "hi world again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(8), ' cout << "hi world again" << endl;');
assertFindState(
editor,
@@ -1970,7 +1970,7 @@ suite('FindModel', () => {
[8, 14, 8, 19]
]
);
assert.equal(editor.getModel()!.getLineContent(6), ' cout << "hello world, Hello!" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(6), ' cout << "hello world, Hello!" << endl;');
findModel.replace();
assertFindState(
@@ -1982,7 +1982,7 @@ suite('FindModel', () => {
[8, 14, 8, 19]
]
);
assert.equal(editor.getModel()!.getLineContent(6), ' cout << "hilo world, Hello!" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(6), ' cout << "hilo world, Hello!" << endl;');
findModel.replace();
assertFindState(
@@ -1993,7 +1993,7 @@ suite('FindModel', () => {
[8, 14, 8, 19]
]
);
assert.equal(editor.getModel()!.getLineContent(7), ' cout << "hilo world again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(7), ' cout << "hilo world again" << endl;');
findModel.replace();
assertFindState(
@@ -2002,7 +2002,7 @@ suite('FindModel', () => {
null,
[]
);
assert.equal(editor.getModel()!.getLineContent(8), ' cout << "hilo world again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(8), ' cout << "hilo world again" << endl;');
findModel.dispose();
findState.dispose();
@@ -2027,10 +2027,10 @@ suite('FindModel', () => {
findModel.replaceAll();
assert.equal(editor.getModel()!.getLineContent(6), ' cout << "hello girl, Hello!" << endl;');
assert.equal(editor.getModel()!.getLineContent(7), ' cout << "hello girl again" << endl;');
assert.equal(editor.getModel()!.getLineContent(8), ' cout << "Hello girl again" << endl;');
assert.equal(editor.getModel()!.getLineContent(9), ' cout << "hellogirl again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(6), ' cout << "hello girl, Hello!" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(7), ' cout << "hello girl again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(8), ' cout << "Hello girl again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(9), ' cout << "hellogirl again" << endl;');
assertFindState(
editor,
@@ -2060,8 +2060,8 @@ suite('FindModel', () => {
findModel.replaceAll();
assert.equal(editor.getModel()!.getLineContent(6), ' cout << "hello girl, Hello!" << endl;');
assert.equal(editor.getModel()!.getLineContent(8), ' cout << "Hello girl again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(6), ' cout << "hello girl, Hello!" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(8), ' cout << "Hello girl again" << endl;');
assertFindState(
editor,
@@ -2094,10 +2094,10 @@ suite('FindModel', () => {
findModel.replaceAll();
assert.equal(editor.getModel()!.getLineContent(6), ' cout << "goodbye world, Goodbye!" << endl;');
assert.equal(editor.getModel()!.getLineContent(7), ' cout << "goodbye world again" << endl;');
assert.equal(editor.getModel()!.getLineContent(8), ' cout << "Goodbye world again" << endl;');
assert.equal(editor.getModel()!.getLineContent(9), ' cout << "goodbyeworld again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(6), ' cout << "goodbye world, Goodbye!" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(7), ' cout << "goodbye world again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(8), ' cout << "Goodbye world again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(9), ' cout << "goodbyeworld again" << endl;');
assertFindState(
editor,
@@ -2134,9 +2134,9 @@ suite('FindModel', () => {
null,
[]
);
assert.equal(editor.getModel()!.getLineContent(6), ' cout << " world, !" << endl;');
assert.equal(editor.getModel()!.getLineContent(7), ' cout << " world again" << endl;');
assert.equal(editor.getModel()!.getLineContent(8), ' cout << " world again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(6), ' cout << " world, !" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(7), ' cout << " world again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(8), ' cout << " world again" << endl;');
findModel.dispose();
findState.dispose();
@@ -2159,7 +2159,7 @@ suite('FindModel', () => {
expectedText += 'a line' + i + '\n';
}
expectedText += 'a ';
assert.equal(editor!.getModel()!.getValue(), expectedText);
assert.strictEqual(editor!.getModel()!.getValue(), expectedText);
findModel.dispose();
findState.dispose();
@@ -2188,9 +2188,9 @@ suite('FindModel', () => {
null,
[]
);
assert.equal(editor.getModel()!.getLineContent(6), ' cout << "hi world, Hello!" << endl;');
assert.equal(editor.getModel()!.getLineContent(7), ' cout << "hi world again" << endl;');
assert.equal(editor.getModel()!.getLineContent(9), ' cout << "hiworld again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(6), ' cout << "hi world, Hello!" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(7), ' cout << "hi world again" << endl;');
assert.strictEqual(editor.getModel()!.getLineContent(9), ' cout << "hiworld again" << endl;');
findModel.dispose();
findState.dispose();
@@ -2219,78 +2219,78 @@ suite('FindModel', () => {
findState.change({ searchString: 'hello', loop: false }, false);
let findModel = new FindModelBoundToEditorModel(editor, findState);
assert.equal(findState.matchesCount, 5);
assert.strictEqual(findState.matchesCount, 5);
// Test next operations
assert.equal(findState.matchesPosition, 0);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 0);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 1);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), false);
assert.strictEqual(findState.matchesPosition, 1);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), false);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 2);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 2);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 3);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 3);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 4);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 4);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 5);
assert.equal(findState.canNavigateForward(), false);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 5);
assert.strictEqual(findState.canNavigateForward(), false);
assert.strictEqual(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 5);
assert.equal(findState.canNavigateForward(), false);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 5);
assert.strictEqual(findState.canNavigateForward(), false);
assert.strictEqual(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 5);
assert.equal(findState.canNavigateForward(), false);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 5);
assert.strictEqual(findState.canNavigateForward(), false);
assert.strictEqual(findState.canNavigateBack(), true);
// Test previous operations
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 4);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 4);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), true);
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 3);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 3);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), true);
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 2);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 2);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), true);
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 1);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), false);
assert.strictEqual(findState.matchesPosition, 1);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), false);
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 1);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), false);
assert.strictEqual(findState.matchesPosition, 1);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), false);
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 1);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), false);
assert.strictEqual(findState.matchesPosition, 1);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), false);
});
@@ -2299,78 +2299,78 @@ suite('FindModel', () => {
findState.change({ searchString: 'hello' }, false);
let findModel = new FindModelBoundToEditorModel(editor, findState);
assert.equal(findState.matchesCount, 5);
assert.strictEqual(findState.matchesCount, 5);
// Test next operations
assert.equal(findState.matchesPosition, 0);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 0);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 1);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 1);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 2);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 2);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 3);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 3);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 4);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 4);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 5);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 5);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 1);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 1);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), true);
findModel.moveToNextMatch();
assert.equal(findState.matchesPosition, 2);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 2);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), true);
// Test previous operations
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 1);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 1);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), true);
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 5);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 5);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), true);
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 4);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 4);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), true);
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 3);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 3);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), true);
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 2);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 2);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), true);
findModel.moveToPrevMatch();
assert.equal(findState.matchesPosition, 1);
assert.equal(findState.canNavigateForward(), true);
assert.equal(findState.canNavigateBack(), true);
assert.strictEqual(findState.matchesPosition, 1);
assert.strictEqual(findState.canNavigateForward(), true);
assert.strictEqual(findState.canNavigateBack(), true);
});

View File

@@ -13,7 +13,7 @@ suite('Replace Pattern test', () => {
let testParse = (input: string, expectedPieces: ReplacePiece[]) => {
let actual = parseReplaceString(input);
let expected = new ReplacePattern(expectedPieces);
assert.deepEqual(actual, expected, 'Parsing ' + input);
assert.deepStrictEqual(actual, expected, 'Parsing ' + input);
};
// no backslash => no treatment
@@ -73,14 +73,14 @@ suite('Replace Pattern test', () => {
let testParse = (input: string, expectedPieces: ReplacePiece[]) => {
let actual = parseReplaceString(input);
let expected = new ReplacePattern(expectedPieces);
assert.deepEqual(actual, expected, 'Parsing ' + input);
assert.deepStrictEqual(actual, expected, 'Parsing ' + input);
};
function assertReplace(target: string, search: RegExp, replaceString: string, expected: string): void {
let replacePattern = parseReplaceString(replaceString);
let m = search.exec(target);
let actual = replacePattern.buildReplaceString(m);
assert.equal(actual, expected, `${target}.replace(${search}, ${replaceString}) === ${expected}`);
assert.strictEqual(actual, expected, `${target}.replace(${search}, ${replaceString}) === ${expected}`);
}
// \U, \u => uppercase \L, \l => lowercase \E => cancel
@@ -107,7 +107,7 @@ suite('Replace Pattern test', () => {
let m = search.exec(target);
let actual = replacePattern.buildReplaceString(m);
assert.deepEqual(actual, expected, `${target}.replace(${search}, ${replaceString})`);
assert.deepStrictEqual(actual, expected, `${target}.replace(${search}, ${replaceString})`);
};
testJSReplaceSemantics('hi', /hi/, 'hello', 'hi'.replace(/hi/, 'hello'));
@@ -136,7 +136,7 @@ suite('Replace Pattern test', () => {
let m = search.exec(target);
let actual = replacePattern.buildReplaceString(m);
assert.equal(actual, expected, `${target}.replace(${search}, ${replaceString}) === ${expected}`);
assert.strictEqual(actual, expected, `${target}.replace(${search}, ${replaceString}) === ${expected}`);
}
assertReplace('bla', /bla/, 'hello', 'hello');
@@ -162,7 +162,7 @@ suite('Replace Pattern test', () => {
let m = search.exec(target);
let actual = replacePattern.buildReplaceString(m);
assert.equal(actual, expected, `${target}.replace(${search}, ${replaceString}) === ${expected}`);
assert.strictEqual(actual, expected, `${target}.replace(${search}, ${replaceString}) === ${expected}`);
}
assertReplace('this is a bla text', /bla/, 'hello', 'hello');
assertReplace('this is a bla text', /this(?=.*bla)/, 'that', 'that');
@@ -184,14 +184,14 @@ suite('Replace Pattern test', () => {
let replacePattern = parseReplaceString('a{$1}');
let matches = /a(z)?/.exec('abcd');
let actual = replacePattern.buildReplaceString(matches);
assert.equal(actual, 'a{}');
assert.strictEqual(actual, 'a{}');
});
test('buildReplaceStringWithCasePreserved test', () => {
function assertReplace(target: string[], replaceString: string, expected: string): void {
let actual: string = '';
actual = buildReplaceStringWithCasePreserved(target, replaceString);
assert.equal(actual, expected);
assert.strictEqual(actual, expected);
}
assertReplace(['abc'], 'Def', 'def');
@@ -219,7 +219,7 @@ suite('Replace Pattern test', () => {
function assertReplace(target: string[], replaceString: string, expected: string): void {
let replacePattern = parseReplaceString(replaceString);
let actual = replacePattern.buildReplaceString(target, true);
assert.equal(actual, expected);
assert.strictEqual(actual, expected);
}
assertReplace(['abc'], 'Def', 'def');

View File

@@ -16,13 +16,12 @@ import { Color } from 'vs/base/common/color';
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { ScrollType } from 'vs/editor/common/editorCommon';
import { getBaseLabel, getPathLabel } from 'vs/base/common/labels';
import { getBaseLabel } from 'vs/base/common/labels';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { Event, Emitter } from 'vs/base/common/event';
import { PeekViewWidget, peekViewTitleForeground, peekViewTitleInfoForeground } from 'vs/editor/contrib/peekView/peekView';
import { basename } from 'vs/base/common/resources';
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';
@@ -31,6 +30,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { splitLines } from 'vs/base/common/strings';
import { ILabelService } from 'vs/platform/label/common/label';
class MessageWidget {
@@ -51,6 +51,7 @@ class MessageWidget {
editor: ICodeEditor,
onRelatedInformation: (related: IRelatedInformation) => void,
private readonly _openerService: IOpenerService,
private readonly _labelService: ILabelService
) {
this._editor = editor;
@@ -169,7 +170,7 @@ class MessageWidget {
let relatedResource = document.createElement('a');
relatedResource.classList.add('filename');
relatedResource.innerText = `${getBaseLabel(related.resource)}(${related.startLineNumber}, ${related.startColumn}): `;
relatedResource.title = getPathLabel(related.resource, undefined);
relatedResource.title = this._labelService.getUriLabel(related.resource);
this._relatedDiagnostics.set(relatedResource, related);
let relatedMessage = document.createElement('span');
@@ -248,7 +249,8 @@ export class MarkerNavigationWidget extends PeekViewWidget {
@IOpenerService private readonly _openerService: IOpenerService,
@IMenuService private readonly _menuService: IMenuService,
@IInstantiationService instantiationService: IInstantiationService,
@IContextKeyService private readonly _contextKeyService: IContextKeyService
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@ILabelService private readonly _labelService: ILabelService
) {
super(editor, { showArrow: true, showFrame: true, isAccessible: true }, instantiationService);
this._severity = MarkerSeverity.Warning;
@@ -310,13 +312,6 @@ export class MarkerNavigationWidget extends PeekViewWidget {
this._icon = dom.append(container, dom.$(''));
}
protected _getActionBarOptions(): IActionBarOptions {
return {
...super._getActionBarOptions(),
orientation: ActionsOrientation.HORIZONTAL
};
}
protected _fillBody(container: HTMLElement): void {
this._parentContainer = container;
container.classList.add('marker-widget');
@@ -326,7 +321,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._openerService);
this._message = new MessageWidget(this._container, this.editor, related => this._onDidSelectRelatedInformation.fire(related), this._openerService, this._labelService);
this._disposables.add(this._message);
}

View File

@@ -51,7 +51,7 @@ export class OneReference {
);
} else {
return localize(
'aria.oneReference.preview', "symbol in {0} on line {1} at column {2}, {3}",
{ key: 'aria.oneReference.preview', comment: ['Placeholders are: 0: filename, 1:line number, 2: column number, 3: preview snippet of source code'] }, "symbol in {0} on line {1} at column {2}, {3}",
basename(this.uri), this.range.startLineNumber, this.range.startColumn, preview.value
);
}

View File

@@ -6,7 +6,7 @@
import * as nls from 'vs/nls';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { IDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IEmptyContentData } from 'vs/editor/browser/controller/mouseTarget';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
@@ -22,11 +22,10 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { editorHoverBackground, editorHoverBorder, editorHoverHighlight, textCodeBlockBackground, textLinkForeground, editorHoverStatusBarBackground, editorHoverForeground } from 'vs/platform/theme/common/colorRegistry';
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { GotoDefinitionAtPositionEditorContribution } from 'vs/editor/contrib/gotoSymbol/link/goToDefinitionAtPosition';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
export class ModesHoverController implements IEditorContribution {
@@ -35,22 +34,8 @@ export class ModesHoverController implements IEditorContribution {
private readonly _toUnhook = new DisposableStore();
private readonly _didChangeConfigurationHandler: IDisposable;
private readonly _contentWidget = new MutableDisposable<ModesContentHoverWidget>();
private readonly _glyphWidget = new MutableDisposable<ModesGlyphHoverWidget>();
get contentWidget(): ModesContentHoverWidget {
if (!this._contentWidget.value) {
this._createHoverWidgets();
}
return this._contentWidget.value!;
}
get glyphWidget(): ModesGlyphHoverWidget {
if (!this._glyphWidget.value) {
this._createHoverWidgets();
}
return this._glyphWidget.value!;
}
private _contentWidget: ModesContentHoverWidget | null;
private _glyphWidget: ModesGlyphHoverWidget | null;
private _isMouseDown: boolean;
private _hoverClicked: boolean;
@@ -64,15 +49,16 @@ export class ModesHoverController implements IEditorContribution {
}
constructor(private readonly _editor: ICodeEditor,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IOpenerService private readonly _openerService: IOpenerService,
@IModeService private readonly _modeService: IModeService,
@IMarkerDecorationsService private readonly _markerDecorationsService: IMarkerDecorationsService,
@IKeybindingService private readonly _keybindingService: IKeybindingService,
@IThemeService private readonly _themeService: IThemeService,
@IContextKeyService _contextKeyService: IContextKeyService
) {
this._isMouseDown = false;
this._hoverClicked = false;
this._contentWidget = null;
this._glyphWidget = null;
this._hookEvents();
@@ -113,8 +99,8 @@ export class ModesHoverController implements IEditorContribution {
}
private _onModelDecorationsChanged(): void {
this.contentWidget.onModelDecorationsChanged();
this.glyphWidget.onModelDecorationsChanged();
this._contentWidget?.onModelDecorationsChanged();
this._glyphWidget?.onModelDecorationsChanged();
}
private _onEditorScrollChanged(e: IScrollEvent): void {
@@ -153,7 +139,7 @@ export class ModesHoverController implements IEditorContribution {
private _onEditorMouseMove(mouseEvent: IEditorMouseEvent): void {
let targetType = mouseEvent.target.type;
if (this._isMouseDown && this._hoverClicked && this.contentWidget.isColorPickerVisible()) {
if (this._isMouseDown && this._hoverClicked) {
return;
}
@@ -162,10 +148,14 @@ export class ModesHoverController implements IEditorContribution {
return;
}
if (this._isHoverSticky && !mouseEvent.event.browserEvent.view?.getSelection()?.isCollapsed) {
// selected text within content hover widget
return;
}
if (
!this._isHoverSticky && targetType === MouseTargetType.CONTENT_WIDGET && mouseEvent.target.detail === ModesContentHoverWidget.ID
&& this._contentWidget.value?.isColorPickerVisible()
&& this._contentWidget?.isColorPickerVisible()
) {
// though the hover is not sticky, the color picker needs to.
return;
@@ -186,26 +176,31 @@ export class ModesHoverController implements IEditorContribution {
}
if (targetType === MouseTargetType.CONTENT_TEXT) {
this.glyphWidget.hide();
this._glyphWidget?.hide();
if (this._isHoverEnabled && mouseEvent.target.range) {
// TODO@rebornix. This should be removed if we move Color Picker out of Hover component.
// Check if mouse is hovering on color decorator
const hoverOnColorDecorator = [...mouseEvent.target.element?.classList.values() || []].find(className => className.startsWith('ced-colorBox'))
&& mouseEvent.target.range.endColumn - mouseEvent.target.range.startColumn === 1;
if (hoverOnColorDecorator) {
// shift the mouse focus by one as color decorator is a `before` decoration of next character.
this.contentWidget.startShowingAt(new Range(mouseEvent.target.range.startLineNumber, mouseEvent.target.range.startColumn + 1, mouseEvent.target.range.endLineNumber, mouseEvent.target.range.endColumn + 1), HoverStartMode.Delayed, false);
} else {
this.contentWidget.startShowingAt(mouseEvent.target.range, HoverStartMode.Delayed, false);
const showAtRange = (
hoverOnColorDecorator // shift the mouse focus by one as color decorator is a `before` decoration of next character.
? new Range(mouseEvent.target.range.startLineNumber, mouseEvent.target.range.startColumn + 1, mouseEvent.target.range.endLineNumber, mouseEvent.target.range.endColumn + 1)
: mouseEvent.target.range
);
if (!this._contentWidget) {
this._contentWidget = new ModesContentHoverWidget(this._editor, this._hoverVisibleKey, this._instantiationService, this._themeService);
}
this._contentWidget.startShowingAt(showAtRange, HoverStartMode.Delayed, false);
}
} else if (targetType === MouseTargetType.GUTTER_GLYPH_MARGIN) {
this.contentWidget.hide();
this._contentWidget?.hide();
if (this._isHoverEnabled && mouseEvent.target.position) {
this.glyphWidget.startShowingAt(mouseEvent.target.position.lineNumber);
if (!this._glyphWidget) {
this._glyphWidget = new ModesGlyphHoverWidget(this._editor, this._modeService, this._openerService);
}
this._glyphWidget.startShowingAt(mouseEvent.target.position.lineNumber);
}
} else {
this._hideWidgets();
@@ -220,29 +215,32 @@ export class ModesHoverController implements IEditorContribution {
}
private _hideWidgets(): void {
if (!this._glyphWidget.value || !this._contentWidget.value || (this._isMouseDown && this._hoverClicked && this._contentWidget.value.isColorPickerVisible())) {
if ((this._isMouseDown && this._hoverClicked && this._contentWidget?.isColorPickerVisible())) {
return;
}
this._glyphWidget.value.hide();
this._contentWidget.value.hide();
this._hoverClicked = false;
this._glyphWidget?.hide();
this._contentWidget?.hide();
}
private _createHoverWidgets() {
this._contentWidget.value = new ModesContentHoverWidget(this._editor, this._hoverVisibleKey, this._markerDecorationsService, this._keybindingService, this._themeService, this._modeService, this._openerService);
this._glyphWidget.value = new ModesGlyphHoverWidget(this._editor, this._modeService, this._openerService);
public isColorPickerVisible(): boolean {
return this._contentWidget?.isColorPickerVisible() || false;
}
public showContentHover(range: Range, mode: HoverStartMode, focus: boolean): void {
this.contentWidget.startShowingAt(range, mode, focus);
if (!this._contentWidget) {
this._contentWidget = new ModesContentHoverWidget(this._editor, this._hoverVisibleKey, this._instantiationService, this._themeService);
}
this._contentWidget.startShowingAt(range, mode, focus);
}
public dispose(): void {
this._unhookEvents();
this._toUnhook.dispose();
this._didChangeConfigurationHandler.dispose();
this._glyphWidget.dispose();
this._contentWidget.dispose();
this._glyphWidget?.dispose();
this._contentWidget?.dispose();
}
}

View File

@@ -3,168 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Widget } from 'vs/base/browser/ui/widget';
import { KeyCode } from 'vs/base/common/keyCodes';
import { IContentWidget, ICodeEditor, IContentWidgetPosition, ContentWidgetPositionPreference, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser';
import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser';
import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { renderHoverAction, HoverWidget } from 'vs/base/browser/ui/hover/hoverWidget';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextKey } from 'vs/platform/contextkey/common/contextkey';
export class ContentHoverWidget extends Widget implements IContentWidget {
protected readonly _hover: HoverWidget;
private readonly _id: string;
protected _editor: ICodeEditor;
private _isVisible: boolean;
protected _showAtPosition: Position | null;
protected _showAtRange: Range | null;
private _stoleFocus: boolean;
// Editor.IContentWidget.allowEditorOverflow
public allowEditorOverflow = true;
protected get isVisible(): boolean {
return this._isVisible;
}
protected set isVisible(value: boolean) {
this._isVisible = value;
this._hover.containerDomNode.classList.toggle('hidden', !this._isVisible);
}
constructor(
id: string,
editor: ICodeEditor,
private readonly _hoverVisibleKey: IContextKey<boolean>,
private readonly _keybindingService: IKeybindingService
) {
super();
this._hover = this._register(new HoverWidget());
this._id = id;
this._editor = editor;
this._isVisible = false;
this._stoleFocus = false;
this.onkeydown(this._hover.containerDomNode, (e: IKeyboardEvent) => {
if (e.equals(KeyCode.Escape)) {
this.hide();
}
});
this._register(this._editor.onDidChangeConfiguration((e: ConfigurationChangedEvent) => {
if (e.hasChanged(EditorOption.fontInfo)) {
this.updateFont();
}
}));
this._editor.onDidLayoutChange(e => this.layout());
this.layout();
this._editor.addContentWidget(this);
this._showAtPosition = null;
this._showAtRange = null;
this._stoleFocus = false;
}
public getId(): string {
return this._id;
}
public getDomNode(): HTMLElement {
return this._hover.containerDomNode;
}
public showAt(position: Position, range: Range | null, focus: boolean): void {
// Position has changed
this._showAtPosition = position;
this._showAtRange = range;
this._hoverVisibleKey.set(true);
this.isVisible = true;
this._editor.layoutContentWidget(this);
// Simply force a synchronous render on the editor
// such that the widget does not really render with left = '0px'
this._editor.render();
this._stoleFocus = focus;
if (focus) {
this._hover.containerDomNode.focus();
}
}
public hide(): void {
if (!this.isVisible) {
return;
}
setTimeout(() => {
// Give commands a chance to see the key
if (!this.isVisible) {
this._hoverVisibleKey.set(false);
}
}, 0);
this.isVisible = false;
this._editor.layoutContentWidget(this);
if (this._stoleFocus) {
this._editor.focus();
}
}
public getPosition(): IContentWidgetPosition | null {
if (this.isVisible) {
return {
position: this._showAtPosition,
range: this._showAtRange,
preference: [
ContentWidgetPositionPreference.ABOVE,
ContentWidgetPositionPreference.BELOW
]
};
}
return null;
}
public dispose(): void {
this._editor.removeContentWidget(this);
super.dispose();
}
private updateFont(): void {
const codeClasses: HTMLElement[] = Array.prototype.slice.call(this._hover.contentsDomNode.getElementsByClassName('code'));
codeClasses.forEach(node => this._editor.applyFontInfo(node));
}
protected updateContents(node: Node): void {
this._hover.contentsDomNode.textContent = '';
this._hover.contentsDomNode.appendChild(node);
this.updateFont();
this._editor.layoutContentWidget(this);
this._hover.onContentsChanged();
}
protected _renderAction(parent: HTMLElement, actionOptions: { label: string, iconClass?: string, run: (target: HTMLElement) => void, commandId: string }): IDisposable {
const keybinding = this._keybindingService.lookupKeybinding(actionOptions.commandId);
const keybindingLabel = keybinding ? keybinding.getLabel() : null;
return renderHoverAction(parent, actionOptions, keybindingLabel);
}
private layout(): void {
const height = Math.max(this._editor.getLayoutInfo().height / 4, 250);
const { fontSize, lineHeight } = this._editor.getOption(EditorOption.fontInfo);
this._hover.contentsDomNode.style.fontSize = `${fontSize}px`;
this._hover.contentsDomNode.style.lineHeight = `${lineHeight}px`;
this._hover.contentsDomNode.style.maxHeight = `${height}px`;
this._hover.contentsDomNode.style.maxWidth = `${Math.max(this._editor.getLayoutInfo().width * 0.66, 500)}px`;
}
}
export class GlyphHoverWidget extends Widget implements IOverlayWidget {

View File

@@ -0,0 +1,126 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
import { IMarkdownString, MarkdownString, isEmptyMarkdownString, markedStringsEquals } from 'vs/base/common/htmlContent';
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Range } from 'vs/editor/common/core/range';
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
import { asArray } from 'vs/base/common/arrays';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelDecoration } from 'vs/editor/common/model';
import { IEditorHover, IEditorHoverParticipant, IHoverPart } from 'vs/editor/contrib/hover/modesContentHover';
import { HoverProviderRegistry } from 'vs/editor/common/modes';
import { getHover } from 'vs/editor/contrib/hover/getHover';
import { Position } from 'vs/editor/common/core/position';
import { CancellationToken } from 'vs/base/common/cancellation';
const $ = dom.$;
export class MarkdownHover implements IHoverPart {
constructor(
public readonly range: Range,
public readonly contents: IMarkdownString[]
) { }
public equals(other: IHoverPart): boolean {
if (other instanceof MarkdownHover) {
return markedStringsEquals(this.contents, other.contents);
}
return false;
}
}
export class MarkdownHoverParticipant implements IEditorHoverParticipant<MarkdownHover> {
constructor(
private readonly _editor: ICodeEditor,
private readonly _hover: IEditorHover,
@IModeService private readonly _modeService: IModeService,
@IOpenerService private readonly _openerService: IOpenerService,
) { }
public createLoadingMessage(range: Range): MarkdownHover {
return new MarkdownHover(range, [new MarkdownString().appendText(nls.localize('modesContentHover.loading', "Loading..."))]);
}
public computeSync(hoverRange: Range, lineDecorations: IModelDecoration[]): MarkdownHover[] {
if (!this._editor.hasModel()) {
return [];
}
const model = this._editor.getModel();
const lineNumber = hoverRange.startLineNumber;
const maxColumn = model.getLineMaxColumn(lineNumber);
const result: MarkdownHover[] = [];
for (const d of lineDecorations) {
const startColumn = (d.range.startLineNumber === lineNumber) ? d.range.startColumn : 1;
const endColumn = (d.range.endLineNumber === lineNumber) ? d.range.endColumn : maxColumn;
const hoverMessage = d.options.hoverMessage;
if (!hoverMessage || isEmptyMarkdownString(hoverMessage)) {
continue;
}
const range = new Range(hoverRange.startLineNumber, startColumn, hoverRange.startLineNumber, endColumn);
result.push(new MarkdownHover(range, asArray(hoverMessage)));
}
return result;
}
public async computeAsync(range: Range, token: CancellationToken): Promise<MarkdownHover[]> {
if (!this._editor.hasModel() || !range) {
return Promise.resolve([]);
}
const model = this._editor.getModel();
if (!HoverProviderRegistry.has(model)) {
return Promise.resolve([]);
}
const hovers = await getHover(model, new Position(
range.startLineNumber,
range.startColumn
), token);
const result: MarkdownHover[] = [];
for (const hover of hovers) {
if (isEmptyMarkdownString(hover.contents)) {
continue;
}
const rng = hover.range ? Range.lift(hover.range) : range;
result.push(new MarkdownHover(rng, hover.contents));
}
return result;
}
public renderHoverParts(hoverParts: MarkdownHover[], fragment: DocumentFragment): IDisposable {
const disposables = new DisposableStore();
for (const hoverPart of hoverParts) {
for (const contents of hoverPart.contents) {
if (isEmptyMarkdownString(contents)) {
continue;
}
const markdownHoverElement = $('div.hover-row.markdown-hover');
const hoverContentsElement = dom.append(markdownHoverElement, $('div.hover-contents'));
const renderer = disposables.add(new MarkdownRenderer({ editor: this._editor }, this._modeService, this._openerService));
disposables.add(renderer.onDidRenderAsync(() => {
hoverContentsElement.className = 'hover-contents code-hover-contents';
this._hover.onContentsChanged();
}));
const renderedContents = disposables.add(renderer.render(contents));
hoverContentsElement.appendChild(renderedContents.element);
fragment.appendChild(markdownHoverElement);
}
}
return disposables;
}
}

View File

@@ -0,0 +1,257 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
import { IDisposable, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Range } from 'vs/editor/common/core/range';
import { CodeActionTriggerType } from 'vs/editor/common/modes';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { IMarker, IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers';
import { basename } from 'vs/base/common/resources';
import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { MarkerController, NextMarkerAction } from 'vs/editor/contrib/gotoError/gotoError';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { CancelablePromise, createCancelablePromise, disposableTimeout } from 'vs/base/common/async';
import { getCodeActions, CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction';
import { QuickFixAction, QuickFixController } from 'vs/editor/contrib/codeAction/codeActionCommands';
import { CodeActionKind, CodeActionTrigger } from 'vs/editor/contrib/codeAction/types';
import { IModelDecoration } from 'vs/editor/common/model';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { Progress } from 'vs/platform/progress/common/progress';
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { renderHoverAction } from 'vs/base/browser/ui/hover/hoverWidget';
import { IEditorHover, IEditorHoverParticipant, IHoverPart } from 'vs/editor/contrib/hover/modesContentHover';
const $ = dom.$;
export class MarkerHover implements IHoverPart {
constructor(
public readonly range: Range,
public readonly marker: IMarker,
) { }
public equals(other: IHoverPart): boolean {
if (other instanceof MarkerHover) {
return IMarkerData.makeKey(this.marker) === IMarkerData.makeKey(other.marker);
}
return false;
}
}
const markerCodeActionTrigger: CodeActionTrigger = {
type: CodeActionTriggerType.Manual,
filter: { include: CodeActionKind.QuickFix }
};
export class MarkerHoverParticipant implements IEditorHoverParticipant<MarkerHover> {
private recentMarkerCodeActionsInfo: { marker: IMarker, hasCodeActions: boolean } | undefined = undefined;
constructor(
private readonly _editor: ICodeEditor,
private readonly _hover: IEditorHover,
@IMarkerDecorationsService private readonly _markerDecorationsService: IMarkerDecorationsService,
@IKeybindingService private readonly _keybindingService: IKeybindingService,
@IOpenerService private readonly _openerService: IOpenerService,
) { }
public computeSync(hoverRange: Range, lineDecorations: IModelDecoration[]): MarkerHover[] {
if (!this._editor.hasModel()) {
return [];
}
const model = this._editor.getModel();
const lineNumber = hoverRange.startLineNumber;
const maxColumn = model.getLineMaxColumn(lineNumber);
const result: MarkerHover[] = [];
for (const d of lineDecorations) {
const startColumn = (d.range.startLineNumber === lineNumber) ? d.range.startColumn : 1;
const endColumn = (d.range.endLineNumber === lineNumber) ? d.range.endColumn : maxColumn;
const marker = this._markerDecorationsService.getMarker(model.uri, d);
if (!marker) {
continue;
}
const range = new Range(hoverRange.startLineNumber, startColumn, hoverRange.startLineNumber, endColumn);
result.push(new MarkerHover(range, marker));
}
return result;
}
public renderHoverParts(hoverParts: MarkerHover[], fragment: DocumentFragment): IDisposable {
if (!hoverParts.length) {
return Disposable.None;
}
const disposables = new DisposableStore();
hoverParts.forEach(msg => fragment.appendChild(this.renderMarkerHover(msg, disposables)));
const markerHoverForStatusbar = hoverParts.length === 1 ? hoverParts[0] : hoverParts.sort((a, b) => MarkerSeverity.compare(a.marker.severity, b.marker.severity))[0];
fragment.appendChild(this.renderMarkerStatusbar(markerHoverForStatusbar, disposables));
return disposables;
}
private renderMarkerHover(markerHover: MarkerHover, disposables: DisposableStore): HTMLElement {
const hoverElement = $('div.hover-row');
const markerElement = dom.append(hoverElement, $('div.marker.hover-contents'));
const { source, message, code, relatedInformation } = markerHover.marker;
this._editor.applyFontInfo(markerElement);
const messageElement = dom.append(markerElement, $('span'));
messageElement.style.whiteSpace = 'pre-wrap';
messageElement.innerText = message;
if (source || code) {
// Code has link
if (code && typeof code !== 'string') {
const sourceAndCodeElement = $('span');
if (source) {
const sourceElement = dom.append(sourceAndCodeElement, $('span'));
sourceElement.innerText = source;
}
const codeLink = dom.append(sourceAndCodeElement, $('a.code-link'));
codeLink.setAttribute('href', code.target.toString());
disposables.add(dom.addDisposableListener(codeLink, 'click', (e) => {
this._openerService.open(code.target);
e.preventDefault();
e.stopPropagation();
}));
const codeElement = dom.append(codeLink, $('span'));
codeElement.innerText = code.value;
const detailsElement = dom.append(markerElement, sourceAndCodeElement);
detailsElement.style.opacity = '0.6';
detailsElement.style.paddingLeft = '6px';
} else {
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 (isNonEmptyArray(relatedInformation)) {
for (const { message, resource, startLineNumber, startColumn } of relatedInformation) {
const relatedInfoContainer = dom.append(markerElement, $('div'));
relatedInfoContainer.style.marginTop = '8px';
const a = dom.append(relatedInfoContainer, $('a'));
a.innerText = `${basename(resource)}(${startLineNumber}, ${startColumn}): `;
a.style.cursor = 'pointer';
disposables.add(dom.addDisposableListener(a, 'click', (e) => {
e.stopPropagation();
e.preventDefault();
if (this._openerService) {
this._openerService.open(resource, {
fromUserGesture: true,
editorOptions: <ITextEditorOptions>{ selection: { startLineNumber, startColumn } }
}).catch(onUnexpectedError);
}
}));
const messageElement = dom.append<HTMLAnchorElement>(relatedInfoContainer, $('span'));
messageElement.innerText = message;
this._editor.applyFontInfo(messageElement);
}
}
return hoverElement;
}
private renderMarkerStatusbar(markerHover: MarkerHover, disposables: DisposableStore): HTMLElement {
const hoverElement = $('div.hover-row.status-bar');
const actionsElement = dom.append(hoverElement, $('div.actions'));
if (markerHover.marker.severity === MarkerSeverity.Error || markerHover.marker.severity === MarkerSeverity.Warning || markerHover.marker.severity === MarkerSeverity.Info) {
disposables.add(this.renderAction(actionsElement, {
label: nls.localize('peek problem', "Peek Problem"),
commandId: NextMarkerAction.ID,
run: () => {
this._hover.hide();
MarkerController.get(this._editor).showAtMarker(markerHover.marker);
this._editor.focus();
}
}));
}
if (!this._editor.getOption(EditorOption.readOnly)) {
const quickfixPlaceholderElement = dom.append(actionsElement, $('div'));
if (this.recentMarkerCodeActionsInfo) {
if (IMarkerData.makeKey(this.recentMarkerCodeActionsInfo.marker) === IMarkerData.makeKey(markerHover.marker)) {
if (!this.recentMarkerCodeActionsInfo.hasCodeActions) {
quickfixPlaceholderElement.textContent = nls.localize('noQuickFixes', "No quick fixes available");
}
} else {
this.recentMarkerCodeActionsInfo = undefined;
}
}
const updatePlaceholderDisposable = this.recentMarkerCodeActionsInfo && !this.recentMarkerCodeActionsInfo.hasCodeActions ? Disposable.None : disposables.add(disposableTimeout(() => quickfixPlaceholderElement.textContent = nls.localize('checkingForQuickFixes', "Checking for quick fixes..."), 200));
if (!quickfixPlaceholderElement.textContent) {
// Have some content in here to avoid flickering
quickfixPlaceholderElement.textContent = String.fromCharCode(0xA0); // &nbsp;
}
const codeActionsPromise = this.getCodeActions(markerHover.marker);
disposables.add(toDisposable(() => codeActionsPromise.cancel()));
codeActionsPromise.then(actions => {
updatePlaceholderDisposable.dispose();
this.recentMarkerCodeActionsInfo = { marker: markerHover.marker, hasCodeActions: actions.validActions.length > 0 };
if (!this.recentMarkerCodeActionsInfo.hasCodeActions) {
actions.dispose();
quickfixPlaceholderElement.textContent = nls.localize('noQuickFixes', "No quick fixes available");
return;
}
quickfixPlaceholderElement.style.display = 'none';
let showing = false;
disposables.add(toDisposable(() => {
if (!showing) {
actions.dispose();
}
}));
disposables.add(this.renderAction(actionsElement, {
label: nls.localize('quick fixes', "Quick Fix..."),
commandId: QuickFixAction.Id,
run: (target) => {
showing = true;
const controller = QuickFixController.get(this._editor);
const elementPosition = dom.getDomNodePagePosition(target);
// Hide the hover pre-emptively, otherwise the editor can close the code actions
// context menu as well when using keyboard navigation
this._hover.hide();
controller.showCodeActions(markerCodeActionTrigger, actions, {
x: elementPosition.left + 6,
y: elementPosition.top + elementPosition.height + 6
});
}
}));
});
}
return hoverElement;
}
private renderAction(parent: HTMLElement, actionOptions: { label: string, iconClass?: string, run: (target: HTMLElement) => void, commandId: string }): IDisposable {
const keybinding = this._keybindingService.lookupKeybinding(actionOptions.commandId);
const keybindingLabel = keybinding ? keybinding.getLabel() : null;
return renderHoverAction(parent, actionOptions, keybindingLabel);
}
private getCodeActions(marker: IMarker): CancelablePromise<CodeActionSet> {
return createCancelablePromise(cancellationToken => {
return getCodeActions(
this._editor.getModel()!,
new Range(marker.startLineNumber, marker.startColumn, marker.endLineNumber, marker.endColumn),
markerCodeActionTrigger,
Progress.None,
cancellationToken);
});
}
}

View File

@@ -3,228 +3,242 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Color, RGBA } from 'vs/base/common/color';
import { IMarkdownString, MarkdownString, isEmptyMarkdownString, markedStringsEquals } from 'vs/base/common/htmlContent';
import { IDisposable, toDisposable, DisposableStore, combinedDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IDisposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle';
import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser';
import { Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import { Range } from 'vs/editor/common/core/range';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { DocumentColorProvider, Hover as MarkdownHover, HoverProviderRegistry, IColor, TokenizationRegistry, CodeActionTriggerType } from 'vs/editor/common/modes';
import { DocumentColorProvider, IColor, TokenizationRegistry } from 'vs/editor/common/modes';
import { getColorPresentations } from 'vs/editor/contrib/colorPicker/color';
import { ColorDetector } from 'vs/editor/contrib/colorPicker/colorDetector';
import { ColorPickerModel } from 'vs/editor/contrib/colorPicker/colorPickerModel';
import { ColorPickerWidget } from 'vs/editor/contrib/colorPicker/colorPickerWidget';
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/browser/core/markdownRenderer';
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';
import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IOpenerService, NullOpenerService } from 'vs/platform/opener/common/opener';
import { MarkerController, NextMarkerAction } from 'vs/editor/contrib/gotoError/gotoError';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { CancelablePromise, createCancelablePromise, disposableTimeout } from 'vs/base/common/async';
import { getCodeActions, CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction';
import { QuickFixAction, QuickFixController } from 'vs/editor/contrib/codeAction/codeActionCommands';
import { CodeActionKind, CodeActionTrigger } from 'vs/editor/contrib/codeAction/types';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IIdentifiedSingleEditOperation, TrackedRangeStickiness } from 'vs/editor/common/model';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { coalesce } from 'vs/base/common/arrays';
import { IIdentifiedSingleEditOperation, IModelDecoration, TrackedRangeStickiness } from 'vs/editor/common/model';
import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions';
import { Constants } from 'vs/base/common/uint';
import { textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { Progress } from 'vs/platform/progress/common/progress';
import { IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Widget } from 'vs/base/browser/ui/widget';
import { KeyCode } from 'vs/base/common/keyCodes';
import { HoverWidget } from 'vs/base/browser/ui/hover/hoverWidget';
import { MarkerHover, MarkerHoverParticipant } from 'vs/editor/contrib/hover/markerHoverParticipant';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { MarkdownHover, MarkdownHoverParticipant } from 'vs/editor/contrib/hover/markdownHoverParticipant';
const $ = dom.$;
export interface IHoverPart {
readonly range: Range;
equals(other: IHoverPart): boolean;
}
class ColorHover {
export interface IEditorHover {
hide(): void;
onContentsChanged(): void;
}
export interface IEditorHoverParticipant<T extends IHoverPart = IHoverPart> {
computeSync(hoverRange: Range, lineDecorations: IModelDecoration[]): T[];
computeAsync?(range: Range, token: CancellationToken): Promise<T[]>;
renderHoverParts(hoverParts: T[], fragment: DocumentFragment): IDisposable;
}
class ColorHover implements IHoverPart {
constructor(
public readonly range: IRange,
public readonly range: Range,
public readonly color: IColor,
public readonly provider: DocumentColorProvider
) { }
equals(other: IHoverPart): boolean {
return false;
}
}
class MarkerHover {
class HoverPartInfo {
constructor(
public readonly range: IRange,
public readonly marker: IMarker,
public readonly owner: IEditorHoverParticipant | null,
public readonly data: IHoverPart
) { }
}
type HoverPart = MarkdownHover | ColorHover | MarkerHover;
class ModesContentComputer implements IHoverComputer<HoverPart[]> {
class ModesContentComputer implements IHoverComputer<HoverPartInfo[]> {
private readonly _editor: ICodeEditor;
private _result: HoverPart[];
private _range?: Range;
private _result: HoverPartInfo[];
private _range: Range | null;
constructor(
editor: ICodeEditor,
private readonly _markerDecorationsService: IMarkerDecorationsService
private readonly _markerHoverParticipant: IEditorHoverParticipant<MarkerHover>,
private readonly _markdownHoverParticipant: MarkdownHoverParticipant
) {
this._editor = editor;
this._result = [];
this._range = null;
}
setRange(range: Range): void {
public setRange(range: Range): void {
this._range = range;
this._result = [];
}
clearResult(): void {
public clearResult(): void {
this._result = [];
}
computeAsync(token: CancellationToken): Promise<HoverPart[]> {
public async computeAsync(token: CancellationToken): Promise<HoverPartInfo[]> {
if (!this._editor.hasModel() || !this._range) {
return Promise.resolve([]);
}
const model = this._editor.getModel();
if (!HoverProviderRegistry.has(model)) {
return Promise.resolve([]);
}
return getHover(model, new Position(
this._range.startLineNumber,
this._range.startColumn
), token);
const markdownHovers = await this._markdownHoverParticipant.computeAsync(this._range, token);
return markdownHovers.map(h => new HoverPartInfo(this._markdownHoverParticipant, h));
}
computeSync(): HoverPart[] {
public computeSync(): HoverPartInfo[] {
if (!this._editor.hasModel() || !this._range) {
return [];
}
const model = this._editor.getModel();
const lineNumber = this._range.startLineNumber;
const hoverRange = this._range;
const lineNumber = hoverRange.startLineNumber;
if (lineNumber > this._editor.getModel().getLineCount()) {
// Illegal line number => no results
return [];
}
const colorDetector = ColorDetector.get(this._editor);
const maxColumn = model.getLineMaxColumn(lineNumber);
const lineDecorations = this._editor.getLineDecorations(lineNumber);
let didFindColor = false;
const hoverRange = this._range;
const result = lineDecorations.map((d): HoverPart | null => {
const lineDecorations = this._editor.getLineDecorations(lineNumber).filter((d) => {
const startColumn = (d.range.startLineNumber === lineNumber) ? d.range.startColumn : 1;
const endColumn = (d.range.endLineNumber === lineNumber) ? d.range.endColumn : maxColumn;
if (startColumn > hoverRange.startColumn || hoverRange.endColumn > endColumn) {
return null;
}
const range = new Range(hoverRange.startLineNumber, startColumn, hoverRange.startLineNumber, endColumn);
const marker = this._markerDecorationsService.getMarker(model, d);
if (marker) {
return new MarkerHover(range, marker);
}
const colorData = colorDetector.getColorData(d.range.getStartPosition());
if (!didFindColor && colorData) {
didFindColor = true;
const { color, range } = colorData.colorInfo;
return new ColorHover(range, color, colorData.provider);
} else {
if (isEmptyMarkdownString(d.options.hoverMessage)) {
return null;
}
const contents: IMarkdownString[] = d.options.hoverMessage ? asArray(d.options.hoverMessage) : [];
return { contents, range };
return false;
}
return true;
});
let result: HoverPartInfo[] = [];
const colorDetector = ColorDetector.get(this._editor);
for (const d of lineDecorations) {
const colorData = colorDetector.getColorData(d.range.getStartPosition());
if (colorData) {
const { color, range } = colorData.colorInfo;
result.push(new HoverPartInfo(null, new ColorHover(Range.lift(range), color, colorData.provider)));
break;
}
}
const markdownHovers = this._markdownHoverParticipant.computeSync(this._range, lineDecorations);
result = result.concat(markdownHovers.map(h => new HoverPartInfo(this._markdownHoverParticipant, h)));
const markerHovers = this._markerHoverParticipant.computeSync(this._range, lineDecorations);
result = result.concat(markerHovers.map(h => new HoverPartInfo(this._markerHoverParticipant, h)));
return coalesce(result);
}
onResult(result: HoverPart[], isFromSynchronousComputation: boolean): void {
public onResult(result: HoverPartInfo[], isFromSynchronousComputation: boolean): void {
// Always put synchronous messages before asynchronous ones
if (isFromSynchronousComputation) {
this._result = result.concat(this._result.sort((a, b) => {
if (a instanceof ColorHover) { // sort picker messages at to the top
return -1;
} else if (b instanceof ColorHover) {
return 1;
}
return 0;
}));
this._result = result.concat(this._result);
} else {
this._result = this._result.concat(result);
}
}
getResult(): HoverPart[] {
public getResult(): HoverPartInfo[] {
return this._result.slice(0);
}
getResultWithLoadingMessage(): HoverPart[] {
return this._result.slice(0).concat([this._getLoadingMessage()]);
}
public getResultWithLoadingMessage(): HoverPartInfo[] {
if (this._range) {
const loadingMessage = new HoverPartInfo(this._markdownHoverParticipant, this._markdownHoverParticipant.createLoadingMessage(this._range));
return this._result.slice(0).concat([loadingMessage]);
private _getLoadingMessage(): HoverPart {
return {
range: this._range,
contents: [new MarkdownString().appendText(nls.localize('modesContentHover.loading', "Loading..."))]
};
}
return this._result.slice(0);
}
}
const markerCodeActionTrigger: CodeActionTrigger = {
type: CodeActionTriggerType.Manual,
filter: { include: CodeActionKind.QuickFix }
};
export class ModesContentHoverWidget extends ContentHoverWidget {
export class ModesContentHoverWidget extends Widget implements IContentWidget, IEditorHover {
static readonly ID = 'editor.contrib.modesContentHoverWidget';
private _messages: HoverPart[];
private readonly _markerHoverParticipant: IEditorHoverParticipant<MarkerHover>;
private readonly _markdownHoverParticipant: MarkdownHoverParticipant;
private readonly _hover: HoverWidget;
private readonly _id: string;
private readonly _editor: ICodeEditor;
private _isVisible: boolean;
private _showAtPosition: Position | null;
private _showAtRange: Range | null;
private _stoleFocus: boolean;
// IContentWidget.allowEditorOverflow
public readonly allowEditorOverflow = true;
private _messages: HoverPartInfo[];
private _lastRange: Range | null;
private readonly _computer: ModesContentComputer;
private readonly _hoverOperation: HoverOperation<HoverPart[]>;
private readonly _hoverOperation: HoverOperation<HoverPartInfo[]>;
private _highlightDecorations: string[];
private _isChangingDecorations: boolean;
private _shouldFocus: boolean;
private _colorPicker: ColorPickerWidget | null;
private _codeLink?: HTMLElement;
private readonly renderDisposable = this._register(new MutableDisposable<IDisposable>());
private _renderDisposable: IDisposable | null;
constructor(
editor: ICodeEditor,
_hoverVisibleKey: IContextKey<boolean>,
markerDecorationsService: IMarkerDecorationsService,
keybindingService: IKeybindingService,
private readonly _hoverVisibleKey: IContextKey<boolean>,
instantiationService: IInstantiationService,
private readonly _themeService: IThemeService,
private readonly _modeService: IModeService,
private readonly _openerService: IOpenerService = NullOpenerService,
) {
super(ModesContentHoverWidget.ID, editor, _hoverVisibleKey, keybindingService);
super();
this._markerHoverParticipant = instantiationService.createInstance(MarkerHoverParticipant, editor, this);
this._markdownHoverParticipant = instantiationService.createInstance(MarkdownHoverParticipant, editor, this);
this._hover = this._register(new HoverWidget());
this._id = ModesContentHoverWidget.ID;
this._editor = editor;
this._isVisible = false;
this._stoleFocus = false;
this._renderDisposable = null;
this.onkeydown(this._hover.containerDomNode, (e: IKeyboardEvent) => {
if (e.equals(KeyCode.Escape)) {
this.hide();
}
});
this._register(this._editor.onDidChangeConfiguration((e: ConfigurationChangedEvent) => {
if (e.hasChanged(EditorOption.fontInfo)) {
this._updateFont();
}
}));
this._editor.onDidLayoutChange(() => this.layout());
this.layout();
this._editor.addContentWidget(this);
this._showAtPosition = null;
this._showAtRange = null;
this._stoleFocus = false;
this._messages = [];
this._lastRange = null;
this._computer = new ModesContentComputer(this._editor, markerDecorationsService);
this._computer = new ModesContentComputer(this._editor, this._markerHoverParticipant, this._markdownHoverParticipant);
this._highlightDecorations = [];
this._isChangingDecorations = false;
this._shouldFocus = false;
@@ -246,15 +260,15 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
this._register(dom.addStandardDisposableListener(this.getDomNode(), dom.EventType.BLUR, () => {
this.getDomNode().classList.remove('colorpicker-hover');
}));
this._register(editor.onDidChangeConfiguration((e) => {
this._register(editor.onDidChangeConfiguration(() => {
this._hoverOperation.setHoverTime(this._editor.getOption(EditorOption.hover).delay);
}));
this._register(TokenizationRegistry.onDidChange((e) => {
if (this.isVisible && this._lastRange && this._messages.length > 0) {
this._register(TokenizationRegistry.onDidChange(() => {
if (this._isVisible && this._lastRange && this._messages.length > 0) {
this._messages = this._messages.map(msg => {
// If a color hover is visible, we need to update the message that
// created it so that the color matches the last chosen color
if (msg instanceof ColorHover && !!this._lastRange?.intersectRanges(msg.range) && this._colorPicker?.model.color) {
if (msg.data instanceof ColorHover && !!this._lastRange?.intersectRanges(msg.data.range) && this._colorPicker?.model.color) {
const color = this._colorPicker.model.color;
const newColor = {
red: color.rgba.r / 255,
@@ -262,7 +276,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
blue: color.rgba.b / 255,
alpha: color.rgba.a
};
return new ColorHover(msg.range, newColor, msg.provider);
return new HoverPartInfo(msg.owner, new ColorHover(msg.data.range, newColor, msg.data.provider));
} else {
return msg;
}
@@ -274,16 +288,81 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
}));
}
dispose(): void {
public dispose(): void {
this._hoverOperation.cancel();
this._editor.removeContentWidget(this);
super.dispose();
}
onModelDecorationsChanged(): void {
public getId(): string {
return this._id;
}
public getDomNode(): HTMLElement {
return this._hover.containerDomNode;
}
public showAt(position: Position, range: Range | null, focus: boolean): void {
// Position has changed
this._showAtPosition = position;
this._showAtRange = range;
this._hoverVisibleKey.set(true);
this._isVisible = true;
this._hover.containerDomNode.classList.toggle('hidden', !this._isVisible);
this._editor.layoutContentWidget(this);
// Simply force a synchronous render on the editor
// such that the widget does not really render with left = '0px'
this._editor.render();
this._stoleFocus = focus;
if (focus) {
this._hover.containerDomNode.focus();
}
}
public getPosition(): IContentWidgetPosition | null {
if (this._isVisible) {
return {
position: this._showAtPosition,
range: this._showAtRange,
preference: [
ContentWidgetPositionPreference.ABOVE,
ContentWidgetPositionPreference.BELOW
]
};
}
return null;
}
private _updateFont(): void {
const codeClasses: HTMLElement[] = Array.prototype.slice.call(this._hover.contentsDomNode.getElementsByClassName('code'));
codeClasses.forEach(node => this._editor.applyFontInfo(node));
}
private _updateContents(node: Node): void {
this._hover.contentsDomNode.textContent = '';
this._hover.contentsDomNode.appendChild(node);
this._updateFont();
this._editor.layoutContentWidget(this);
this._hover.onContentsChanged();
}
private layout(): void {
const height = Math.max(this._editor.getLayoutInfo().height / 4, 250);
const { fontSize, lineHeight } = this._editor.getOption(EditorOption.fontInfo);
this._hover.contentsDomNode.style.fontSize = `${fontSize}px`;
this._hover.contentsDomNode.style.lineHeight = `${lineHeight}px`;
this._hover.contentsDomNode.style.maxHeight = `${height}px`;
this._hover.contentsDomNode.style.maxWidth = `${Math.max(this._editor.getLayoutInfo().width * 0.66, 500)}px`;
}
public onModelDecorationsChanged(): void {
if (this._isChangingDecorations) {
return;
}
if (this.isVisible) {
if (this._isVisible) {
// The decorations have changed and the hover is visible,
// we need to recompute the displayed text
this._hoverOperation.cancel();
@@ -295,7 +374,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
}
}
startShowingAt(range: Range, mode: HoverStartMode, focus: boolean): void {
public startShowingAt(range: Range, mode: HoverStartMode, focus: boolean): void {
if (this._lastRange && this._lastRange.equalsRange(range)) {
// We have to show the widget at the exact same range as before, so no work is needed
return;
@@ -303,17 +382,17 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
this._hoverOperation.cancel();
if (this.isVisible) {
if (this._isVisible) {
// The range might have changed, but the hover is visible
// Instead of hiding it completely, filter out messages that are still in the new range and
// kick off a new computation
if (!this._showAtPosition || this._showAtPosition.lineNumber !== range.startLineNumber) {
this.hide();
} else {
let filteredMessages: HoverPart[] = [];
let filteredMessages: HoverPartInfo[] = [];
for (let i = 0, len = this._messages.length; i < len; i++) {
const msg = this._messages[i];
const rng = msg.range;
const rng = msg.data.range;
if (rng && rng.startColumn <= range.startColumn && rng.endColumn >= range.endColumn) {
filteredMessages.push(msg);
}
@@ -335,25 +414,45 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
this._hoverOperation.start(mode);
}
hide(): void {
public hide(): void {
this._lastRange = null;
this._hoverOperation.cancel();
super.hide();
if (this._isVisible) {
setTimeout(() => {
// Give commands a chance to see the key
if (!this._isVisible) {
this._hoverVisibleKey.set(false);
}
}, 0);
this._isVisible = false;
this._hover.containerDomNode.classList.toggle('hidden', !this._isVisible);
this._editor.layoutContentWidget(this);
if (this._stoleFocus) {
this._editor.focus();
}
}
this._isChangingDecorations = true;
this._highlightDecorations = this._editor.deltaDecorations(this._highlightDecorations, []);
this._isChangingDecorations = false;
this.renderDisposable.clear();
if (this._renderDisposable) {
this._renderDisposable.dispose();
this._renderDisposable = null;
}
this._colorPicker = null;
}
isColorPickerVisible(): boolean {
if (this._colorPicker) {
return true;
}
return false;
public isColorPickerVisible(): boolean {
return !!this._colorPicker;
}
private _withResult(result: HoverPart[], complete: boolean): void {
public onContentsChanged(): void {
this._hover.onContentsChanged();
}
private _withResult(result: HoverPartInfo[], complete: boolean): void {
this._messages = result;
if (this._lastRange && this._messages.length > 0) {
@@ -363,20 +462,24 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
}
}
private _renderMessages(renderRange: Range, messages: HoverPart[]): void {
this.renderDisposable.dispose();
private _renderMessages(renderRange: Range, messages: HoverPartInfo[]): void {
if (this._renderDisposable) {
this._renderDisposable.dispose();
this._renderDisposable = null;
}
this._colorPicker = null;
// update column from which to show
let renderColumn = Constants.MAX_SAFE_SMALL_INTEGER;
let highlightRange: Range | null = messages[0].range ? Range.lift(messages[0].range) : null;
let highlightRange: Range | null = messages[0].data.range ? Range.lift(messages[0].data.range) : null;
let fragment = document.createDocumentFragment();
let isEmptyHoverContent = true;
let containColorPicker = false;
const markdownDisposeables = new DisposableStore();
const disposables = new DisposableStore();
const markerMessages: MarkerHover[] = [];
messages.forEach((msg) => {
const markdownParts: MarkdownHover[] = [];
messages.forEach((_msg) => {
const msg = _msg.data;
if (!msg.range) {
return;
}
@@ -464,46 +567,37 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
this._colorPicker = widget;
this.showAt(range.getStartPosition(), range, this._shouldFocus);
this.updateContents(fragment);
this._updateContents(fragment);
this._colorPicker.layout();
this.renderDisposable.value = combinedDisposable(colorListener, colorChangeListener, widget, markdownDisposeables);
this._renderDisposable = combinedDisposable(colorListener, colorChangeListener, widget, disposables);
});
} else {
if (msg instanceof MarkerHover) {
markerMessages.push(msg);
isEmptyHoverContent = false;
} else {
msg.contents
.filter(contents => !isEmptyMarkdownString(contents))
.forEach(contents => {
const markdownHoverElement = $('div.hover-row.markdown-hover');
const hoverContentsElement = dom.append(markdownHoverElement, $('div.hover-contents'));
const renderer = markdownDisposeables.add(new MarkdownRenderer({ editor: this._editor }, this._modeService, this._openerService));
markdownDisposeables.add(renderer.onDidRenderAsync(() => {
hoverContentsElement.className = 'hover-contents code-hover-contents';
this._hover.onContentsChanged();
}));
const renderedContents = markdownDisposeables.add(renderer.render(contents));
hoverContentsElement.appendChild(renderedContents.element);
fragment.appendChild(markdownHoverElement);
isEmptyHoverContent = false;
});
if (msg instanceof MarkdownHover) {
markdownParts.push(msg);
}
}
}
});
if (markerMessages.length) {
markerMessages.forEach(msg => fragment.appendChild(this.renderMarkerHover(msg)));
const markerHoverForStatusbar = markerMessages.length === 1 ? markerMessages[0] : markerMessages.sort((a, b) => MarkerSeverity.compare(a.marker.severity, b.marker.severity))[0];
fragment.appendChild(this.renderMarkerStatusbar(markerHoverForStatusbar));
if (markdownParts.length > 0) {
disposables.add(this._markdownHoverParticipant.renderHoverParts(markdownParts, fragment));
}
if (markerMessages.length) {
disposables.add(this._markerHoverParticipant.renderHoverParts(markerMessages, fragment));
}
this._renderDisposable = disposables;
// show
if (!containColorPicker && !isEmptyHoverContent) {
if (!containColorPicker && fragment.hasChildNodes()) {
this.showAt(new Position(renderRange.startLineNumber, renderColumn), highlightRange, this._shouldFocus);
this.updateContents(fragment);
this._updateContents(fragment);
}
this._isChangingDecorations = true;
@@ -514,175 +608,17 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
this._isChangingDecorations = false;
}
private renderMarkerHover(markerHover: MarkerHover): HTMLElement {
const hoverElement = $('div.hover-row');
const markerElement = dom.append(hoverElement, $('div.marker.hover-contents'));
const { source, message, code, relatedInformation } = markerHover.marker;
this._editor.applyFontInfo(markerElement);
const messageElement = dom.append(markerElement, $('span'));
messageElement.style.whiteSpace = 'pre-wrap';
messageElement.innerText = message;
if (source || code) {
// Code has link
if (code && typeof code !== 'string') {
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.target.toString());
this._codeLink.onclick = (e) => {
this._openerService.open(code.target);
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';
} else {
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 (isNonEmptyArray(relatedInformation)) {
for (const { message, resource, startLineNumber, startColumn } of relatedInformation) {
const relatedInfoContainer = dom.append(markerElement, $('div'));
relatedInfoContainer.style.marginTop = '8px';
const a = dom.append(relatedInfoContainer, $('a'));
a.innerText = `${basename(resource)}(${startLineNumber}, ${startColumn}): `;
a.style.cursor = 'pointer';
a.onclick = e => {
e.stopPropagation();
e.preventDefault();
if (this._openerService) {
this._openerService.open(resource.with({ fragment: `${startLineNumber},${startColumn}` }), { fromUserGesture: true }).catch(onUnexpectedError);
}
};
const messageElement = dom.append<HTMLAnchorElement>(relatedInfoContainer, $('span'));
messageElement.innerText = message;
this._editor.applyFontInfo(messageElement);
}
}
return hoverElement;
}
private recentMarkerCodeActionsInfo: { marker: IMarker, hasCodeActions: boolean } | undefined = undefined;
private renderMarkerStatusbar(markerHover: MarkerHover): HTMLElement {
const hoverElement = $('div.hover-row.status-bar');
const disposables = new DisposableStore();
const actionsElement = dom.append(hoverElement, $('div.actions'));
if (markerHover.marker.severity === MarkerSeverity.Error || markerHover.marker.severity === MarkerSeverity.Warning || markerHover.marker.severity === MarkerSeverity.Info) {
disposables.add(this._renderAction(actionsElement, {
label: nls.localize('peek problem', "Peek Problem"),
commandId: NextMarkerAction.ID,
run: () => {
this.hide();
MarkerController.get(this._editor).showAtMarker(markerHover.marker);
this._editor.focus();
}
}));
}
if (!this._editor.getOption(EditorOption.readOnly)) {
const quickfixPlaceholderElement = dom.append(actionsElement, $('div'));
if (this.recentMarkerCodeActionsInfo) {
if (IMarkerData.makeKey(this.recentMarkerCodeActionsInfo.marker) === IMarkerData.makeKey(markerHover.marker)) {
if (!this.recentMarkerCodeActionsInfo.hasCodeActions) {
quickfixPlaceholderElement.textContent = nls.localize('noQuickFixes', "No quick fixes available");
}
} else {
this.recentMarkerCodeActionsInfo = undefined;
}
}
const updatePlaceholderDisposable = disposables.add(disposableTimeout(() => quickfixPlaceholderElement.textContent = nls.localize('checkingForQuickFixes', "Checking for quick fixes..."), 64));
const codeActionsPromise = this.getCodeActions(markerHover.marker);
disposables.add(toDisposable(() => codeActionsPromise.cancel()));
codeActionsPromise.then(actions => {
updatePlaceholderDisposable.dispose();
this.recentMarkerCodeActionsInfo = { marker: markerHover.marker, hasCodeActions: actions.validActions.length > 0 };
if (!this.recentMarkerCodeActionsInfo.hasCodeActions) {
actions.dispose();
quickfixPlaceholderElement.textContent = nls.localize('noQuickFixes', "No quick fixes available");
return;
}
quickfixPlaceholderElement.style.display = 'none';
let showing = false;
disposables.add(toDisposable(() => {
if (!showing) {
actions.dispose();
}
}));
disposables.add(this._renderAction(actionsElement, {
label: nls.localize('quick fixes', "Quick Fix..."),
commandId: QuickFixAction.Id,
run: (target) => {
showing = true;
const controller = QuickFixController.get(this._editor);
const elementPosition = dom.getDomNodePagePosition(target);
// Hide the hover pre-emptively, otherwise the editor can close the code actions
// context menu as well when using keyboard navigation
this.hide();
controller.showCodeActions(markerCodeActionTrigger, actions, {
x: elementPosition.left + 6,
y: elementPosition.top + elementPosition.height + 6
});
}
}));
});
}
this.renderDisposable.value = disposables;
return hoverElement;
}
private getCodeActions(marker: IMarker): CancelablePromise<CodeActionSet> {
return createCancelablePromise(cancellationToken => {
return getCodeActions(
this._editor.getModel()!,
new Range(marker.startLineNumber, marker.startColumn, marker.endLineNumber, marker.endColumn),
markerCodeActionTrigger,
Progress.None,
cancellationToken);
});
}
private static readonly _DECORATION_OPTIONS = ModelDecorationOptions.register({
className: 'hoverHighlight'
});
}
function hoverContentsEquals(first: HoverPart[], second: HoverPart[]): boolean {
if ((!first && second) || (first && !second) || first.length !== second.length) {
function hoverContentsEquals(first: HoverPartInfo[], second: HoverPartInfo[]): boolean {
if (first.length !== second.length) {
return false;
}
for (let i = 0; i < first.length; i++) {
const firstElement = first[i];
const secondElement = second[i];
if (firstElement instanceof MarkerHover && secondElement instanceof MarkerHover) {
return IMarkerData.makeKey(firstElement.marker) === IMarkerData.makeKey(secondElement.marker);
}
if (firstElement instanceof ColorHover || secondElement instanceof ColorHover) {
return false;
}
if (firstElement instanceof MarkerHover || secondElement instanceof MarkerHover) {
return false;
}
if (!markedStringsEquals(firstElement.contents, secondElement.contents)) {
if (!first[i].data.equals(second[i].data)) {
return false;
}
}

View File

@@ -0,0 +1,227 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { RunOnceScheduler } from 'vs/base/common/async';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { hash } from 'vs/base/common/hash';
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { IContentDecorationRenderOptions, IEditorContribution } from 'vs/editor/common/editorCommon';
import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model';
import { InlineHintsProvider, InlineHintsProviderRegistry, InlineHint } from 'vs/editor/common/modes';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { flatten } from 'vs/base/common/arrays';
import { editorInlineHintForeground, editorInlineHintBackground } from 'vs/platform/theme/common/colorRegistry';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { Range } from 'vs/editor/common/core/range';
import { LanguageFeatureRequestDelays } from 'vs/editor/common/modes/languageFeatureRegistry';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { URI } from 'vs/base/common/uri';
import { IRange } from 'vs/base/common/range';
import { assertType } from 'vs/base/common/types';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
const MAX_DECORATORS = 500;
export interface InlineHintsData {
list: InlineHint[];
provider: InlineHintsProvider;
}
export async function getInlineHints(model: ITextModel, ranges: Range[], token: CancellationToken): Promise<InlineHintsData[]> {
const datas: InlineHintsData[] = [];
const providers = InlineHintsProviderRegistry.ordered(model).reverse();
const promises = flatten(providers.map(provider => ranges.map(range => Promise.resolve(provider.provideInlineHints(model, range, token)).then(result => {
if (result) {
datas.push({ list: result, provider });
}
}, err => {
onUnexpectedExternalError(err);
}))));
await Promise.all(promises);
return datas;
}
export class InlineHintsController implements IEditorContribution {
static readonly ID: string = 'editor.contrib.InlineHints';
// static get(editor: ICodeEditor): InlineHintsController {
// return editor.getContribution<InlineHintsController>(this.ID);
// }
private readonly _disposables = new DisposableStore();
private readonly _sessionDisposables = new DisposableStore();
private readonly _getInlineHintsDelays = new LanguageFeatureRequestDelays(InlineHintsProviderRegistry, 250, 2500);
private _decorationsTypeIds: string[] = [];
private _decorationIds: string[] = [];
constructor(
private readonly _editor: ICodeEditor,
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService,
@IThemeService private readonly _themeService: IThemeService,
) {
this._disposables.add(InlineHintsProviderRegistry.onDidChange(() => this._update()));
this._disposables.add(_themeService.onDidColorThemeChange(() => this._update()));
this._disposables.add(_editor.onDidChangeModel(() => this._update()));
this._disposables.add(_editor.onDidChangeModelLanguage(() => this._update()));
this._disposables.add(_editor.onDidChangeConfiguration(e => {
if (e.hasChanged(EditorOption.inlineHints)) {
this._update();
}
}));
this._update();
}
dispose(): void {
this._sessionDisposables.dispose();
this._removeAllDecorations();
this._disposables.dispose();
}
private _update(): void {
this._sessionDisposables.clear();
if (!this._editor.getOption(EditorOption.inlineHints).enabled) {
this._removeAllDecorations();
return;
}
const model = this._editor.getModel();
if (!model || !InlineHintsProviderRegistry.has(model)) {
this._removeAllDecorations();
return;
}
const scheduler = new RunOnceScheduler(async () => {
const t1 = Date.now();
const cts = new CancellationTokenSource();
this._sessionDisposables.add(toDisposable(() => cts.dispose(true)));
const visibleRanges = this._editor.getVisibleRangesPlusViewportAboveBelow();
const result = await getInlineHints(model, visibleRanges, cts.token);
// update moving average
const newDelay = this._getInlineHintsDelays.update(model, Date.now() - t1);
scheduler.delay = newDelay;
// render hints
this._updateHintsDecorators(result);
}, this._getInlineHintsDelays.get(model));
this._sessionDisposables.add(scheduler);
// update inline hints when content or scroll position changes
this._sessionDisposables.add(this._editor.onDidChangeModelContent(() => scheduler.schedule()));
this._disposables.add(this._editor.onDidScrollChange(() => scheduler.schedule()));
scheduler.schedule();
// update inline hints when any any provider fires an event
const providerListener = new DisposableStore();
this._sessionDisposables.add(providerListener);
for (const provider of InlineHintsProviderRegistry.all(model)) {
if (typeof provider.onDidChangeInlineHints === 'function') {
providerListener.add(provider.onDidChangeInlineHints(() => scheduler.schedule()));
}
}
}
private _updateHintsDecorators(hintsData: InlineHintsData[]): void {
const { fontSize, fontFamily } = this._getLayoutInfo();
const backgroundColor = this._themeService.getColorTheme().getColor(editorInlineHintBackground);
const fontColor = this._themeService.getColorTheme().getColor(editorInlineHintForeground);
const newDecorationsTypeIds: string[] = [];
const newDecorationsData: IModelDeltaDecoration[] = [];
for (const { list: hints } of hintsData) {
for (let j = 0; j < hints.length && newDecorationsData.length < MAX_DECORATORS; j++) {
const { text, range, description: hoverMessage, whitespaceBefore, whitespaceAfter } = hints[j];
const marginBefore = whitespaceBefore ? (fontSize / 3) | 0 : 0;
const marginAfter = whitespaceAfter ? (fontSize / 3) | 0 : 0;
const before: IContentDecorationRenderOptions = {
contentText: text,
backgroundColor: `${backgroundColor}`,
color: `${fontColor}`,
margin: `0px ${marginAfter}px 0px ${marginBefore}px`,
fontSize: `${fontSize}px`,
fontFamily: fontFamily,
padding: `0px ${(fontSize / 4) | 0}px`,
borderRadius: `${(fontSize / 4) | 0}px`,
};
const key = 'inlineHints-' + hash(before).toString(16);
this._codeEditorService.registerDecorationType(key, { before }, undefined, this._editor);
// decoration types are ref-counted which means we only need to
// call register und remove equally often
newDecorationsTypeIds.push(key);
const options = this._codeEditorService.resolveDecorationOptions(key, true);
if (typeof hoverMessage === 'string') {
options.hoverMessage = new MarkdownString().appendText(hoverMessage);
} else if (hoverMessage) {
options.hoverMessage = hoverMessage;
}
newDecorationsData.push({
range,
options
});
}
}
this._decorationsTypeIds.forEach(this._codeEditorService.removeDecorationType, this._codeEditorService);
this._decorationsTypeIds = newDecorationsTypeIds;
this._decorationIds = this._editor.deltaDecorations(this._decorationIds, newDecorationsData);
}
private _getLayoutInfo() {
const options = this._editor.getOption(EditorOption.inlineHints);
const editorFontSize = this._editor.getOption(EditorOption.fontSize);
let fontSize = options.fontSize;
if (!fontSize || fontSize < 5 || fontSize > editorFontSize) {
fontSize = (editorFontSize * .9) | 0;
}
const fontFamily = options.fontFamily;
return { fontSize, fontFamily };
}
private _removeAllDecorations(): void {
this._decorationIds = this._editor.deltaDecorations(this._decorationIds, []);
this._decorationsTypeIds.forEach(this._codeEditorService.removeDecorationType, this._codeEditorService);
this._decorationsTypeIds = [];
}
}
registerEditorContribution(InlineHintsController.ID, InlineHintsController);
CommandsRegistry.registerCommand('_executeInlineHintProvider', async (accessor, ...args: [URI, IRange]): Promise<InlineHint[]> => {
const [uri, range] = args;
assertType(URI.isUri(uri));
assertType(Range.isIRange(range));
const ref = await accessor.get(ITextModelService).createModelReference(uri);
try {
const data = await getInlineHints(ref.object.textEditorModel, [Range.lift(range)], CancellationToken.None);
return flatten(data.map(item => item.list)).sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range));
} finally {
ref.dispose();
}
});

View File

@@ -939,43 +939,39 @@ export class TransposeAction extends EditorAction {
export abstract class AbstractCaseAction extends EditorAction {
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
let selections = editor.getSelections();
const selections = editor.getSelections();
if (selections === null) {
return;
}
let model = editor.getModel();
const model = editor.getModel();
if (model === null) {
return;
}
let wordSeparators = editor.getOption(EditorOption.wordSeparators);
const wordSeparators = editor.getOption(EditorOption.wordSeparators);
const textEdits: IIdentifiedSingleEditOperation[] = [];
let commands: ICommand[] = [];
for (let i = 0, len = selections.length; i < len; i++) {
let selection = selections[i];
for (const selection of selections) {
if (selection.isEmpty()) {
let cursor = selection.getStartPosition();
const cursor = selection.getStartPosition();
const word = editor.getConfiguredWordAtPosition(cursor);
if (!word) {
continue;
}
let wordRange = new Range(cursor.lineNumber, word.startColumn, cursor.lineNumber, word.endColumn);
let text = model.getValueInRange(wordRange);
commands.push(new ReplaceCommandThatPreservesSelection(wordRange, this._modifyText(text, wordSeparators),
new Selection(cursor.lineNumber, cursor.column, cursor.lineNumber, cursor.column)));
const wordRange = new Range(cursor.lineNumber, word.startColumn, cursor.lineNumber, word.endColumn);
const text = model.getValueInRange(wordRange);
textEdits.push(EditOperation.replace(wordRange, this._modifyText(text, wordSeparators)));
} else {
let text = model.getValueInRange(selection);
commands.push(new ReplaceCommandThatPreservesSelection(selection, this._modifyText(text, wordSeparators), selection));
const text = model.getValueInRange(selection);
textEdits.push(EditOperation.replace(selection, this._modifyText(text, wordSeparators)));
}
}
editor.pushUndoStop();
editor.executeCommands(this.id, commands);
editor.executeEdits(this.id, textEdits);
editor.pushUndoStop();
}
@@ -1049,6 +1045,25 @@ export class TitleCaseAction extends AbstractCaseAction {
}
}
export class SnakeCaseAction extends AbstractCaseAction {
constructor() {
super({
id: 'editor.action.transformToSnakecase',
label: nls.localize('editor.transformToSnakecase', "Transform to Snake Case"),
alias: 'Transform to Snake Case',
precondition: EditorContextKeys.writable
});
}
protected _modifyText(text: string, wordSeparators: string): string {
return (text
.replace(/(\p{Ll})(\p{Lu})/gmu, '$1_$2')
.replace(/([^\b_])(\p{Lu})(\p{Ll})/gmu, '$1_$2$3')
.toLocaleLowerCase()
);
}
}
registerEditorAction(CopyLinesUpAction);
registerEditorAction(CopyLinesDownAction);
registerEditorAction(DuplicateSelectionAction);
@@ -1069,3 +1084,4 @@ registerEditorAction(TransposeAction);
registerEditorAction(UpperCaseAction);
registerEditorAction(LowerCaseAction);
registerEditorAction(TitleCaseAction);
registerEditorAction(SnakeCaseAction);

View File

@@ -299,13 +299,13 @@ export class MoveLinesCommand implements ICommand {
}
}
private matchEnterRule(model: ITextModel, indentConverter: IIndentConverter, tabSize: number, line: number, oneLineAbove: number, oneLineAboveText?: string) {
private matchEnterRule(model: ITextModel, indentConverter: IIndentConverter, tabSize: number, line: number, oneLineAbove: number, previousLineText?: string) {
let validPrecedingLine = oneLineAbove;
while (validPrecedingLine >= 1) {
// ship empty lines as empty lines just inherit indentation
let lineContent;
if (validPrecedingLine === oneLineAbove && oneLineAboveText !== undefined) {
lineContent = oneLineAboveText;
if (validPrecedingLine === oneLineAbove && previousLineText !== undefined) {
lineContent = previousLineText;
} else {
lineContent = model.getLineContent(validPrecedingLine);
}

View File

@@ -207,8 +207,8 @@ suite('Editor Contrib - Duplicate Selection', () => {
withTestCodeEditor(lines.join('\n'), {}, (editor) => {
editor.setSelections(selections);
duplicateSelectionAction.run(null!, editor, {});
assert.deepEqual(editor.getValue(), expectedLines.join('\n'));
assert.deepEqual(editor.getSelections()!.map(s => s.toString()), expectedSelections.map(s => s.toString()));
assert.deepStrictEqual(editor.getValue(), expectedLines.join('\n'));
assert.deepStrictEqual(editor.getSelections()!.map(s => s.toString()), expectedSelections.map(s => s.toString()));
});
}

View File

@@ -8,7 +8,7 @@ import { Position } from 'vs/editor/common/core/position';
import { Selection } from 'vs/editor/common/core/selection';
import { Handler } from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
import { TitleCaseAction, DeleteAllLeftAction, DeleteAllRightAction, IndentLinesAction, InsertLineAfterAction, InsertLineBeforeAction, JoinLinesAction, LowerCaseAction, SortLinesAscendingAction, SortLinesDescendingAction, TransposeAction, UpperCaseAction, DeleteLinesAction } from 'vs/editor/contrib/linesOperations/linesOperations';
import { TitleCaseAction, DeleteAllLeftAction, DeleteAllRightAction, IndentLinesAction, InsertLineAfterAction, InsertLineBeforeAction, JoinLinesAction, LowerCaseAction, SortLinesAscendingAction, SortLinesDescendingAction, TransposeAction, UpperCaseAction, DeleteLinesAction, SnakeCaseAction } from 'vs/editor/contrib/linesOperations/linesOperations';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
import type { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@@ -19,7 +19,7 @@ function assertSelection(editor: ICodeEditor, expected: Selection | Selection[])
if (!Array.isArray(expected)) {
expected = [expected];
}
assert.deepEqual(editor.getSelections(), expected);
assert.deepStrictEqual(editor.getSelections(), expected);
}
function executeAction(action: EditorAction, editor: ICodeEditor): void {
@@ -40,7 +40,7 @@ suite('Editor Contrib - Line Operations', () => {
editor.setSelection(new Selection(1, 1, 3, 5));
executeAction(sortLinesAscendingAction, editor);
assert.deepEqual(model.getLinesContent(), [
assert.deepStrictEqual(model.getLinesContent(), [
'alpha',
'beta',
'omicron'
@@ -65,7 +65,7 @@ suite('Editor Contrib - Line Operations', () => {
editor.setSelections([new Selection(1, 1, 3, 5), new Selection(5, 1, 7, 5)]);
executeAction(sortLinesAscendingAction, editor);
assert.deepEqual(model.getLinesContent(), [
assert.deepStrictEqual(model.getLinesContent(), [
'alpha',
'beta',
'omicron',
@@ -79,7 +79,7 @@ suite('Editor Contrib - Line Operations', () => {
new Selection(5, 1, 7, 7)
];
editor.getSelections()!.forEach((actualSelection, index) => {
assert.deepEqual(actualSelection.toString(), expectedSelections[index].toString());
assert.deepStrictEqual(actualSelection.toString(), expectedSelections[index].toString());
});
});
});
@@ -98,7 +98,7 @@ suite('Editor Contrib - Line Operations', () => {
editor.setSelection(new Selection(1, 1, 3, 7));
executeAction(sortLinesDescendingAction, editor);
assert.deepEqual(model.getLinesContent(), [
assert.deepStrictEqual(model.getLinesContent(), [
'omicron',
'beta',
'alpha'
@@ -123,7 +123,7 @@ suite('Editor Contrib - Line Operations', () => {
editor.setSelections([new Selection(1, 1, 3, 7), new Selection(5, 1, 7, 7)]);
executeAction(sortLinesDescendingAction, editor);
assert.deepEqual(model.getLinesContent(), [
assert.deepStrictEqual(model.getLinesContent(), [
'omicron',
'beta',
'alpha',
@@ -137,7 +137,7 @@ suite('Editor Contrib - Line Operations', () => {
new Selection(5, 1, 7, 5)
];
editor.getSelections()!.forEach((actualSelection, index) => {
assert.deepEqual(actualSelection.toString(), expectedSelections[index].toString());
assert.deepStrictEqual(actualSelection.toString(), expectedSelections[index].toString());
});
});
});
@@ -157,12 +157,12 @@ suite('Editor Contrib - Line Operations', () => {
editor.setSelection(new Selection(1, 2, 1, 2));
executeAction(deleteAllLeftAction, editor);
assert.equal(model.getLineContent(1), 'ne');
assert.strictEqual(model.getLineContent(1), 'ne');
editor.setSelections([new Selection(2, 2, 2, 2), new Selection(3, 2, 3, 2)]);
executeAction(deleteAllLeftAction, editor);
assert.equal(model.getLineContent(2), 'wo');
assert.equal(model.getLineContent(3), 'hree');
assert.strictEqual(model.getLineContent(2), 'wo');
assert.strictEqual(model.getLineContent(3), 'hree');
});
});
@@ -178,16 +178,16 @@ suite('Editor Contrib - Line Operations', () => {
editor.setSelection(new Selection(2, 1, 2, 1));
executeAction(deleteAllLeftAction, editor);
assert.equal(model.getLineContent(1), 'onetwo');
assert.strictEqual(model.getLineContent(1), 'onetwo');
editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1)]);
executeAction(deleteAllLeftAction, editor);
assert.equal(model.getLinesContent()[0], 'onetwothree');
assert.equal(model.getLinesContent().length, 1);
assert.strictEqual(model.getLinesContent()[0], 'onetwothree');
assert.strictEqual(model.getLinesContent().length, 1);
editor.setSelection(new Selection(1, 1, 1, 1));
executeAction(deleteAllLeftAction, editor);
assert.equal(model.getLinesContent()[0], 'onetwothree');
assert.strictEqual(model.getLinesContent()[0], 'onetwothree');
});
});
@@ -213,25 +213,25 @@ suite('Editor Contrib - Line Operations', () => {
executeAction(deleteAllLeftAction, editor);
let selections = editor.getSelections()!;
assert.equal(model.getLineContent(2), '');
assert.equal(model.getLineContent(3), ' waso waso');
assert.equal(model.getLineContent(5), '');
assert.strictEqual(model.getLineContent(2), '');
assert.strictEqual(model.getLineContent(3), ' waso waso');
assert.strictEqual(model.getLineContent(5), '');
assert.deepEqual([
assert.deepStrictEqual([
selections[0].startLineNumber,
selections[0].startColumn,
selections[0].endLineNumber,
selections[0].endColumn
], [3, 1, 3, 1]);
assert.deepEqual([
assert.deepStrictEqual([
selections[1].startLineNumber,
selections[1].startColumn,
selections[1].endLineNumber,
selections[1].endColumn
], [2, 1, 2, 1]);
assert.deepEqual([
assert.deepStrictEqual([
selections[2].startLineNumber,
selections[2].startColumn,
selections[2].endLineNumber,
@@ -241,17 +241,17 @@ suite('Editor Contrib - Line Operations', () => {
executeAction(deleteAllLeftAction, editor);
selections = editor.getSelections()!;
assert.equal(model.getLineContent(1), 'hi my name is Carlos Matos waso waso');
assert.equal(selections.length, 2);
assert.strictEqual(model.getLineContent(1), 'hi my name is Carlos Matos waso waso');
assert.strictEqual(selections.length, 2);
assert.deepEqual([
assert.deepStrictEqual([
selections[0].startLineNumber,
selections[0].startColumn,
selections[0].endLineNumber,
selections[0].endColumn
], [1, 27, 1, 27]);
assert.deepEqual([
assert.deepStrictEqual([
selections[1].startLineNumber,
selections[1].startColumn,
selections[1].endLineNumber,
@@ -277,23 +277,23 @@ suite('Editor Contrib - Line Operations', () => {
editor.setSelections([new Selection(1, 2, 1, 2), new Selection(1, 4, 1, 4)]);
executeAction(deleteAllLeftAction, editor);
assert.equal(model.getLineContent(1), 'lo');
assert.strictEqual(model.getLineContent(1), 'lo');
editor.setSelections([new Selection(2, 2, 2, 2), new Selection(2, 4, 2, 5)]);
executeAction(deleteAllLeftAction, editor);
assert.equal(model.getLineContent(2), 'd');
assert.strictEqual(model.getLineContent(2), 'd');
editor.setSelections([new Selection(3, 2, 3, 5), new Selection(3, 7, 3, 7)]);
executeAction(deleteAllLeftAction, editor);
assert.equal(model.getLineContent(3), 'world');
assert.strictEqual(model.getLineContent(3), 'world');
editor.setSelections([new Selection(4, 3, 4, 3), new Selection(4, 5, 5, 4)]);
executeAction(deleteAllLeftAction, editor);
assert.equal(model.getLineContent(4), 'jour');
assert.strictEqual(model.getLineContent(4), 'jour');
editor.setSelections([new Selection(5, 3, 6, 3), new Selection(6, 5, 7, 5), new Selection(7, 7, 7, 7)]);
executeAction(deleteAllLeftAction, editor);
assert.equal(model.getLineContent(5), 'world');
assert.strictEqual(model.getLineContent(5), 'world');
});
});
@@ -310,16 +310,16 @@ suite('Editor Contrib - Line Operations', () => {
editor.setSelection(new Selection(1, 1, 1, 1));
editor.trigger('keyboard', Handler.Type, { text: 'Typing some text here on line ' });
assert.equal(model.getLineContent(1), 'Typing some text here on line one');
assert.deepEqual(editor.getSelection(), new Selection(1, 31, 1, 31));
assert.strictEqual(model.getLineContent(1), 'Typing some text here on line one');
assert.deepStrictEqual(editor.getSelection(), new Selection(1, 31, 1, 31));
executeAction(deleteAllLeftAction, editor);
assert.equal(model.getLineContent(1), 'one');
assert.deepEqual(editor.getSelection(), new Selection(1, 1, 1, 1));
assert.strictEqual(model.getLineContent(1), 'one');
assert.deepStrictEqual(editor.getSelection(), new Selection(1, 1, 1, 1));
CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
assert.equal(model.getLineContent(1), 'Typing some text here on line one');
assert.deepEqual(editor.getSelection(), new Selection(1, 31, 1, 31));
assert.strictEqual(model.getLineContent(1), 'Typing some text here on line one');
assert.deepStrictEqual(editor.getSelection(), new Selection(1, 31, 1, 31));
});
});
});
@@ -345,27 +345,27 @@ suite('Editor Contrib - Line Operations', () => {
editor.setSelection(new Selection(1, 2, 1, 2));
executeAction(joinLinesAction, editor);
assert.equal(model.getLineContent(1), 'hello world');
assert.strictEqual(model.getLineContent(1), 'hello world');
assertSelection(editor, new Selection(1, 6, 1, 6));
editor.setSelection(new Selection(2, 2, 2, 2));
executeAction(joinLinesAction, editor);
assert.equal(model.getLineContent(2), 'hello world');
assert.strictEqual(model.getLineContent(2), 'hello world');
assertSelection(editor, new Selection(2, 7, 2, 7));
editor.setSelection(new Selection(3, 2, 3, 2));
executeAction(joinLinesAction, editor);
assert.equal(model.getLineContent(3), 'hello world');
assert.strictEqual(model.getLineContent(3), 'hello world');
assertSelection(editor, new Selection(3, 7, 3, 7));
editor.setSelection(new Selection(4, 2, 5, 3));
executeAction(joinLinesAction, editor);
assert.equal(model.getLineContent(4), 'hello world');
assert.strictEqual(model.getLineContent(4), 'hello world');
assertSelection(editor, new Selection(4, 2, 4, 8));
editor.setSelection(new Selection(5, 1, 7, 3));
executeAction(joinLinesAction, editor);
assert.equal(model.getLineContent(5), 'hello world');
assert.strictEqual(model.getLineContent(5), 'hello world');
assertSelection(editor, new Selection(5, 1, 5, 3));
});
});
@@ -381,8 +381,8 @@ suite('Editor Contrib - Line Operations', () => {
editor.setSelection(new Selection(2, 1, 2, 1));
executeAction(joinLinesAction, editor);
assert.equal(model.getLineContent(1), 'hello');
assert.equal(model.getLineContent(2), 'world');
assert.strictEqual(model.getLineContent(1), 'hello');
assert.strictEqual(model.getLineContent(2), 'world');
assertSelection(editor, new Selection(2, 6, 2, 6));
});
});
@@ -416,7 +416,7 @@ suite('Editor Contrib - Line Operations', () => {
]);
executeAction(joinLinesAction, editor);
assert.equal(model.getLinesContent().join('\n'), 'hello world\nhello world\nhello world\nhello world\n\nhello world');
assert.strictEqual(model.getLinesContent().join('\n'), 'hello world\nhello world\nhello world\nhello world\n\nhello world');
assertSelection(editor, [
/** primary cursor */
new Selection(3, 4, 3, 8),
@@ -440,16 +440,16 @@ suite('Editor Contrib - Line Operations', () => {
editor.setSelection(new Selection(1, 6, 1, 6));
editor.trigger('keyboard', Handler.Type, { text: ' my dear' });
assert.equal(model.getLineContent(1), 'hello my dear');
assert.deepEqual(editor.getSelection(), new Selection(1, 14, 1, 14));
assert.strictEqual(model.getLineContent(1), 'hello my dear');
assert.deepStrictEqual(editor.getSelection(), new Selection(1, 14, 1, 14));
executeAction(joinLinesAction, editor);
assert.equal(model.getLineContent(1), 'hello my dear world');
assert.deepEqual(editor.getSelection(), new Selection(1, 14, 1, 14));
assert.strictEqual(model.getLineContent(1), 'hello my dear world');
assert.deepStrictEqual(editor.getSelection(), new Selection(1, 14, 1, 14));
CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
assert.equal(model.getLineContent(1), 'hello my dear');
assert.deepEqual(editor.getSelection(), new Selection(1, 14, 1, 14));
assert.strictEqual(model.getLineContent(1), 'hello my dear');
assert.deepStrictEqual(editor.getSelection(), new Selection(1, 14, 1, 14));
});
});
});
@@ -467,27 +467,27 @@ suite('Editor Contrib - Line Operations', () => {
editor.setSelection(new Selection(1, 1, 1, 1));
executeAction(transposeAction, editor);
assert.equal(model.getLineContent(1), 'hello world');
assert.strictEqual(model.getLineContent(1), 'hello world');
assertSelection(editor, new Selection(1, 2, 1, 2));
editor.setSelection(new Selection(1, 6, 1, 6));
executeAction(transposeAction, editor);
assert.equal(model.getLineContent(1), 'hell oworld');
assert.strictEqual(model.getLineContent(1), 'hell oworld');
assertSelection(editor, new Selection(1, 7, 1, 7));
editor.setSelection(new Selection(1, 12, 1, 12));
executeAction(transposeAction, editor);
assert.equal(model.getLineContent(1), 'hell oworl');
assert.strictEqual(model.getLineContent(1), 'hell oworl');
assertSelection(editor, new Selection(2, 2, 2, 2));
editor.setSelection(new Selection(3, 1, 3, 1));
executeAction(transposeAction, editor);
assert.equal(model.getLineContent(3), '');
assert.strictEqual(model.getLineContent(3), '');
assertSelection(editor, new Selection(4, 1, 4, 1));
editor.setSelection(new Selection(4, 2, 4, 2));
executeAction(transposeAction, editor);
assert.equal(model.getLineContent(4), ' ');
assert.strictEqual(model.getLineContent(4), ' ');
assertSelection(editor, new Selection(4, 3, 4, 3));
}
);
@@ -509,22 +509,22 @@ suite('Editor Contrib - Line Operations', () => {
editor.setSelection(new Selection(1, 1, 1, 1));
executeAction(transposeAction, editor);
assert.equal(model.getLineContent(2), '');
assert.strictEqual(model.getLineContent(2), '');
assertSelection(editor, new Selection(2, 1, 2, 1));
editor.setSelection(new Selection(3, 6, 3, 6));
executeAction(transposeAction, editor);
assert.equal(model.getLineContent(4), 'oworld');
assert.strictEqual(model.getLineContent(4), 'oworld');
assertSelection(editor, new Selection(4, 2, 4, 2));
editor.setSelection(new Selection(6, 12, 6, 12));
executeAction(transposeAction, editor);
assert.equal(model.getLineContent(7), 'd');
assert.strictEqual(model.getLineContent(7), 'd');
assertSelection(editor, new Selection(7, 2, 7, 2));
editor.setSelection(new Selection(8, 12, 8, 12));
executeAction(transposeAction, editor);
assert.equal(model.getLineContent(8), 'hello world');
assert.strictEqual(model.getLineContent(8), 'hello world');
assertSelection(editor, new Selection(8, 12, 8, 12));
}
);
@@ -534,52 +534,131 @@ suite('Editor Contrib - Line Operations', () => {
withTestCodeEditor(
[
'hello world',
'öçşğü'
'öçşğü',
'parseHTMLString',
'getElementById',
'insertHTML',
'PascalCase',
'CSSSelectorsList',
'iD',
'tEST',
'öçşÖÇŞğüĞÜ',
'audioConverter.convertM4AToMP3();',
'snake_case',
'Capital_Snake_Case',
`function helloWorld() {
return someGlobalObject.printHelloWorld("en", "utf-8");
}
helloWorld();`.replace(/^\s+/gm, '')
], {}, (editor) => {
let model = editor.getModel()!;
let uppercaseAction = new UpperCaseAction();
let lowercaseAction = new LowerCaseAction();
let titlecaseAction = new TitleCaseAction();
let snakecaseAction = new SnakeCaseAction();
editor.setSelection(new Selection(1, 1, 1, 12));
executeAction(uppercaseAction, editor);
assert.equal(model.getLineContent(1), 'HELLO WORLD');
assert.strictEqual(model.getLineContent(1), 'HELLO WORLD');
assertSelection(editor, new Selection(1, 1, 1, 12));
editor.setSelection(new Selection(1, 1, 1, 12));
executeAction(lowercaseAction, editor);
assert.equal(model.getLineContent(1), 'hello world');
assert.strictEqual(model.getLineContent(1), 'hello world');
assertSelection(editor, new Selection(1, 1, 1, 12));
editor.setSelection(new Selection(1, 3, 1, 3));
executeAction(uppercaseAction, editor);
assert.equal(model.getLineContent(1), 'HELLO world');
assert.strictEqual(model.getLineContent(1), 'HELLO world');
assertSelection(editor, new Selection(1, 3, 1, 3));
editor.setSelection(new Selection(1, 4, 1, 4));
executeAction(lowercaseAction, editor);
assert.equal(model.getLineContent(1), 'hello world');
assert.strictEqual(model.getLineContent(1), 'hello world');
assertSelection(editor, new Selection(1, 4, 1, 4));
editor.setSelection(new Selection(1, 1, 1, 12));
executeAction(titlecaseAction, editor);
assert.equal(model.getLineContent(1), 'Hello World');
assert.strictEqual(model.getLineContent(1), 'Hello World');
assertSelection(editor, new Selection(1, 1, 1, 12));
editor.setSelection(new Selection(2, 1, 2, 6));
executeAction(uppercaseAction, editor);
assert.equal(model.getLineContent(2), 'ÖÇŞĞÜ');
assert.strictEqual(model.getLineContent(2), 'ÖÇŞĞÜ');
assertSelection(editor, new Selection(2, 1, 2, 6));
editor.setSelection(new Selection(2, 1, 2, 6));
executeAction(lowercaseAction, editor);
assert.equal(model.getLineContent(2), 'öçşğü');
assert.strictEqual(model.getLineContent(2), 'öçşğü');
assertSelection(editor, new Selection(2, 1, 2, 6));
editor.setSelection(new Selection(2, 1, 2, 6));
executeAction(titlecaseAction, editor);
assert.equal(model.getLineContent(2), 'Öçşğü');
assert.strictEqual(model.getLineContent(2), 'Öçşğü');
assertSelection(editor, new Selection(2, 1, 2, 6));
editor.setSelection(new Selection(3, 1, 3, 16));
executeAction(snakecaseAction, editor);
assert.strictEqual(model.getLineContent(3), 'parse_html_string');
assertSelection(editor, new Selection(3, 1, 3, 18));
editor.setSelection(new Selection(4, 1, 4, 15));
executeAction(snakecaseAction, editor);
assert.strictEqual(model.getLineContent(4), 'get_element_by_id');
assertSelection(editor, new Selection(4, 1, 4, 18));
editor.setSelection(new Selection(5, 1, 5, 11));
executeAction(snakecaseAction, editor);
assert.strictEqual(model.getLineContent(5), 'insert_html');
assertSelection(editor, new Selection(5, 1, 5, 12));
editor.setSelection(new Selection(6, 1, 6, 11));
executeAction(snakecaseAction, editor);
assert.strictEqual(model.getLineContent(6), 'pascal_case');
assertSelection(editor, new Selection(6, 1, 6, 12));
editor.setSelection(new Selection(7, 1, 7, 17));
executeAction(snakecaseAction, editor);
assert.strictEqual(model.getLineContent(7), 'css_selectors_list');
assertSelection(editor, new Selection(7, 1, 7, 19));
editor.setSelection(new Selection(8, 1, 8, 3));
executeAction(snakecaseAction, editor);
assert.strictEqual(model.getLineContent(8), 'i_d');
assertSelection(editor, new Selection(8, 1, 8, 4));
editor.setSelection(new Selection(9, 1, 9, 5));
executeAction(snakecaseAction, editor);
assert.strictEqual(model.getLineContent(9), 't_est');
assertSelection(editor, new Selection(9, 1, 9, 6));
editor.setSelection(new Selection(10, 1, 10, 11));
executeAction(snakecaseAction, editor);
assert.strictEqual(model.getLineContent(10), 'öçş_öç_şğü_ğü');
assertSelection(editor, new Selection(10, 1, 10, 14));
editor.setSelection(new Selection(11, 1, 11, 34));
executeAction(snakecaseAction, editor);
assert.strictEqual(model.getLineContent(11), 'audio_converter.convert_m4a_to_mp3();');
assertSelection(editor, new Selection(11, 1, 11, 38));
editor.setSelection(new Selection(12, 1, 12, 11));
executeAction(snakecaseAction, editor);
assert.strictEqual(model.getLineContent(12), 'snake_case');
assertSelection(editor, new Selection(12, 1, 12, 11));
editor.setSelection(new Selection(13, 1, 13, 19));
executeAction(snakecaseAction, editor);
assert.strictEqual(model.getLineContent(13), 'capital_snake_case');
assertSelection(editor, new Selection(13, 1, 13, 19));
editor.setSelection(new Selection(14, 1, 17, 14));
executeAction(snakecaseAction, editor);
assert.strictEqual(model.getValueInRange(new Selection(14, 1, 17, 15)), `function hello_world() {
return some_global_object.print_hello_world("en", "utf-8");
}
hello_world();`.replace(/^\s+/gm, ''));
assertSelection(editor, new Selection(14, 1, 17, 15));
}
);
@@ -597,27 +676,27 @@ suite('Editor Contrib - Line Operations', () => {
editor.setSelection(new Selection(1, 1, 1, 12));
executeAction(titlecaseAction, editor);
assert.equal(model.getLineContent(1), 'Foo Bar Baz');
assert.strictEqual(model.getLineContent(1), 'Foo Bar Baz');
editor.setSelection(new Selection(2, 1, 2, 12));
executeAction(titlecaseAction, editor);
assert.equal(model.getLineContent(2), 'Foo\'Bar\'Baz');
assert.strictEqual(model.getLineContent(2), 'Foo\'Bar\'Baz');
editor.setSelection(new Selection(3, 1, 3, 12));
executeAction(titlecaseAction, editor);
assert.equal(model.getLineContent(3), 'Foo[Bar]Baz');
assert.strictEqual(model.getLineContent(3), 'Foo[Bar]Baz');
editor.setSelection(new Selection(4, 1, 4, 12));
executeAction(titlecaseAction, editor);
assert.equal(model.getLineContent(4), 'Foo`Bar~Baz');
assert.strictEqual(model.getLineContent(4), 'Foo`Bar~Baz');
editor.setSelection(new Selection(5, 1, 5, 12));
executeAction(titlecaseAction, editor);
assert.equal(model.getLineContent(5), 'Foo^Bar%Baz');
assert.strictEqual(model.getLineContent(5), 'Foo^Bar%Baz');
editor.setSelection(new Selection(6, 1, 6, 12));
executeAction(titlecaseAction, editor);
assert.equal(model.getLineContent(6), 'Foo$Bar!Baz');
assert.strictEqual(model.getLineContent(6), 'Foo$Bar!Baz');
}
);
@@ -632,22 +711,22 @@ suite('Editor Contrib - Line Operations', () => {
editor.setSelection(new Selection(1, 1, 1, 1));
executeAction(uppercaseAction, editor);
assert.equal(model.getLineContent(1), '');
assert.strictEqual(model.getLineContent(1), '');
assertSelection(editor, new Selection(1, 1, 1, 1));
editor.setSelection(new Selection(1, 1, 1, 1));
executeAction(lowercaseAction, editor);
assert.equal(model.getLineContent(1), '');
assert.strictEqual(model.getLineContent(1), '');
assertSelection(editor, new Selection(1, 1, 1, 1));
editor.setSelection(new Selection(2, 2, 2, 2));
executeAction(uppercaseAction, editor);
assert.equal(model.getLineContent(2), ' ');
assert.strictEqual(model.getLineContent(2), ' ');
assertSelection(editor, new Selection(2, 2, 2, 2));
editor.setSelection(new Selection(2, 2, 2, 2));
executeAction(lowercaseAction, editor);
assert.equal(model.getLineContent(2), ' ');
assert.strictEqual(model.getLineContent(2), ' ');
assertSelection(editor, new Selection(2, 2, 2, 2));
}
);
@@ -660,18 +739,18 @@ suite('Editor Contrib - Line Operations', () => {
const action = new DeleteAllRightAction();
executeAction(action, editor);
assert.deepEqual(model.getLinesContent(), ['']);
assert.deepEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]);
assert.deepStrictEqual(model.getLinesContent(), ['']);
assert.deepStrictEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]);
editor.setSelection(new Selection(1, 1, 1, 1));
executeAction(action, editor);
assert.deepEqual(model.getLinesContent(), ['']);
assert.deepEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]);
assert.deepStrictEqual(model.getLinesContent(), ['']);
assert.deepStrictEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]);
editor.setSelections([new Selection(1, 1, 1, 1), new Selection(1, 1, 1, 1), new Selection(1, 1, 1, 1)]);
executeAction(action, editor);
assert.deepEqual(model.getLinesContent(), ['']);
assert.deepEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]);
assert.deepStrictEqual(model.getLinesContent(), ['']);
assert.deepStrictEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]);
});
});
@@ -685,18 +764,18 @@ suite('Editor Contrib - Line Operations', () => {
editor.setSelection(new Selection(1, 2, 1, 5));
executeAction(action, editor);
assert.deepEqual(model.getLinesContent(), ['ho', 'world']);
assert.deepEqual(editor.getSelections(), [new Selection(1, 2, 1, 2)]);
assert.deepStrictEqual(model.getLinesContent(), ['ho', 'world']);
assert.deepStrictEqual(editor.getSelections(), [new Selection(1, 2, 1, 2)]);
editor.setSelection(new Selection(1, 1, 2, 4));
executeAction(action, editor);
assert.deepEqual(model.getLinesContent(), ['ld']);
assert.deepEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]);
assert.deepStrictEqual(model.getLinesContent(), ['ld']);
assert.deepStrictEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]);
editor.setSelection(new Selection(1, 1, 1, 3));
executeAction(action, editor);
assert.deepEqual(model.getLinesContent(), ['']);
assert.deepEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]);
assert.deepStrictEqual(model.getLinesContent(), ['']);
assert.deepStrictEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]);
});
});
@@ -710,13 +789,13 @@ suite('Editor Contrib - Line Operations', () => {
editor.setSelection(new Selection(1, 3, 1, 3));
executeAction(action, editor);
assert.deepEqual(model.getLinesContent(), ['he', 'world']);
assert.deepEqual(editor.getSelections(), [new Selection(1, 3, 1, 3)]);
assert.deepStrictEqual(model.getLinesContent(), ['he', 'world']);
assert.deepStrictEqual(editor.getSelections(), [new Selection(1, 3, 1, 3)]);
editor.setSelection(new Selection(2, 1, 2, 1));
executeAction(action, editor);
assert.deepEqual(model.getLinesContent(), ['he', '']);
assert.deepEqual(editor.getSelections(), [new Selection(2, 1, 2, 1)]);
assert.deepStrictEqual(model.getLinesContent(), ['he', '']);
assert.deepStrictEqual(editor.getSelections(), [new Selection(2, 1, 2, 1)]);
});
});
@@ -730,18 +809,18 @@ suite('Editor Contrib - Line Operations', () => {
editor.setSelection(new Selection(1, 6, 1, 6));
executeAction(action, editor);
assert.deepEqual(model.getLinesContent(), ['helloworld']);
assert.deepEqual(editor.getSelections(), [new Selection(1, 6, 1, 6)]);
assert.deepStrictEqual(model.getLinesContent(), ['helloworld']);
assert.deepStrictEqual(editor.getSelections(), [new Selection(1, 6, 1, 6)]);
editor.setSelection(new Selection(1, 6, 1, 6));
executeAction(action, editor);
assert.deepEqual(model.getLinesContent(), ['hello']);
assert.deepEqual(editor.getSelections(), [new Selection(1, 6, 1, 6)]);
assert.deepStrictEqual(model.getLinesContent(), ['hello']);
assert.deepStrictEqual(editor.getSelections(), [new Selection(1, 6, 1, 6)]);
editor.setSelection(new Selection(1, 6, 1, 6));
executeAction(action, editor);
assert.deepEqual(model.getLinesContent(), ['hello']);
assert.deepEqual(editor.getSelections(), [new Selection(1, 6, 1, 6)]);
assert.deepStrictEqual(model.getLinesContent(), ['hello']);
assert.deepStrictEqual(editor.getSelections(), [new Selection(1, 6, 1, 6)]);
});
});
@@ -760,35 +839,35 @@ suite('Editor Contrib - Line Operations', () => {
new Selection(3, 4, 3, 4),
]);
executeAction(action, editor);
assert.deepEqual(model.getLinesContent(), ['hethere', 'wor']);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(model.getLinesContent(), ['hethere', 'wor']);
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 3, 1, 3),
new Selection(2, 4, 2, 4)
]);
executeAction(action, editor);
assert.deepEqual(model.getLinesContent(), ['he', 'wor']);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(model.getLinesContent(), ['he', 'wor']);
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 3, 1, 3),
new Selection(2, 4, 2, 4)
]);
executeAction(action, editor);
assert.deepEqual(model.getLinesContent(), ['hewor']);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(model.getLinesContent(), ['hewor']);
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 3, 1, 3),
new Selection(1, 6, 1, 6)
]);
executeAction(action, editor);
assert.deepEqual(model.getLinesContent(), ['he']);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(model.getLinesContent(), ['he']);
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 3, 1, 3)
]);
executeAction(action, editor);
assert.deepEqual(model.getLinesContent(), ['he']);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(model.getLinesContent(), ['he']);
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 3, 1, 3)
]);
});
@@ -809,20 +888,20 @@ suite('Editor Contrib - Line Operations', () => {
new Selection(3, 4, 3, 4),
]);
executeAction(action, editor);
assert.deepEqual(model.getLinesContent(), ['hethere', 'wor']);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(model.getLinesContent(), ['hethere', 'wor']);
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 3, 1, 3),
new Selection(2, 4, 2, 4)
]);
CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 3, 1, 3),
new Selection(1, 6, 1, 6),
new Selection(3, 4, 3, 4)
]);
CoreEditingCommands.Redo.runEditorCommand(null, editor, null);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 3, 1, 3),
new Selection(2, 4, 2, 4)
]);
@@ -847,27 +926,27 @@ suite('Editor Contrib - Line Operations', () => {
}
testInsertLineBefore(1, 3, (model, viewModel) => {
assert.deepEqual(viewModel.getSelection(), new Selection(1, 1, 1, 1));
assert.equal(model.getLineContent(1), '');
assert.equal(model.getLineContent(2), 'First line');
assert.equal(model.getLineContent(3), 'Second line');
assert.equal(model.getLineContent(4), 'Third line');
assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 1, 1, 1));
assert.strictEqual(model.getLineContent(1), '');
assert.strictEqual(model.getLineContent(2), 'First line');
assert.strictEqual(model.getLineContent(3), 'Second line');
assert.strictEqual(model.getLineContent(4), 'Third line');
});
testInsertLineBefore(2, 3, (model, viewModel) => {
assert.deepEqual(viewModel.getSelection(), new Selection(2, 1, 2, 1));
assert.equal(model.getLineContent(1), 'First line');
assert.equal(model.getLineContent(2), '');
assert.equal(model.getLineContent(3), 'Second line');
assert.equal(model.getLineContent(4), 'Third line');
assert.deepStrictEqual(viewModel.getSelection(), new Selection(2, 1, 2, 1));
assert.strictEqual(model.getLineContent(1), 'First line');
assert.strictEqual(model.getLineContent(2), '');
assert.strictEqual(model.getLineContent(3), 'Second line');
assert.strictEqual(model.getLineContent(4), 'Third line');
});
testInsertLineBefore(3, 3, (model, viewModel) => {
assert.deepEqual(viewModel.getSelection(), new Selection(3, 1, 3, 1));
assert.equal(model.getLineContent(1), 'First line');
assert.equal(model.getLineContent(2), 'Second line');
assert.equal(model.getLineContent(3), '');
assert.equal(model.getLineContent(4), 'Third line');
assert.deepStrictEqual(viewModel.getSelection(), new Selection(3, 1, 3, 1));
assert.strictEqual(model.getLineContent(1), 'First line');
assert.strictEqual(model.getLineContent(2), 'Second line');
assert.strictEqual(model.getLineContent(3), '');
assert.strictEqual(model.getLineContent(4), 'Third line');
});
});
@@ -888,27 +967,27 @@ suite('Editor Contrib - Line Operations', () => {
}
testInsertLineAfter(1, 3, (model, viewModel) => {
assert.deepEqual(viewModel.getSelection(), new Selection(2, 1, 2, 1));
assert.equal(model.getLineContent(1), 'First line');
assert.equal(model.getLineContent(2), '');
assert.equal(model.getLineContent(3), 'Second line');
assert.equal(model.getLineContent(4), 'Third line');
assert.deepStrictEqual(viewModel.getSelection(), new Selection(2, 1, 2, 1));
assert.strictEqual(model.getLineContent(1), 'First line');
assert.strictEqual(model.getLineContent(2), '');
assert.strictEqual(model.getLineContent(3), 'Second line');
assert.strictEqual(model.getLineContent(4), 'Third line');
});
testInsertLineAfter(2, 3, (model, viewModel) => {
assert.deepEqual(viewModel.getSelection(), new Selection(3, 1, 3, 1));
assert.equal(model.getLineContent(1), 'First line');
assert.equal(model.getLineContent(2), 'Second line');
assert.equal(model.getLineContent(3), '');
assert.equal(model.getLineContent(4), 'Third line');
assert.deepStrictEqual(viewModel.getSelection(), new Selection(3, 1, 3, 1));
assert.strictEqual(model.getLineContent(1), 'First line');
assert.strictEqual(model.getLineContent(2), 'Second line');
assert.strictEqual(model.getLineContent(3), '');
assert.strictEqual(model.getLineContent(4), 'Third line');
});
testInsertLineAfter(3, 3, (model, viewModel) => {
assert.deepEqual(viewModel.getSelection(), new Selection(4, 1, 4, 1));
assert.equal(model.getLineContent(1), 'First line');
assert.equal(model.getLineContent(2), 'Second line');
assert.equal(model.getLineContent(3), 'Third line');
assert.equal(model.getLineContent(4), '');
assert.deepStrictEqual(viewModel.getSelection(), new Selection(4, 1, 4, 1));
assert.strictEqual(model.getLineContent(1), 'First line');
assert.strictEqual(model.getLineContent(2), 'Second line');
assert.strictEqual(model.getLineContent(3), 'Third line');
assert.strictEqual(model.getLineContent(4), '');
});
});
@@ -928,11 +1007,11 @@ suite('Editor Contrib - Line Operations', () => {
editor.setPosition(new Position(1, 2));
executeAction(indentLinesAction, editor);
assert.equal(model.getLineContent(1), '\tfunction baz() {');
assert.deepEqual(editor.getSelection(), new Selection(1, 3, 1, 3));
assert.strictEqual(model.getLineContent(1), '\tfunction baz() {');
assert.deepStrictEqual(editor.getSelection(), new Selection(1, 3, 1, 3));
CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
assert.equal(model.getLineContent(1), '\tf\tunction baz() {');
assert.strictEqual(model.getLineContent(1), '\tf\tunction baz() {');
});
model.dispose();
@@ -953,8 +1032,8 @@ suite('Editor Contrib - Line Operations', () => {
editor.setPosition(new Position(1, 1));
executeAction(indentLinesAction, editor);
assert.equal(model.getLineContent(1), '\tSome text');
assert.deepEqual(editor.getSelection(), new Selection(1, 2, 1, 2));
assert.strictEqual(model.getLineContent(1), '\tSome text');
assert.deepStrictEqual(editor.getSelection(), new Selection(1, 2, 1, 2));
});
model.dispose();
@@ -972,8 +1051,8 @@ suite('Editor Contrib - Line Operations', () => {
editor.setPosition(new Position(1, 1));
executeAction(indentLinesAction, editor);
assert.equal(model.getLineContent(1), ' ');
assert.deepEqual(editor.getSelection(), new Selection(1, 5, 1, 5));
assert.strictEqual(model.getLineContent(1), ' ');
assert.deepStrictEqual(editor.getSelection(), new Selection(1, 5, 1, 5));
});
model.dispose();
@@ -995,7 +1074,7 @@ suite('Editor Contrib - Line Operations', () => {
const deleteLinesAction = new DeleteLinesAction();
executeAction(deleteLinesAction, editor);
assert.equal(editor.getValue(), 'a\nc');
assert.strictEqual(editor.getValue(), 'a\nc');
});
});
@@ -1007,8 +1086,8 @@ suite('Editor Contrib - Line Operations', () => {
const deleteLinesAction = new DeleteLinesAction();
executeAction(deleteLinesAction, editor);
assert.equal(editor.getValue(), resultingText.join('\n'));
assert.deepEqual(editor.getSelections(), resultingSelections);
assert.strictEqual(editor.getValue(), resultingText.join('\n'));
assert.deepStrictEqual(editor.getSelections(), resultingSelections);
});
}

View File

@@ -57,7 +57,7 @@ function getHoverMessage(link: Link, useMetaKey: boolean): MarkdownString {
nativeLabel = ` "${nativeLabelText}"`;
}
}
const hoverMessage = new MarkdownString('', true).appendMarkdown(`[${label}](${link.url.toString()}${nativeLabel}) (${kb})`);
const hoverMessage = new MarkdownString('', true).appendMarkdown(`[${label}](${link.url.toString(true)}${nativeLabel}) (${kb})`);
return hoverMessage;
} else {
return new MarkdownString().appendText(`${label} (${kb})`);
@@ -327,7 +327,7 @@ export class LinkDetector implements IEditorContribution {
}
}
return this.openerService.open(uri, { openToSide, fromUserGesture });
return this.openerService.open(uri, { openToSide, fromUserGesture, allowContributedOpeners: true });
}, err => {
const messageOrError =

View File

@@ -850,7 +850,6 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut
this.updateSoon.schedule();
} else {
this._setState(null);
}
} else {
this._update();
@@ -1016,11 +1015,6 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut
});
this.decorations = this.editor.deltaDecorations(this.decorations, decorations);
const currentFindState = CommonFindController.get(this.editor).getState();
if (currentFindState.isRegex || currentFindState.matchCase || currentFindState.wholeWord) {
CommonFindController.get(this.editor).highlightFindOptions(true);
}
}
private static readonly _SELECTION_HIGHLIGHT_OVERVIEW = ModelDecorationOptions.register({

View File

@@ -25,7 +25,7 @@ suite('Multicursor', () => {
editor.setSelection(new Selection(2, 1, 2, 1));
addCursorUpAction.run(null!, editor, {});
assert.equal(viewModel.getSelections().length, 2);
assert.strictEqual(viewModel.getSelections().length, 2);
editor.trigger('test', Handler.Paste, {
text: '1\n2',
@@ -35,8 +35,8 @@ suite('Multicursor', () => {
]
});
assert.equal(editor.getModel()!.getLineContent(1), '1abc');
assert.equal(editor.getModel()!.getLineContent(2), '2def');
assert.strictEqual(editor.getModel()!.getLineContent(1), '1abc');
assert.strictEqual(editor.getModel()!.getLineContent(2), '2def');
});
});
@@ -46,7 +46,7 @@ suite('Multicursor', () => {
], {}, (editor, viewModel) => {
let addCursorDownAction = new InsertCursorBelow();
addCursorDownAction.run(null!, editor, {});
assert.equal(viewModel.getSelections().length, 1);
assert.strictEqual(viewModel.getSelections().length, 1);
});
});
@@ -90,7 +90,7 @@ suite('Multicursor selection', () => {
editor.setSelection(new Selection(2, 9, 2, 16));
selectHighlightsAction.run(null!, editor);
assert.deepEqual(editor.getSelections()!.map(fromRange), [
assert.deepStrictEqual(editor.getSelections()!.map(fromRange), [
[2, 9, 2, 16],
[1, 9, 1, 16],
[3, 9, 3, 16],
@@ -98,7 +98,7 @@ suite('Multicursor selection', () => {
editor.trigger('test', 'removeSecondaryCursors', null);
assert.deepEqual(fromRange(editor.getSelection()!), [2, 9, 2, 16]);
assert.deepStrictEqual(fromRange(editor.getSelection()!), [2, 9, 2, 16]);
multiCursorSelectController.dispose();
findController.dispose();
@@ -121,13 +121,13 @@ suite('Multicursor selection', () => {
findController.getState().change({ searchString: 'some+thing', isRegex: true, isRevealed: true }, false);
selectHighlightsAction.run(null!, editor);
assert.deepEqual(editor.getSelections()!.map(fromRange), [
assert.deepStrictEqual(editor.getSelections()!.map(fromRange), [
[1, 1, 1, 10],
[2, 1, 2, 11],
[3, 1, 3, 12],
]);
assert.equal(findController.getState().searchString, 'some+thing');
assert.strictEqual(findController.getState().searchString, 'some+thing');
multiCursorSelectController.dispose();
findController.dispose();
@@ -154,14 +154,14 @@ suite('Multicursor selection', () => {
editor.setSelection(new Selection(2, 1, 3, 4));
addSelectionToNextFindMatch.run(null!, editor);
assert.deepEqual(editor.getSelections()!.map(fromRange), [
assert.deepStrictEqual(editor.getSelections()!.map(fromRange), [
[2, 1, 3, 4],
[8, 1, 9, 4]
]);
editor.trigger('test', 'removeSecondaryCursors', null);
assert.deepEqual(fromRange(editor.getSelection()!), [2, 1, 3, 4]);
assert.deepStrictEqual(fromRange(editor.getSelection()!), [2, 1, 3, 4]);
multiCursorSelectController.dispose();
findController.dispose();
@@ -182,7 +182,7 @@ suite('Multicursor selection', () => {
editor.setSelection(new Selection(1, 1, 1, 4));
addSelectionToNextFindMatch.run(null!, editor);
assert.deepEqual(editor.getSelections()!.map(fromRange), [
assert.deepStrictEqual(editor.getSelections()!.map(fromRange), [
[1, 1, 1, 4],
[1, 4, 1, 7]
]);
@@ -190,7 +190,7 @@ suite('Multicursor selection', () => {
addSelectionToNextFindMatch.run(null!, editor);
addSelectionToNextFindMatch.run(null!, editor);
addSelectionToNextFindMatch.run(null!, editor);
assert.deepEqual(editor.getSelections()!.map(fromRange), [
assert.deepStrictEqual(editor.getSelections()!.map(fromRange), [
[1, 1, 1, 4],
[1, 4, 1, 7],
[2, 1, 2, 4],
@@ -199,14 +199,14 @@ suite('Multicursor selection', () => {
]);
editor.trigger('test', Handler.Type, { text: 'z' });
assert.deepEqual(editor.getSelections()!.map(fromRange), [
assert.deepStrictEqual(editor.getSelections()!.map(fromRange), [
[1, 2, 1, 2],
[1, 3, 1, 3],
[2, 2, 2, 2],
[3, 2, 3, 2],
[3, 3, 3, 3]
]);
assert.equal(editor.getValue(), [
assert.strictEqual(editor.getValue(), [
'zz',
'z',
'zz',
@@ -239,14 +239,14 @@ suite('Multicursor selection', () => {
editor.setSelection(new Selection(2, 1, 3, 4));
addSelectionToNextFindMatch.run(null!, editor);
assert.deepEqual(editor.getSelections()!.map(fromRange), [
assert.deepStrictEqual(editor.getSelections()!.map(fromRange), [
[2, 1, 3, 4],
[8, 1, 9, 4]
]);
editor.trigger('test', 'removeSecondaryCursors', null);
assert.deepEqual(fromRange(editor.getSelection()!), [2, 1, 3, 4]);
assert.deepStrictEqual(fromRange(editor.getSelection()!), [2, 1, 3, 4]);
multiCursorSelectController.dispose();
findController.dispose();
@@ -284,25 +284,25 @@ suite('Multicursor selection', () => {
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
new Selection(3, 1, 3, 4),
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
new Selection(3, 1, 3, 4),
@@ -323,20 +323,20 @@ suite('Multicursor selection', () => {
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
new Selection(3, 1, 3, 4),
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
new Selection(3, 1, 3, 4),
@@ -357,20 +357,20 @@ suite('Multicursor selection', () => {
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
new Selection(3, 1, 3, 4),
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
new Selection(3, 1, 3, 4),
@@ -392,14 +392,14 @@ suite('Multicursor selection', () => {
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
new Selection(3, 1, 3, 4),
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
new Selection(3, 1, 3, 4),
@@ -421,14 +421,14 @@ suite('Multicursor selection', () => {
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 5, 1, 10),
new Selection(2, 5, 2, 10),
new Selection(3, 5, 3, 8),
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 5, 1, 10),
new Selection(2, 5, 2, 10),
new Selection(3, 5, 3, 8),
@@ -450,20 +450,20 @@ suite('Multicursor selection', () => {
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 5),
new Selection(2, 1, 2, 5),
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 5),
new Selection(2, 1, 2, 5),
new Selection(3, 1, 3, 5),
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 5),
new Selection(2, 1, 2, 5),
new Selection(3, 1, 3, 5),
@@ -471,7 +471,7 @@ suite('Multicursor selection', () => {
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 5),
new Selection(2, 1, 2, 5),
new Selection(3, 1, 3, 5),
@@ -480,7 +480,7 @@ suite('Multicursor selection', () => {
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 5),
new Selection(2, 1, 2, 5),
new Selection(3, 1, 3, 5),
@@ -508,18 +508,18 @@ suite('Multicursor selection', () => {
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(4, 1, 4, 4),
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(4, 1, 4, 4),
new Selection(6, 2, 6, 5),
@@ -534,12 +534,12 @@ suite('Multicursor selection', () => {
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(4, 1, 4, 4),
]);
@@ -550,7 +550,7 @@ suite('Multicursor selection', () => {
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
]);
@@ -565,14 +565,14 @@ suite('Multicursor selection', () => {
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(4, 1, 4, 4),
new Selection(6, 2, 6, 5),
]);
action.run(null!, editor);
assert.deepEqual(editor.getSelections(), [
assert.deepStrictEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(4, 1, 4, 4),
new Selection(6, 2, 6, 5),

View File

@@ -10,7 +10,7 @@
line-height: 1.5em;
}
.monaco-editor .parameter-hints-widget > .wrapper {
.monaco-editor .parameter-hints-widget > .phwrapper {
max-width: 440px;
display: flex;
flex-direction: row;

View File

@@ -14,7 +14,7 @@ import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentW
import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions';
import * as modes from 'vs/editor/common/modes';
import { IModeService } from 'vs/editor/common/services/modeService';
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
import { IMarkdownRenderResult, MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
import { Context } from 'vs/editor/contrib/parameterHints/provideSignatureHelp';
import * as nls from 'vs/nls';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
@@ -27,6 +27,7 @@ import { Codicon } from 'vs/base/common/codicons';
import { assertIsDefined } from 'vs/base/common/types';
import { ColorScheme } from 'vs/platform/theme/common/theme';
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
import { IMarkdownString } from 'vs/base/common/htmlContent';
const $ = dom.$;
@@ -81,7 +82,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget {
private createParamaterHintDOMNodes() {
const element = $('.editor-widget.parameter-hints-widget');
const wrapper = dom.append(element, $('.wrapper'));
const wrapper = dom.append(element, $('.phwrapper'));
wrapper.tabIndex = -1;
const controls = dom.append(wrapper, $('.controls'));
@@ -225,8 +226,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget {
if (typeof activeParameter.documentation === 'string') {
documentation.textContent = activeParameter.documentation;
} else {
const renderedContents = this.renderDisposeables.add(this.markdownRenderer.render(activeParameter.documentation));
renderedContents.element.classList.add('markdown-docs');
const renderedContents = this.renderMarkdownDocs(activeParameter.documentation);
documentation.appendChild(renderedContents.element);
}
dom.append(this.domNodes.docs, $('p', {}, documentation));
@@ -237,8 +237,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget {
} else if (typeof signature.documentation === 'string') {
dom.append(this.domNodes.docs, $('p', {}, signature.documentation));
} else {
const renderedContents = this.renderDisposeables.add(this.markdownRenderer.render(signature.documentation));
renderedContents.element.classList.add('markdown-docs');
const renderedContents = this.renderMarkdownDocs(signature.documentation);
dom.append(this.domNodes.docs, renderedContents.element);
}
@@ -265,6 +264,16 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget {
this.domNodes.scrollbar.scanDomNode();
}
private renderMarkdownDocs(markdown: IMarkdownString | undefined): IMarkdownRenderResult {
const renderedContents = this.renderDisposeables.add(this.markdownRenderer.render(markdown, {
asyncRenderCallback: () => {
this.domNodes?.scrollbar.scanDomNode();
}
}));
renderedContents.element.classList.add('markdown-docs');
return renderedContents;
}
private hasDocs(signature: modes.SignatureInformation, activeParameter: modes.ParameterInformation | undefined): boolean {
if (activeParameter && typeof activeParameter.documentation === 'string' && assertIsDefined(activeParameter.documentation).length > 0) {
return true;
@@ -360,7 +369,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget {
const height = Math.max(this.editor.getLayoutInfo().height / 4, 250);
const maxHeight = `${height}px`;
this.domNodes.element.style.maxHeight = maxHeight;
const wrapper = this.domNodes.element.getElementsByClassName('wrapper') as HTMLCollectionOf<HTMLElement>;
const wrapper = this.domNodes.element.getElementsByClassName('phwrapper') as HTMLCollectionOf<HTMLElement>;
if (wrapper.length) {
wrapper[0].style.maxHeight = maxHeight;
}

View File

@@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { first } from 'vs/base/common/async';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { ITextModel } from 'vs/editor/common/model';
@@ -20,19 +19,26 @@ export const Context = {
MultipleSignatures: new RawContextKey<boolean>('parameterHintsMultipleSignatures', false),
};
export function provideSignatureHelp(
export async function provideSignatureHelp(
model: ITextModel,
position: Position,
context: modes.SignatureHelpContext,
token: CancellationToken
): Promise<modes.SignatureHelpResult | null | undefined> {
): Promise<modes.SignatureHelpResult | undefined> {
const supports = modes.SignatureHelpProviderRegistry.ordered(model);
return first(supports.map(support => () => {
return Promise.resolve(support.provideSignatureHelp(model, position, token, context))
.catch<modes.SignatureHelpResult | undefined>(e => onUnexpectedExternalError(e));
}));
for (const support of supports) {
try {
const result = await support.provideSignatureHelp(model, position, token, context);
if (result) {
return result;
}
} catch (err) {
onUnexpectedExternalError(err);
}
}
return undefined;
}
CommandsRegistry.registerCommand('_executeSignatureHelpProvider', async (accessor, ...args: [URI, IPosition, string?]) => {

View File

@@ -6,7 +6,7 @@
import 'vs/css!./media/peekViewWidget';
import * as dom from 'vs/base/browser/dom';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { ActionBar, IActionBarOptions } from 'vs/base/browser/ui/actionbar/actionbar';
import { ActionBar, ActionsOrientation, IActionBarOptions } from 'vs/base/browser/ui/actionbar/actionbar';
import { Action } from 'vs/base/common/actions';
import { Color } from 'vs/base/common/color';
import { Emitter } from 'vs/base/common/event';
@@ -25,8 +25,7 @@ import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { registerColor, contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { Codicon } from 'vs/base/common/codicons';
import { MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions';
import { MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { createActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
export const IPeekViewService = createDecorator<IPeekViewService>('IPeekViewService');
export interface IPeekViewService {
@@ -107,6 +106,7 @@ export abstract class PeekViewWidget extends ZoneWidget {
private readonly _onDidClose = new Emitter<PeekViewWidget>();
readonly onDidClose = this._onDidClose.event;
private disposed?: true;
protected _headElement?: HTMLDivElement;
protected _primaryHeading?: HTMLElement;
@@ -125,8 +125,11 @@ export abstract class PeekViewWidget extends ZoneWidget {
}
dispose(): void {
super.dispose();
this._onDidClose.fire(this);
if (!this.disposed) {
this.disposed = true; // prevent consumers who dispose on onDidClose from looping
super.dispose();
this._onDidClose.fire(this);
}
}
style(styles: IPeekViewStyles): void {
@@ -204,15 +207,8 @@ export abstract class PeekViewWidget extends ZoneWidget {
protected _getActionBarOptions(): IActionBarOptions {
return {
actionViewItemProvider: action => {
if (action instanceof MenuItemAction) {
return this.instantiationService.createInstance(MenuEntryActionViewItem, action);
} else if (action instanceof SubmenuItemAction) {
return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action);
}
return undefined;
}
actionViewItemProvider: createActionViewItem.bind(undefined, this.instantiationService),
orientation: ActionsOrientation.HORIZONTAL
};
}

View File

@@ -10,7 +10,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { stripCodicons } from 'vs/base/common/codicons';
import { stripIcons } from 'vs/base/common/iconLabels';
export abstract class AbstractEditorCommandsQuickAccessProvider extends AbstractCommandsQuickAccessProvider {
@@ -41,7 +41,7 @@ export abstract class AbstractEditorCommandsQuickAccessProvider extends Abstract
editorCommandPicks.push({
commandId: editorAction.id,
commandAlias: editorAction.alias,
label: stripCodicons(editorAction.label) || editorAction.id,
label: stripIcons(editorAction.label) || editorAction.id,
});
}

View File

@@ -12,11 +12,10 @@ import { ITextModel } from 'vs/editor/common/model';
import { IRange, Range } from 'vs/editor/common/core/range';
import { AbstractEditorNavigationQuickAccessProvider, IEditorNavigationQuickAccessOptions, IQuickAccessTextEditorContext } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess';
import { DocumentSymbol, SymbolKinds, SymbolTag, DocumentSymbolProviderRegistry, SymbolKind } from 'vs/editor/common/modes';
import { OutlineModel, OutlineElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
import { OutlineModel } from 'vs/editor/contrib/documentSymbols/outlineModel';
import { trim, format } from 'vs/base/common/strings';
import { prepareQuery, IPreparedQuery, pieceToQuery, scoreFuzzy2 } from 'vs/base/common/fuzzyScorer';
import { IMatch } from 'vs/base/common/filters';
import { Iterable } from 'vs/base/common/iterator';
import { Codicon } from 'vs/base/common/codicons';
export interface IGotoSymbolQuickPickItem extends IQuickPickItem {
@@ -144,7 +143,7 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit
// Resolve symbols from document once and reuse this
// request for all filtering and typing then on
const symbolsPromise = this.getDocumentSymbols(model, true, token);
const symbolsPromise = this.getDocumentSymbols(model, token);
// Set initial picks and update on type
let picksCts: CancellationTokenSource | undefined = undefined;
@@ -418,49 +417,9 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit
return result;
}
protected async getDocumentSymbols(document: ITextModel, flatten: boolean, token: CancellationToken): Promise<DocumentSymbol[]> {
protected async getDocumentSymbols(document: ITextModel, token: CancellationToken): Promise<DocumentSymbol[]> {
const model = await OutlineModel.create(document, token);
if (token.isCancellationRequested) {
return [];
}
const roots: DocumentSymbol[] = [];
for (const child of model.children.values()) {
if (child instanceof OutlineElement) {
roots.push(child.symbol);
} else {
roots.push(...Iterable.map(child.children.values(), child => child.symbol));
}
}
let flatEntries: DocumentSymbol[] = [];
if (flatten) {
this.flattenDocumentSymbols(flatEntries, roots, '');
} else {
flatEntries = roots;
}
return flatEntries.sort((symbolA, symbolB) => Range.compareRangesUsingStarts(symbolA.range, symbolB.range));
}
private flattenDocumentSymbols(bucket: DocumentSymbol[], entries: DocumentSymbol[], overrideContainerLabel: string): void {
for (const entry of entries) {
bucket.push({
kind: entry.kind,
tags: entry.tags,
name: entry.name,
detail: entry.detail,
containerName: entry.containerName || overrideContainerLabel,
range: entry.range,
selectionRange: entry.selectionRange,
children: undefined, // we flatten it...
});
// Recurse over children
if (entry.children) {
this.flattenDocumentSymbols(bucket, entry.children, entry.name);
}
}
return token.isCancellationRequested ? [] : model.asListOfDocumentSymbols();
}
}

View File

@@ -26,7 +26,7 @@ export class BracketSelectionRangeProvider implements SelectionRangeProvider {
return result;
}
private static readonly _maxDuration = 30;
public static _maxDuration = 30;
private static readonly _maxRounds = 2;
private static _bracketsRightYield(resolve: () => void, round: number, model: ITextModel, pos: Position, ranges: Map<string, LinkedList<Range>>): void {

View File

@@ -16,7 +16,7 @@ import { BracketSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/bra
import { provideSelectionRanges } from 'vs/editor/contrib/smartSelect/smartSelect';
import { CancellationToken } from 'vs/base/common/cancellation';
import { WordSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/wordSelections';
import { TestTextResourcePropertiesService } from 'vs/editor/test/common/services/modelService.test';
import { TestTextResourcePropertiesService } from 'vs/editor/test/common/services/testTextResourcePropertiesService';
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
import { NullLogService } from 'vs/platform/log/common/log';
import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService';
@@ -45,6 +45,16 @@ class MockJSMode extends MockMode {
suite('SmartSelect', () => {
const OriginalBracketSelectionRangeProviderMaxDuration = BracketSelectionRangeProvider._maxDuration;
suiteSetup(() => {
BracketSelectionRangeProvider._maxDuration = 5000; // 5 seconds
});
suiteTeardown(() => {
BracketSelectionRangeProvider._maxDuration = OriginalBracketSelectionRangeProviderMaxDuration;
});
let modelService: ModelServiceImpl;
let mode: MockJSMode;

View File

@@ -12,11 +12,11 @@ import { VariableResolver, Variable, Text } from 'vs/editor/contrib/snippet/snip
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { getLeadingWhitespace, commonPrefixLength, isFalsyOrWhitespace, splitLines } from 'vs/base/common/strings';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { isSingleFolderWorkspaceIdentifier, toWorkspaceIdentifier, WORKSPACE_EXTENSION, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { toWorkspaceIdentifier, WORKSPACE_EXTENSION, IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { ILabelService } from 'vs/platform/label/common/label';
import { normalizeDriveLetter } from 'vs/base/common/labels';
import { URI } from 'vs/base/common/uri';
import { OvertypingCapturer } from 'vs/editor/contrib/suggest/suggestOvertypingCapturer';
import { generateUuid } from 'vs/base/common/uuid';
export const KnownSnippetVariableNames: { [key: string]: true } = Object.freeze({
'CURRENT_YEAR': true,
@@ -42,6 +42,7 @@ export const KnownSnippetVariableNames: { [key: string]: true } = Object.freeze(
'TM_FILENAME_BASE': true,
'TM_DIRECTORY': true,
'TM_FILEPATH': true,
'RELATIVE_FILEPATH': true,
'BLOCK_COMMENT_START': true,
'BLOCK_COMMENT_END': true,
'LINE_COMMENT': true,
@@ -49,6 +50,7 @@ export const KnownSnippetVariableNames: { [key: string]: true } = Object.freeze(
'WORKSPACE_FOLDER': true,
'RANDOM': true,
'RANDOM_HEX': true,
'UUID': true
});
export class CompositeSnippetVariableResolver implements VariableResolver {
@@ -177,6 +179,8 @@ export class ModelBasedVariableResolver implements VariableResolver {
} else if (name === 'TM_FILEPATH' && this._labelService) {
return this._labelService.getUriLabel(this._model.uri);
} else if (name === 'RELATIVE_FILEPATH' && this._labelService) {
return this._labelService.getUriLabel(this._model.uri, { relative: true, noPrefix: true });
}
return undefined;
@@ -309,9 +313,9 @@ export class WorkspaceBasedVariableResolver implements VariableResolver {
return undefined;
}
private _resolveWorkspaceName(workspaceIdentifier: IWorkspaceIdentifier | URI): string | undefined {
private _resolveWorkspaceName(workspaceIdentifier: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): string | undefined {
if (isSingleFolderWorkspaceIdentifier(workspaceIdentifier)) {
return path.basename(workspaceIdentifier.path);
return path.basename(workspaceIdentifier.uri.path);
}
let filename = path.basename(workspaceIdentifier.configPath.path);
@@ -320,9 +324,9 @@ export class WorkspaceBasedVariableResolver implements VariableResolver {
}
return filename;
}
private _resoveWorkspacePath(workspaceIdentifier: IWorkspaceIdentifier | URI): string | undefined {
private _resoveWorkspacePath(workspaceIdentifier: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): string | undefined {
if (isSingleFolderWorkspaceIdentifier(workspaceIdentifier)) {
return normalizeDriveLetter(workspaceIdentifier.fsPath);
return normalizeDriveLetter(workspaceIdentifier.uri.fsPath);
}
let filename = path.basename(workspaceIdentifier.configPath.path);
@@ -340,9 +344,10 @@ export class RandomBasedVariableResolver implements VariableResolver {
if (name === 'RANDOM') {
return Math.random().toString().slice(-6);
}
else if (name === 'RANDOM_HEX') {
} else if (name === 'RANDOM_HEX') {
return Math.random().toString(16).slice(-6);
} else if (name === 'UUID') {
return generateUuid();
}
return undefined;

View File

@@ -62,34 +62,34 @@ suite('SnippetController', () => {
editor.setPosition({ lineNumber: 4, column: 2 });
snippetController.insert(template);
assert.equal(editor.getModel()!.getLineContent(4), '\tfor (var index; index < array.length; index++) {');
assert.equal(editor.getModel()!.getLineContent(5), '\t\tvar element = array[index];');
assert.equal(editor.getModel()!.getLineContent(6), '\t\t');
assert.equal(editor.getModel()!.getLineContent(7), '\t}');
assert.strictEqual(editor.getModel()!.getLineContent(4), '\tfor (var index; index < array.length; index++) {');
assert.strictEqual(editor.getModel()!.getLineContent(5), '\t\tvar element = array[index];');
assert.strictEqual(editor.getModel()!.getLineContent(6), '\t\t');
assert.strictEqual(editor.getModel()!.getLineContent(7), '\t}');
editor.trigger('test', 'type', { text: 'i' });
assert.equal(editor.getModel()!.getLineContent(4), '\tfor (var i; i < array.length; i++) {');
assert.equal(editor.getModel()!.getLineContent(5), '\t\tvar element = array[i];');
assert.equal(editor.getModel()!.getLineContent(6), '\t\t');
assert.equal(editor.getModel()!.getLineContent(7), '\t}');
assert.strictEqual(editor.getModel()!.getLineContent(4), '\tfor (var i; i < array.length; i++) {');
assert.strictEqual(editor.getModel()!.getLineContent(5), '\t\tvar element = array[i];');
assert.strictEqual(editor.getModel()!.getLineContent(6), '\t\t');
assert.strictEqual(editor.getModel()!.getLineContent(7), '\t}');
snippetController.next();
editor.trigger('test', 'type', { text: 'arr' });
assert.equal(editor.getModel()!.getLineContent(4), '\tfor (var i; i < arr.length; i++) {');
assert.equal(editor.getModel()!.getLineContent(5), '\t\tvar element = arr[i];');
assert.equal(editor.getModel()!.getLineContent(6), '\t\t');
assert.equal(editor.getModel()!.getLineContent(7), '\t}');
assert.strictEqual(editor.getModel()!.getLineContent(4), '\tfor (var i; i < arr.length; i++) {');
assert.strictEqual(editor.getModel()!.getLineContent(5), '\t\tvar element = arr[i];');
assert.strictEqual(editor.getModel()!.getLineContent(6), '\t\t');
assert.strictEqual(editor.getModel()!.getLineContent(7), '\t}');
snippetController.prev();
editor.trigger('test', 'type', { text: 'j' });
assert.equal(editor.getModel()!.getLineContent(4), '\tfor (var j; j < arr.length; j++) {');
assert.equal(editor.getModel()!.getLineContent(5), '\t\tvar element = arr[j];');
assert.equal(editor.getModel()!.getLineContent(6), '\t\t');
assert.equal(editor.getModel()!.getLineContent(7), '\t}');
assert.strictEqual(editor.getModel()!.getLineContent(4), '\tfor (var j; j < arr.length; j++) {');
assert.strictEqual(editor.getModel()!.getLineContent(5), '\t\tvar element = arr[j];');
assert.strictEqual(editor.getModel()!.getLineContent(6), '\t\t');
assert.strictEqual(editor.getModel()!.getLineContent(7), '\t}');
snippetController.next();
snippetController.next();
assert.deepEqual(editor.getPosition(), new Position(6, 3));
assert.deepStrictEqual(editor.getPosition(), new Position(6, 3));
});
});
@@ -98,13 +98,13 @@ suite('SnippetController', () => {
editor.setPosition({ lineNumber: 4, column: 2 });
snippetController.insert(template);
assert.equal(editor.getModel()!.getLineContent(4), '\tfor (var index; index < array.length; index++) {');
assert.equal(editor.getModel()!.getLineContent(5), '\t\tvar element = array[index];');
assert.equal(editor.getModel()!.getLineContent(6), '\t\t');
assert.equal(editor.getModel()!.getLineContent(7), '\t}');
assert.strictEqual(editor.getModel()!.getLineContent(4), '\tfor (var index; index < array.length; index++) {');
assert.strictEqual(editor.getModel()!.getLineContent(5), '\t\tvar element = array[index];');
assert.strictEqual(editor.getModel()!.getLineContent(6), '\t\t');
assert.strictEqual(editor.getModel()!.getLineContent(7), '\t}');
snippetController.cancel();
assert.deepEqual(editor.getPosition(), new Position(4, 16));
assert.deepStrictEqual(editor.getPosition(), new Position(4, 16));
});
});
@@ -121,7 +121,7 @@ suite('SnippetController', () => {
// text: null
// }]);
// assert.equal(snippetController.isInSnippetMode(), false);
// assert.strictEqual(snippetController.isInSnippetMode(), false);
// });
// });
@@ -138,7 +138,7 @@ suite('SnippetController', () => {
// text: null
// }]);
// assert.equal(snippetController.isInSnippetMode(), false);
// assert.strictEqual(snippetController.isInSnippetMode(), false);
// });
// });
@@ -155,7 +155,7 @@ suite('SnippetController', () => {
// text: '\nHello'
// }]);
// assert.equal(snippetController.isInSnippetMode(), false);
// assert.strictEqual(snippetController.isInSnippetMode(), false);
// });
// });
@@ -172,7 +172,7 @@ suite('SnippetController', () => {
// text: '\nHello'
// }]);
// assert.equal(snippetController.isInSnippetMode(), false);
// assert.strictEqual(snippetController.isInSnippetMode(), false);
// });
// });
@@ -183,7 +183,7 @@ suite('SnippetController', () => {
editor.getModel()!.setValue('goodbye');
assert.equal(snippetController.isInSnippetMode(), false);
assert.strictEqual(snippetController.isInSnippetMode(), false);
});
});
@@ -194,7 +194,7 @@ suite('SnippetController', () => {
editor.getModel()!.undo();
assert.equal(snippetController.isInSnippetMode(), false);
assert.strictEqual(snippetController.isInSnippetMode(), false);
});
});
@@ -205,7 +205,7 @@ suite('SnippetController', () => {
editor.setPosition({ lineNumber: 1, column: 1 });
assert.equal(snippetController.isInSnippetMode(), false);
assert.strictEqual(snippetController.isInSnippetMode(), false);
});
});
@@ -216,7 +216,7 @@ suite('SnippetController', () => {
editor.setModel(null);
assert.equal(snippetController.isInSnippetMode(), false);
assert.strictEqual(snippetController.isInSnippetMode(), false);
});
});
@@ -227,7 +227,7 @@ suite('SnippetController', () => {
snippetController.dispose();
assert.equal(snippetController.isInSnippetMode(), false);
assert.strictEqual(snippetController.isInSnippetMode(), false);
});
});
@@ -241,7 +241,7 @@ suite('SnippetController', () => {
codeSnippet = 'foo$0';
snippetController.insert(codeSnippet);
assert.equal(editor.getSelections()!.length, 2);
assert.strictEqual(editor.getSelections()!.length, 2);
const [first, second] = editor.getSelections()!;
assert.ok(first.equalsRange({ startLineNumber: 1, startColumn: 4, endLineNumber: 1, endColumn: 4 }), first.toString());
assert.ok(second.equalsRange({ startLineNumber: 2, startColumn: 4, endLineNumber: 2, endColumn: 4 }), second.toString());
@@ -256,7 +256,7 @@ suite('SnippetController', () => {
codeSnippet = 'foo$0bar';
snippetController.insert(codeSnippet);
assert.equal(editor.getSelections()!.length, 2);
assert.strictEqual(editor.getSelections()!.length, 2);
const [first, second] = editor.getSelections()!;
assert.ok(first.equalsRange({ startLineNumber: 1, startColumn: 4, endLineNumber: 1, endColumn: 4 }), first.toString());
assert.ok(second.equalsRange({ startLineNumber: 2, startColumn: 4, endLineNumber: 2, endColumn: 4 }), second.toString());
@@ -271,7 +271,7 @@ suite('SnippetController', () => {
codeSnippet = 'foo$0bar';
snippetController.insert(codeSnippet);
assert.equal(editor.getSelections()!.length, 2);
assert.strictEqual(editor.getSelections()!.length, 2);
const [first, second] = editor.getSelections()!;
assert.ok(first.equalsRange({ startLineNumber: 1, startColumn: 4, endLineNumber: 1, endColumn: 4 }), first.toString());
assert.ok(second.equalsRange({ startLineNumber: 1, startColumn: 14, endLineNumber: 1, endColumn: 14 }), second.toString());
@@ -286,7 +286,7 @@ suite('SnippetController', () => {
codeSnippet = 'foo\n$0\nbar';
snippetController.insert(codeSnippet);
assert.equal(editor.getSelections()!.length, 2);
assert.strictEqual(editor.getSelections()!.length, 2);
const [first, second] = editor.getSelections()!;
assert.ok(first.equalsRange({ startLineNumber: 2, startColumn: 1, endLineNumber: 2, endColumn: 1 }), first.toString());
assert.ok(second.equalsRange({ startLineNumber: 4, startColumn: 1, endLineNumber: 4, endColumn: 1 }), second.toString());
@@ -301,7 +301,7 @@ suite('SnippetController', () => {
codeSnippet = 'foo\n$0\nbar';
snippetController.insert(codeSnippet);
assert.equal(editor.getSelections()!.length, 2);
assert.strictEqual(editor.getSelections()!.length, 2);
const [first, second] = editor.getSelections()!;
assert.ok(first.equalsRange({ startLineNumber: 2, startColumn: 1, endLineNumber: 2, endColumn: 1 }), first.toString());
assert.ok(second.equalsRange({ startLineNumber: 4, startColumn: 1, endLineNumber: 4, endColumn: 1 }), second.toString());
@@ -315,7 +315,7 @@ suite('SnippetController', () => {
codeSnippet = 'xo$0r';
snippetController.insert(codeSnippet, { overwriteBefore: 1 });
assert.equal(editor.getSelections()!.length, 1);
assert.strictEqual(editor.getSelections()!.length, 1);
assert.ok(editor.getSelection()!.equalsRange({ startLineNumber: 2, startColumn: 8, endColumn: 8, endLineNumber: 2 }));
});
});
@@ -328,9 +328,9 @@ suite('SnippetController', () => {
codeSnippet = '{{% url_**$1** %}}';
controller.insert(codeSnippet, { overwriteBefore: 2 });
assert.equal(editor.getSelections()!.length, 1);
assert.strictEqual(editor.getSelections()!.length, 1);
assert.ok(editor.getSelection()!.equalsRange({ startLineNumber: 1, startColumn: 27, endLineNumber: 1, endColumn: 27 }));
assert.equal(editor.getModel()!.getValue(), 'example example {{% url_**** %}}');
assert.strictEqual(editor.getModel()!.getValue(), 'example example {{% url_**** %}}');
}, ['example example sc']);
@@ -346,9 +346,9 @@ suite('SnippetController', () => {
controller.insert(codeSnippet, { overwriteBefore: 2 });
assert.equal(editor.getSelections()!.length, 1);
assert.strictEqual(editor.getSelections()!.length, 1);
assert.ok(editor.getSelection()!.equalsRange({ startLineNumber: 2, startColumn: 2, endLineNumber: 2, endColumn: 2 }), editor.getSelection()!.toString());
assert.equal(editor.getModel()!.getValue(), 'afterEach((done) => {\n\ttest\n});');
assert.strictEqual(editor.getModel()!.getValue(), 'afterEach((done) => {\n\ttest\n});');
}, ['af']);
@@ -364,9 +364,9 @@ suite('SnippetController', () => {
controller.insert(codeSnippet, { overwriteBefore: 2 });
assert.equal(editor.getSelections()!.length, 1);
assert.strictEqual(editor.getSelections()!.length, 1);
assert.ok(editor.getSelection()!.equalsRange({ startLineNumber: 2, startColumn: 1, endLineNumber: 2, endColumn: 1 }), editor.getSelection()!.toString());
assert.equal(editor.getModel()!.getValue(), 'afterEach((done) => {\n\ttest\n});');
assert.strictEqual(editor.getModel()!.getValue(), 'afterEach((done) => {\n\ttest\n});');
}, ['af']);
@@ -380,8 +380,8 @@ suite('SnippetController', () => {
controller.insert(codeSnippet, { overwriteBefore: 8 });
assert.equal(editor.getModel()!.getValue(), 'after');
assert.equal(editor.getSelections()!.length, 1);
assert.strictEqual(editor.getModel()!.getValue(), 'after');
assert.strictEqual(editor.getSelections()!.length, 1);
assert.ok(editor.getSelection()!.equalsRange({ startLineNumber: 1, startColumn: 4, endLineNumber: 1, endColumn: 4 }), editor.getSelection()!.toString());
}, ['afterone']);
@@ -404,7 +404,7 @@ suite('SnippetController', () => {
controller.insert(codeSnippet, { overwriteBefore: 2 });
assert.equal(editor.getSelections()!.length, 2);
assert.strictEqual(editor.getSelections()!.length, 2);
const [first, second] = editor.getSelections()!;
assert.ok(first.equalsRange({ startLineNumber: 5, startColumn: 3, endLineNumber: 5, endColumn: 3 }), first.toString());
@@ -429,7 +429,7 @@ suite('SnippetController', () => {
controller.insert(codeSnippet, { overwriteBefore: 2 });
assert.equal(editor.getSelections()!.length, 1);
assert.strictEqual(editor.getSelections()!.length, 1);
const [first] = editor.getSelections()!;
assert.ok(first.equalsRange({ startLineNumber: 2, startColumn: 3, endLineNumber: 2, endColumn: 3 }), first.toString());
@@ -465,7 +465,7 @@ suite('SnippetController', () => {
codeSnippet = '_foo';
controller.insert(codeSnippet, { overwriteBefore: 1 });
assert.equal(editor.getModel()!.getValue(), 'this._foo\nabc_foo');
assert.strictEqual(editor.getModel()!.getValue(), 'this._foo\nabc_foo');
}, ['this._', 'abc']);
@@ -478,7 +478,7 @@ suite('SnippetController', () => {
codeSnippet = 'XX';
controller.insert(codeSnippet, { overwriteBefore: 1 });
assert.equal(editor.getModel()!.getValue(), 'this.XX\nabcXX');
assert.strictEqual(editor.getModel()!.getValue(), 'this.XX\nabcXX');
}, ['this._', 'abc']);
@@ -492,7 +492,7 @@ suite('SnippetController', () => {
codeSnippet = '_foo';
controller.insert(codeSnippet, { overwriteBefore: 1 });
assert.equal(editor.getModel()!.getValue(), 'this._foo\nabc_foo\ndef_foo');
assert.strictEqual(editor.getModel()!.getValue(), 'this._foo\nabc_foo\ndef_foo');
}, ['this._', 'abc', 'def_']);
@@ -506,7 +506,7 @@ suite('SnippetController', () => {
codeSnippet = '._foo';
controller.insert(codeSnippet, { overwriteBefore: 2 });
assert.equal(editor.getModel()!.getValue(), 'this._foo\nabc._foo\ndef._foo');
assert.strictEqual(editor.getModel()!.getValue(), 'this._foo\nabc._foo\ndef._foo');
}, ['this._', 'abc', 'def._']);
@@ -520,7 +520,7 @@ suite('SnippetController', () => {
codeSnippet = '._foo';
controller.insert(codeSnippet, { overwriteBefore: 2 });
assert.equal(editor.getModel()!.getValue(), 'this._foo\nabc._foo\ndef._foo');
assert.strictEqual(editor.getModel()!.getValue(), 'this._foo\nabc._foo\ndef._foo');
}, ['this._', 'abc', 'def._']);
@@ -534,7 +534,7 @@ suite('SnippetController', () => {
codeSnippet = '._foo';
controller.insert(codeSnippet, { overwriteBefore: 2 });
assert.equal(editor.getModel()!.getValue(), 'this._._foo\na._foo\ndef._._foo');
assert.strictEqual(editor.getModel()!.getValue(), 'this._._foo\na._foo\ndef._._foo');
}, ['this._', 'abc', 'def._']);
@@ -550,7 +550,7 @@ suite('SnippetController', () => {
codeSnippet = 'document';
controller.insert(codeSnippet, { overwriteBefore: 3 });
assert.equal(editor.getModel()!.getValue(), '{document}\n{document && true}');
assert.strictEqual(editor.getModel()!.getValue(), '{document}\n{document && true}');
}, ['{foo}', '{foo && true}']);
});
@@ -565,7 +565,7 @@ suite('SnippetController', () => {
codeSnippet = 'for (var ${1:i}=0; ${1:i}<len; ${1:i}++) { $0 }';
controller.insert(codeSnippet);
assert.equal(editor.getModel()!.getValue(), 'for (var i=0; i<len; i++) { }for (var i=0; i<len; i++) { }');
assert.strictEqual(editor.getModel()!.getValue(), 'for (var i=0; i<len; i++) { }for (var i=0; i<len; i++) { }');
}, ['for (var i=0; i<len; i++) { }']);
@@ -578,7 +578,7 @@ suite('SnippetController', () => {
codeSnippet = 'for (let ${1:i}=0; ${1:i}<len; ${1:i}++) { $0 }';
controller.insert(codeSnippet);
assert.equal(editor.getModel()!.getValue(), 'for (let i=0; i<len; i++) { }for (var i=0; i<len; i++) { }');
assert.strictEqual(editor.getModel()!.getValue(), 'for (let i=0; i<len; i++) { }for (var i=0; i<len; i++) { }');
}, ['for (var i=0; i<len; i++) { }']);

View File

@@ -21,13 +21,13 @@ suite('SnippetController2', function () {
const actual = s.shift()!;
assert.ok(selection.equalsSelection(actual), `actual=${selection.toString()} <> expected=${actual.toString()}`);
}
assert.equal(s.length, 0);
assert.strictEqual(s.length, 0);
}
function assertContextKeys(service: MockContextKeyService, inSnippet: boolean, hasPrev: boolean, hasNext: boolean): void {
assert.equal(SnippetController2.InSnippetMode.getValue(service), inSnippet, `inSnippetMode`);
assert.equal(SnippetController2.HasPrevTabstop.getValue(service), hasPrev, `HasPrevTabstop`);
assert.equal(SnippetController2.HasNextTabstop.getValue(service), hasNext, `HasNextTabstop`);
assert.strictEqual(SnippetController2.InSnippetMode.getValue(service), inSnippet, `inSnippetMode`);
assert.strictEqual(SnippetController2.HasPrevTabstop.getValue(service), hasPrev, `HasPrevTabstop`);
assert.strictEqual(SnippetController2.HasNextTabstop.getValue(service), hasNext, `HasNextTabstop`);
}
let editor: ICodeEditor;
@@ -40,7 +40,7 @@ suite('SnippetController2', function () {
model = createTextModel('if\n $state\nfi');
editor = createTestCodeEditor({ model: model });
editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)]);
assert.equal(model.getEOL(), '\n');
assert.strictEqual(model.getEOL(), '\n');
});
teardown(function () {
@@ -78,9 +78,9 @@ suite('SnippetController2', function () {
assertContextKeys(contextKeys, false, false, false);
editor.trigger('test', 'type', { text: '\t' });
assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), false);
assert.equal(SnippetController2.HasNextTabstop.getValue(contextKeys), false);
assert.equal(SnippetController2.HasPrevTabstop.getValue(contextKeys), false);
assert.strictEqual(SnippetController2.InSnippetMode.getValue(contextKeys), false);
assert.strictEqual(SnippetController2.HasNextTabstop.getValue(contextKeys), false);
assert.strictEqual(SnippetController2.HasPrevTabstop.getValue(contextKeys), false);
});
test('insert, insert -> cursor moves out (left/right)', function () {
@@ -111,7 +111,7 @@ suite('SnippetController2', function () {
const ctrl = new SnippetController2(editor, logService, contextKeys);
ctrl.insert('foo${1:bar}foo$0');
assert.equal(SnippetController2.InSnippetMode.getValue(contextKeys), true);
assert.strictEqual(SnippetController2.InSnippetMode.getValue(contextKeys), true);
assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11));
// bad selection change

Some files were not shown because too many files have changed in this diff Show More