提交 7f438ed3 编写于 作者: A Alex Dima

Eliminate ILineContext

上级 89f732b2
......@@ -328,52 +328,46 @@ export class CursorCollection {
};
let electricCharSupport = LanguageConfigurationRegistry.getElectricCharacterSupport(this.model.getMode().getId());
if (electricCharSupport) {
let electricChars: string[] = null;
try {
electricChars = electricCharSupport.getElectricCharacters();
} catch (e) {
onUnexpectedError(e);
electricChars = null;
}
if (electricChars) {
for (i = 0; i < electricChars.length; i++) {
result.electricChars[electricChars[i]] = true;
}
let electricChars: string[] = null;
try {
electricChars = LanguageConfigurationRegistry.getElectricCharacters(this.model.getMode().getId());
} catch (e) {
onUnexpectedError(e);
electricChars = null;
}
if (electricChars) {
for (i = 0; i < electricChars.length; i++) {
result.electricChars[electricChars[i]] = true;
}
}
let characterPairSupport = LanguageConfigurationRegistry.getCharacterPairSupport(this.model.getMode().getId());
if (characterPairSupport) {
let autoClosingPairs: IAutoClosingPair[];
try {
autoClosingPairs = characterPairSupport.getAutoClosingPairs();
} catch (e) {
onUnexpectedError(e);
autoClosingPairs = null;
}
if (autoClosingPairs) {
for (i = 0; i < autoClosingPairs.length; i++) {
result.autoClosingPairsOpen[autoClosingPairs[i].open] = autoClosingPairs[i].close;
result.autoClosingPairsClose[autoClosingPairs[i].close] = autoClosingPairs[i].open;
}
let autoClosingPairs: IAutoClosingPair[];
try {
autoClosingPairs = LanguageConfigurationRegistry.getAutoClosingPairs(this.model.getMode().getId());
} catch (e) {
onUnexpectedError(e);
autoClosingPairs = null;
}
if (autoClosingPairs) {
for (i = 0; i < autoClosingPairs.length; i++) {
result.autoClosingPairsOpen[autoClosingPairs[i].open] = autoClosingPairs[i].close;
result.autoClosingPairsClose[autoClosingPairs[i].close] = autoClosingPairs[i].open;
}
}
let surroundingPairs: IAutoClosingPair[];
try {
surroundingPairs = characterPairSupport.getSurroundingPairs();
} catch (e) {
onUnexpectedError(e);
surroundingPairs = null;
}
if (surroundingPairs) {
for (i = 0; i < surroundingPairs.length; i++) {
result.surroundingPairs[surroundingPairs[i].open] = surroundingPairs[i].close;
}
let surroundingPairs: IAutoClosingPair[];
try {
surroundingPairs = LanguageConfigurationRegistry.getSurroundingPairs(this.model.getMode().getId());
} catch (e) {
onUnexpectedError(e);
surroundingPairs = null;
}
if (surroundingPairs) {
for (i = 0; i < surroundingPairs.length; i++) {
result.surroundingPairs[surroundingPairs[i].open] = surroundingPairs[i].close;
}
}
return result;
}
}
\ No newline at end of file
}
......@@ -1471,12 +1471,6 @@ export class OneCursorOp {
return false;
}
let characterPairSupport = LanguageConfigurationRegistry.getCharacterPairSupport(cursor.model.getMode().getId());
if (!characterPairSupport) {
return false;
}
let position = cursor.getPosition();
let lineText = cursor.model.getLineContent(position.lineNumber);
let beforeCharacter = lineText[position.column - 1];
......@@ -1495,11 +1489,11 @@ export class OneCursorOp {
}
}
let lineContext = cursor.model.getLineContext(position.lineNumber);
let lineTokens = cursor.model.getLineTokens(position.lineNumber, false);
let shouldAutoClosePair = false;
try {
shouldAutoClosePair = characterPairSupport.shouldAutoClosePair(ch, lineContext, position.column - 1);
shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(ch, lineTokens, position.column - 1);
} catch (e) {
onUnexpectedError(e);
}
......@@ -1571,16 +1565,13 @@ export class OneCursorOp {
let position = cursor.getPosition();
let lineText = cursor.model.getLineContent(position.lineNumber);
let lineContext = cursor.model.getLineContext(position.lineNumber);
let lineTokens = cursor.model.getLineTokens(position.lineNumber, false);
let electricAction: IElectricAction;
let electricCharSupport = LanguageConfigurationRegistry.getElectricCharacterSupport(cursor.model.getMode().getId());
if (electricCharSupport) {
try {
electricAction = electricCharSupport.onElectricCharacter(lineContext, position.column - 2);
} catch (e) {
onUnexpectedError(e);
}
try {
electricAction = LanguageConfigurationRegistry.onElectricCharacter(lineTokens, position.column - 2);
} catch (e) {
onUnexpectedError(e);
}
if (electricAction) {
......
......@@ -75,23 +75,29 @@ export class LineToken {
export class LineTokens {
_lineTokensBrand: void;
private _map: TokensInflatorMap;
private _tokens: number[];
private _textLength: number;
private readonly _map: TokensInflatorMap;
private readonly _tokens: number[];
private readonly _text: string;
private readonly _textLength: number;
readonly modeTransitions: ModeTransition[];
constructor(map: TokensInflatorMap, tokens: number[], modeTransitions: ModeTransition[], textLength: number) {
constructor(map: TokensInflatorMap, tokens: number[], modeTransitions: ModeTransition[], text: string) {
this._map = map;
this._tokens = tokens;
this.modeTransitions = modeTransitions;
this._textLength = textLength;
this._text = text;
this._textLength = this._text.length;
}
public getTokenCount(): number {
return this._tokens.length;
}
public getLineContent(): string {
return this._text;
}
public getTokenStartOffset(tokenIndex: number): number {
return TokensBinaryEncoding.getStartIndex(this._tokens[tokenIndex]);
}
......@@ -108,25 +114,39 @@ export class LineTokens {
}
public equals(other: LineTokens): boolean {
if (other instanceof LineTokens) {
if (this._map !== other._map) {
return false;
}
if (this._tokens.length !== other._tokens.length) {
return false;
}
for (let i = 0, len = this._tokens.length; i < len; i++) {
if (this._tokens[i] !== other._tokens[i]) {
return false;
}
}
return true;
}
if (!(other instanceof LineTokens)) {
return false;
}
if (this._text !== other._text) {
return false;
}
if (this._map !== other._map) {
return false;
}
if (!ModeTransition.equals(this.modeTransitions, other.modeTransitions)) {
return false;
}
if (this._tokens.length !== other._tokens.length) {
return false;
}
for (let i = 0, len = this._tokens.length; i < len; i++) {
if (this._tokens[i] !== other._tokens[i]) {
return false;
}
}
return true;
}
/**
* Find the token containing offset `offset`.
* For example, with the following tokens [0, 5), [5, 9), [9, infinity)
* Searching for 0, 1, 2, 3 or 4 will return 0.
* Searching for 5, 6, 7 or 8 will return 1.
* Searching for 9, 10, 11, ... will return 2.
* @param offset The search offset
* @return The index of the token containing the offset.
*/
public findTokenIndexAtOffset(offset: number): number {
return TokensBinaryEncoding.findIndexOfOffset(this._tokens, offset);
}
......
......@@ -20,4 +20,23 @@ export class ModeTransition {
public static findIndexInSegmentsArray(arr: ModeTransition[], desiredIndex: number): number {
return Arrays.findIndexInSegmentsArray(arr, desiredIndex);
}
public static equals(a: ModeTransition[], b: ModeTransition[]): boolean {
let aLen = a.length;
let bLen = b.length;
if (aLen !== bLen) {
return false;
}
for (let i = 0; i < aLen; i++) {
let aModeTransition = a[i];
let bModeTransition = b[i];
if (aModeTransition.startIndex !== bModeTransition.startIndex) {
return false;
}
if (aModeTransition.modeId !== bModeTransition.modeId) {
return false;
}
}
return true;
}
}
......@@ -10,7 +10,7 @@ import * as types from 'vs/base/common/types';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { ServicesAccessor, IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation';
import { ILineContext, IMode } from 'vs/editor/common/modes';
import { IMode } from 'vs/editor/common/modes';
import { LineTokens } from 'vs/editor/common/core/lineTokens';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { IDisposable } from 'vs/base/common/lifecycle';
......@@ -1882,12 +1882,6 @@ export interface ITokenizedModel extends ITextModel {
*/
getLineTokens(lineNumber: number, inaccurateTokensAcceptable?: boolean): LineTokens;
/**
* Tokenize if necessary and get the tokenization result for the line `lineNumber`, as returned by the language mode.
* @internal
*/
getLineContext(lineNumber: number): ILineContext;
/**
* Get the current language mode associated with the model.
*/
......
......@@ -195,8 +195,6 @@ export class ModelLine {
}
public getTokens(map: TokensInflatorMap): LineTokens {
const textLength = this._text.length;
let lineTokens = this._lineTokens;
if (!lineTokens) {
if (this._text.length === 0) {
......@@ -206,7 +204,7 @@ export class ModelLine {
}
}
return new LineTokens(map, lineTokens, this.getModeTransitions(map.topLevelModeId), textLength);
return new LineTokens(map, lineTokens, this.getModeTransitions(map.topLevelModeId), this._text);
}
// --- END TOKENS
......
......@@ -11,11 +11,9 @@ import { StopWatch } from 'vs/base/common/stopwatch';
import * as timer from 'vs/base/common/timer';
import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ModelLine } from 'vs/editor/common/model/modelLine';
import { TextModel } from 'vs/editor/common/model/textModel';
import { WordHelper } from 'vs/editor/common/model/textModelWithTokensHelpers';
import { TokenIterator } from 'vs/editor/common/model/tokenIterator';
import { ITokenizationSupport, ILineContext, ILineTokens, IMode, IState, TokenizationRegistry, IRichEditBrackets } from 'vs/editor/common/modes';
import { ITokenizationSupport, ILineTokens, IMode, IState, TokenizationRegistry, IRichEditBrackets } from 'vs/editor/common/modes';
import { NULL_MODE_ID, nullTokenize } from 'vs/editor/common/modes/nullMode';
import { ignoreBracketsInToken } from 'vs/editor/common/modes/supports';
import { BracketsUtils } from 'vs/editor/common/modes/supports/richEditBrackets';
......@@ -25,6 +23,7 @@ import { Position } from 'vs/editor/common/core/position';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { Token } from 'vs/editor/common/core/token';
import { LineTokens, LineToken } from 'vs/editor/common/core/lineTokens';
import { getWordAtText } from 'vs/editor/common/model/wordHelper';
class Mode implements IMode {
......@@ -74,42 +73,6 @@ class ModelTokensChangedEventBuilder {
}
}
/**
* TODO@Alex: remove this wrapper
*/
class LineContext implements ILineContext {
public modeTransitions: ModeTransition[];
private _text: string;
private _lineTokens: LineTokens;
constructor(topLevelModeId: string, line: ModelLine, map: TokensInflatorMap) {
this.modeTransitions = line.getModeTransitions(topLevelModeId);
this._text = line.text;
this._lineTokens = line.getTokens(map);
}
public getLineContent(): string {
return this._text;
}
public getTokenCount(): number {
return this._lineTokens.getTokenCount();
}
public getTokenStartOffset(tokenIndex: number): number {
return this._lineTokens.getTokenStartOffset(tokenIndex);
}
public getTokenType(tokenIndex: number): string {
return this._lineTokens.getTokenType(tokenIndex);
}
public findIndexOfOffset(offset: number): number {
return this._lineTokens.findTokenIndexAtOffset(offset);
}
}
export class TextModelWithTokens extends TextModel implements editorCommon.ITokenizedModel {
private static MODE_TOKENIZATION_FAILED_MSG = nls.localize('mode.tokenizationSupportFailed', "The mode has failed while tokenizing the input.");
......@@ -239,18 +202,6 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
return this._lines[lineNumber - 1].getTokens(this._tokensInflatorMap);
}
public getLineContext(lineNumber: number): ILineContext {
if (lineNumber < 1 || lineNumber > this.getLineCount()) {
throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`');
}
this._withModelTokensChangedEventBuilder((eventBuilder) => {
this._updateTokensUntilLine(eventBuilder, lineNumber, true);
});
return new LineContext(this.getModeId(), this._lines[lineNumber - 1], this._tokensInflatorMap);
}
public getMode(): IMode {
return new Mode(this._languageId);
}
......@@ -489,10 +440,6 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
// Having tokens allows implementing additional helper methods
protected _getWordDefinition(): RegExp {
return WordHelper.massageWordDefinitionOf(this.getModeId());
}
public getWordAtPosition(_position: editorCommon.IPosition): editorCommon.IWordAtPosition {
this._assertNotDisposed();
let position = this.validatePosition(_position);
......@@ -500,11 +447,44 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
if (this._invalidLineStartIndex <= position.lineNumber) {
// this line is not tokenized
return WordHelper.getWordAtPosition(lineContent, position.column, this.getModeId(), null);
return getWordAtText(
position.column,
LanguageConfigurationRegistry.getWordDefinition(this.getModeId()),
lineContent,
0
);
}
let modeTransitions = this._lines[position.lineNumber - 1].getModeTransitions(this.getModeId());
return WordHelper.getWordAtPosition(lineContent, position.column, this.getModeId(), modeTransitions);
let offset = position.column - 1;
let modeIndex = ModeTransition.findIndexInSegmentsArray(modeTransitions, offset);
let modeStartOffset = modeTransitions[modeIndex].startIndex;
let modeEndOffset = (modeIndex + 1 < modeTransitions.length ? modeTransitions[modeIndex + 1].startIndex : lineContent.length);
let modeId = modeTransitions[modeIndex].modeId;
let result = getWordAtText(
position.column,
LanguageConfigurationRegistry.getWordDefinition(modeId),
lineContent.substring(modeStartOffset, modeEndOffset),
modeStartOffset
);
if (!result && modeIndex > 0 && modeTransitions[modeIndex].startIndex === offset) {
// The position is right at the beginning of `modeIndex`, so try looking at `modeIndex` - 1 too
let prevModeStartOffset = modeTransitions[modeIndex - 1].startIndex;
let prevModeId = modeTransitions[modeIndex - 1].modeId;
result = getWordAtText(
position.column,
LanguageConfigurationRegistry.getWordDefinition(prevModeId),
lineContent.substring(prevModeStartOffset, modeStartOffset),
prevModeStartOffset
);
}
return result;
}
public getWordUntilPosition(position: editorCommon.IPosition): editorCommon.IWordAtPosition {
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IWordAtPosition } from 'vs/editor/common/editorCommon';
import { ModeTransition } from 'vs/editor/common/core/modeTransition';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { getWordAtText, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper';
export class WordHelper {
public static massageWordDefinitionOf(modeId: string): RegExp {
let wordDefinition = LanguageConfigurationRegistry.getWordDefinition(modeId);
return ensureValidWordDefinition(wordDefinition);
}
private static _getWordAtColumn(txt: string, column: number, modeIndex: number, modeTransitions: ModeTransition[]): IWordAtPosition {
let modeStartIndex = modeTransitions[modeIndex].startIndex;
let modeEndIndex = (modeIndex + 1 < modeTransitions.length ? modeTransitions[modeIndex + 1].startIndex : txt.length);
let modeId = modeTransitions[modeIndex].modeId;
return getWordAtText(
column, WordHelper.massageWordDefinitionOf(modeId),
txt.substring(modeStartIndex, modeEndIndex), modeStartIndex
);
}
public static getWordAtPosition(lineContent: string, column: number, topModeId: string, modeTransitions: ModeTransition[]): IWordAtPosition {
if (!modeTransitions || modeTransitions.length === 0) {
return getWordAtText(column, WordHelper.massageWordDefinitionOf(topModeId), lineContent, 0);
}
let result: IWordAtPosition = null;
let columnIndex = column - 1;
let modeIndex = ModeTransition.findIndexInSegmentsArray(modeTransitions, columnIndex);
result = WordHelper._getWordAtColumn(lineContent, column, modeIndex, modeTransitions);
if (!result && modeIndex > 0 && modeTransitions[modeIndex].startIndex === columnIndex) {
// The position is right at the beginning of `modeIndex`, so try looking at `modeIndex` - 1 too
result = WordHelper._getWordAtColumn(lineContent, column, modeIndex - 1, modeTransitions);
}
return result;
}
}
......@@ -36,47 +36,6 @@ export interface IModeDescriptor {
id: string;
}
/**
* @internal
*/
export interface ILineContext {
/**
* Get the content of the line.
*/
getLineContent(): string;
/**
* The mode transitions on this line.
*/
modeTransitions: ModeTransition[];
/**
* Get the number of tokens on this line.
*/
getTokenCount(): number;
/**
* Get the start offset of the token at `tokenIndex`.
*/
getTokenStartOffset(tokenIndex: number): number;
/**
* Get the type of the token at `tokenIndex`.
*/
getTokenType(tokenIndex: number): string;
/**
* Find the token containing offset `offset`.
* For example, with the following tokens [0, 5), [5, 9), [9, infinity)
* Searching for 0, 1, 2, 3 or 4 will return 0.
* Searching for 5, 6, 7 or 8 will return 1.
* Searching for 9, 10, 11, ... will return 2.
* @param offset The search offset
* @return The index of the token containing the offset.
*/
findIndexOfOffset(offset: number): number;
}
/**
* A mode. Will soon be obsolete.
*/
......@@ -773,22 +732,6 @@ export interface EnterAction {
removeText?: number;
}
/**
* @internal
*/
export interface IRichEditElectricCharacter {
getElectricCharacters(): string[];
// Should return opening bracket type to match indentation with
onElectricCharacter(context: ILineContext, offset: number): IElectricAction;
}
/**
* @internal
*/
export interface IRichEditOnEnter {
onEnter(model: editorCommon.ITokenizedModel, position: editorCommon.IPosition): EnterAction;
}
/**
* Interface used to support insertion of mode specific comments.
* @internal
......@@ -804,15 +747,6 @@ export interface IAutoClosingPair {
close: string;
}
/**
* @internal
*/
export interface IRichEditCharacterPair {
getAutoClosingPairs(): IAutoClosingPairConditional[];
shouldAutoClosePair(character: string, context: ILineContext, offset: number): boolean;
getSurroundingPairs(): IAutoClosingPair[];
}
/**
* @internal
*/
......
......@@ -5,9 +5,9 @@
'use strict';
import {
ICommentsConfiguration, IRichEditBrackets, IRichEditCharacterPair, IAutoClosingPair,
IAutoClosingPairConditional, IRichEditOnEnter, CharacterPair,
IRichEditElectricCharacter, EnterAction, IndentAction
ICommentsConfiguration, IRichEditBrackets, IAutoClosingPair,
IAutoClosingPairConditional, CharacterPair,
EnterAction, IndentAction, IElectricAction
} from 'vs/editor/common/modes';
import { CharacterPairSupport } from 'vs/editor/common/modes/supports/characterPair';
import { BracketElectricCharacterSupport, IBracketElectricCharacterContribution } from 'vs/editor/common/modes/supports/electricCharacter';
......@@ -16,10 +16,11 @@ import { RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBracke
import Event, { Emitter } from 'vs/base/common/event';
import { ITokenizedModel } from 'vs/editor/common/editorCommon';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Position } from 'vs/editor/common/core/position';
import * as strings from 'vs/base/common/strings';
import { IDisposable } from 'vs/base/common/lifecycle';
import { DEFAULT_WORD_REGEXP } from 'vs/editor/common/model/wordHelper';
import { DEFAULT_WORD_REGEXP, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper';
import { createScopedLineTokens } from 'vs/editor/common/modes/supports';
import { LineTokens } from 'vs/editor/common/core/lineTokens';
/**
* Describes how comments for a language work.
......@@ -90,9 +91,9 @@ export class RichEditSupport {
public electricCharacter: BracketElectricCharacterSupport;
public comments: ICommentsConfiguration;
public characterPair: IRichEditCharacterPair;
public characterPair: CharacterPairSupport;
public wordDefinition: RegExp;
public onEnter: IRichEditOnEnter;
public onEnter: OnEnterSupport;
public brackets: IRichEditBrackets;
constructor(modeId: string, previous: RichEditSupport, rawConf: LanguageConfiguration) {
......@@ -112,8 +113,8 @@ export class RichEditSupport {
this._handleComments(modeId, this._conf);
this.characterPair = new CharacterPairSupport(LanguageConfigurationRegistry, modeId, this._conf);
this.electricCharacter = new BracketElectricCharacterSupport(LanguageConfigurationRegistry, modeId, this.brackets, this.characterPair.getAutoClosingPairs(), this._conf.__electricCharacterSupport);
this.characterPair = new CharacterPairSupport(this._conf);
this.electricCharacter = new BracketElectricCharacterSupport(this.brackets, this.characterPair.getAutoClosingPairs(), this._conf.__electricCharacterSupport);
this.wordDefinition = this._conf.wordPattern || DEFAULT_WORD_REGEXP;
}
......@@ -150,7 +151,7 @@ export class RichEditSupport {
}
if (!empty) {
this.onEnter = new OnEnterSupport(LanguageConfigurationRegistry, modeId, onEnter);
this.onEnter = new OnEnterSupport(onEnter);
}
}
......@@ -198,7 +199,9 @@ export class LanguageConfigurationRegistryImpl {
return this._entries[modeId];
}
public getElectricCharacterSupport(modeId: string): IRichEditElectricCharacter {
// begin electricCharacter
private _getElectricCharacterSupport(modeId: string): BracketElectricCharacterSupport {
let value = this._getRichEditSupport(modeId);
if (!value) {
return null;
......@@ -206,6 +209,28 @@ export class LanguageConfigurationRegistryImpl {
return value.electricCharacter || null;
}
public getElectricCharacters(modeId: string): string[] {
let electricCharacterSupport = this._getElectricCharacterSupport(modeId);
if (!electricCharacterSupport) {
return [];
}
return electricCharacterSupport.getElectricCharacters();
}
/**
* Should return opening bracket type to match indentation with
*/
public onElectricCharacter(context: LineTokens, offset: number): IElectricAction {
let scopedLineTokens = createScopedLineTokens(context, offset);
let electricCharacterSupport = this._getElectricCharacterSupport(scopedLineTokens.modeId);
if (!electricCharacterSupport) {
return null;
}
return electricCharacterSupport.onElectricCharacter(scopedLineTokens, offset - scopedLineTokens.firstCharOffset);
}
// end electricCharacter
public getComments(modeId: string): ICommentsConfiguration {
let value = this._getRichEditSupport(modeId);
if (!value) {
......@@ -214,7 +239,9 @@ export class LanguageConfigurationRegistryImpl {
return value.comments || null;
}
public getCharacterPairSupport(modeId: string): IRichEditCharacterPair {
// begin characterPair
private _getCharacterPairSupport(modeId: string): CharacterPairSupport {
let value = this._getRichEditSupport(modeId);
if (!value) {
return null;
......@@ -222,15 +249,44 @@ export class LanguageConfigurationRegistryImpl {
return value.characterPair || null;
}
public getAutoClosingPairs(modeId: string): IAutoClosingPairConditional[] {
let characterPairSupport = this._getCharacterPairSupport(modeId);
if (!characterPairSupport) {
return [];
}
return characterPairSupport.getAutoClosingPairs();
}
public getSurroundingPairs(modeId: string): IAutoClosingPair[] {
let characterPairSupport = this._getCharacterPairSupport(modeId);
if (!characterPairSupport) {
return [];
}
return characterPairSupport.getSurroundingPairs();
}
public shouldAutoClosePair(character: string, context: LineTokens, offset: number): boolean {
let scopedLineTokens = createScopedLineTokens(context, offset);
let characterPairSupport = this._getCharacterPairSupport(scopedLineTokens.modeId);
if (!characterPairSupport) {
return false;
}
return characterPairSupport.shouldAutoClosePair(character, scopedLineTokens, offset - scopedLineTokens.firstCharOffset);
}
// end characterPair
public getWordDefinition(modeId: string): RegExp {
let value = this._getRichEditSupport(modeId);
if (!value) {
return null;
return ensureValidWordDefinition(null);
}
return value.wordDefinition || null;
return ensureValidWordDefinition(value.wordDefinition || null);
}
public getOnEnterSupport(modeId: string): IRichEditOnEnter {
// begin onEnter
private _getOnEnterSupport(modeId: string): OnEnterSupport {
let value = this._getRichEditSupport(modeId);
if (!value) {
return null;
......@@ -239,18 +295,35 @@ export class LanguageConfigurationRegistryImpl {
}
public getRawEnterActionAtPosition(model: ITokenizedModel, lineNumber: number, column: number): EnterAction {
let result: EnterAction;
let onEnterSupport = this.getOnEnterSupport(model.getMode().getId());
let lineTokens = model.getLineTokens(lineNumber, false);
let scopedLineTokens = createScopedLineTokens(lineTokens, column - 1);
let onEnterSupport = this._getOnEnterSupport(scopedLineTokens.modeId);
if (!onEnterSupport) {
return null;
}
if (onEnterSupport) {
try {
result = onEnterSupport.onEnter(model, new Position(lineNumber, column));
} catch (e) {
onUnexpectedError(e);
let scopedLineText = scopedLineTokens.getLineContent();
let beforeEnterText = scopedLineText.substr(0, column - 1 - scopedLineTokens.firstCharOffset);
let afterEnterText = scopedLineText.substr(column - 1 - scopedLineTokens.firstCharOffset);
let oneLineAboveText = '';
if (lineNumber > 1 && scopedLineTokens.firstCharOffset === 0) {
// This is not the first line and the entire line belongs to this mode
let oneLineAboveLineTokens = model.getLineTokens(lineNumber - 1, false);
let oneLineAboveMaxColumn = model.getLineMaxColumn(lineNumber - 1);
let oneLineAboveScopedLineTokens = createScopedLineTokens(oneLineAboveLineTokens, oneLineAboveMaxColumn - 1);
if (oneLineAboveScopedLineTokens.modeId === scopedLineTokens.modeId) {
// The line above ends with text belonging to the same mode
oneLineAboveText = oneLineAboveScopedLineTokens.getLineContent();
}
}
let result: EnterAction = null;
try {
result = onEnterSupport.onEnter(oneLineAboveText, beforeEnterText, afterEnterText);
} catch (e) {
onUnexpectedError(e);
}
return result;
}
......@@ -290,6 +363,8 @@ export class LanguageConfigurationRegistryImpl {
};
}
// end onEnter
public getBracketsSupport(modeId: string): IRichEditBrackets {
let value = this._getRichEditSupport(modeId);
if (!value) {
......
......@@ -8,6 +8,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import * as modes from 'vs/editor/common/modes';
import { ModeTransition } from 'vs/editor/common/core/modeTransition';
import { Token } from 'vs/editor/common/core/token';
import { LineTokens } from 'vs/editor/common/core/lineTokens';
export class RawLineTokens implements modes.ILineTokens {
_lineTokensBrand: void;
......@@ -27,73 +28,76 @@ export class RawLineTokens implements modes.ILineTokens {
}
}
export function handleEvent<T>(context: modes.ILineContext, offset: number, runner: (modeId: string, newContext: modes.ILineContext, offset: number) => T): T {
export function createScopedLineTokens(context: LineTokens, offset: number): ScopedLineTokens {
let modeTransitions = context.modeTransitions;
if (modeTransitions.length === 1) {
return runner(modeTransitions[0].modeId, context, offset);
return new ScopedLineTokens(context, modeTransitions[0].modeId, 0, context.getTokenCount(), 0, context.getLineContent().length);
}
let modeIndex = ModeTransition.findIndexInSegmentsArray(modeTransitions, offset);
let nestedModeId = modeTransitions[modeIndex].modeId;
let modeStartIndex = modeTransitions[modeIndex].startIndex;
let firstTokenInModeIndex = context.findIndexOfOffset(modeStartIndex);
let nextCharacterAfterModeIndex = -1;
let nextTokenAfterMode = -1;
let firstTokenIndex = context.findTokenIndexAtOffset(modeStartIndex);
let lastCharOffset = -1;
let lastTokenIndex = -1;
if (modeIndex + 1 < modeTransitions.length) {
nextTokenAfterMode = context.findIndexOfOffset(modeTransitions[modeIndex + 1].startIndex);
nextCharacterAfterModeIndex = context.getTokenStartOffset(nextTokenAfterMode);
lastTokenIndex = context.findTokenIndexAtOffset(modeTransitions[modeIndex + 1].startIndex);
lastCharOffset = context.getTokenStartOffset(lastTokenIndex);
} else {
nextTokenAfterMode = context.getTokenCount();
nextCharacterAfterModeIndex = context.getLineContent().length;
lastTokenIndex = context.getTokenCount();
lastCharOffset = context.getLineContent().length;
}
let firstTokenCharacterOffset = context.getTokenStartOffset(firstTokenInModeIndex);
let newCtx = new FilteredLineContext(context, nestedModeId, firstTokenInModeIndex, nextTokenAfterMode, firstTokenCharacterOffset, nextCharacterAfterModeIndex);
return runner(nestedModeId, newCtx, offset - firstTokenCharacterOffset);
let firstCharOffset = context.getTokenStartOffset(firstTokenIndex);
return new ScopedLineTokens(context, nestedModeId, firstTokenIndex, lastTokenIndex, firstCharOffset, lastCharOffset);
}
export class FilteredLineContext implements modes.ILineContext {
public modeTransitions: ModeTransition[];
private _actual: modes.ILineContext;
private _firstTokenInModeIndex: number;
private _nextTokenAfterMode: number;
private _firstTokenCharacterOffset: number;
private _nextCharacterAfterModeIndex: number;
constructor(actual: modes.ILineContext, modeId: string,
firstTokenInModeIndex: number, nextTokenAfterMode: number,
firstTokenCharacterOffset: number, nextCharacterAfterModeIndex: number) {
this.modeTransitions = [new ModeTransition(0, modeId)];
export class ScopedLineTokens {
_scopedLineTokensBrand: void;
public readonly modeId: string;
private readonly _actual: LineTokens;
private readonly _firstTokenIndex: number;
private readonly _lastTokenIndex: number;
public readonly firstCharOffset: number;
private readonly _lastCharOffset: number;
constructor(
actual: LineTokens,
modeId: string,
firstTokenIndex: number,
lastTokenIndex: number,
firstCharOffset: number,
lastCharOffset: number
) {
this._actual = actual;
this._firstTokenInModeIndex = firstTokenInModeIndex;
this._nextTokenAfterMode = nextTokenAfterMode;
this._firstTokenCharacterOffset = firstTokenCharacterOffset;
this._nextCharacterAfterModeIndex = nextCharacterAfterModeIndex;
this.modeId = modeId;
this._firstTokenIndex = firstTokenIndex;
this._lastTokenIndex = lastTokenIndex;
this.firstCharOffset = firstCharOffset;
this._lastCharOffset = lastCharOffset;
}
public getLineContent(): string {
var actualLineContent = this._actual.getLineContent();
return actualLineContent.substring(this._firstTokenCharacterOffset, this._nextCharacterAfterModeIndex);
return actualLineContent.substring(this.firstCharOffset, this._lastCharOffset);
}
public getTokenCount(): number {
return this._nextTokenAfterMode - this._firstTokenInModeIndex;
return this._lastTokenIndex - this._firstTokenIndex;
}
public findIndexOfOffset(offset: number): number {
return this._actual.findIndexOfOffset(offset + this._firstTokenCharacterOffset) - this._firstTokenInModeIndex;
public findTokenIndexAtOffset(offset: number): number {
return this._actual.findTokenIndexAtOffset(offset + this.firstCharOffset) - this._firstTokenIndex;
}
public getTokenStartOffset(tokenIndex: number): number {
return this._actual.getTokenStartOffset(tokenIndex + this._firstTokenInModeIndex) - this._firstTokenCharacterOffset;
return this._actual.getTokenStartOffset(tokenIndex + this._firstTokenIndex) - this.firstCharOffset;
}
public getTokenType(tokenIndex: number): string {
return this._actual.getTokenType(tokenIndex + this._firstTokenInModeIndex);
return this._actual.getTokenType(tokenIndex + this._firstTokenIndex);
}
}
......
......@@ -4,20 +4,15 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IAutoClosingPair, IAutoClosingPairConditional, ILineContext, IRichEditCharacterPair, CharacterPair } from 'vs/editor/common/modes';
import { handleEvent } from 'vs/editor/common/modes/supports';
import { LanguageConfigurationRegistryImpl } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { IAutoClosingPair, IAutoClosingPairConditional, CharacterPair } from 'vs/editor/common/modes';
import { ScopedLineTokens } from 'vs/editor/common/modes/supports';
export class CharacterPairSupport implements IRichEditCharacterPair {
export class CharacterPairSupport {
private _registry: LanguageConfigurationRegistryImpl;
private _modeId: string;
private _autoClosingPairs: IAutoClosingPairConditional[];
private _surroundingPairs: IAutoClosingPair[];
private readonly _autoClosingPairs: IAutoClosingPairConditional[];
private readonly _surroundingPairs: IAutoClosingPair[];
constructor(registry: LanguageConfigurationRegistryImpl, modeId: string, config: { brackets?: CharacterPair[]; autoClosingPairs?: IAutoClosingPairConditional[], surroundingPairs?: IAutoClosingPair[] }) {
this._registry = registry;
this._modeId = modeId;
constructor(config: { brackets?: CharacterPair[]; autoClosingPairs?: IAutoClosingPairConditional[], surroundingPairs?: IAutoClosingPair[] }) {
this._autoClosingPairs = config.autoClosingPairs;
if (!this._autoClosingPairs) {
this._autoClosingPairs = config.brackets ? config.brackets.map(b => ({ open: b[0], close: b[1] })) : [];
......@@ -29,41 +24,29 @@ export class CharacterPairSupport implements IRichEditCharacterPair {
return this._autoClosingPairs;
}
public shouldAutoClosePair(character: string, context: ILineContext, offset: number): boolean {
return handleEvent(context, offset, (nestedModeId: string, context: ILineContext, offset: number) => {
if (this._modeId === nestedModeId) {
// Always complete on empty line
if (context.getTokenCount() === 0) {
return true;
}
public shouldAutoClosePair(character: string, context: ScopedLineTokens, offset: number): boolean {
// Always complete on empty line
if (context.getTokenCount() === 0) {
return true;
}
var tokenIndex = context.findIndexOfOffset(offset - 1);
var tokenType = context.getTokenType(tokenIndex);
var tokenIndex = context.findTokenIndexAtOffset(offset - 1);
var tokenType = context.getTokenType(tokenIndex);
for (var i = 0; i < this._autoClosingPairs.length; ++i) {
if (this._autoClosingPairs[i].open === character) {
if (this._autoClosingPairs[i].notIn) {
for (var notInIndex = 0; notInIndex < this._autoClosingPairs[i].notIn.length; ++notInIndex) {
if (tokenType.indexOf(this._autoClosingPairs[i].notIn[notInIndex]) > -1) {
return false;
}
}
for (var i = 0; i < this._autoClosingPairs.length; ++i) {
if (this._autoClosingPairs[i].open === character) {
if (this._autoClosingPairs[i].notIn) {
for (var notInIndex = 0; notInIndex < this._autoClosingPairs[i].notIn.length; ++notInIndex) {
if (tokenType.indexOf(this._autoClosingPairs[i].notIn[notInIndex]) > -1) {
return false;
}
break;
}
}
return true;
}
let characterPairSupport = this._registry.getCharacterPairSupport(nestedModeId);
if (characterPairSupport) {
return characterPairSupport.shouldAutoClosePair(character, context, offset);
break;
}
}
return null;
});
return true;
}
public getSurroundingPairs(): IAutoClosingPair[] {
......
......@@ -6,9 +6,8 @@
import * as strings from 'vs/base/common/strings';
import * as modes from 'vs/editor/common/modes';
import { handleEvent, ignoreBracketsInToken } from 'vs/editor/common/modes/supports';
import { ScopedLineTokens, ignoreBracketsInToken } from 'vs/editor/common/modes/supports';
import { BracketsUtils } from 'vs/editor/common/modes/supports/richEditBrackets';
import { LanguageConfigurationRegistryImpl } from 'vs/editor/common/modes/languageConfigurationRegistry';
/**
* Definition of documentation comments (e.g. Javadoc/JSdoc)
......@@ -25,18 +24,14 @@ export interface IBracketElectricCharacterContribution {
embeddedElectricCharacters?: string[];
}
export class BracketElectricCharacterSupport implements modes.IRichEditElectricCharacter {
export class BracketElectricCharacterSupport {
private _registry: LanguageConfigurationRegistryImpl;
private _modeId: string;
private contribution: IBracketElectricCharacterContribution;
private brackets: Brackets;
private readonly contribution: IBracketElectricCharacterContribution;
private readonly brackets: Brackets;
constructor(registry: LanguageConfigurationRegistryImpl, modeId: string, brackets: modes.IRichEditBrackets, autoClosePairs: modes.IAutoClosingPairConditional[], contribution: IBracketElectricCharacterContribution) {
this._registry = registry;
this._modeId = modeId;
constructor(brackets: modes.IRichEditBrackets, autoClosePairs: modes.IAutoClosingPairConditional[], contribution: IBracketElectricCharacterContribution) {
this.contribution = contribution || {};
this.brackets = new Brackets(modeId, brackets, autoClosePairs, this.contribution.docComment);
this.brackets = new Brackets(brackets, autoClosePairs, this.contribution.docComment);
}
public getElectricCharacters(): string[] {
......@@ -46,30 +41,17 @@ export class BracketElectricCharacterSupport implements modes.IRichEditElectricC
return this.brackets.getElectricCharacters();
}
public onElectricCharacter(context: modes.ILineContext, offset: number): modes.IElectricAction {
return handleEvent(context, offset, (nestedModeId: string, context: modes.ILineContext, offset: number) => {
if (this._modeId === nestedModeId) {
return this.brackets.onElectricCharacter(context, offset);
}
let electricCharacterSupport = this._registry.getElectricCharacterSupport(nestedModeId);
if (electricCharacterSupport) {
return electricCharacterSupport.onElectricCharacter(context, offset);
}
return null;
});
public onElectricCharacter(context: ScopedLineTokens, offset: number): modes.IElectricAction {
return this.brackets.onElectricCharacter(context, offset);
}
}
export class Brackets {
private _modeId: string;
private _richEditBrackets: modes.IRichEditBrackets;
private _complexAutoClosePairs: modes.IAutoClosingPairConditional[];
private readonly _richEditBrackets: modes.IRichEditBrackets;
private readonly _complexAutoClosePairs: modes.IAutoClosingPairConditional[];
constructor(modeId: string, richEditBrackets: modes.IRichEditBrackets, autoClosePairs: modes.IAutoClosingPairConditional[], docComment?: IDocComment) {
this._modeId = modeId;
constructor(richEditBrackets: modes.IRichEditBrackets, autoClosePairs: modes.IAutoClosingPairConditional[], docComment?: IDocComment) {
this._richEditBrackets = richEditBrackets;
this._complexAutoClosePairs = autoClosePairs.filter(pair => pair.open.length > 1 && !!pair.close);
if (docComment) {
......@@ -102,7 +84,7 @@ export class Brackets {
return result;
}
public onElectricCharacter(context: modes.ILineContext, offset: number): modes.IElectricAction {
public onElectricCharacter(context: ScopedLineTokens, offset: number): modes.IElectricAction {
if (context.getTokenCount() === 0) {
return null;
}
......@@ -121,7 +103,7 @@ export class Brackets {
return true;
}
private _onElectricAutoIndent(context: modes.ILineContext, offset: number): modes.IElectricAction {
private _onElectricAutoIndent(context: ScopedLineTokens, offset: number): modes.IElectricAction {
if (!this._richEditBrackets || this._richEditBrackets.brackets.length === 0) {
return null;
......@@ -130,7 +112,7 @@ export class Brackets {
let reversedBracketRegex = this._richEditBrackets.reversedRegex;
let lineText = context.getLineContent();
let tokenIndex = context.findIndexOfOffset(offset);
let tokenIndex = context.findTokenIndexAtOffset(offset);
let tokenStart = context.getTokenStartOffset(tokenIndex);
let tokenEnd = offset + 1;
......@@ -157,7 +139,7 @@ export class Brackets {
return null;
}
private _onElectricAutoClose(context: modes.ILineContext, offset: number): modes.IElectricAction {
private _onElectricAutoClose(context: ScopedLineTokens, offset: number): modes.IElectricAction {
if (!this._complexAutoClosePairs.length) {
return null;
......@@ -180,7 +162,7 @@ export class Brackets {
}
// check if the full open bracket matches
let lastTokenIndex = context.findIndexOfOffset(offset);
let lastTokenIndex = context.findTokenIndexAtOffset(offset);
if (line.substring(context.getTokenStartOffset(lastTokenIndex), offset + 1/* include electric char*/) !== pair.open) {
continue;
}
......
......@@ -6,10 +6,7 @@
import { onUnexpectedError } from 'vs/base/common/errors';
import * as strings from 'vs/base/common/strings';
import { IPosition, ITextModel, ITokenizedModel } from 'vs/editor/common/editorCommon';
import { EnterAction, ILineContext, IRichEditOnEnter, IndentAction, CharacterPair } from 'vs/editor/common/modes';
import { handleEvent } from 'vs/editor/common/modes/supports';
import { LanguageConfigurationRegistryImpl } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { EnterAction, IndentAction, CharacterPair } from 'vs/editor/common/modes';
/**
* Describes indentation rules for a language.
......@@ -64,20 +61,17 @@ interface IProcessedBracketPair {
closeRegExp: RegExp;
}
export class OnEnterSupport implements IRichEditOnEnter {
export class OnEnterSupport {
private static _INDENT: EnterAction = { indentAction: IndentAction.Indent };
private static _INDENT_OUTDENT: EnterAction = { indentAction: IndentAction.IndentOutdent };
private static _OUTDENT: EnterAction = { indentAction: IndentAction.Outdent };
private _registry: LanguageConfigurationRegistryImpl;
private _modeId: string;
private _brackets: IProcessedBracketPair[];
private _indentationRules: IndentationRule;
private _regExpRules: OnEnterRule[];
private readonly _brackets: IProcessedBracketPair[];
private readonly _indentationRules: IndentationRule;
private readonly _regExpRules: OnEnterRule[];
constructor(registry: LanguageConfigurationRegistryImpl, modeId: string, opts?: IOnEnterSupportOptions) {
this._registry = registry;
constructor(opts?: IOnEnterSupportOptions) {
opts = opts || {};
opts.brackets = opts.brackets || [
['(', ')'],
......@@ -85,7 +79,6 @@ export class OnEnterSupport implements IRichEditOnEnter {
['[', ']']
];
this._modeId = modeId;
this._brackets = opts.brackets.map((bracket) => {
return {
open: bracket[0],
......@@ -98,34 +91,7 @@ export class OnEnterSupport implements IRichEditOnEnter {
this._indentationRules = opts.indentationRules;
}
public onEnter(model: ITokenizedModel, position: IPosition): EnterAction {
var context = model.getLineContext(position.lineNumber);
return handleEvent(context, position.column - 1, (nestedModeId: string, context: ILineContext, offset: number) => {
if (this._modeId === nestedModeId) {
return this._onEnter(model, position);
}
let onEnterSupport = this._registry.getOnEnterSupport(nestedModeId);
if (onEnterSupport) {
return onEnterSupport.onEnter(model, position);
}
return null;
});
}
private _onEnter(model: ITextModel, position: IPosition): EnterAction {
let lineText = model.getLineContent(position.lineNumber);
let beforeEnterText = lineText.substr(0, position.column - 1);
let afterEnterText = lineText.substr(position.column - 1);
let oneLineAboveText = position.lineNumber === 1 ? '' : model.getLineContent(position.lineNumber - 1);
return this._actualOnEnter(oneLineAboveText, beforeEnterText, afterEnterText);
}
_actualOnEnter(oneLineAboveText: string, beforeEnterText: string, afterEnterText: string): EnterAction {
public onEnter(oneLineAboveText: string, beforeEnterText: string, afterEnterText: string): EnterAction {
// (1): `regExpRules`
for (let i = 0, len = this._regExpRules.length; i < len; i++) {
let rule = this._regExpRules[i];
......
......@@ -11,11 +11,11 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { SimpleWorkerClient, logOnceWebWorkerWarning } from 'vs/base/common/worker/simpleWorker';
import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { WordHelper } from 'vs/editor/common/model/textModelWithTokensHelpers';
import { IInplaceReplaceSupportResult, ILink, ISuggestResult, LinkProviderRegistry, LinkProvider } from 'vs/editor/common/modes';
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { EditorSimpleWorkerImpl } from 'vs/editor/common/services/editorSimpleWorker';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
/**
* Stop syncing a model to the worker if it was not needed for 1 min.
......@@ -321,7 +321,7 @@ export class EditorWorkerClient extends Disposable {
if (!model) {
return null;
}
let wordDefRegExp = WordHelper.massageWordDefinitionOf(model.getModeId());
let wordDefRegExp = LanguageConfigurationRegistry.getWordDefinition(model.getModeId());
let wordDef = wordDefRegExp.source;
let wordDefFlags = (wordDefRegExp.global ? 'g' : '') + (wordDefRegExp.ignoreCase ? 'i' : '') + (wordDefRegExp.multiline ? 'm' : '');
return proxy.textualSuggest(resource.toString(), position, wordDef, wordDefFlags);
......@@ -334,7 +334,7 @@ export class EditorWorkerClient extends Disposable {
if (!model) {
return null;
}
let wordDefRegExp = WordHelper.massageWordDefinitionOf(model.getModeId());
let wordDefRegExp = LanguageConfigurationRegistry.getWordDefinition(model.getModeId());
let wordDef = wordDefRegExp.source;
let wordDefFlags = (wordDefRegExp.global ? 'g' : '') + (wordDefRegExp.ignoreCase ? 'i' : '') + (wordDefRegExp.multiline ? 'm' : '');
return proxy.navigateValueSet(resource.toString(), range, up, wordDef, wordDefFlags);
......
......@@ -17,7 +17,7 @@ import {
ICursorPositionChangedEvent, ICursorSelectionChangedEvent
} from 'vs/editor/common/editorCommon';
import { Model } from 'vs/editor/common/model/model';
import { IMode, IndentAction } from 'vs/editor/common/modes';
import { IndentAction } from 'vs/editor/common/modes';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { MockConfiguration } from 'vs/editor/test/common/mocks/mockConfiguration';
import { MockMode } from 'vs/editor/test/common/mocks/mockMode';
......@@ -1148,7 +1148,7 @@ suite('Editor Controller - Regression tests', () => {
modeId: new BracketMode().getId()
}, (model, cursor) => {
// ensure is tokenized
model.getLineContext(1);
model.getLineTokens(1, false);
moveTo(cursor, 1, 20);
......
......@@ -6,16 +6,43 @@
import * as assert from 'assert';
import { Brackets } from 'vs/editor/common/modes/supports/electricCharacter';
import { createLineContextFromTokenText } from 'vs/editor/test/common/modesTestUtils';
import { createFakeLineTokens } from 'vs/editor/test/common/modesTestUtils';
import { LineTokens } from 'vs/editor/common/core/lineTokens';
import { Token } from 'vs/editor/common/core/token';
import { ModeTransition } from 'vs/editor/common/core/modeTransition';
import { createScopedLineTokens, ScopedLineTokens } from 'vs/editor/common/modes/supports';
interface TokenText {
text: string;
type: string;
}
function createLineContextFromTokenText(modeId: string, tokens: TokenText[]): LineTokens {
let line = '';
let processedTokens: Token[] = [];
let indexSoFar = 0;
for (let i = 0; i < tokens.length; ++i) {
processedTokens.push(new Token(indexSoFar, tokens[i].type));
line += tokens[i].text;
indexSoFar += tokens[i].text.length;
}
return createFakeLineTokens(line, modeId, processedTokens, [new ModeTransition(0, modeId)]);
}
function createScopedLineTokensFromTokenText(modeId: string, tokens: TokenText[]): ScopedLineTokens {
return createScopedLineTokens(createLineContextFromTokenText(modeId, tokens), 0);
}
suite('Editor Modes - Auto Indentation', () => {
test('Doc comments', () => {
var brackets = new Brackets('test', null, [{ open: '/**', close: ' */' }]);
var brackets = new Brackets(null, [{ open: '/**', close: ' */' }]);
assert.equal(brackets.onElectricCharacter(createLineContextFromTokenText([
assert.equal(brackets.onElectricCharacter(createScopedLineTokensFromTokenText('test', [
{ text: '/**', type: 'doc' },
]), 2).appendText, ' */');
assert.equal(brackets.onElectricCharacter(createLineContextFromTokenText([
assert.equal(brackets.onElectricCharacter(createScopedLineTokensFromTokenText('test', [
{ text: '/**', type: 'doc' },
{ text: ' ', type: 'doc' },
{ text: '*/', type: 'doc' },
......
......@@ -11,7 +11,7 @@ import { OnEnterSupport } from 'vs/editor/common/modes/supports/onEnter';
suite('OnEnter', () => {
test('uses indentationRules', () => {
var support = new OnEnterSupport(null, null, {
var support = new OnEnterSupport({
indentationRules: {
decreaseIndentPattern: /^\s*((?!\S.*\/[*]).*[*]\/\s*)?[})\]]|^\s*(case\b.*|default):\s*(\/\/.*|\/[*].*[*]\/\s*)?$/,
increaseIndentPattern: /(\{[^}"']*|\([^)"']*|\[[^\]"']*|^\s*(\{\}|\(\)|\[\]|(case\b.*|default):))\s*(\/\/.*|\/[*].*[*]\/\s*)?$/,
......@@ -21,7 +21,7 @@ suite('OnEnter', () => {
});
var testIndentAction = (oneLineAboveText: string, beforeText: string, afterText: string, expected: IndentAction) => {
var actual = support._actualOnEnter(oneLineAboveText, beforeText, afterText);
var actual = support.onEnter(oneLineAboveText, beforeText, afterText);
if (expected === IndentAction.None) {
assert.equal(actual, null);
} else {
......@@ -42,11 +42,11 @@ suite('OnEnter', () => {
['(', ')'],
['begin', 'end']
];
var support = new OnEnterSupport(null, null, {
var support = new OnEnterSupport({
brackets: brackets
});
var testIndentAction = (beforeText: string, afterText: string, expected: IndentAction) => {
var actual = support._actualOnEnter('', beforeText, afterText);
var actual = support.onEnter('', beforeText, afterText);
if (expected === IndentAction.None) {
assert.equal(actual, null);
} else {
......@@ -75,7 +75,7 @@ suite('OnEnter', () => {
});
test('uses regExpRules', () => {
var support = new OnEnterSupport(null, null, {
var support = new OnEnterSupport({
regExpRules: [
{
beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
......@@ -101,7 +101,7 @@ suite('OnEnter', () => {
]
});
var testIndentAction = (beforeText: string, afterText: string, expectedIndentAction: IndentAction, expectedAppendText: string, removeText: number = 0) => {
var actual = support._actualOnEnter('', beforeText, afterText);
var actual = support.onEnter('', beforeText, afterText);
if (expectedIndentAction === null) {
assert.equal(actual, null, 'isNull:' + beforeText);
} else {
......
......@@ -7,9 +7,9 @@
import * as assert from 'assert';
import * as modes from 'vs/editor/common/modes';
import { AbstractState, ITokenizationResult } from 'vs/editor/common/modes/abstractState';
import { handleEvent } from 'vs/editor/common/modes/supports';
import { createScopedLineTokens } from 'vs/editor/common/modes/supports';
import { IModeLocator, ILeavingNestedModeData, TokenizationSupport } from 'vs/editor/common/modes/supports/tokenizationSupport';
import { createMockLineContext } from 'vs/editor/test/common/modesTestUtils';
import { createFakeLineTokens } from 'vs/editor/test/common/modesTestUtils';
import { MockMode } from 'vs/editor/test/common/mocks/mockMode';
import { ModeTransition } from 'vs/editor/common/core/modeTransition';
import { Token } from 'vs/editor/common/core/token';
......@@ -315,27 +315,25 @@ suite('Editor Modes - Tokenization', () => {
{ startIndex: 5, id: 'B' }
]);
handleEvent(createMockLineContext('abc (def', lineTokens), 0, (modeId: string, context: modes.ILineContext, offset: number) => {
assert.deepEqual(modeId, 'A');
assert.equal(context.getTokenCount(), 3);
assert.equal(context.getTokenStartOffset(0), 0);
assert.equal(context.getTokenType(0), 'A.abc');
assert.equal(context.getTokenStartOffset(1), 3);
assert.equal(context.getTokenType(1), '');
assert.equal(context.getTokenStartOffset(2), 4);
assert.equal(context.getTokenType(2), 'A.(');
assert.deepEqual(offset, 0);
assert.equal(context.getLineContent(), 'abc (');
});
handleEvent(createMockLineContext('abc (def', lineTokens), 6, (modeId: string, context: modes.ILineContext, offset: number) => {
assert.deepEqual(modeId, 'B');
assert.equal(context.getTokenCount(), 1);
assert.equal(context.getTokenStartOffset(0), 0);
assert.equal(context.getTokenType(0), 'B.def');
assert.deepEqual(offset, 1);
assert.equal(context.getLineContent(), 'def');
});
let scopedLineTokens1 = createScopedLineTokens(createFakeLineTokens('abc (def', switchingMode.getId(), lineTokens.tokens, lineTokens.modeTransitions), 0);
assert.deepEqual(scopedLineTokens1.modeId, 'A');
assert.equal(scopedLineTokens1.getTokenCount(), 3);
assert.equal(scopedLineTokens1.getTokenStartOffset(0), 0);
assert.equal(scopedLineTokens1.getTokenType(0), 'A.abc');
assert.equal(scopedLineTokens1.getTokenStartOffset(1), 3);
assert.equal(scopedLineTokens1.getTokenType(1), '');
assert.equal(scopedLineTokens1.getTokenStartOffset(2), 4);
assert.equal(scopedLineTokens1.getTokenType(2), 'A.(');
assert.deepEqual(scopedLineTokens1.firstCharOffset, 0);
assert.equal(scopedLineTokens1.getLineContent(), 'abc (');
let scopedLineTokens2 = createScopedLineTokens(createFakeLineTokens('abc (def', switchingMode.getId(), lineTokens.tokens, lineTokens.modeTransitions), 6);
assert.deepEqual(scopedLineTokens2.modeId, 'B');
assert.equal(scopedLineTokens2.getTokenCount(), 1);
assert.equal(scopedLineTokens2.getTokenStartOffset(0), 0);
assert.equal(scopedLineTokens2.getTokenType(0), 'B.def');
assert.deepEqual(scopedLineTokens2.firstCharOffset, 5);
assert.equal(scopedLineTokens2.getLineContent(), 'def');
lineTokens = switchingModeTokenize('ghi jkl', lineTokens.endState);
assertTokens(lineTokens.tokens, [
......
......@@ -4,63 +4,13 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Arrays } from 'vs/editor/common/core/arrays';
import * as modes from 'vs/editor/common/modes';
import { ModeTransition } from 'vs/editor/common/core/modeTransition';
import { Token } from 'vs/editor/common/core/token';
import { LineTokens } from 'vs/editor/common/core/lineTokens';
import { TokensBinaryEncoding, TokensInflatorMap } from 'vs/editor/common/model/tokensBinaryEncoding';
export interface TokenText {
text: string;
type: string;
export function createFakeLineTokens(line: string, modeId: string, tokens: Token[], modeTransitions: ModeTransition[]): LineTokens {
let map = new TokensInflatorMap(modeId);
let deflatedTokens = TokensBinaryEncoding.deflateArr(map, tokens);
return new LineTokens(map, deflatedTokens, modeTransitions, line);
}
export function createLineContextFromTokenText(tokens: TokenText[]): modes.ILineContext {
let line = '';
let processedTokens: Token[] = [];
let indexSoFar = 0;
for (let i = 0; i < tokens.length; ++i) {
processedTokens.push(new Token(indexSoFar, tokens[i].type));
line += tokens[i].text;
indexSoFar += tokens[i].text.length;
}
return new TestLineContext(line, processedTokens, null);
}
export function createMockLineContext(line: string, tokens: modes.ILineTokens): modes.ILineContext {
return new TestLineContext(line, tokens.tokens, tokens.modeTransitions);
}
class TestLineContext implements modes.ILineContext {
public modeTransitions: ModeTransition[];
private _line: string;
private _tokens: Token[];
constructor(line: string, tokens: Token[], modeTransitions: ModeTransition[]) {
this.modeTransitions = modeTransitions;
this._line = line;
this._tokens = tokens;
}
public getLineContent(): string {
return this._line;
}
public getTokenCount(): number {
return this._tokens.length;
}
public getTokenStartOffset(tokenIndex: number): number {
return this._tokens[tokenIndex].startIndex;
}
public getTokenType(tokenIndex: number): string {
return this._tokens[tokenIndex].type;
}
public findIndexOfOffset(offset: number): number {
return Arrays.findIndexInSegmentsArray(this._tokens, offset);
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册