mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-02 17:23:40 -05:00
SQL Operations Studio Public Preview 1 (0.23) release source code
This commit is contained in:
553
src/vs/editor/common/controller/cursorCommon.ts
Normal file
553
src/vs/editor/common/controller/cursorCommon.ts
Normal file
@@ -0,0 +1,553 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { ICommand, TextModelResolvedOptions, IConfiguration, IModel, ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
import { Selection, ISelection } from 'vs/editor/common/core/selection';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { LanguageIdentifier } from 'vs/editor/common/modes';
|
||||
import { IAutoClosingPair } from 'vs/editor/common/modes/languageConfiguration';
|
||||
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
|
||||
import { IViewModel } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents';
|
||||
import { VerticalRevealType } from 'vs/editor/common/view/viewEvents';
|
||||
|
||||
export interface IColumnSelectData {
|
||||
toViewLineNumber: number;
|
||||
toViewVisualColumn: number;
|
||||
}
|
||||
|
||||
export const enum RevealTarget {
|
||||
Primary = 0,
|
||||
TopMost = 1,
|
||||
BottomMost = 2
|
||||
}
|
||||
|
||||
export interface ICursors {
|
||||
readonly context: CursorContext;
|
||||
getPrimaryCursor(): CursorState;
|
||||
getLastAddedCursorIndex(): number;
|
||||
getAll(): CursorState[];
|
||||
|
||||
getColumnSelectData(): IColumnSelectData;
|
||||
setColumnSelectData(columnSelectData: IColumnSelectData): void;
|
||||
|
||||
setStates(source: string, reason: CursorChangeReason, states: CursorState[]): void;
|
||||
reveal(horizontal: boolean, target: RevealTarget, scrollType: ScrollType): void;
|
||||
revealRange(revealHorizontal: boolean, viewRange: Range, verticalType: VerticalRevealType, scrollType: ScrollType): void;
|
||||
|
||||
scrollTo(desiredScrollTop: number): void;
|
||||
}
|
||||
|
||||
export interface CharacterMap {
|
||||
[char: string]: string;
|
||||
}
|
||||
|
||||
export class CursorConfiguration {
|
||||
_cursorMoveConfigurationBrand: void;
|
||||
|
||||
public readonly readOnly: boolean;
|
||||
public readonly tabSize: number;
|
||||
public readonly insertSpaces: boolean;
|
||||
public readonly oneIndent: string;
|
||||
public readonly pageSize: number;
|
||||
public readonly lineHeight: number;
|
||||
public readonly useTabStops: boolean;
|
||||
public readonly wordSeparators: string;
|
||||
public readonly emptySelectionClipboard: boolean;
|
||||
public readonly autoClosingBrackets: boolean;
|
||||
public readonly autoIndent: boolean;
|
||||
public readonly autoClosingPairsOpen: CharacterMap;
|
||||
public readonly autoClosingPairsClose: CharacterMap;
|
||||
public readonly surroundingPairs: CharacterMap;
|
||||
public readonly electricChars: { [key: string]: boolean; };
|
||||
|
||||
public static shouldRecreate(e: IConfigurationChangedEvent): boolean {
|
||||
return (
|
||||
e.layoutInfo
|
||||
|| e.wordSeparators
|
||||
|| e.emptySelectionClipboard
|
||||
|| e.autoClosingBrackets
|
||||
|| e.useTabStops
|
||||
|| e.lineHeight
|
||||
|| e.readOnly
|
||||
);
|
||||
}
|
||||
|
||||
constructor(
|
||||
languageIdentifier: LanguageIdentifier,
|
||||
oneIndent: string,
|
||||
modelOptions: TextModelResolvedOptions,
|
||||
configuration: IConfiguration
|
||||
) {
|
||||
let c = configuration.editor;
|
||||
|
||||
this.readOnly = c.readOnly;
|
||||
this.tabSize = modelOptions.tabSize;
|
||||
this.insertSpaces = modelOptions.insertSpaces;
|
||||
this.oneIndent = oneIndent;
|
||||
this.pageSize = Math.floor(c.layoutInfo.height / c.fontInfo.lineHeight) - 2;
|
||||
this.lineHeight = c.lineHeight;
|
||||
this.useTabStops = c.useTabStops;
|
||||
this.wordSeparators = c.wordSeparators;
|
||||
this.emptySelectionClipboard = c.emptySelectionClipboard;
|
||||
this.autoClosingBrackets = c.autoClosingBrackets;
|
||||
this.autoIndent = c.autoIndent;
|
||||
|
||||
this.autoClosingPairsOpen = {};
|
||||
this.autoClosingPairsClose = {};
|
||||
this.surroundingPairs = {};
|
||||
this.electricChars = {};
|
||||
|
||||
let electricChars = CursorConfiguration._getElectricCharacters(languageIdentifier);
|
||||
if (electricChars) {
|
||||
for (let i = 0; i < electricChars.length; i++) {
|
||||
this.electricChars[electricChars[i]] = true;
|
||||
}
|
||||
}
|
||||
|
||||
let autoClosingPairs = CursorConfiguration._getAutoClosingPairs(languageIdentifier);
|
||||
if (autoClosingPairs) {
|
||||
for (let i = 0; i < autoClosingPairs.length; i++) {
|
||||
this.autoClosingPairsOpen[autoClosingPairs[i].open] = autoClosingPairs[i].close;
|
||||
this.autoClosingPairsClose[autoClosingPairs[i].close] = autoClosingPairs[i].open;
|
||||
}
|
||||
}
|
||||
|
||||
let surroundingPairs = CursorConfiguration._getSurroundingPairs(languageIdentifier);
|
||||
if (surroundingPairs) {
|
||||
for (let i = 0; i < surroundingPairs.length; i++) {
|
||||
this.surroundingPairs[surroundingPairs[i].open] = surroundingPairs[i].close;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public normalizeIndentation(str: string): string {
|
||||
return TextModel.normalizeIndentation(str, this.tabSize, this.insertSpaces);
|
||||
}
|
||||
|
||||
private static _getElectricCharacters(languageIdentifier: LanguageIdentifier): string[] {
|
||||
try {
|
||||
return LanguageConfigurationRegistry.getElectricCharacters(languageIdentifier.id);
|
||||
} catch (e) {
|
||||
onUnexpectedError(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static _getAutoClosingPairs(languageIdentifier: LanguageIdentifier): IAutoClosingPair[] {
|
||||
try {
|
||||
return LanguageConfigurationRegistry.getAutoClosingPairs(languageIdentifier.id);
|
||||
} catch (e) {
|
||||
onUnexpectedError(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static _getSurroundingPairs(languageIdentifier: LanguageIdentifier): IAutoClosingPair[] {
|
||||
try {
|
||||
return LanguageConfigurationRegistry.getSurroundingPairs(languageIdentifier.id);
|
||||
} catch (e) {
|
||||
onUnexpectedError(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a simple model (either the model or the view model).
|
||||
*/
|
||||
export interface ICursorSimpleModel {
|
||||
getLineCount(): number;
|
||||
getLineContent(lineNumber: number): string;
|
||||
getLineMinColumn(lineNumber: number): number;
|
||||
getLineMaxColumn(lineNumber: number): number;
|
||||
getLineFirstNonWhitespaceColumn(lineNumber: number): number;
|
||||
getLineLastNonWhitespaceColumn(lineNumber: number): number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the cursor state on either the model or on the view model.
|
||||
*/
|
||||
export class SingleCursorState {
|
||||
_singleCursorStateBrand: void;
|
||||
|
||||
// --- selection can start as a range (think double click and drag)
|
||||
public readonly selectionStart: Range;
|
||||
public readonly selectionStartLeftoverVisibleColumns: number;
|
||||
public readonly position: Position;
|
||||
public readonly leftoverVisibleColumns: number;
|
||||
public readonly selection: Selection;
|
||||
|
||||
constructor(
|
||||
selectionStart: Range,
|
||||
selectionStartLeftoverVisibleColumns: number,
|
||||
position: Position,
|
||||
leftoverVisibleColumns: number,
|
||||
) {
|
||||
this.selectionStart = selectionStart;
|
||||
this.selectionStartLeftoverVisibleColumns = selectionStartLeftoverVisibleColumns;
|
||||
this.position = position;
|
||||
this.leftoverVisibleColumns = leftoverVisibleColumns;
|
||||
this.selection = SingleCursorState._computeSelection(this.selectionStart, this.position);
|
||||
}
|
||||
|
||||
public equals(other: SingleCursorState) {
|
||||
return (
|
||||
this.selectionStartLeftoverVisibleColumns === other.selectionStartLeftoverVisibleColumns
|
||||
&& this.leftoverVisibleColumns === other.leftoverVisibleColumns
|
||||
&& this.position.equals(other.position)
|
||||
&& this.selectionStart.equalsRange(other.selectionStart)
|
||||
);
|
||||
}
|
||||
|
||||
public hasSelection(): boolean {
|
||||
return (!this.selection.isEmpty() || !this.selectionStart.isEmpty());
|
||||
}
|
||||
|
||||
public move(inSelectionMode: boolean, lineNumber: number, column: number, leftoverVisibleColumns: number): SingleCursorState {
|
||||
if (inSelectionMode) {
|
||||
// move just position
|
||||
return new SingleCursorState(
|
||||
this.selectionStart,
|
||||
this.selectionStartLeftoverVisibleColumns,
|
||||
new Position(lineNumber, column),
|
||||
leftoverVisibleColumns
|
||||
);
|
||||
} else {
|
||||
// move everything
|
||||
return new SingleCursorState(
|
||||
new Range(lineNumber, column, lineNumber, column),
|
||||
leftoverVisibleColumns,
|
||||
new Position(lineNumber, column),
|
||||
leftoverVisibleColumns
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static _computeSelection(selectionStart: Range, position: Position): Selection {
|
||||
let startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number;
|
||||
if (selectionStart.isEmpty()) {
|
||||
startLineNumber = selectionStart.startLineNumber;
|
||||
startColumn = selectionStart.startColumn;
|
||||
endLineNumber = position.lineNumber;
|
||||
endColumn = position.column;
|
||||
} else {
|
||||
if (position.isBeforeOrEqual(selectionStart.getStartPosition())) {
|
||||
startLineNumber = selectionStart.endLineNumber;
|
||||
startColumn = selectionStart.endColumn;
|
||||
endLineNumber = position.lineNumber;
|
||||
endColumn = position.column;
|
||||
} else {
|
||||
startLineNumber = selectionStart.startLineNumber;
|
||||
startColumn = selectionStart.startColumn;
|
||||
endLineNumber = position.lineNumber;
|
||||
endColumn = position.column;
|
||||
}
|
||||
}
|
||||
return new Selection(
|
||||
startLineNumber,
|
||||
startColumn,
|
||||
endLineNumber,
|
||||
endColumn
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class CursorContext {
|
||||
_cursorContextBrand: void;
|
||||
|
||||
public readonly model: IModel;
|
||||
public readonly viewModel: IViewModel;
|
||||
public readonly config: CursorConfiguration;
|
||||
|
||||
constructor(configuration: IConfiguration, model: IModel, viewModel: IViewModel) {
|
||||
this.model = model;
|
||||
this.viewModel = viewModel;
|
||||
this.config = new CursorConfiguration(
|
||||
this.model.getLanguageIdentifier(),
|
||||
this.model.getOneIndent(),
|
||||
this.model.getOptions(),
|
||||
configuration
|
||||
);
|
||||
}
|
||||
|
||||
public validateViewPosition(viewPosition: Position, modelPosition: Position): Position {
|
||||
return this.viewModel.coordinatesConverter.validateViewPosition(viewPosition, modelPosition);
|
||||
}
|
||||
|
||||
public validateViewRange(viewRange: Range, expectedModelRange: Range): Range {
|
||||
return this.viewModel.coordinatesConverter.validateViewRange(viewRange, expectedModelRange);
|
||||
}
|
||||
|
||||
public convertViewRangeToModelRange(viewRange: Range): Range {
|
||||
return this.viewModel.coordinatesConverter.convertViewRangeToModelRange(viewRange);
|
||||
}
|
||||
|
||||
public convertViewPositionToModelPosition(lineNumber: number, column: number): Position {
|
||||
return this.viewModel.coordinatesConverter.convertViewPositionToModelPosition(new Position(lineNumber, column));
|
||||
}
|
||||
|
||||
public convertModelPositionToViewPosition(modelPosition: Position): Position {
|
||||
return this.viewModel.coordinatesConverter.convertModelPositionToViewPosition(modelPosition);
|
||||
}
|
||||
|
||||
public convertModelRangeToViewRange(modelRange: Range): Range {
|
||||
return this.viewModel.coordinatesConverter.convertModelRangeToViewRange(modelRange);
|
||||
}
|
||||
|
||||
public getCurrentScrollTop(): number {
|
||||
return this.viewModel.viewLayout.getCurrentScrollTop();
|
||||
}
|
||||
|
||||
public getCompletelyVisibleViewRange(): Range {
|
||||
return this.viewModel.getCompletelyVisibleViewRange();
|
||||
}
|
||||
|
||||
public getCompletelyVisibleModelRange(): Range {
|
||||
const viewRange = this.viewModel.getCompletelyVisibleViewRange();
|
||||
return this.viewModel.coordinatesConverter.convertViewRangeToModelRange(viewRange);
|
||||
}
|
||||
|
||||
public getCompletelyVisibleViewRangeAtScrollTop(scrollTop: number): Range {
|
||||
return this.viewModel.getCompletelyVisibleViewRangeAtScrollTop(scrollTop);
|
||||
}
|
||||
|
||||
public getCompletelyVisibleModelRangeAtScrollTop(scrollTop: number): Range {
|
||||
const viewRange = this.viewModel.getCompletelyVisibleViewRangeAtScrollTop(scrollTop);
|
||||
return this.viewModel.coordinatesConverter.convertViewRangeToModelRange(viewRange);
|
||||
}
|
||||
|
||||
public getVerticalOffsetForViewLine(viewLineNumber: number): number {
|
||||
return this.viewModel.viewLayout.getVerticalOffsetForLineNumber(viewLineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
export class CursorState {
|
||||
_cursorStateBrand: void;
|
||||
|
||||
public static fromModelState(modelState: SingleCursorState): CursorState {
|
||||
return new CursorState(modelState, null);
|
||||
}
|
||||
|
||||
public static fromViewState(viewState: SingleCursorState): CursorState {
|
||||
return new CursorState(null, viewState);
|
||||
}
|
||||
|
||||
public static fromModelSelection(modelSelection: ISelection): CursorState {
|
||||
const selectionStartLineNumber = modelSelection.selectionStartLineNumber;
|
||||
const selectionStartColumn = modelSelection.selectionStartColumn;
|
||||
const positionLineNumber = modelSelection.positionLineNumber;
|
||||
const positionColumn = modelSelection.positionColumn;
|
||||
const modelState = new SingleCursorState(
|
||||
new Range(selectionStartLineNumber, selectionStartColumn, selectionStartLineNumber, selectionStartColumn), 0,
|
||||
new Position(positionLineNumber, positionColumn), 0
|
||||
);
|
||||
return CursorState.fromModelState(modelState);
|
||||
}
|
||||
|
||||
public static fromModelSelections(modelSelections: ISelection[]): CursorState[] {
|
||||
let states: CursorState[] = [];
|
||||
for (let i = 0, len = modelSelections.length; i < len; i++) {
|
||||
states[i] = this.fromModelSelection(modelSelections[i]);
|
||||
}
|
||||
return states;
|
||||
}
|
||||
|
||||
public static ensureInEditableRange(context: CursorContext, states: CursorState[]): CursorState[] {
|
||||
const model = context.model;
|
||||
if (!model.hasEditableRange()) {
|
||||
return states;
|
||||
}
|
||||
|
||||
const modelEditableRange = model.getEditableRange();
|
||||
const viewEditableRange = context.convertModelRangeToViewRange(modelEditableRange);
|
||||
|
||||
let result: CursorState[] = [];
|
||||
for (let i = 0, len = states.length; i < len; i++) {
|
||||
const state = states[i];
|
||||
|
||||
if (state.modelState) {
|
||||
const newModelState = CursorState._ensureInEditableRange(state.modelState, modelEditableRange);
|
||||
result[i] = newModelState ? CursorState.fromModelState(newModelState) : state;
|
||||
} else {
|
||||
const newViewState = CursorState._ensureInEditableRange(state.viewState, viewEditableRange);
|
||||
result[i] = newViewState ? CursorState.fromViewState(newViewState) : state;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static _ensureInEditableRange(state: SingleCursorState, editableRange: Range): SingleCursorState {
|
||||
const position = state.position;
|
||||
|
||||
if (position.lineNumber < editableRange.startLineNumber || (position.lineNumber === editableRange.startLineNumber && position.column < editableRange.startColumn)) {
|
||||
return new SingleCursorState(
|
||||
state.selectionStart, state.selectionStartLeftoverVisibleColumns,
|
||||
new Position(editableRange.startLineNumber, editableRange.startColumn), 0
|
||||
);
|
||||
}
|
||||
|
||||
if (position.lineNumber > editableRange.endLineNumber || (position.lineNumber === editableRange.endLineNumber && position.column > editableRange.endColumn)) {
|
||||
return new SingleCursorState(
|
||||
state.selectionStart, state.selectionStartLeftoverVisibleColumns,
|
||||
new Position(editableRange.endLineNumber, editableRange.endColumn), 0
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
readonly modelState: SingleCursorState;
|
||||
readonly viewState: SingleCursorState;
|
||||
|
||||
constructor(modelState: SingleCursorState, viewState: SingleCursorState) {
|
||||
this.modelState = modelState;
|
||||
this.viewState = viewState;
|
||||
}
|
||||
|
||||
public equals(other: CursorState): boolean {
|
||||
return (this.viewState.equals(other.viewState) && this.modelState.equals(other.modelState));
|
||||
}
|
||||
}
|
||||
|
||||
export class EditOperationResult {
|
||||
_editOperationResultBrand: void;
|
||||
|
||||
readonly commands: ICommand[];
|
||||
readonly shouldPushStackElementBefore: boolean;
|
||||
readonly shouldPushStackElementAfter: boolean;
|
||||
|
||||
constructor(
|
||||
commands: ICommand[],
|
||||
opts: {
|
||||
shouldPushStackElementBefore: boolean;
|
||||
shouldPushStackElementAfter: boolean;
|
||||
}
|
||||
) {
|
||||
this.commands = commands;
|
||||
this.shouldPushStackElementBefore = opts.shouldPushStackElementBefore;
|
||||
this.shouldPushStackElementAfter = opts.shouldPushStackElementAfter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Common operations that work and make sense both on the model and on the view model.
|
||||
*/
|
||||
export class CursorColumns {
|
||||
|
||||
public static isLowSurrogate(model: ICursorSimpleModel, lineNumber: number, charOffset: number): boolean {
|
||||
let lineContent = model.getLineContent(lineNumber);
|
||||
if (charOffset < 0 || charOffset >= lineContent.length) {
|
||||
return false;
|
||||
}
|
||||
return strings.isLowSurrogate(lineContent.charCodeAt(charOffset));
|
||||
}
|
||||
|
||||
public static isHighSurrogate(model: ICursorSimpleModel, lineNumber: number, charOffset: number): boolean {
|
||||
let lineContent = model.getLineContent(lineNumber);
|
||||
if (charOffset < 0 || charOffset >= lineContent.length) {
|
||||
return false;
|
||||
}
|
||||
return strings.isHighSurrogate(lineContent.charCodeAt(charOffset));
|
||||
}
|
||||
|
||||
public static isInsideSurrogatePair(model: ICursorSimpleModel, lineNumber: number, column: number): boolean {
|
||||
return this.isHighSurrogate(model, lineNumber, column - 2);
|
||||
}
|
||||
|
||||
public static visibleColumnFromColumn(lineContent: string, column: number, tabSize: number): number {
|
||||
let endOffset = lineContent.length;
|
||||
if (endOffset > column - 1) {
|
||||
endOffset = column - 1;
|
||||
}
|
||||
|
||||
let result = 0;
|
||||
for (let i = 0; i < endOffset; i++) {
|
||||
let charCode = lineContent.charCodeAt(i);
|
||||
if (charCode === CharCode.Tab) {
|
||||
result = this.nextTabStop(result, tabSize);
|
||||
} else {
|
||||
result = result + 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static visibleColumnFromColumn2(config: CursorConfiguration, model: ICursorSimpleModel, position: Position): number {
|
||||
return this.visibleColumnFromColumn(model.getLineContent(position.lineNumber), position.column, config.tabSize);
|
||||
}
|
||||
|
||||
public static columnFromVisibleColumn(lineContent: string, visibleColumn: number, tabSize: number): number {
|
||||
if (visibleColumn <= 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const lineLength = lineContent.length;
|
||||
|
||||
let beforeVisibleColumn = 0;
|
||||
for (let i = 0; i < lineLength; i++) {
|
||||
let charCode = lineContent.charCodeAt(i);
|
||||
|
||||
let afterVisibleColumn: number;
|
||||
if (charCode === CharCode.Tab) {
|
||||
afterVisibleColumn = this.nextTabStop(beforeVisibleColumn, tabSize);
|
||||
} else {
|
||||
afterVisibleColumn = beforeVisibleColumn + 1;
|
||||
}
|
||||
|
||||
if (afterVisibleColumn >= visibleColumn) {
|
||||
let prevDelta = visibleColumn - beforeVisibleColumn;
|
||||
let afterDelta = afterVisibleColumn - visibleColumn;
|
||||
if (afterDelta < prevDelta) {
|
||||
return i + 2;
|
||||
} else {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
beforeVisibleColumn = afterVisibleColumn;
|
||||
}
|
||||
|
||||
// walked the entire string
|
||||
return lineLength + 1;
|
||||
}
|
||||
|
||||
public static columnFromVisibleColumn2(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, visibleColumn: number): number {
|
||||
let result = this.columnFromVisibleColumn(model.getLineContent(lineNumber), visibleColumn, config.tabSize);
|
||||
|
||||
let minColumn = model.getLineMinColumn(lineNumber);
|
||||
if (result < minColumn) {
|
||||
return minColumn;
|
||||
}
|
||||
|
||||
let maxColumn = model.getLineMaxColumn(lineNumber);
|
||||
if (result > maxColumn) {
|
||||
return maxColumn;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns)
|
||||
*/
|
||||
public static nextTabStop(visibleColumn: number, tabSize: number): number {
|
||||
return visibleColumn + tabSize - visibleColumn % tabSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns)
|
||||
*/
|
||||
public static prevTabStop(column: number, tabSize: number): number {
|
||||
return column - 1 - (column - 1) % tabSize;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user