未验证 提交 fb80c0e4 编写于 作者: A Alexandru Dima 提交者: GitHub

Merge pull request #107126 from Timmmm/atomic_tabs

Add atomic tabs option
...@@ -18,6 +18,7 @@ import { ViewContext } from 'vs/editor/common/view/viewContext'; ...@@ -18,6 +18,7 @@ import { ViewContext } from 'vs/editor/common/view/viewContext';
import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; import { IViewModel } from 'vs/editor/common/viewModel/viewModel';
import { CursorColumns } from 'vs/editor/common/controller/cursorCommon'; import { CursorColumns } from 'vs/editor/common/controller/cursorCommon';
import * as dom from 'vs/base/browser/dom'; import * as dom from 'vs/base/browser/dom';
import { AtomicTabMoveOperations, Direction } from 'vs/editor/common/controller/cursorAtomicMoveOperations';
export interface IViewZoneData { export interface IViewZoneData {
viewZoneId: string; viewZoneId: string;
...@@ -239,6 +240,7 @@ export class HitTestContext { ...@@ -239,6 +240,7 @@ export class HitTestContext {
public readonly layoutInfo: EditorLayoutInfo; public readonly layoutInfo: EditorLayoutInfo;
public readonly viewDomNode: HTMLElement; public readonly viewDomNode: HTMLElement;
public readonly lineHeight: number; public readonly lineHeight: number;
public readonly atomicSoftTabs: boolean;
public readonly typicalHalfwidthCharacterWidth: number; public readonly typicalHalfwidthCharacterWidth: number;
public readonly lastRenderData: PointerHandlerLastRenderData; public readonly lastRenderData: PointerHandlerLastRenderData;
...@@ -251,6 +253,7 @@ export class HitTestContext { ...@@ -251,6 +253,7 @@ export class HitTestContext {
this.layoutInfo = options.get(EditorOption.layoutInfo); this.layoutInfo = options.get(EditorOption.layoutInfo);
this.viewDomNode = viewHelper.viewDomNode; this.viewDomNode = viewHelper.viewDomNode;
this.lineHeight = options.get(EditorOption.lineHeight); this.lineHeight = options.get(EditorOption.lineHeight);
this.atomicSoftTabs = options.get(EditorOption.atomicSoftTabs);
this.typicalHalfwidthCharacterWidth = options.get(EditorOption.fontInfo).typicalHalfwidthCharacterWidth; this.typicalHalfwidthCharacterWidth = options.get(EditorOption.fontInfo).typicalHalfwidthCharacterWidth;
this.lastRenderData = lastRenderData; this.lastRenderData = lastRenderData;
this._context = context; this._context = context;
...@@ -1010,6 +1013,17 @@ export class MouseTargetFactory { ...@@ -1010,6 +1013,17 @@ 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);
if (newPosition !== -1) {
return new Position(position.lineNumber, newPosition + minColumn);
}
return position;
}
private static _doHitTest(ctx: HitTestContext, request: BareHitTestRequest): IHitTestResult { private static _doHitTest(ctx: HitTestContext, request: BareHitTestRequest): IHitTestResult {
// State of the art (18.10.2012): // State of the art (18.10.2012):
// The spec says browsers should support document.caretPositionFromPoint, but nobody implemented it (http://dev.w3.org/csswg/cssom-view/) // The spec says browsers should support document.caretPositionFromPoint, but nobody implemented it (http://dev.w3.org/csswg/cssom-view/)
...@@ -1028,24 +1042,24 @@ export class MouseTargetFactory { ...@@ -1028,24 +1042,24 @@ export class MouseTargetFactory {
// Thank you browsers for making this so 'easy' :) // Thank you browsers for making this so 'easy' :)
let result: IHitTestResult;
if (typeof document.caretRangeFromPoint === 'function') { if (typeof document.caretRangeFromPoint === 'function') {
result = this._doHitTestWithCaretRangeFromPoint(ctx, request);
return this._doHitTestWithCaretRangeFromPoint(ctx, request);
} else if ((<any>document).caretPositionFromPoint) { } else if ((<any>document).caretPositionFromPoint) {
result = this._doHitTestWithCaretPositionFromPoint(ctx, request.pos.toClientCoordinates());
return this._doHitTestWithCaretPositionFromPoint(ctx, request.pos.toClientCoordinates());
} else if ((<any>document.body).createTextRange) { } else if ((<any>document.body).createTextRange) {
result = this._doHitTestWithMoveToPoint(ctx, request.pos.toClientCoordinates());
return this._doHitTestWithMoveToPoint(ctx, request.pos.toClientCoordinates()); } else {
result = {
position: null,
hitTarget: null
};
} }
// Snap to the nearest soft tab boundary if atomic soft tabs are enabled.
return { if (result.position && ctx.atomicSoftTabs) {
position: null, result.position = this._snapToSoftTabBoundary(result.position, ctx.model);
hitTarget: null }
}; return result;
} }
} }
......
...@@ -420,6 +420,11 @@ export interface IEditorOptions { ...@@ -420,6 +420,11 @@ export interface IEditorOptions {
* Defaults to advanced. * Defaults to advanced.
*/ */
autoIndent?: 'none' | 'keep' | 'brackets' | 'advanced' | 'full'; autoIndent?: 'none' | 'keep' | 'brackets' | 'advanced' | 'full';
/**
* Emulate selection behaviour of hard tabs when using soft tabs (spaces) for indentation.
* This means selection will snap to indentation boundaries.
*/
atomicSoftTabs?: boolean;
/** /**
* Enable format on type. * Enable format on type.
* Defaults to false. * Defaults to false.
...@@ -3631,6 +3636,7 @@ export const enum EditorOption { ...@@ -3631,6 +3636,7 @@ export const enum EditorOption {
autoIndent, autoIndent,
automaticLayout, automaticLayout,
autoSurround, autoSurround,
atomicSoftTabs,
codeLens, codeLens,
codeLensFontFamily, codeLensFontFamily,
codeLensFontSize, codeLensFontSize,
...@@ -3859,6 +3865,10 @@ export const EditorOptions = { ...@@ -3859,6 +3865,10 @@ export const EditorOptions = {
description: nls.localize('autoSurround', "Controls whether the editor should automatically surround selections when typing quotes or brackets.") description: nls.localize('autoSurround', "Controls whether the editor should automatically surround selections when typing quotes or brackets.")
} }
)), )),
atomicSoftTabs: register(new EditorBooleanOption(
EditorOption.atomicSoftTabs, 'atomicSoftTabs', false,
{ description: nls.localize('atomicSoftTabs', "Emulate selection behaviour of hard tabs when using soft tabs (spaces) for indentation. This means selection will snap to indentation boundaries.") }
)),
codeLens: register(new EditorBooleanOption( codeLens: register(new EditorBooleanOption(
EditorOption.codeLens, 'codeLens', true, EditorOption.codeLens, 'codeLens', true,
{ description: nls.localize('codeLens', "Controls whether the editor shows CodeLens.") } { description: nls.localize('codeLens', "Controls whether the editor shows CodeLens.") }
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CharCode } from 'vs/base/common/charCode';
import { CursorColumns } from 'vs/editor/common/controller/cursorCommon';
export const enum Direction {
Left,
Right,
Nearest,
}
export class AtomicTabMoveOperations {
/**
* Get the visible column at the position. If we get to a non-whitespace character first
* or past the end of string then return -1.
*
* **Note** `position` and the return value are 0-based.
*/
public static whitespaceVisibleColumn(lineContent: string, position: number, tabSize: number): [number, number, number] {
const lineLength = lineContent.length;
let visibleColumn = 0;
let prevTabStopPosition = -1;
let prevTabStopVisibleColumn = -1;
for (let i = 0; i < lineLength; i++) {
if (i === position) {
return [prevTabStopPosition, prevTabStopVisibleColumn, visibleColumn];
}
if (visibleColumn % tabSize === 0) {
prevTabStopPosition = i;
prevTabStopVisibleColumn = visibleColumn;
}
const chCode = lineContent.charCodeAt(i);
switch (chCode) {
case CharCode.Space:
visibleColumn += 1;
break;
case CharCode.Tab:
// Skip to the next multiple of tabSize.
visibleColumn = CursorColumns.nextRenderTabStop(visibleColumn, tabSize);
break;
default:
return [-1, -1, -1];
}
}
if (position === lineLength) {
return [prevTabStopPosition, prevTabStopVisibleColumn, visibleColumn];
}
return [-1, -1, -1];
}
/**
* Return the position that should result from a move left, right or to the
* nearest tab, if atomic tabs are enabled. Left and right are used for the
* arrow key movements, nearest is used for mouse selection. It returns
* -1 if atomic tabs are not relevant and you should fall back to normal
* behaviour.
*
* **Note**: `position` and the return value are 0-based.
*/
public static atomicPosition(lineContent: string, position: number, tabSize: number, direction: Direction): number {
const lineLength = lineContent.length;
// Get the 0-based visible column corresponding to the position, or return
// -1 if it is not in the initial whitespace.
const [prevTabStopPosition, prevTabStopVisibleColumn, visibleColumn] = AtomicTabMoveOperations.whitespaceVisibleColumn(lineContent, position, tabSize);
if (visibleColumn === -1) {
return -1;
}
// Is the output left or right of the current position. The case for nearest
// where it is the same as the current position is handled in the switch.
let left: boolean;
switch (direction) {
case Direction.Left:
left = true;
break;
case Direction.Right:
left = false;
break;
case Direction.Nearest:
// The code below assumes the output position is either left or right
// of the input position. If it is the same, return immediately.
if (visibleColumn % tabSize === 0) {
return position;
}
// Go to the nearest indentation.
left = visibleColumn % tabSize <= (tabSize / 2);
break;
}
// If going left, we can just use the info about the last tab stop position and
// last tab stop visible column that we computed in the first walk over the whitespace.
if (left) {
if (prevTabStopPosition === -1) {
return -1;
}
// If the direction is left, we need to keep scanning right to ensure
// that targetVisibleColumn + tabSize is before non-whitespace.
// This is so that when we press left at the end of a partial
// indentation it only goes one character. For example ' foo' with
// tabSize 4, should jump from position 6 to position 5, not 4.
let currentVisibleColumn = prevTabStopVisibleColumn;
for (let i = prevTabStopPosition; i < lineLength; ++i) {
if (currentVisibleColumn === prevTabStopVisibleColumn + tabSize) {
// It is a full indentation.
return prevTabStopPosition;
}
const chCode = lineContent.charCodeAt(i);
switch (chCode) {
case CharCode.Space:
currentVisibleColumn += 1;
break;
case CharCode.Tab:
currentVisibleColumn = CursorColumns.nextRenderTabStop(currentVisibleColumn, tabSize);
break;
default:
return -1;
}
}
if (currentVisibleColumn === prevTabStopVisibleColumn + tabSize) {
return prevTabStopPosition;
}
// It must have been a partial indentation.
return -1;
}
// We are going right.
const targetVisibleColumn = CursorColumns.nextRenderTabStop(visibleColumn, tabSize);
// We can just continue from where whitespaceVisibleColumn got to.
let currentVisibleColumn = visibleColumn;
for (let i = position; i < lineLength; i++) {
if (currentVisibleColumn === targetVisibleColumn) {
return i;
}
const chCode = lineContent.charCodeAt(i);
switch (chCode) {
case CharCode.Space:
currentVisibleColumn += 1;
break;
case CharCode.Tab:
currentVisibleColumn = CursorColumns.nextRenderTabStop(currentVisibleColumn, tabSize);
break;
default:
return -1;
}
}
// This condition handles when the target column is at the end of the line.
if (currentVisibleColumn === targetVisibleColumn) {
return lineLength;
}
return -1;
}
}
...@@ -62,6 +62,7 @@ export class CursorConfiguration { ...@@ -62,6 +62,7 @@ export class CursorConfiguration {
public readonly tabSize: number; public readonly tabSize: number;
public readonly indentSize: number; public readonly indentSize: number;
public readonly insertSpaces: boolean; public readonly insertSpaces: boolean;
public readonly atomicSoftTabs: boolean;
public readonly pageSize: number; public readonly pageSize: number;
public readonly lineHeight: number; public readonly lineHeight: number;
public readonly useTabStops: boolean; public readonly useTabStops: boolean;
...@@ -113,6 +114,7 @@ export class CursorConfiguration { ...@@ -113,6 +114,7 @@ export class CursorConfiguration {
this.tabSize = modelOptions.tabSize; this.tabSize = modelOptions.tabSize;
this.indentSize = modelOptions.indentSize; this.indentSize = modelOptions.indentSize;
this.insertSpaces = modelOptions.insertSpaces; this.insertSpaces = modelOptions.insertSpaces;
this.atomicSoftTabs = options.get(EditorOption.atomicSoftTabs);
this.lineHeight = options.get(EditorOption.lineHeight); this.lineHeight = options.get(EditorOption.lineHeight);
this.pageSize = Math.max(1, Math.floor(layoutInfo.height / this.lineHeight) - 2); this.pageSize = Math.max(1, Math.floor(layoutInfo.height / this.lineHeight) - 2);
this.useTabStops = options.get(EditorOption.useTabStops); this.useTabStops = options.get(EditorOption.useTabStops);
...@@ -554,14 +556,14 @@ export class CursorColumns { ...@@ -554,14 +556,14 @@ export class CursorColumns {
} }
/** /**
* ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns) * ATTENTION: This works with 0-based columns (as opposed to the regular 1-based columns)
*/ */
public static prevRenderTabStop(column: number, tabSize: number): number { public static prevRenderTabStop(column: number, tabSize: number): number {
return column - 1 - (column - 1) % tabSize; return column - 1 - (column - 1) % tabSize;
} }
/** /**
* ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns) * ATTENTION: This works with 0-based columns (as opposed to the regular 1-based columns)
*/ */
public static prevIndentTabStop(column: number, indentSize: number): number { public static prevIndentTabStop(column: number, indentSize: number): number {
return column - 1 - (column - 1) % indentSize; return column - 1 - (column - 1) % indentSize;
......
...@@ -8,6 +8,7 @@ import { Position } from 'vs/editor/common/core/position'; ...@@ -8,6 +8,7 @@ import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range'; import { Range } from 'vs/editor/common/core/range';
import * as strings from 'vs/base/common/strings'; import * as strings from 'vs/base/common/strings';
import { Constants } from 'vs/base/common/uint'; import { Constants } from 'vs/base/common/uint';
import { AtomicTabMoveOperations, Direction } from 'vs/editor/common/controller/cursorAtomicMoveOperations';
export class CursorPosition { export class CursorPosition {
_cursorPositionBrand: void; _cursorPositionBrand: void;
...@@ -35,8 +36,20 @@ export class MoveOperations { ...@@ -35,8 +36,20 @@ export class MoveOperations {
return new Position(lineNumber, column); return new Position(lineNumber, column);
} }
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) {
return this.leftPosition(model, lineNumber, column);
}
return new Position(lineNumber, minColumn + newPosition);
}
public static left(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number): CursorPosition { public static left(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number): CursorPosition {
const pos = MoveOperations.leftPosition(model, lineNumber, column); const pos = config.atomicSoftTabs
? MoveOperations.leftPositionAtomicSoftTabs(model, lineNumber, column, config.tabSize)
: MoveOperations.leftPosition(model, lineNumber, column);
return new CursorPosition(pos.lineNumber, pos.column, 0); return new CursorPosition(pos.lineNumber, pos.column, 0);
} }
...@@ -67,8 +80,20 @@ export class MoveOperations { ...@@ -67,8 +80,20 @@ export class MoveOperations {
return new Position(lineNumber, column); return new Position(lineNumber, column);
} }
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);
if (newPosition === -1) {
return this.rightPosition(model, lineNumber, column);
}
return new Position(lineNumber, minColumn + newPosition);
}
public static right(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number): CursorPosition { public static right(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number): CursorPosition {
const pos = MoveOperations.rightPosition(model, lineNumber, column); const pos = config.atomicSoftTabs
? MoveOperations.rightPositionAtomicSoftTabs(model, lineNumber, column, config.tabSize, config.indentSize)
: MoveOperations.rightPosition(model, lineNumber, column);
return new CursorPosition(pos.lineNumber, pos.column, 0); return new CursorPosition(pos.lineNumber, pos.column, 0);
} }
......
...@@ -178,117 +178,118 @@ export enum EditorOption { ...@@ -178,117 +178,118 @@ export enum EditorOption {
autoIndent = 8, autoIndent = 8,
automaticLayout = 9, automaticLayout = 9,
autoSurround = 10, autoSurround = 10,
codeLens = 11, atomicSoftTabs = 11,
codeLensFontFamily = 12, codeLens = 12,
codeLensFontSize = 13, codeLensFontFamily = 13,
colorDecorators = 14, codeLensFontSize = 14,
columnSelection = 15, colorDecorators = 15,
comments = 16, columnSelection = 16,
contextmenu = 17, comments = 17,
copyWithSyntaxHighlighting = 18, contextmenu = 18,
cursorBlinking = 19, copyWithSyntaxHighlighting = 19,
cursorSmoothCaretAnimation = 20, cursorBlinking = 20,
cursorStyle = 21, cursorSmoothCaretAnimation = 21,
cursorSurroundingLines = 22, cursorStyle = 22,
cursorSurroundingLinesStyle = 23, cursorSurroundingLines = 23,
cursorWidth = 24, cursorSurroundingLinesStyle = 24,
disableLayerHinting = 25, cursorWidth = 25,
disableMonospaceOptimizations = 26, disableLayerHinting = 26,
dragAndDrop = 27, disableMonospaceOptimizations = 27,
emptySelectionClipboard = 28, dragAndDrop = 28,
extraEditorClassName = 29, emptySelectionClipboard = 29,
fastScrollSensitivity = 30, extraEditorClassName = 30,
find = 31, fastScrollSensitivity = 31,
fixedOverflowWidgets = 32, find = 32,
folding = 33, fixedOverflowWidgets = 33,
foldingStrategy = 34, folding = 34,
foldingHighlight = 35, foldingStrategy = 35,
unfoldOnClickAfterEndOfLine = 36, foldingHighlight = 36,
fontFamily = 37, unfoldOnClickAfterEndOfLine = 37,
fontInfo = 38, fontFamily = 38,
fontLigatures = 39, fontInfo = 39,
fontSize = 40, fontLigatures = 40,
fontWeight = 41, fontSize = 41,
formatOnPaste = 42, fontWeight = 42,
formatOnType = 43, formatOnPaste = 43,
glyphMargin = 44, formatOnType = 44,
gotoLocation = 45, glyphMargin = 45,
hideCursorInOverviewRuler = 46, gotoLocation = 46,
highlightActiveIndentGuide = 47, hideCursorInOverviewRuler = 47,
hover = 48, highlightActiveIndentGuide = 48,
inDiffEditor = 49, hover = 49,
letterSpacing = 50, inDiffEditor = 50,
lightbulb = 51, letterSpacing = 51,
lineDecorationsWidth = 52, lightbulb = 52,
lineHeight = 53, lineDecorationsWidth = 53,
lineNumbers = 54, lineHeight = 54,
lineNumbersMinChars = 55, lineNumbers = 55,
links = 56, lineNumbersMinChars = 56,
matchBrackets = 57, links = 57,
minimap = 58, matchBrackets = 58,
mouseStyle = 59, minimap = 59,
mouseWheelScrollSensitivity = 60, mouseStyle = 60,
mouseWheelZoom = 61, mouseWheelScrollSensitivity = 61,
multiCursorMergeOverlapping = 62, mouseWheelZoom = 62,
multiCursorModifier = 63, multiCursorMergeOverlapping = 63,
multiCursorPaste = 64, multiCursorModifier = 64,
occurrencesHighlight = 65, multiCursorPaste = 65,
overviewRulerBorder = 66, occurrencesHighlight = 66,
overviewRulerLanes = 67, overviewRulerBorder = 67,
padding = 68, overviewRulerLanes = 68,
parameterHints = 69, padding = 69,
peekWidgetDefaultFocus = 70, parameterHints = 70,
definitionLinkOpensInPeek = 71, peekWidgetDefaultFocus = 71,
quickSuggestions = 72, definitionLinkOpensInPeek = 72,
quickSuggestionsDelay = 73, quickSuggestions = 73,
readOnly = 74, quickSuggestionsDelay = 74,
renameOnType = 75, readOnly = 75,
renderControlCharacters = 76, renameOnType = 76,
renderIndentGuides = 77, renderControlCharacters = 77,
renderFinalNewline = 78, renderIndentGuides = 78,
renderLineHighlight = 79, renderFinalNewline = 79,
renderLineHighlightOnlyWhenFocus = 80, renderLineHighlight = 80,
renderValidationDecorations = 81, renderLineHighlightOnlyWhenFocus = 81,
renderWhitespace = 82, renderValidationDecorations = 82,
revealHorizontalRightPadding = 83, renderWhitespace = 83,
roundedSelection = 84, revealHorizontalRightPadding = 84,
rulers = 85, roundedSelection = 85,
scrollbar = 86, rulers = 86,
scrollBeyondLastColumn = 87, scrollbar = 87,
scrollBeyondLastLine = 88, scrollBeyondLastColumn = 88,
scrollPredominantAxis = 89, scrollBeyondLastLine = 89,
selectionClipboard = 90, scrollPredominantAxis = 90,
selectionHighlight = 91, selectionClipboard = 91,
selectOnLineNumbers = 92, selectionHighlight = 92,
showFoldingControls = 93, selectOnLineNumbers = 93,
showUnused = 94, showFoldingControls = 94,
snippetSuggestions = 95, showUnused = 95,
smartSelect = 96, snippetSuggestions = 96,
smoothScrolling = 97, smartSelect = 97,
stopRenderingLineAfter = 98, smoothScrolling = 98,
suggest = 99, stopRenderingLineAfter = 99,
suggestFontSize = 100, suggest = 100,
suggestLineHeight = 101, suggestFontSize = 101,
suggestOnTriggerCharacters = 102, suggestLineHeight = 102,
suggestSelection = 103, suggestOnTriggerCharacters = 103,
tabCompletion = 104, suggestSelection = 104,
tabIndex = 105, tabCompletion = 105,
unusualLineTerminators = 106, tabIndex = 106,
useTabStops = 107, unusualLineTerminators = 107,
wordSeparators = 108, useTabStops = 108,
wordWrap = 109, wordSeparators = 109,
wordWrapBreakAfterCharacters = 110, wordWrap = 110,
wordWrapBreakBeforeCharacters = 111, wordWrapBreakAfterCharacters = 111,
wordWrapColumn = 112, wordWrapBreakBeforeCharacters = 112,
wordWrapMinified = 113, wordWrapColumn = 113,
wrappingIndent = 114, wordWrapMinified = 114,
wrappingStrategy = 115, wrappingIndent = 115,
showDeprecated = 116, wrappingStrategy = 116,
editorClassName = 117, showDeprecated = 117,
pixelRatio = 118, editorClassName = 118,
tabFocusMode = 119, pixelRatio = 119,
layoutInfo = 120, tabFocusMode = 120,
wrappingInfo = 121 layoutInfo = 121,
wrappingInfo = 122
} }
/** /**
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { AtomicTabMoveOperations, Direction } from 'vs/editor/common/controller/cursorAtomicMoveOperations';
suite('Cursor move command test', () => {
test('Test whitespaceVisibleColumn', () => {
const testCases = [
{
lineContent: ' ',
tabSize: 4,
expectedPrevTabStopPosition: [-1, 0, 0, 0, 0, 4, 4, 4, 4, -1],
expectedPrevTabStopVisibleColumn: [-1, 0, 0, 0, 0, 4, 4, 4, 4, -1],
expectedVisibleColumn: [0, 1, 2, 3, 4, 5, 6, 7, 8, -1],
},
{
lineContent: ' ',
tabSize: 4,
expectedPrevTabStopPosition: [-1, 0, 0, -1],
expectedPrevTabStopVisibleColumn: [-1, 0, 0, -1],
expectedVisibleColumn: [0, 1, 2, -1],
},
{
lineContent: '\t',
tabSize: 4,
expectedPrevTabStopPosition: [-1, 0, -1],
expectedPrevTabStopVisibleColumn: [-1, 0, -1],
expectedVisibleColumn: [0, 4, -1],
},
{
lineContent: '\t ',
tabSize: 4,
expectedPrevTabStopPosition: [-1, 0, 1, -1],
expectedPrevTabStopVisibleColumn: [-1, 0, 4, -1],
expectedVisibleColumn: [0, 4, 5, -1],
},
{
lineContent: ' \t\t ',
tabSize: 4,
expectedPrevTabStopPosition: [-1, 0, 0, 2, 3, -1],
expectedPrevTabStopVisibleColumn: [-1, 0, 0, 4, 8, -1],
expectedVisibleColumn: [0, 1, 4, 8, 9, -1],
},
{
lineContent: ' \tA',
tabSize: 4,
expectedPrevTabStopPosition: [-1, 0, 0, -1, -1],
expectedPrevTabStopVisibleColumn: [-1, 0, 0, -1, -1],
expectedVisibleColumn: [0, 1, 4, -1, -1],
},
{
lineContent: 'A',
tabSize: 4,
expectedPrevTabStopPosition: [-1, -1, -1],
expectedPrevTabStopVisibleColumn: [-1, -1, -1],
expectedVisibleColumn: [0, -1, -1],
},
{
lineContent: '',
tabSize: 4,
expectedPrevTabStopPosition: [-1, -1],
expectedPrevTabStopVisibleColumn: [-1, -1],
expectedVisibleColumn: [0, -1],
},
];
for (const testCase of testCases) {
const maxPosition = testCase.expectedVisibleColumn.length;
for (let position = 0; position < maxPosition; position++) {
const actual = AtomicTabMoveOperations.whitespaceVisibleColumn(testCase.lineContent, position, testCase.tabSize);
const expected = [
testCase.expectedPrevTabStopPosition[position],
testCase.expectedPrevTabStopVisibleColumn[position],
testCase.expectedVisibleColumn[position]
];
assert.deepStrictEqual(actual, expected);
}
}
});
test('Test atomicPosition', () => {
const testCases = [
{
lineContent: ' ',
tabSize: 4,
expectedLeft: [-1, 0, 0, 0, 0, 4, 4, 4, 4, -1],
expectedRight: [4, 4, 4, 4, 8, 8, 8, 8, -1, -1],
expectedNearest: [0, 0, 0, 4, 4, 4, 4, 8, 8, -1],
},
{
lineContent: ' \t',
tabSize: 4,
expectedLeft: [-1, 0, 0, -1],
expectedRight: [2, 2, -1, -1],
expectedNearest: [0, 0, 2, -1],
},
{
lineContent: '\t ',
tabSize: 4,
expectedLeft: [-1, 0, -1, -1],
expectedRight: [1, -1, -1, -1],
expectedNearest: [0, 1, -1, -1],
},
{
lineContent: ' \t ',
tabSize: 4,
expectedLeft: [-1, 0, 0, -1, -1],
expectedRight: [2, 2, -1, -1, -1],
expectedNearest: [0, 0, 2, -1, -1],
},
{
lineContent: ' A',
tabSize: 4,
expectedLeft: [-1, 0, 0, 0, 0, 4, 4, 4, 4, -1, -1],
expectedRight: [4, 4, 4, 4, 8, 8, 8, 8, -1, -1, -1],
expectedNearest: [0, 0, 0, 4, 4, 4, 4, 8, 8, -1, -1],
},
{
lineContent: ' foo',
tabSize: 4,
expectedLeft: [-1, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1],
expectedRight: [4, 4, 4, 4, -1, -1, -1, -1, -1, -1, -1],
expectedNearest: [0, 0, 0, 4, 4, -1, -1, -1, -1, -1, -1],
},
];
for (const testCase of testCases) {
for (const { direction, expected } of [
{
direction: Direction.Left,
expected: testCase.expectedLeft,
},
{
direction: Direction.Right,
expected: testCase.expectedRight,
},
{
direction: Direction.Nearest,
expected: testCase.expectedNearest,
},
]) {
const actual = expected.map((_, i) => AtomicTabMoveOperations.atomicPosition(testCase.lineContent, i, testCase.tabSize, direction));
assert.deepStrictEqual(actual, expected);
}
}
});
});
...@@ -2967,6 +2967,11 @@ declare namespace monaco.editor { ...@@ -2967,6 +2967,11 @@ declare namespace monaco.editor {
* Defaults to advanced. * Defaults to advanced.
*/ */
autoIndent?: 'none' | 'keep' | 'brackets' | 'advanced' | 'full'; autoIndent?: 'none' | 'keep' | 'brackets' | 'advanced' | 'full';
/**
* Emulate selection behaviour of hard tabs when using soft tabs (spaces) for indentation.
* This means selection will snap to indentation boundaries.
*/
atomicSoftTabs?: boolean;
/** /**
* Enable format on type. * Enable format on type.
* Defaults to false. * Defaults to false.
...@@ -3898,117 +3903,118 @@ declare namespace monaco.editor { ...@@ -3898,117 +3903,118 @@ declare namespace monaco.editor {
autoIndent = 8, autoIndent = 8,
automaticLayout = 9, automaticLayout = 9,
autoSurround = 10, autoSurround = 10,
codeLens = 11, atomicSoftTabs = 11,
codeLensFontFamily = 12, codeLens = 12,
codeLensFontSize = 13, codeLensFontFamily = 13,
colorDecorators = 14, codeLensFontSize = 14,
columnSelection = 15, colorDecorators = 15,
comments = 16, columnSelection = 16,
contextmenu = 17, comments = 17,
copyWithSyntaxHighlighting = 18, contextmenu = 18,
cursorBlinking = 19, copyWithSyntaxHighlighting = 19,
cursorSmoothCaretAnimation = 20, cursorBlinking = 20,
cursorStyle = 21, cursorSmoothCaretAnimation = 21,
cursorSurroundingLines = 22, cursorStyle = 22,
cursorSurroundingLinesStyle = 23, cursorSurroundingLines = 23,
cursorWidth = 24, cursorSurroundingLinesStyle = 24,
disableLayerHinting = 25, cursorWidth = 25,
disableMonospaceOptimizations = 26, disableLayerHinting = 26,
dragAndDrop = 27, disableMonospaceOptimizations = 27,
emptySelectionClipboard = 28, dragAndDrop = 28,
extraEditorClassName = 29, emptySelectionClipboard = 29,
fastScrollSensitivity = 30, extraEditorClassName = 30,
find = 31, fastScrollSensitivity = 31,
fixedOverflowWidgets = 32, find = 32,
folding = 33, fixedOverflowWidgets = 33,
foldingStrategy = 34, folding = 34,
foldingHighlight = 35, foldingStrategy = 35,
unfoldOnClickAfterEndOfLine = 36, foldingHighlight = 36,
fontFamily = 37, unfoldOnClickAfterEndOfLine = 37,
fontInfo = 38, fontFamily = 38,
fontLigatures = 39, fontInfo = 39,
fontSize = 40, fontLigatures = 40,
fontWeight = 41, fontSize = 41,
formatOnPaste = 42, fontWeight = 42,
formatOnType = 43, formatOnPaste = 43,
glyphMargin = 44, formatOnType = 44,
gotoLocation = 45, glyphMargin = 45,
hideCursorInOverviewRuler = 46, gotoLocation = 46,
highlightActiveIndentGuide = 47, hideCursorInOverviewRuler = 47,
hover = 48, highlightActiveIndentGuide = 48,
inDiffEditor = 49, hover = 49,
letterSpacing = 50, inDiffEditor = 50,
lightbulb = 51, letterSpacing = 51,
lineDecorationsWidth = 52, lightbulb = 52,
lineHeight = 53, lineDecorationsWidth = 53,
lineNumbers = 54, lineHeight = 54,
lineNumbersMinChars = 55, lineNumbers = 55,
links = 56, lineNumbersMinChars = 56,
matchBrackets = 57, links = 57,
minimap = 58, matchBrackets = 58,
mouseStyle = 59, minimap = 59,
mouseWheelScrollSensitivity = 60, mouseStyle = 60,
mouseWheelZoom = 61, mouseWheelScrollSensitivity = 61,
multiCursorMergeOverlapping = 62, mouseWheelZoom = 62,
multiCursorModifier = 63, multiCursorMergeOverlapping = 63,
multiCursorPaste = 64, multiCursorModifier = 64,
occurrencesHighlight = 65, multiCursorPaste = 65,
overviewRulerBorder = 66, occurrencesHighlight = 66,
overviewRulerLanes = 67, overviewRulerBorder = 67,
padding = 68, overviewRulerLanes = 68,
parameterHints = 69, padding = 69,
peekWidgetDefaultFocus = 70, parameterHints = 70,
definitionLinkOpensInPeek = 71, peekWidgetDefaultFocus = 71,
quickSuggestions = 72, definitionLinkOpensInPeek = 72,
quickSuggestionsDelay = 73, quickSuggestions = 73,
readOnly = 74, quickSuggestionsDelay = 74,
renameOnType = 75, readOnly = 75,
renderControlCharacters = 76, renameOnType = 76,
renderIndentGuides = 77, renderControlCharacters = 77,
renderFinalNewline = 78, renderIndentGuides = 78,
renderLineHighlight = 79, renderFinalNewline = 79,
renderLineHighlightOnlyWhenFocus = 80, renderLineHighlight = 80,
renderValidationDecorations = 81, renderLineHighlightOnlyWhenFocus = 81,
renderWhitespace = 82, renderValidationDecorations = 82,
revealHorizontalRightPadding = 83, renderWhitespace = 83,
roundedSelection = 84, revealHorizontalRightPadding = 84,
rulers = 85, roundedSelection = 85,
scrollbar = 86, rulers = 86,
scrollBeyondLastColumn = 87, scrollbar = 87,
scrollBeyondLastLine = 88, scrollBeyondLastColumn = 88,
scrollPredominantAxis = 89, scrollBeyondLastLine = 89,
selectionClipboard = 90, scrollPredominantAxis = 90,
selectionHighlight = 91, selectionClipboard = 91,
selectOnLineNumbers = 92, selectionHighlight = 92,
showFoldingControls = 93, selectOnLineNumbers = 93,
showUnused = 94, showFoldingControls = 94,
snippetSuggestions = 95, showUnused = 95,
smartSelect = 96, snippetSuggestions = 96,
smoothScrolling = 97, smartSelect = 97,
stopRenderingLineAfter = 98, smoothScrolling = 98,
suggest = 99, stopRenderingLineAfter = 99,
suggestFontSize = 100, suggest = 100,
suggestLineHeight = 101, suggestFontSize = 101,
suggestOnTriggerCharacters = 102, suggestLineHeight = 102,
suggestSelection = 103, suggestOnTriggerCharacters = 103,
tabCompletion = 104, suggestSelection = 104,
tabIndex = 105, tabCompletion = 105,
unusualLineTerminators = 106, tabIndex = 106,
useTabStops = 107, unusualLineTerminators = 107,
wordSeparators = 108, useTabStops = 108,
wordWrap = 109, wordSeparators = 109,
wordWrapBreakAfterCharacters = 110, wordWrap = 110,
wordWrapBreakBeforeCharacters = 111, wordWrapBreakAfterCharacters = 111,
wordWrapColumn = 112, wordWrapBreakBeforeCharacters = 112,
wordWrapMinified = 113, wordWrapColumn = 113,
wrappingIndent = 114, wordWrapMinified = 114,
wrappingStrategy = 115, wrappingIndent = 115,
showDeprecated = 116, wrappingStrategy = 116,
editorClassName = 117, showDeprecated = 117,
pixelRatio = 118, editorClassName = 118,
tabFocusMode = 119, pixelRatio = 119,
layoutInfo = 120, tabFocusMode = 120,
wrappingInfo = 121 layoutInfo = 121,
wrappingInfo = 122
} }
export const EditorOptions: { export const EditorOptions: {
acceptSuggestionOnCommitCharacter: IEditorOption<EditorOption.acceptSuggestionOnCommitCharacter, boolean>; acceptSuggestionOnCommitCharacter: IEditorOption<EditorOption.acceptSuggestionOnCommitCharacter, boolean>;
...@@ -4022,6 +4028,7 @@ declare namespace monaco.editor { ...@@ -4022,6 +4028,7 @@ declare namespace monaco.editor {
autoIndent: IEditorOption<EditorOption.autoIndent, EditorAutoIndentStrategy>; autoIndent: IEditorOption<EditorOption.autoIndent, EditorAutoIndentStrategy>;
automaticLayout: IEditorOption<EditorOption.automaticLayout, boolean>; automaticLayout: IEditorOption<EditorOption.automaticLayout, boolean>;
autoSurround: IEditorOption<EditorOption.autoSurround, EditorAutoSurroundStrategy>; autoSurround: IEditorOption<EditorOption.autoSurround, EditorAutoSurroundStrategy>;
atomicSoftTabs: IEditorOption<EditorOption.atomicSoftTabs, boolean>;
codeLens: IEditorOption<EditorOption.codeLens, boolean>; codeLens: IEditorOption<EditorOption.codeLens, boolean>;
codeLensFontFamily: IEditorOption<EditorOption.codeLensFontFamily, string>; codeLensFontFamily: IEditorOption<EditorOption.codeLensFontFamily, string>;
codeLensFontSize: IEditorOption<EditorOption.codeLensFontSize, number>; codeLensFontSize: IEditorOption<EditorOption.codeLensFontSize, number>;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册