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

Eliminate ILineContext

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