Merge from vscode 8a997f7321ae6612fc0e6eb3eac4f358a6233bfb

This commit is contained in:
ADS Merger
2020-02-11 07:08:19 +00:00
parent 0f934081e1
commit 085752f111
217 changed files with 2561 additions and 2063 deletions

View File

@@ -88,6 +88,7 @@ export interface ISerializedFontInfo {
readonly canUseHalfwidthRightwardsArrow: boolean;
readonly spaceWidth: number;
middotWidth: number;
wsmiddotWidth: number;
readonly maxDigitWidth: number;
}
@@ -161,6 +162,7 @@ class CSSBasedConfiguration extends Disposable {
// 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;
const fontInfo = new FontInfo(savedFontInfo, false);
this._writeToCache(fontInfo, fontInfo);
}
@@ -186,6 +188,7 @@ class CSSBasedConfiguration extends Disposable {
canUseHalfwidthRightwardsArrow: readConfig.canUseHalfwidthRightwardsArrow,
spaceWidth: Math.max(readConfig.spaceWidth, 5),
middotWidth: Math.max(readConfig.middotWidth, 5),
wsmiddotWidth: Math.max(readConfig.wsmiddotWidth, 5),
maxDigitWidth: Math.max(readConfig.maxDigitWidth, 5),
}, false);
}
@@ -226,9 +229,12 @@ class CSSBasedConfiguration extends Disposable {
const rightwardsArrow = this.createRequest('→', CharWidthRequestType.Regular, all, monospace);
const halfwidthRightwardsArrow = this.createRequest('→', CharWidthRequestType.Regular, all, null);
// middle dot character
// U+00B7 - MIDDLE DOT
const middot = this.createRequest('·', CharWidthRequestType.Regular, all, monospace);
// U+2E31 - WORD SEPARATOR MIDDLE DOT
const wsmiddotWidth = this.createRequest(String.fromCharCode(0x2E31), CharWidthRequestType.Regular, all, null);
// monospace test: some characters
this.createRequest('|', CharWidthRequestType.Regular, all, monospace);
this.createRequest('/', CharWidthRequestType.Regular, all, monospace);
@@ -294,6 +300,7 @@ class CSSBasedConfiguration extends Disposable {
canUseHalfwidthRightwardsArrow: canUseHalfwidthRightwardsArrow,
spaceWidth: space.width,
middotWidth: middot.width,
wsmiddotWidth: wsmiddotWidth.width,
maxDigitWidth: maxDigitWidth
}, canTrustBrowserZoomLevel);
}
@@ -379,7 +386,11 @@ export class Configuration extends CommonEditorConfiguration {
emptySelectionClipboard: browser.isWebKit || browser.isFirefox,
pixelRatio: browser.getPixelRatio(),
zoomLevel: browser.getZoomLevel(),
accessibilitySupport: this.accessibilityService.isScreenReaderOptimized() ? AccessibilitySupport.Enabled : AccessibilitySupport.Disabled
accessibilitySupport: (
this.accessibilityService.isScreenReaderOptimized()
? AccessibilitySupport.Enabled
: this.accessibilityService.getAccessibilitySupport()
)
};
}

View File

@@ -57,19 +57,6 @@ export interface ITextAreaInputHost {
deduceModelPosition(viewAnchorPosition: Position, deltaOffset: number, lineFeedCnt: number): Position;
}
const enum TextAreaInputEventType {
none,
compositionstart,
compositionupdate,
compositionend,
input,
cut,
copy,
paste,
focus,
blur
}
interface CompositionEvent extends UIEvent {
readonly data: string;
readonly locale: string;
@@ -155,7 +142,6 @@ export class TextAreaInput extends Disposable {
private readonly _host: ITextAreaInputHost;
private readonly _textArea: TextAreaWrapper;
private _lastTextAreaEvent: TextAreaInputEventType;
private readonly _asyncTriggerCut: RunOnceScheduler;
private _textAreaState: TextAreaState;
@@ -169,7 +155,6 @@ export class TextAreaInput extends Disposable {
super();
this._host = host;
this._textArea = this._register(new TextAreaWrapper(textArea));
this._lastTextAreaEvent = TextAreaInputEventType.none;
this._asyncTriggerCut = this._register(new RunOnceScheduler(() => this._onCut.fire(), 0));
this._textAreaState = TextAreaState.EMPTY;
@@ -200,8 +185,6 @@ export class TextAreaInput extends Disposable {
}));
this._register(dom.addDisposableListener(textArea.domNode, 'compositionstart', (e: CompositionEvent) => {
this._lastTextAreaEvent = TextAreaInputEventType.compositionstart;
if (this._isDoingComposition) {
return;
}
@@ -218,10 +201,10 @@ export class TextAreaInput extends Disposable {
/**
* Deduce the typed input from a text area's value and the last observed state.
*/
const deduceInputFromTextAreaValue = (couldBeEmojiInput: boolean, couldBeTypingAtOffset0: boolean): [TextAreaState, ITypeData] => {
const deduceInputFromTextAreaValue = (couldBeEmojiInput: boolean): [TextAreaState, ITypeData] => {
const oldState = this._textAreaState;
const newState = TextAreaState.readFromTextArea(this._textArea);
return [newState, TextAreaState.deduceInput(oldState, newState, couldBeEmojiInput, couldBeTypingAtOffset0)];
return [newState, TextAreaState.deduceInput(oldState, newState, couldBeEmojiInput)];
};
/**
@@ -258,10 +241,8 @@ export class TextAreaInput extends Disposable {
};
this._register(dom.addDisposableListener(textArea.domNode, 'compositionupdate', (e: CompositionEvent) => {
this._lastTextAreaEvent = TextAreaInputEventType.compositionupdate;
if (compositionDataInValid(e.locale)) {
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false, /*couldBeTypingAtOffset0*/false);
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false);
this._textAreaState = newState;
this._onType.fire(typeInput);
this._onCompositionUpdate.fire(e);
@@ -275,7 +256,6 @@ export class TextAreaInput extends Disposable {
}));
this._register(dom.addDisposableListener(textArea.domNode, 'compositionend', (e: CompositionEvent) => {
this._lastTextAreaEvent = TextAreaInputEventType.compositionend;
// 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) {
@@ -283,7 +263,7 @@ export class TextAreaInput extends Disposable {
}
if (compositionDataInValid(e.locale)) {
// https://github.com/Microsoft/monaco-editor/issues/339
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false, /*couldBeTypingAtOffset0*/false);
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false);
this._textAreaState = newState;
this._onType.fire(typeInput);
} else {
@@ -307,10 +287,6 @@ export class TextAreaInput extends Disposable {
}));
this._register(dom.addDisposableListener(textArea.domNode, 'input', () => {
// We want to find out if this is the first `input` after a `focus`.
const previousEventWasFocus = (this._lastTextAreaEvent === TextAreaInputEventType.focus);
this._lastTextAreaEvent = TextAreaInputEventType.input;
// Pretend here we touched the text area, as the `input` event will most likely
// result in a `selectionchange` event which we want to ignore
this._textArea.setIgnoreSelectionChangeTime('received input event');
@@ -319,7 +295,7 @@ export class TextAreaInput extends Disposable {
return;
}
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/platform.isMacintosh, /*couldBeTypingAtOffset0*/previousEventWasFocus && platform.isMacintosh);
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/platform.isMacintosh);
if (typeInput.replaceCharCnt === 0 && typeInput.text.length === 1 && strings.isHighSurrogate(typeInput.text.charCodeAt(0))) {
// Ignore invalid input but keep it around for next time
return;
@@ -341,8 +317,6 @@ export class TextAreaInput extends Disposable {
// --- Clipboard operations
this._register(dom.addDisposableListener(textArea.domNode, 'cut', (e: ClipboardEvent) => {
this._lastTextAreaEvent = TextAreaInputEventType.cut;
// Pretend here we touched the text area, as the `cut` event will most likely
// result in a `selectionchange` event which we want to ignore
this._textArea.setIgnoreSelectionChangeTime('received cut event');
@@ -352,14 +326,10 @@ export class TextAreaInput extends Disposable {
}));
this._register(dom.addDisposableListener(textArea.domNode, 'copy', (e: ClipboardEvent) => {
this._lastTextAreaEvent = TextAreaInputEventType.copy;
this._ensureClipboardGetsEditorSelection(e);
}));
this._register(dom.addDisposableListener(textArea.domNode, 'paste', (e: ClipboardEvent) => {
this._lastTextAreaEvent = TextAreaInputEventType.paste;
// Pretend here we touched the text area, as the `paste` event will most likely
// result in a `selectionchange` event which we want to ignore
this._textArea.setIgnoreSelectionChangeTime('received paste event');
@@ -379,11 +349,9 @@ export class TextAreaInput extends Disposable {
}));
this._register(dom.addDisposableListener(textArea.domNode, 'focus', () => {
this._lastTextAreaEvent = TextAreaInputEventType.focus;
this._setHasFocus(true);
}));
this._register(dom.addDisposableListener(textArea.domNode, 'blur', () => {
this._lastTextAreaEvent = TextAreaInputEventType.blur;
this._setHasFocus(false);
}));
}
@@ -625,7 +593,8 @@ class ClipboardEventUtils {
if ((<any>window).clipboardData) {
e.preventDefault();
return (<any>window).clipboardData.getData('Text');
const text: string = (<any>window).clipboardData.getData('Text');
return [text, null];
}
throw new Error('ClipboardEventUtils.getTextData: Cannot use text data!');

View File

@@ -96,7 +96,7 @@ export class TextAreaState {
return new TextAreaState(text, 0, text.length, null, null);
}
public static deduceInput(previousState: TextAreaState, currentState: TextAreaState, couldBeEmojiInput: boolean, couldBeTypingAtOffset0: boolean): ITypeData {
public static deduceInput(previousState: TextAreaState, currentState: TextAreaState, couldBeEmojiInput: boolean): ITypeData {
if (!previousState) {
// This is the EMPTY state
return {
@@ -116,18 +116,6 @@ export class TextAreaState {
let currentSelectionStart = currentState.selectionStart;
let currentSelectionEnd = currentState.selectionEnd;
if (couldBeTypingAtOffset0 && previousValue.length > 0 && previousSelectionStart === previousSelectionEnd && currentSelectionStart === currentSelectionEnd) {
// See https://github.com/Microsoft/vscode/issues/42251
// where typing always happens at offset 0 in the textarea
// when using a custom title area in OSX and moving the window
if (!strings.startsWith(currentValue, previousValue) && strings.endsWith(currentValue, previousValue)) {
// Looks like something was typed at offset 0
// ==> pretend we placed the cursor at offset 0 to begin with...
previousSelectionStart = 0;
previousSelectionEnd = 0;
}
}
// Strip the previous suffix from the value (without interfering with the current selection)
const previousSuffix = previousValue.substring(previousSelectionEnd);
const currentSuffix = currentValue.substring(currentSelectionEnd);

View File

@@ -74,6 +74,7 @@ export class ViewLineOptions {
public readonly renderControlCharacters: boolean;
public readonly spaceWidth: number;
public readonly middotWidth: number;
public readonly wsmiddotWidth: number;
public readonly useMonospaceOptimizations: boolean;
public readonly canUseHalfwidthRightwardsArrow: boolean;
public readonly lineHeight: number;
@@ -88,6 +89,7 @@ export class ViewLineOptions {
this.renderControlCharacters = options.get(EditorOption.renderControlCharacters);
this.spaceWidth = fontInfo.spaceWidth;
this.middotWidth = fontInfo.middotWidth;
this.wsmiddotWidth = fontInfo.wsmiddotWidth;
this.useMonospaceOptimizations = (
fontInfo.isMonospace
&& !options.get(EditorOption.disableMonospaceOptimizations)
@@ -105,6 +107,7 @@ export class ViewLineOptions {
&& this.renderControlCharacters === other.renderControlCharacters
&& this.spaceWidth === other.spaceWidth
&& this.middotWidth === other.middotWidth
&& this.wsmiddotWidth === other.wsmiddotWidth
&& this.useMonospaceOptimizations === other.useMonospaceOptimizations
&& this.canUseHalfwidthRightwardsArrow === other.canUseHalfwidthRightwardsArrow
&& this.lineHeight === other.lineHeight
@@ -219,6 +222,7 @@ export class ViewLine implements IVisibleLine {
lineData.startVisibleColumn,
options.spaceWidth,
options.middotWidth,
options.wsmiddotWidth,
options.stopRenderingLineAfter,
options.renderWhitespace,
options.renderControlCharacters,

View File

@@ -8,7 +8,7 @@ import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
import { ISashEvent, IVerticalSashLayoutProvider, Sash, SashState } from 'vs/base/browser/ui/sash/sash';
import { RunOnceScheduler, IntervalTimer } from 'vs/base/common/async';
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';
@@ -48,6 +48,7 @@ import { Constants } from 'vs/base/common/uint';
import { EditorExtensionsRegistry, IDiffEditorContributionDescription } from 'vs/editor/browser/editorExtensions';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IEditorProgressService, IProgressRunner } from 'vs/platform/progress/common/progress';
import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver';
interface IEditorDiffDecorations {
decorations: IModelDeltaDecoration[];
@@ -179,10 +180,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
private readonly _overviewDomElement: HTMLElement;
private readonly _overviewViewportDomElement: FastDomNode<HTMLElement>;
private _width: number;
private _height: number;
private _reviewHeight: number;
private readonly _measureDomElementToken: IntervalTimer | null;
private readonly _elementSizeObserver: ElementSizeObserver;
private readonly originalEditor: CodeEditorWidget;
private readonly _originalDomNode: HTMLElement;
@@ -329,9 +327,10 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this._isVisible = true;
this._isHandlingScrollEvent = false;
this._width = 0;
this._height = 0;
this._reviewHeight = 0;
this._elementSizeObserver = this._register(new ElementSizeObserver(this._containerDomElement, undefined, () => this._onDidContainerSizeChanged()));
if (options.automaticLayout) {
this._elementSizeObserver.startObserving();
}
this._diffComputationResult = null;
@@ -360,12 +359,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this._containerDomElement.appendChild(this._reviewPane.shadow.domNode);
this._containerDomElement.appendChild(this._reviewPane.actionBarContainer.domNode);
if (options.automaticLayout) {
this._measureDomElementToken = new IntervalTimer();
this._measureDomElementToken.cancelAndSet(() => this._measureDomElement(false), 100);
} else {
this._measureDomElementToken = null;
}
// enableSplitViewResizing
this._enableSplitViewResizing = true;
@@ -563,10 +557,6 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
this._beginUpdateDecorationsTimeout = -1;
}
if (this._measureDomElementToken) {
this._measureDomElementToken.dispose();
}
this._cleanViewZonesAndDecorations();
if (this._originalOverviewRuler) {
@@ -866,7 +856,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
}
public layout(dimension?: editorCommon.IDimension): void {
this._measureDomElement(false, dimension);
this._elementSizeObserver.observe(dimension);
}
public focus(): void {
@@ -907,35 +897,21 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
//------------ begin layouting methods
private _measureDomElement(forceDoLayoutCall: boolean, dimensions?: editorCommon.IDimension): void {
dimensions = dimensions || {
width: this._containerDomElement.clientWidth,
height: this._containerDomElement.clientHeight
};
if (dimensions.width <= 0) {
this._width = 0;
this._height = 0;
this._reviewHeight = 0;
return;
}
if (!forceDoLayoutCall && dimensions.width === this._width && dimensions.height === this._height) {
// Nothing has changed
return;
}
this._width = dimensions.width;
this._height = dimensions.height;
this._reviewHeight = this._reviewPane.isVisible() ? this._height : 0;
private _onDidContainerSizeChanged(): void {
this._doLayout();
}
private _getReviewHeight(): number {
return this._reviewPane.isVisible() ? this._elementSizeObserver.getHeight() : 0;
}
private _layoutOverviewRulers(): void {
if (!this._originalOverviewRuler || !this._modifiedOverviewRuler) {
return;
}
const height = this._elementSizeObserver.getHeight();
const reviewHeight = this._getReviewHeight();
let freeSpace = DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH - 2 * DiffEditorWidget.ONE_OVERVIEW_WIDTH;
let layoutInfo = this.modifiedEditor.getLayoutInfo();
if (layoutInfo) {
@@ -943,13 +919,13 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
top: 0,
width: DiffEditorWidget.ONE_OVERVIEW_WIDTH,
right: freeSpace + DiffEditorWidget.ONE_OVERVIEW_WIDTH,
height: (this._height - this._reviewHeight)
height: (height - reviewHeight)
});
this._modifiedOverviewRuler.setLayout({
top: 0,
right: 0,
width: DiffEditorWidget.ONE_OVERVIEW_WIDTH,
height: (this._height - this._reviewHeight)
height: (height - reviewHeight)
});
}
}
@@ -1095,33 +1071,38 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
}
public doLayout(): void {
this._measureDomElement(true);
this._elementSizeObserver.observe();
this._doLayout();
}
private _doLayout(): void {
const width = this._elementSizeObserver.getWidth();
const height = this._elementSizeObserver.getHeight();
const reviewHeight = this._getReviewHeight();
let splitPoint = this._strategy.layout();
this._originalDomNode.style.width = splitPoint + 'px';
this._originalDomNode.style.left = '0px';
this._modifiedDomNode.style.width = (this._width - splitPoint) + 'px';
this._modifiedDomNode.style.width = (width - splitPoint) + 'px';
this._modifiedDomNode.style.left = splitPoint + 'px';
this._overviewDomElement.style.top = '0px';
this._overviewDomElement.style.height = (this._height - this._reviewHeight) + 'px';
this._overviewDomElement.style.height = (height - reviewHeight) + 'px';
this._overviewDomElement.style.width = DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH + 'px';
this._overviewDomElement.style.left = (this._width - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH) + 'px';
this._overviewDomElement.style.left = (width - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH) + 'px';
this._overviewViewportDomElement.setWidth(DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH);
this._overviewViewportDomElement.setHeight(30);
this.originalEditor.layout({ width: splitPoint, height: (this._height - this._reviewHeight) });
this.modifiedEditor.layout({ width: this._width - splitPoint - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH, height: (this._height - this._reviewHeight) });
this.originalEditor.layout({ width: splitPoint, height: (height - reviewHeight) });
this.modifiedEditor.layout({ width: width - splitPoint - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH, height: (height - reviewHeight) });
if (this._originalOverviewRuler || this._modifiedOverviewRuler) {
this._layoutOverviewRulers();
}
this._reviewPane.layout(this._height - this._reviewHeight, this._width, this._reviewHeight);
this._reviewPane.layout(height - reviewHeight, width, reviewHeight);
this._layoutOverviewViewport();
}
@@ -1162,11 +1143,11 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
private _createDataSource(): IDataSource {
return {
getWidth: () => {
return this._width;
return this._elementSizeObserver.getWidth();
},
getHeight: () => {
return (this._height - this._reviewHeight);
return (this._elementSizeObserver.getHeight() - this._getReviewHeight());
},
getContainerDomNode: () => {
@@ -1200,7 +1181,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
}
// Just do a layout, the strategy might need it
this._measureDomElement(true);
this._doLayout();
}
private _getLineChangeAtOrBeforeLineNumber(lineNumber: number, startLineNumberExtractor: (lineChange: editorCommon.ILineChange) => number): editorCommon.ILineChange | null {
@@ -2181,6 +2162,7 @@ class InlineViewZonesComputer extends ViewZonesComputer {
0,
fontInfo.spaceWidth,
fontInfo.middotWidth,
fontInfo.wsmiddotWidth,
options.get(EditorOption.stopRenderingLineAfter),
options.get(EditorOption.renderWhitespace),
options.get(EditorOption.renderControlCharacters),

View File

@@ -783,6 +783,7 @@ export class DiffReview extends Disposable {
0,
fontInfo.spaceWidth,
fontInfo.middotWidth,
fontInfo.wsmiddotWidth,
options.get(EditorOption.stopRenderingLineAfter),
options.get(EditorOption.renderWhitespace),
options.get(EditorOption.renderControlCharacters),

View File

@@ -366,6 +366,10 @@ export interface IEditorOptions {
* Defaults to 10 (ms)
*/
quickSuggestionsDelay?: number;
/**
* Controls the spacing around the editor.
*/
padding?: IEditorPaddingOptions;
/**
* Parameter hint options.
*/
@@ -2082,6 +2086,65 @@ function _multiCursorModifierFromString(multiCursorModifier: 'ctrlCmd' | 'alt'):
//#endregion
//#region padding
/**
* Configuration options for editor padding
*/
export interface IEditorPaddingOptions {
/**
* Spacing between top edge of editor and first line.
*/
top?: number;
/**
* Spacing between bottom edge of editor and last line.
*/
bottom?: number;
}
export interface InternalEditorPaddingOptions {
readonly top: number;
readonly bottom: number;
}
class EditorPadding extends BaseEditorOption<EditorOption.padding, InternalEditorPaddingOptions> {
constructor() {
super(
EditorOption.padding, 'padding', { top: 0, bottom: 0 },
{
'editor.padding.top': {
type: 'number',
default: 0,
minimum: 0,
maximum: 1000,
description: nls.localize('padding.top', "Controls the amount of space between the top edge of the editor and the first line.")
},
'editor.padding.bottom': {
type: 'number',
default: 0,
minimum: 0,
maximum: 1000,
description: nls.localize('padding.bottom', "Controls the amount of space between the bottom edge of the editor and the last line.")
}
}
);
}
public validate(_input: any): InternalEditorPaddingOptions {
if (typeof _input !== 'object') {
return this.defaultValue;
}
const input = _input as IEditorPaddingOptions;
return {
top: EditorIntOption.clampedInt(input.top, 0, 0, 1000),
bottom: EditorIntOption.clampedInt(input.bottom, 0, 0, 1000)
};
}
}
//#endregion
//#region parameterHints
/**
@@ -3188,6 +3251,7 @@ export const enum EditorOption {
occurrencesHighlight,
overviewRulerBorder,
overviewRulerLanes,
padding,
parameterHints,
peekWidgetDefaultFocus,
definitionLinkOpensInPeek,
@@ -3575,6 +3639,7 @@ export const EditorOptions = {
EditorOption.overviewRulerLanes, 'overviewRulerLanes',
3, 0, 3
)),
padding: register(new EditorPadding()),
parameterHints: register(new EditorParameterHints()),
peekWidgetDefaultFocus: register(new EditorStringEnumOption(
EditorOption.peekWidgetDefaultFocus, 'peekWidgetDefaultFocus',
@@ -3634,7 +3699,7 @@ export const EditorOptions = {
)),
renderWhitespace: register(new EditorStringEnumOption(
EditorOption.renderWhitespace, 'renderWhitespace',
'none' as 'none' | 'boundary' | 'selection' | 'all',
'selection' as 'selection' | 'none' | 'boundary' | 'all',
['none', 'boundary', 'selection', 'all'] as const,
{
enumDescriptions: [

View File

@@ -135,6 +135,7 @@ export class FontInfo extends BareFontInfo {
readonly canUseHalfwidthRightwardsArrow: boolean;
readonly spaceWidth: number;
readonly middotWidth: number;
readonly wsmiddotWidth: number;
readonly maxDigitWidth: number;
/**
@@ -154,6 +155,7 @@ export class FontInfo extends BareFontInfo {
canUseHalfwidthRightwardsArrow: boolean;
spaceWidth: number;
middotWidth: number;
wsmiddotWidth: number;
maxDigitWidth: number;
}, isTrusted: boolean) {
super(opts);
@@ -164,6 +166,7 @@ export class FontInfo extends BareFontInfo {
this.canUseHalfwidthRightwardsArrow = opts.canUseHalfwidthRightwardsArrow;
this.spaceWidth = opts.spaceWidth;
this.middotWidth = opts.middotWidth;
this.wsmiddotWidth = opts.wsmiddotWidth;
this.maxDigitWidth = opts.maxDigitWidth;
}
@@ -183,6 +186,7 @@ export class FontInfo extends BareFontInfo {
&& this.canUseHalfwidthRightwardsArrow === other.canUseHalfwidthRightwardsArrow
&& this.spaceWidth === other.spaceWidth
&& this.middotWidth === other.middotWidth
&& this.wsmiddotWidth === other.wsmiddotWidth
&& this.maxDigitWidth === other.maxDigitWidth
);
}

View File

@@ -61,6 +61,11 @@ export interface ITextEditorModel extends IEditorModel {
* Figure out if this model is resolved or not.
*/
isResolved(): this is IResolvedTextEditorModel;
/**
* The mode id of the text model if known.
*/
getMode(): string | undefined;
}
export interface IResolvedTextEditorModel extends ITextEditorModel {

View File

@@ -229,53 +229,54 @@ export enum EditorOption {
occurrencesHighlight = 61,
overviewRulerBorder = 62,
overviewRulerLanes = 63,
parameterHints = 64,
peekWidgetDefaultFocus = 65,
definitionLinkOpensInPeek = 66,
quickSuggestions = 67,
quickSuggestionsDelay = 68,
readOnly = 69,
renderControlCharacters = 70,
renderIndentGuides = 71,
renderFinalNewline = 72,
renderLineHighlight = 73,
renderValidationDecorations = 74,
renderWhitespace = 75,
revealHorizontalRightPadding = 76,
roundedSelection = 77,
rulers = 78,
scrollbar = 79,
scrollBeyondLastColumn = 80,
scrollBeyondLastLine = 81,
scrollPredominantAxis = 82,
selectionClipboard = 83,
selectionHighlight = 84,
selectOnLineNumbers = 85,
showFoldingControls = 86,
showUnused = 87,
snippetSuggestions = 88,
smoothScrolling = 89,
stopRenderingLineAfter = 90,
suggest = 91,
suggestFontSize = 92,
suggestLineHeight = 93,
suggestOnTriggerCharacters = 94,
suggestSelection = 95,
tabCompletion = 96,
useTabStops = 97,
wordSeparators = 98,
wordWrap = 99,
wordWrapBreakAfterCharacters = 100,
wordWrapBreakBeforeCharacters = 101,
wordWrapColumn = 102,
wordWrapMinified = 103,
wrappingIndent = 104,
wrappingStrategy = 105,
editorClassName = 106,
pixelRatio = 107,
tabFocusMode = 108,
layoutInfo = 109,
wrappingInfo = 110
padding = 64,
parameterHints = 65,
peekWidgetDefaultFocus = 66,
definitionLinkOpensInPeek = 67,
quickSuggestions = 68,
quickSuggestionsDelay = 69,
readOnly = 70,
renderControlCharacters = 71,
renderIndentGuides = 72,
renderFinalNewline = 73,
renderLineHighlight = 74,
renderValidationDecorations = 75,
renderWhitespace = 76,
revealHorizontalRightPadding = 77,
roundedSelection = 78,
rulers = 79,
scrollbar = 80,
scrollBeyondLastColumn = 81,
scrollBeyondLastLine = 82,
scrollPredominantAxis = 83,
selectionClipboard = 84,
selectionHighlight = 85,
selectOnLineNumbers = 86,
showFoldingControls = 87,
showUnused = 88,
snippetSuggestions = 89,
smoothScrolling = 90,
stopRenderingLineAfter = 91,
suggest = 92,
suggestFontSize = 93,
suggestLineHeight = 94,
suggestOnTriggerCharacters = 95,
suggestSelection = 96,
tabCompletion = 97,
useTabStops = 98,
wordSeparators = 99,
wordWrap = 100,
wordWrapBreakAfterCharacters = 101,
wordWrapBreakBeforeCharacters = 102,
wordWrapColumn = 103,
wordWrapMinified = 104,
wrappingIndent = 105,
wrappingStrategy = 106,
editorClassName = 107,
pixelRatio = 108,
tabFocusMode = 109,
layoutInfo = 110,
wrappingInfo = 111
}
/**

View File

@@ -111,8 +111,10 @@ export class LinesLayout {
private _minWidth: number;
private _lineCount: number;
private _lineHeight: number;
private _paddingTop: number;
private _paddingBottom: number;
constructor(lineCount: number, lineHeight: number) {
constructor(lineCount: number, lineHeight: number, paddingTop: number, paddingBottom: number) {
this._instanceId = strings.singleLetterHash(++LinesLayout.INSTANCE_COUNT);
this._pendingChanges = new PendingChanges();
this._lastWhitespaceId = 0;
@@ -121,6 +123,8 @@ export class LinesLayout {
this._minWidth = -1; /* marker for not being computed */
this._lineCount = lineCount;
this._lineHeight = lineHeight;
this._paddingTop = paddingTop;
this._paddingBottom = paddingBottom;
}
/**
@@ -158,6 +162,14 @@ export class LinesLayout {
this._lineHeight = lineHeight;
}
/**
* Changes the padding used to calculate vertical offsets.
*/
public setPadding(paddingTop: number, paddingBottom: number): void {
this._paddingTop = paddingTop;
this._paddingBottom = paddingBottom;
}
/**
* Set the number of lines.
*
@@ -404,7 +416,8 @@ export class LinesLayout {
this._checkPendingChanges();
const linesHeight = this._lineHeight * this._lineCount;
const whitespacesHeight = this.getWhitespacesTotalHeight();
return linesHeight + whitespacesHeight;
return linesHeight + whitespacesHeight + this._paddingTop + this._paddingBottom;
}
/**
@@ -495,7 +508,7 @@ export class LinesLayout {
const previousWhitespacesHeight = this.getWhitespaceAccumulatedHeightBeforeLineNumber(lineNumber);
return previousLinesHeight + previousWhitespacesHeight;
return previousLinesHeight + previousWhitespacesHeight + this._paddingTop;
}
/**
@@ -719,7 +732,7 @@ export class LinesLayout {
} else {
previousWhitespacesHeight = 0;
}
return previousLinesHeight + previousWhitespacesHeight;
return previousLinesHeight + previousWhitespacesHeight + this._paddingTop;
}
public getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset: number): number {

View File

@@ -161,8 +161,9 @@ export class ViewLayout extends Disposable implements IViewLayout {
this._configuration = configuration;
const options = this._configuration.options;
const layoutInfo = options.get(EditorOption.layoutInfo);
const padding = options.get(EditorOption.padding);
this._linesLayout = new LinesLayout(lineCount, options.get(EditorOption.lineHeight));
this._linesLayout = new LinesLayout(lineCount, options.get(EditorOption.lineHeight), padding.top, padding.bottom);
this._scrollable = this._register(new EditorScrollable(0, scheduleAtNextAnimationFrame));
this._configureSmoothScrollDuration();
@@ -202,6 +203,10 @@ export class ViewLayout extends Disposable implements IViewLayout {
if (e.hasChanged(EditorOption.lineHeight)) {
this._linesLayout.setLineHeight(options.get(EditorOption.lineHeight));
}
if (e.hasChanged(EditorOption.padding)) {
const padding = options.get(EditorOption.padding);
this._linesLayout.setPadding(padding.top, padding.bottom);
}
if (e.hasChanged(EditorOption.layoutInfo)) {
const layoutInfo = options.get(EditorOption.layoutInfo);
const width = layoutInfo.contentWidth;

View File

@@ -68,7 +68,8 @@ export class RenderLineInput {
public readonly tabSize: number;
public readonly startVisibleColumn: number;
public readonly spaceWidth: number;
public readonly middotWidth: number;
public readonly renderSpaceWidth: number;
public readonly renderSpaceCharCode: number;
public readonly stopRenderingLineAfter: number;
public readonly renderWhitespace: RenderWhitespace;
public readonly renderControlCharacters: boolean;
@@ -94,6 +95,7 @@ export class RenderLineInput {
startVisibleColumn: number,
spaceWidth: number,
middotWidth: number,
wsmiddotWidth: number,
stopRenderingLineAfter: number,
renderWhitespace: 'none' | 'boundary' | 'selection' | 'all',
renderControlCharacters: boolean,
@@ -112,7 +114,6 @@ export class RenderLineInput {
this.tabSize = tabSize;
this.startVisibleColumn = startVisibleColumn;
this.spaceWidth = spaceWidth;
this.middotWidth = middotWidth;
this.stopRenderingLineAfter = stopRenderingLineAfter;
this.renderWhitespace = (
renderWhitespace === 'all'
@@ -126,6 +127,16 @@ export class RenderLineInput {
this.renderControlCharacters = renderControlCharacters;
this.fontLigatures = fontLigatures;
this.selectionsOnLine = selectionsOnLine && selectionsOnLine.sort((a, b) => a.startOffset < b.startOffset ? -1 : 1);
const wsmiddotDiff = Math.abs(wsmiddotWidth - spaceWidth);
const middotDiff = Math.abs(middotWidth - spaceWidth);
if (wsmiddotDiff < middotDiff) {
this.renderSpaceWidth = wsmiddotWidth;
this.renderSpaceCharCode = 0x2E31; // U+2E31 - WORD SEPARATOR MIDDLE DOT
} else {
this.renderSpaceWidth = middotWidth;
this.renderSpaceCharCode = 0xB7; // U+00B7 - MIDDLE DOT
}
}
private sameSelection(otherSelections: LineRange[] | null): boolean {
@@ -162,6 +173,8 @@ export class RenderLineInput {
&& this.tabSize === other.tabSize
&& this.startVisibleColumn === other.startVisibleColumn
&& this.spaceWidth === other.spaceWidth
&& this.renderSpaceWidth === other.renderSpaceWidth
&& this.renderSpaceCharCode === other.renderSpaceCharCode
&& this.stopRenderingLineAfter === other.stopRenderingLineAfter
&& this.renderWhitespace === other.renderWhitespace
&& this.renderControlCharacters === other.renderControlCharacters
@@ -383,7 +396,7 @@ class ResolvedRenderLineInput {
public readonly startVisibleColumn: number,
public readonly containsRTL: boolean,
public readonly spaceWidth: number,
public readonly middotWidth: number,
public readonly renderSpaceCharCode: number,
public readonly renderWhitespace: RenderWhitespace,
public readonly renderControlCharacters: boolean,
) {
@@ -392,7 +405,6 @@ class ResolvedRenderLineInput {
}
function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput {
const useMonospaceOptimizations = input.useMonospaceOptimizations;
const lineContent = input.lineContent;
let isOverflowing: boolean;
@@ -408,7 +420,7 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput
let tokens = transformAndRemoveOverflowing(input.lineTokens, input.fauxIndentLength, len);
if (input.renderWhitespace === RenderWhitespace.All || input.renderWhitespace === RenderWhitespace.Boundary || (input.renderWhitespace === RenderWhitespace.Selection && !!input.selectionsOnLine)) {
tokens = _applyRenderWhitespace(lineContent, len, input.continuesWithWrappedLine, tokens, input.fauxIndentLength, input.tabSize, input.startVisibleColumn, useMonospaceOptimizations, input.selectionsOnLine, input.renderWhitespace === RenderWhitespace.Boundary);
tokens = _applyRenderWhitespace(input, lineContent, len, tokens);
}
let containsForeignElements = ForeignElementType.None;
if (input.lineDecorations.length > 0) {
@@ -431,7 +443,7 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput
}
return new ResolvedRenderLineInput(
useMonospaceOptimizations,
input.useMonospaceOptimizations,
input.canUseHalfwidthRightwardsArrow,
lineContent,
len,
@@ -443,7 +455,7 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput
input.startVisibleColumn,
input.containsRTL,
input.spaceWidth,
input.middotWidth,
input.renderSpaceCharCode,
input.renderWhitespace,
input.renderControlCharacters
);
@@ -553,7 +565,16 @@ function splitLargeTokens(lineContent: string, tokens: LinePart[], onlyAtSpaces:
* Moreover, a token is created for every visual indent because on some fonts the glyphs used for rendering whitespace (&rarr; or &middot;) do not have the same width as &nbsp;.
* The rendering phase will generate `style="width:..."` for these tokens.
*/
function _applyRenderWhitespace(lineContent: string, len: number, continuesWithWrappedLine: boolean, tokens: LinePart[], fauxIndentLength: number, tabSize: number, startVisibleColumn: number, useMonospaceOptimizations: boolean, selections: LineRange[] | null, onlyBoundary: boolean): LinePart[] {
function _applyRenderWhitespace(input: RenderLineInput, lineContent: string, len: number, tokens: LinePart[]): LinePart[] {
const continuesWithWrappedLine = input.continuesWithWrappedLine;
const fauxIndentLength = input.fauxIndentLength;
const tabSize = input.tabSize;
const startVisibleColumn = input.startVisibleColumn;
const useMonospaceOptimizations = input.useMonospaceOptimizations;
const selections = input.selectionsOnLine;
const onlyBoundary = (input.renderWhitespace === RenderWhitespace.Boundary);
const generateLinePartForEachWhitespace = (input.renderSpaceWidth !== input.spaceWidth);
let result: LinePart[] = [], resultLen = 0;
let tokenIndex = 0;
@@ -616,7 +637,14 @@ function _applyRenderWhitespace(lineContent: string, len: number, continuesWithW
// was in whitespace token
if (!isInWhitespace || (!useMonospaceOptimizations && tmpIndent >= tabSize)) {
// leaving whitespace token or entering a new indent
result[resultLen++] = new LinePart(charIndex, 'mtkw');
if (generateLinePartForEachWhitespace) {
const lastEndIndex = (resultLen > 0 ? result[resultLen - 1].endIndex : fauxIndentLength);
for (let i = lastEndIndex + 1; i <= charIndex; i++) {
result[resultLen++] = new LinePart(i, 'mtkw');
}
} else {
result[resultLen++] = new LinePart(charIndex, 'mtkw');
}
tmpIndent = tmpIndent % tabSize;
}
} else {
@@ -661,7 +689,18 @@ function _applyRenderWhitespace(lineContent: string, len: number, continuesWithW
}
}
result[resultLen++] = new LinePart(len, generateWhitespace ? 'mtkw' : tokenType);
if (generateWhitespace) {
if (generateLinePartForEachWhitespace) {
const lastEndIndex = (resultLen > 0 ? result[resultLen - 1].endIndex : fauxIndentLength);
for (let i = lastEndIndex + 1; i <= len; i++) {
result[resultLen++] = new LinePart(i, 'mtkw');
}
} else {
result[resultLen++] = new LinePart(len, 'mtkw');
}
} else {
result[resultLen++] = new LinePart(len, tokenType);
}
return result;
}
@@ -739,13 +778,10 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
const startVisibleColumn = input.startVisibleColumn;
const containsRTL = input.containsRTL;
const spaceWidth = input.spaceWidth;
const middotWidth = input.middotWidth;
const renderSpaceCharCode = input.renderSpaceCharCode;
const renderWhitespace = input.renderWhitespace;
const renderControlCharacters = input.renderControlCharacters;
// use U+2E31 - WORD SEPARATOR MIDDLE DOT or U+00B7 - MIDDLE DOT
const spaceRenderWhitespaceCharacter = (middotWidth > spaceWidth ? 0x2E31 : 0xB7);
const characterMapping = new CharacterMapping(len + 1, parts.length);
let charIndex = 0;
@@ -815,7 +851,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
} else { // must be CharCode.Space
charWidth = 1;
sb.write1(spaceRenderWhitespaceCharacter); // &middot; or word separator middle dot
sb.write1(renderSpaceCharCode); // &middot; or word separator middle dot
}
charOffsetInPart += charWidth;

View File

@@ -611,7 +611,14 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
return;
} else {
const scrollAdjustment = this._getHeight();
let scrollAdjustment = this._getHeight();
// if the editor has top padding, factor that into the zone height
scrollAdjustment -= this._codeEditor.getOption(EditorOption.padding).top;
if (scrollAdjustment <= 0) {
return;
}
viewZone.heightInPx = scrollAdjustment;
this._viewZoneId = accessor.addZone(viewZone);

View File

@@ -304,7 +304,7 @@ export class MarkerController implements IEditorContribution {
model.currentMarker = marker;
}
private _onMarkerChanged(changedResources: URI[]): void {
private _onMarkerChanged(changedResources: readonly URI[]): void {
const editorModel = this._editor.getModel();
if (!editorModel) {
return;

View File

@@ -134,10 +134,10 @@ class MessageWidget {
detailsElement.appendChild(codeElement);
} else {
this._codeLink = dom.$('a.code-link');
this._codeLink.setAttribute('href', `${code.link.toString()}`);
this._codeLink.setAttribute('href', `${code.target.toString()}`);
this._codeLink.onclick = (e) => {
this._openerService.open(code.link);
this._openerService.open(code.target);
e.preventDefault();
e.stopPropagation();
};

View File

@@ -516,10 +516,10 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
sourceElement.innerText = source;
}
this._codeLink = dom.append(sourceAndCodeElement, $('a.code-link'));
this._codeLink.setAttribute('href', code.link.toString());
this._codeLink.setAttribute('href', code.target.toString());
this._codeLink.onclick = (e) => {
this._openerService.open(code.link);
this._openerService.open(code.target);
e.preventDefault();
e.stopPropagation();
};

View File

@@ -468,6 +468,7 @@ export class SnippetSession {
// that ensures the primiary cursor stays primary despite not being
// the one with lowest start position
edits[idx] = EditOperation.replace(snippetSelection, snippet.toString());
edits[idx].identifier = { major: idx, minor: 0 }; // mark the edit so only our undo edits will be used to generate end cursors
snippets[idx] = new OneSnippet(editor, snippet, offset);
}
@@ -507,7 +508,11 @@ export class SnippetSession {
if (this._snippets[0].hasPlaceholder) {
return this._move(true);
} else {
return undoEdits.map(edit => Selection.fromPositions(edit.range.getEndPosition()));
return (
undoEdits
.filter(edit => !!edit.identifier) // only use our undo edits
.map(edit => Selection.fromPositions(edit.range.getEndPosition()))
);
}
});
this._editor.revealRange(this._editor.getSelections()[0]);
@@ -529,7 +534,11 @@ export class SnippetSession {
if (this._snippets[0].hasPlaceholder) {
return this._move(undefined);
} else {
return undoEdits.map(edit => Selection.fromPositions(edit.range.getEndPosition()));
return (
undoEdits
.filter(edit => !!edit.identifier) // only use our undo edits
.map(edit => Selection.fromPositions(edit.range.getEndPosition()))
);
}
});
}

View File

@@ -11,6 +11,7 @@ import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKe
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { NullLogService } from 'vs/platform/log/common/log';
import { Handler } from 'vs/editor/common/editorCommon';
import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
suite('SnippetController2', function () {
@@ -428,4 +429,13 @@ suite('SnippetController2', function () {
assertSelections(editor, new Selection(2, 5, 2, 5));
assertContextKeys(contextKeys, false, false, false);
});
test('issue #90135: confusing trim whitespace edits', function () {
const ctrl = new SnippetController2(editor, logService, contextKeys);
model.setValue('');
CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
ctrl.insert('\nfoo');
assertSelections(editor, new Selection(2, 8, 2, 8));
});
});

View File

@@ -127,6 +127,7 @@ export class Colorizer {
0,
0,
0,
0,
-1,
'none',
false,
@@ -197,6 +198,7 @@ function _fakeColorize(lines: string[], tabSize: number): string {
0,
0,
0,
0,
-1,
'none',
false,
@@ -236,6 +238,7 @@ function _actualColorize(lines: string[], tabSize: number, tokenizationSupport:
0,
0,
0,
0,
-1,
'none',
false,

View File

@@ -84,6 +84,10 @@ export class SimpleModel implements IResolvedTextEditorModel {
public isResolved(): boolean {
return true;
}
public getMode(): string | undefined {
return this.model.getModeId();
}
}
export interface IOpenEditorDelegate {

View File

@@ -90,7 +90,7 @@ export interface IGlobalEditorOptions {
tabSize?: number;
/**
* Insert spaces when pressing `Tab`.
* This setting is overridden based on the file contents when detectIndentation` is on.
* This setting is overridden based on the file contents when `detectIndentation` is on.
* Defaults to true.
*/
insertSpaces?: boolean;

View File

@@ -501,7 +501,7 @@ export class MonarchTokenizer implements modes.ITokenizationSupport {
}
let result = line.search(regex);
if (result === -1) {
if (result === -1 || (result !== 0 && rule.matchOnlyAtLineStart)) {
continue;
}

View File

@@ -123,7 +123,7 @@ suite('TextAreaState', () => {
textArea.dispose();
});
function testDeduceInput(prevState: TextAreaState | null, value: string, selectionStart: number, selectionEnd: number, couldBeEmojiInput: boolean, couldBeTypingAtOffset0: boolean, expected: string, expectedCharReplaceCnt: number): void {
function testDeduceInput(prevState: TextAreaState | null, value: string, selectionStart: number, selectionEnd: number, couldBeEmojiInput: boolean, expected: string, expectedCharReplaceCnt: number): void {
prevState = prevState || TextAreaState.EMPTY;
let textArea = new MockTextAreaWrapper();
@@ -132,7 +132,7 @@ suite('TextAreaState', () => {
textArea._selectionEnd = selectionEnd;
let newState = TextAreaState.readFromTextArea(textArea);
let actual = TextAreaState.deduceInput(prevState, newState, couldBeEmojiInput, couldBeTypingAtOffset0);
let actual = TextAreaState.deduceInput(prevState, newState, couldBeEmojiInput);
assert.equal(actual.text, expected);
assert.equal(actual.replaceCharCnt, expectedCharReplaceCnt);
@@ -153,7 +153,7 @@ suite('TextAreaState', () => {
testDeduceInput(
TextAreaState.EMPTY,
'',
0, 1, true, false,
0, 1, true,
'', 0
);
@@ -163,7 +163,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('', 0, 1, null, null),
'せ',
0, 1, true, false,
0, 1, true,
'せ', 1
);
@@ -173,7 +173,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せ', 0, 1, null, null),
'せn',
0, 2, true, false,
0, 2, true,
'せn', 1
);
@@ -183,7 +183,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せn', 0, 2, null, null),
'せん',
0, 2, true, false,
0, 2, true,
'せん', 2
);
@@ -193,7 +193,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せん', 0, 2, null, null),
'せんs',
0, 3, true, false,
0, 3, true,
'せんs', 2
);
@@ -203,7 +203,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せんs', 0, 3, null, null),
'せんせ',
0, 3, true, false,
0, 3, true,
'せんせ', 3
);
@@ -213,7 +213,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せんせ', 0, 3, null, null),
'せんせ',
0, 3, true, false,
0, 3, true,
'せんせ', 3
);
@@ -223,7 +223,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せんせ', 0, 3, null, null),
'せんせい',
0, 4, true, false,
0, 4, true,
'せんせい', 3
);
@@ -233,7 +233,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せんせい', 0, 4, null, null),
'せんせい',
4, 4, true, false,
4, 4, true,
'', 0
);
});
@@ -252,7 +252,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せんせい', 0, 4, null, null),
'せんせい',
0, 4, true, false,
0, 4, true,
'せんせい', 4
);
@@ -262,7 +262,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せんせい', 0, 4, null, null),
'先生',
0, 2, true, false,
0, 2, true,
'先生', 4
);
@@ -272,7 +272,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('先生', 0, 2, null, null),
'先生',
2, 2, true, false,
2, 2, true,
'', 0
);
});
@@ -281,7 +281,7 @@ suite('TextAreaState', () => {
testDeduceInput(
null,
'a',
0, 1, true, false,
0, 1, true,
'a', 0
);
});
@@ -290,7 +290,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState(']\n', 1, 2, null, null),
']\n',
2, 2, true, false,
2, 2, true,
'\n', 0
);
});
@@ -299,7 +299,7 @@ suite('TextAreaState', () => {
testDeduceInput(
null,
'a',
1, 1, true, false,
1, 1, true,
'a', 0
);
});
@@ -308,7 +308,7 @@ suite('TextAreaState', () => {
testDeduceInput(
TextAreaState.EMPTY,
'a',
0, 1, true, false,
0, 1, true,
'a', 0
);
});
@@ -317,7 +317,7 @@ suite('TextAreaState', () => {
testDeduceInput(
TextAreaState.EMPTY,
'a',
1, 1, true, false,
1, 1, true,
'a', 0
);
});
@@ -326,7 +326,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 0, 12, null, null),
'H',
1, 1, true, false,
1, 1, true,
'H', 0
);
});
@@ -335,7 +335,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 12, 12, null, null),
'Hello world!a',
13, 13, true, false,
13, 13, true,
'a', 0
);
});
@@ -344,7 +344,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 0, 0, null, null),
'aHello world!',
1, 1, true, false,
1, 1, true,
'a', 0
);
});
@@ -353,7 +353,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 6, 11, null, null),
'Hello other!',
11, 11, true, false,
11, 11, true,
'other', 0
);
});
@@ -362,7 +362,7 @@ suite('TextAreaState', () => {
testDeduceInput(
TextAreaState.EMPTY,
'これは',
3, 3, true, false,
3, 3, true,
'これは', 0
);
});
@@ -371,7 +371,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 0, 0, null, null),
'Aello world!',
1, 1, true, false,
1, 1, true,
'A', 0
);
});
@@ -380,7 +380,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 5, 5, null, null),
'Hellö world!',
4, 5, true, false,
4, 5, true,
'ö', 0
);
});
@@ -389,7 +389,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 5, 5, null, null),
'Hellöö world!',
5, 5, true, false,
5, 5, true,
'öö', 1
);
});
@@ -398,7 +398,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 5, 5, null, null),
'Helöö world!',
5, 5, true, false,
5, 5, true,
'öö', 2
);
});
@@ -407,7 +407,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 5, 5, null, null),
'Hellö world!',
5, 5, true, false,
5, 5, true,
'ö', 1
);
});
@@ -416,7 +416,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('a', 0, 1, null, null),
'a',
1, 1, true, false,
1, 1, true,
'a', 0
);
});
@@ -425,7 +425,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('x x', 0, 1, null, null),
'x x',
1, 1, true, false,
1, 1, true,
'x', 0
);
});
@@ -455,7 +455,7 @@ suite('TextAreaState', () => {
'some6 text',
'some7 text'
].join('\n'),
4, 4, true, false,
4, 4, true,
'📅', 0
);
});
@@ -469,7 +469,7 @@ suite('TextAreaState', () => {
null, null
),
'some💊1 text',
6, 6, true, false,
6, 6, true,
'💊', 0
);
});
@@ -483,7 +483,7 @@ suite('TextAreaState', () => {
null, null
),
'qwertyu\nasdfghj\nzxcvbnm🎈',
25, 25, true, false,
25, 25, true,
'🎈', 0
);
});
@@ -498,39 +498,11 @@ suite('TextAreaState', () => {
null, null
),
'some⌨1 text',
6, 6, true, false,
6, 6, true,
'⌨️', 0
);
});
test('issue #42251: Minor issue, character swapped when typing', () => {
// Typing on OSX occurs at offset 0 after moving the window using the custom (non-native) titlebar.
testDeduceInput(
new TextAreaState(
'ab',
2, 2,
null, null
),
'cab',
1, 1, true, true,
'c', 0
);
});
test('issue #49480: Double curly braces inserted', () => {
// Characters get doubled
testDeduceInput(
new TextAreaState(
'aa',
2, 2,
null, null
),
'aaa',
3, 3, true, true,
'a', 0
);
});
suite('PagedScreenReaderStrategy', () => {
function testPagedScreenReaderStrategy(lines: string[], selection: Selection, expected: TextAreaState): void {

View File

@@ -42,6 +42,7 @@ export class TestConfiguration extends CommonEditorConfiguration {
canUseHalfwidthRightwardsArrow: true,
spaceWidth: 10,
middotWidth: 10,
wsmiddotWidth: 10,
maxDigitWidth: 10,
}, true);
}

View File

@@ -28,7 +28,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
test('LinesLayout 1', () => {
// Start off with 10 lines
let linesLayout = new LinesLayout(10, 10);
let linesLayout = new LinesLayout(10, 10, 0, 0);
// lines: [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
// whitespace: -
@@ -137,7 +137,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
test('LinesLayout 2', () => {
// Start off with 10 lines and one whitespace after line 2, of height 5
let linesLayout = new LinesLayout(10, 1);
let linesLayout = new LinesLayout(10, 1, 0, 0);
let a = insertWhitespace(linesLayout, 2, 0, 5, 0);
// 10 lines
@@ -232,8 +232,103 @@ suite('Editor ViewLayout - LinesLayout', () => {
assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 9);
});
test('LinesLayout Padding', () => {
// Start off with 10 lines
let linesLayout = new LinesLayout(10, 10, 15, 20);
// lines: [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
// whitespace: -
assert.equal(linesLayout.getLinesTotalHeight(), 135);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 15);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 25);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 35);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 45);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 55);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 65);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(7), 75);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(8), 85);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(9), 95);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(10), 105);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(0), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(10), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(15), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(24), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(25), 2);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(34), 2);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(35), 3);
// Add whitespace of height 5px after 2nd line
insertWhitespace(linesLayout, 2, 0, 5, 0);
// lines: [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
// whitespace: a(2,5)
assert.equal(linesLayout.getLinesTotalHeight(), 140);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 15);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 25);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 40);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 50);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(0), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(10), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(25), 2);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(34), 2);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(35), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(39), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(40), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(41), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(49), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(50), 4);
// Add two more whitespaces of height 5px
insertWhitespace(linesLayout, 3, 0, 5, 0);
insertWhitespace(linesLayout, 4, 0, 5, 0);
// lines: [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
// whitespace: a(2,5), b(3, 5), c(4, 5)
assert.equal(linesLayout.getLinesTotalHeight(), 150);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(1), 15);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(2), 25);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(3), 40);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(4), 55);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(5), 70);
assert.equal(linesLayout.getVerticalOffsetForLineNumber(6), 80);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(0), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(15), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(24), 1);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(30), 2);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(35), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(39), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(40), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(49), 3);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(50), 4);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(54), 4);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(55), 4);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(64), 4);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(65), 5);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(69), 5);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(70), 5);
assert.equal(linesLayout.getLineNumberAtOrAfterVerticalOffset(80), 6);
assert.equal(linesLayout.getVerticalOffsetForWhitespaceIndex(0), 35); // 35 -> 40
assert.equal(linesLayout.getVerticalOffsetForWhitespaceIndex(1), 50); // 50 -> 55
assert.equal(linesLayout.getVerticalOffsetForWhitespaceIndex(2), 65);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(0), 0);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(34), 0);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(35), 0);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(39), 0);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(40), 1);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(49), 1);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(50), 1);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(54), 1);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(55), 2);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(64), 2);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(65), 2);
assert.equal(linesLayout.getWhitespaceIndexAtOrAfterVerticallOffset(70), -1);
});
test('LinesLayout getLineNumberAtOrAfterVerticalOffset', () => {
let linesLayout = new LinesLayout(10, 1);
let linesLayout = new LinesLayout(10, 1, 0, 0);
insertWhitespace(linesLayout, 6, 0, 10, 0);
// 10 lines
@@ -282,7 +377,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
});
test('LinesLayout getCenteredLineInViewport', () => {
let linesLayout = new LinesLayout(10, 1);
let linesLayout = new LinesLayout(10, 1, 0, 0);
insertWhitespace(linesLayout, 6, 0, 10, 0);
// 10 lines
@@ -365,7 +460,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
});
test('LinesLayout getLinesViewportData 1', () => {
let linesLayout = new LinesLayout(10, 10);
let linesLayout = new LinesLayout(10, 10, 0, 0);
insertWhitespace(linesLayout, 6, 0, 100, 0);
// 10 lines
@@ -498,7 +593,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
});
test('LinesLayout getLinesViewportData 2 & getWhitespaceViewportData', () => {
let linesLayout = new LinesLayout(10, 10);
let linesLayout = new LinesLayout(10, 10, 0, 0);
let a = insertWhitespace(linesLayout, 6, 0, 100, 0);
let b = insertWhitespace(linesLayout, 7, 0, 50, 0);
@@ -569,7 +664,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
});
test('LinesLayout getWhitespaceAtVerticalOffset', () => {
let linesLayout = new LinesLayout(10, 10);
let linesLayout = new LinesLayout(10, 10, 0, 0);
let a = insertWhitespace(linesLayout, 6, 0, 100, 0);
let b = insertWhitespace(linesLayout, 7, 0, 50, 0);
@@ -612,7 +707,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
test('LinesLayout', () => {
const linesLayout = new LinesLayout(100, 20);
const linesLayout = new LinesLayout(100, 20, 0, 0);
// Insert a whitespace after line number 2, of height 10
const a = insertWhitespace(linesLayout, 2, 0, 10, 0);
@@ -963,7 +1058,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
});
test('LinesLayout changeWhitespaceAfterLineNumber & getFirstWhitespaceIndexAfterLineNumber', () => {
const linesLayout = new LinesLayout(100, 20);
const linesLayout = new LinesLayout(100, 20, 0, 0);
const a = insertWhitespace(linesLayout, 0, 0, 1, 0);
const b = insertWhitespace(linesLayout, 7, 0, 1, 0);
@@ -1087,7 +1182,7 @@ suite('Editor ViewLayout - LinesLayout', () => {
});
test('LinesLayout Bug', () => {
const linesLayout = new LinesLayout(100, 20);
const linesLayout = new LinesLayout(100, 20, 0, 0);
const a = insertWhitespace(linesLayout, 0, 0, 1, 0);
const b = insertWhitespace(linesLayout, 7, 0, 1, 0);

View File

@@ -40,6 +40,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
0,
0,
0,
-1,
'none',
false,
@@ -92,6 +93,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
0,
0,
0,
-1,
'none',
false,
@@ -147,6 +149,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
10,
10,
10,
6,
'boundary',
false,
@@ -241,6 +244,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
10,
10,
10,
-1,
'boundary',
false,
@@ -306,6 +310,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -371,6 +376,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -413,6 +419,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -446,6 +453,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -549,6 +557,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -590,6 +599,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -622,6 +632,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -671,6 +682,7 @@ suite('viewLineRenderer.renderLine', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -755,6 +767,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
-1,
renderWhitespace,
false,
@@ -783,6 +796,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -825,6 +839,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -1242,6 +1257,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
-1,
'none',
false,
@@ -1285,6 +1301,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
-1,
'all',
false,
@@ -1320,6 +1337,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
-1,
'all',
false,
@@ -1356,6 +1374,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
-1,
'all',
false,
@@ -1388,6 +1407,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'none',
false,
@@ -1424,6 +1444,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'none',
false,
@@ -1460,6 +1481,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'none',
false,
@@ -1493,6 +1515,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'none',
false,
@@ -1525,6 +1548,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'all',
false,
@@ -1563,6 +1587,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'none',
false,
@@ -1595,6 +1620,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'none',
false,
@@ -1629,6 +1655,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'none',
false,
@@ -1662,6 +1689,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'boundary',
false,
@@ -1693,6 +1721,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'none',
false,
@@ -1728,6 +1757,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
10000,
'none',
false,
@@ -1759,6 +1789,7 @@ suite('viewLineRenderer.renderLine 2', () => {
0,
10,
10,
10,
-1,
'none',
false,

View File

@@ -58,6 +58,7 @@ function getLineBreakData(factory: ILineBreaksComputerFactory, tabSize: number,
canUseHalfwidthRightwardsArrow: true,
spaceWidth: 7,
middotWidth: 7,
wsmiddotWidth: 7,
maxDigitWidth: 7
}, false);
const lineBreaksComputer = factory.createLineBreaksComputer(fontInfo, tabSize, breakAfter, wrappingIndent);