Add support for including line feeds at the end of lines (microsoft/monaco-editor#2265)

上级 4a121608
...@@ -359,7 +359,7 @@ export class TextModelTokenization extends Disposable { ...@@ -359,7 +359,7 @@ export class TextModelTokenization extends Disposable {
const text = this._textModel.getLineContent(lineIndex + 1); const text = this._textModel.getLineContent(lineIndex + 1);
const lineStartState = this._tokenizationStateStore.getBeginState(lineIndex); const lineStartState = this._tokenizationStateStore.getBeginState(lineIndex);
const r = safeTokenize(languageIdentifier, this._tokenizationSupport, text, lineStartState!); const r = safeTokenize(languageIdentifier, this._tokenizationSupport, text, true, lineStartState!);
builder.add(lineIndex + 1, r.tokens); builder.add(lineIndex + 1, r.tokens);
this._tokenizationStateStore.setEndState(linesLength, lineIndex, r.endState); this._tokenizationStateStore.setEndState(linesLength, lineIndex, r.endState);
lineIndex = this._tokenizationStateStore.invalidLineStartIndex - 1; // -1 because the outer loop increments it lineIndex = this._tokenizationStateStore.invalidLineStartIndex - 1; // -1 because the outer loop increments it
...@@ -410,13 +410,13 @@ export class TextModelTokenization extends Disposable { ...@@ -410,13 +410,13 @@ export class TextModelTokenization extends Disposable {
const languageIdentifier = this._textModel.getLanguageIdentifier(); const languageIdentifier = this._textModel.getLanguageIdentifier();
let state = initialState; let state = initialState;
for (let i = fakeLines.length - 1; i >= 0; i--) { for (let i = fakeLines.length - 1; i >= 0; i--) {
let r = safeTokenize(languageIdentifier, this._tokenizationSupport, fakeLines[i], state); let r = safeTokenize(languageIdentifier, this._tokenizationSupport, fakeLines[i], false, state);
state = r.endState; state = r.endState;
} }
for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) { for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
let text = this._textModel.getLineContent(lineNumber); let text = this._textModel.getLineContent(lineNumber);
let r = safeTokenize(languageIdentifier, this._tokenizationSupport, text, state); let r = safeTokenize(languageIdentifier, this._tokenizationSupport, text, true, state);
builder.add(lineNumber, r.tokens); builder.add(lineNumber, r.tokens);
this._tokenizationStateStore.setFakeTokens(lineNumber - 1); this._tokenizationStateStore.setFakeTokens(lineNumber - 1);
state = r.endState; state = r.endState;
...@@ -443,12 +443,12 @@ function initializeTokenization(textModel: TextModel): [ITokenizationSupport | n ...@@ -443,12 +443,12 @@ function initializeTokenization(textModel: TextModel): [ITokenizationSupport | n
return [tokenizationSupport, initialState]; return [tokenizationSupport, initialState];
} }
function safeTokenize(languageIdentifier: LanguageIdentifier, tokenizationSupport: ITokenizationSupport | null, text: string, state: IState): TokenizationResult2 { function safeTokenize(languageIdentifier: LanguageIdentifier, tokenizationSupport: ITokenizationSupport | null, text: string, hasEOL: boolean, state: IState): TokenizationResult2 {
let r: TokenizationResult2 | null = null; let r: TokenizationResult2 | null = null;
if (tokenizationSupport) { if (tokenizationSupport) {
try { try {
r = tokenizationSupport.tokenize2(text, state.clone(), 0); r = tokenizationSupport.tokenize2(text, hasEOL, state.clone(), 0);
} catch (e) { } catch (e) {
onUnexpectedError(e); onUnexpectedError(e);
} }
......
...@@ -211,9 +211,9 @@ export interface ITokenizationSupport { ...@@ -211,9 +211,9 @@ export interface ITokenizationSupport {
getInitialState(): IState; getInitialState(): IState;
// add offsetDelta to each of the returned indices // add offsetDelta to each of the returned indices
tokenize(line: string, state: IState, offsetDelta: number): TokenizationResult; tokenize(line: string, hasEOL: boolean, state: IState, offsetDelta: number): TokenizationResult;
tokenize2(line: string, state: IState, offsetDelta: number): TokenizationResult2; tokenize2(line: string, hasEOL: boolean, state: IState, offsetDelta: number): TokenizationResult2;
} }
/** /**
......
...@@ -12,12 +12,12 @@ import { NULL_STATE, nullTokenize2 } from 'vs/editor/common/modes/nullMode'; ...@@ -12,12 +12,12 @@ import { NULL_STATE, nullTokenize2 } from 'vs/editor/common/modes/nullMode';
export interface IReducedTokenizationSupport { export interface IReducedTokenizationSupport {
getInitialState(): IState; getInitialState(): IState;
tokenize2(line: string, state: IState, offsetDelta: number): TokenizationResult2; tokenize2(line: string, hasEOL: boolean, state: IState, offsetDelta: number): TokenizationResult2;
} }
const fallback: IReducedTokenizationSupport = { const fallback: IReducedTokenizationSupport = {
getInitialState: () => NULL_STATE, getInitialState: () => NULL_STATE,
tokenize2: (buffer: string, state: IState, deltaOffset: number) => nullTokenize2(LanguageId.Null, buffer, state, deltaOffset) tokenize2: (buffer: string, hasEOL: boolean, state: IState, deltaOffset: number) => nullTokenize2(LanguageId.Null, buffer, state, deltaOffset)
}; };
export function tokenizeToString(text: string, tokenizationSupport: IReducedTokenizationSupport = fallback): string { export function tokenizeToString(text: string, tokenizationSupport: IReducedTokenizationSupport = fallback): string {
...@@ -110,7 +110,7 @@ function _tokenizeToString(text: string, tokenizationSupport: IReducedTokenizati ...@@ -110,7 +110,7 @@ function _tokenizeToString(text: string, tokenizationSupport: IReducedTokenizati
result += `<br/>`; result += `<br/>`;
} }
let tokenizationResult = tokenizationSupport.tokenize2(line, currentState, 0); let tokenizationResult = tokenizationSupport.tokenize2(line, true, currentState, 0);
LineTokens.convertToEndOffset(tokenizationResult.tokens, line.length); LineTokens.convertToEndOffset(tokenizationResult.tokens, line.length);
let lineTokens = new LineTokens(tokenizationResult.tokens, line); let lineTokens = new LineTokens(tokenizationResult.tokens, line);
let viewLineTokens = lineTokens.inflate(); let viewLineTokens = lineTokens.inflate();
......
...@@ -1083,7 +1083,7 @@ suite('Editor Contrib - Line Comment in mixed modes', () => { ...@@ -1083,7 +1083,7 @@ suite('Editor Contrib - Line Comment in mixed modes', () => {
tokenize: () => { tokenize: () => {
throw new Error('not implemented'); throw new Error('not implemented');
}, },
tokenize2: (line: string, state: modes.IState): TokenizationResult2 => { tokenize2: (line: string, hasEOL: boolean, state: modes.IState): TokenizationResult2 => {
let languageId = (/^ /.test(line) ? INNER_LANGUAGE_ID : OUTER_LANGUAGE_ID); let languageId = (/^ /.test(line) ? INNER_LANGUAGE_ID : OUTER_LANGUAGE_ID);
let tokens = new Uint32Array(1 << 1); let tokens = new Uint32Array(1 << 1);
......
...@@ -71,7 +71,7 @@ suite('SuggestModel - Context', function () { ...@@ -71,7 +71,7 @@ suite('SuggestModel - Context', function () {
this._register(TokenizationRegistry.register(this.getLanguageIdentifier().language, { this._register(TokenizationRegistry.register(this.getLanguageIdentifier().language, {
getInitialState: (): IState => NULL_STATE, getInitialState: (): IState => NULL_STATE,
tokenize: undefined!, tokenize: undefined!,
tokenize2: (line: string, state: IState): TokenizationResult2 => { tokenize2: (line: string, hasEOL: boolean, state: IState): TokenizationResult2 => {
const tokensArr: number[] = []; const tokensArr: number[] = [];
let prevLanguageId: LanguageIdentifier | undefined = undefined; let prevLanguageId: LanguageIdentifier | undefined = undefined;
for (let i = 0; i < line.length; i++) { for (let i = 0; i < line.length; i++) {
......
...@@ -222,7 +222,7 @@ function _actualColorize(lines: string[], tabSize: number, tokenizationSupport: ...@@ -222,7 +222,7 @@ function _actualColorize(lines: string[], tabSize: number, tokenizationSupport:
for (let i = 0, length = lines.length; i < length; i++) { for (let i = 0, length = lines.length; i < length; i++) {
let line = lines[i]; let line = lines[i];
let tokenizeResult = tokenizationSupport.tokenize2(line, state, 0); let tokenizeResult = tokenizationSupport.tokenize2(line, true, state, 0);
LineTokens.convertToEndOffset(tokenizeResult.tokens, line.length); LineTokens.convertToEndOffset(tokenizeResult.tokens, line.length);
let lineTokens = new LineTokens(tokenizeResult.tokens, line); let lineTokens = new LineTokens(tokenizeResult.tokens, line);
const isBasicASCII = ViewLineRenderingData.isBasicASCII(line, /* check for basic ASCII */true); const isBasicASCII = ViewLineRenderingData.isBasicASCII(line, /* check for basic ASCII */true);
......
...@@ -137,8 +137,8 @@ function getSafeTokenizationSupport(languageIdentifier: LanguageIdentifier): ITo ...@@ -137,8 +137,8 @@ function getSafeTokenizationSupport(languageIdentifier: LanguageIdentifier): ITo
} }
return { return {
getInitialState: () => NULL_STATE, getInitialState: () => NULL_STATE,
tokenize: (line: string, state: IState, deltaOffset: number) => nullTokenize(languageIdentifier.language, line, state, deltaOffset), tokenize: (line: string, hasEOL: boolean, state: IState, deltaOffset: number) => nullTokenize(languageIdentifier.language, line, state, deltaOffset),
tokenize2: (line: string, state: IState, deltaOffset: number) => nullTokenize2(languageIdentifier.id, line, state, deltaOffset) tokenize2: (line: string, hasEOL: boolean, state: IState, deltaOffset: number) => nullTokenize2(languageIdentifier.id, line, state, deltaOffset)
}; };
} }
...@@ -293,8 +293,8 @@ class InspectTokensWidget extends Disposable implements IContentWidget { ...@@ -293,8 +293,8 @@ class InspectTokensWidget extends Disposable implements IContentWidget {
private _getTokensAtLine(lineNumber: number): ICompleteLineTokenization { private _getTokensAtLine(lineNumber: number): ICompleteLineTokenization {
let stateBeforeLine = this._getStateBeforeLine(lineNumber); let stateBeforeLine = this._getStateBeforeLine(lineNumber);
let tokenizationResult1 = this._tokenizationSupport.tokenize(this._model.getLineContent(lineNumber), stateBeforeLine, 0); let tokenizationResult1 = this._tokenizationSupport.tokenize(this._model.getLineContent(lineNumber), true, stateBeforeLine, 0);
let tokenizationResult2 = this._tokenizationSupport.tokenize2(this._model.getLineContent(lineNumber), stateBeforeLine, 0); let tokenizationResult2 = this._tokenizationSupport.tokenize2(this._model.getLineContent(lineNumber), true, stateBeforeLine, 0);
return { return {
startState: stateBeforeLine, startState: stateBeforeLine,
...@@ -308,7 +308,7 @@ class InspectTokensWidget extends Disposable implements IContentWidget { ...@@ -308,7 +308,7 @@ class InspectTokensWidget extends Disposable implements IContentWidget {
let state: IState = this._tokenizationSupport.getInitialState(); let state: IState = this._tokenizationSupport.getInitialState();
for (let i = 1; i < lineNumber; i++) { for (let i = 1; i < lineNumber; i++) {
let tokenizationResult = this._tokenizationSupport.tokenize(this._model.getLineContent(i), state, 0); let tokenizationResult = this._tokenizationSupport.tokenize(this._model.getLineContent(i), true, state, 0);
state = tokenizationResult.endState; state = tokenizationResult.endState;
} }
......
...@@ -276,7 +276,7 @@ function getSafeTokenizationSupport(language: string): Omit<modes.ITokenizationS ...@@ -276,7 +276,7 @@ function getSafeTokenizationSupport(language: string): Omit<modes.ITokenizationS
} }
return { return {
getInitialState: () => NULL_STATE, getInitialState: () => NULL_STATE,
tokenize: (line: string, state: modes.IState, deltaOffset: number) => nullTokenize(language, line, state, deltaOffset) tokenize: (line: string, hasEOL: boolean, state: modes.IState, deltaOffset: number) => nullTokenize(language, line, state, deltaOffset)
}; };
} }
...@@ -294,7 +294,7 @@ export function tokenize(text: string, languageId: string): Token[][] { ...@@ -294,7 +294,7 @@ export function tokenize(text: string, languageId: string): Token[][] {
let state = tokenizationSupport.getInitialState(); let state = tokenizationSupport.getInitialState();
for (let i = 0, len = lines.length; i < len; i++) { for (let i = 0, len = lines.length; i < len; i++) {
let line = lines[i]; let line = lines[i];
let tokenizationResult = tokenizationSupport.tokenize(line, state, 0); let tokenizationResult = tokenizationSupport.tokenize(line, true, state, 0);
result[i] = tokenizationResult.tokens; result[i] = tokenizationResult.tokens;
state = tokenizationResult.endState; state = tokenizationResult.endState;
......
...@@ -88,14 +88,14 @@ export class EncodedTokenizationSupport2Adapter implements modes.ITokenizationSu ...@@ -88,14 +88,14 @@ export class EncodedTokenizationSupport2Adapter implements modes.ITokenizationSu
return this._actual.getInitialState(); return this._actual.getInitialState();
} }
public tokenize(line: string, state: modes.IState, offsetDelta: number): TokenizationResult { public tokenize(line: string, hasEOL: boolean, state: modes.IState, offsetDelta: number): TokenizationResult {
if (typeof this._actual.tokenize === 'function') { if (typeof this._actual.tokenize === 'function') {
return TokenizationSupport2Adapter.adaptTokenize(this._languageIdentifier.language, <{ tokenize(line: string, state: modes.IState): ILineTokens; }>this._actual, line, state, offsetDelta); return TokenizationSupport2Adapter.adaptTokenize(this._languageIdentifier.language, <{ tokenize(line: string, state: modes.IState): ILineTokens; }>this._actual, line, state, offsetDelta);
} }
throw new Error('Not supported!'); throw new Error('Not supported!');
} }
public tokenize2(line: string, state: modes.IState): TokenizationResult2 { public tokenize2(line: string, hasEOL: boolean, state: modes.IState): TokenizationResult2 {
let result = this._actual.tokenizeEncoded(line, state); let result = this._actual.tokenizeEncoded(line, state);
return new TokenizationResult2(result.tokens, result.endState); return new TokenizationResult2(result.tokens, result.endState);
} }
...@@ -158,7 +158,7 @@ export class TokenizationSupport2Adapter implements modes.ITokenizationSupport { ...@@ -158,7 +158,7 @@ export class TokenizationSupport2Adapter implements modes.ITokenizationSupport {
return new TokenizationResult(tokens, endState); return new TokenizationResult(tokens, endState);
} }
public tokenize(line: string, state: modes.IState, offsetDelta: number): TokenizationResult { public tokenize(line: string, hasEOL: boolean, state: modes.IState, offsetDelta: number): TokenizationResult {
return TokenizationSupport2Adapter.adaptTokenize(this._languageIdentifier.language, this._actual, line, state, offsetDelta); return TokenizationSupport2Adapter.adaptTokenize(this._languageIdentifier.language, this._actual, line, state, offsetDelta);
} }
...@@ -200,7 +200,7 @@ export class TokenizationSupport2Adapter implements modes.ITokenizationSupport { ...@@ -200,7 +200,7 @@ export class TokenizationSupport2Adapter implements modes.ITokenizationSupport {
return actualResult; return actualResult;
} }
public tokenize2(line: string, state: modes.IState, offsetDelta: number): TokenizationResult2 { public tokenize2(line: string, hasEOL: boolean, state: modes.IState, offsetDelta: number): TokenizationResult2 {
let actualResult = this._actual.tokenize(line, state); let actualResult = this._actual.tokenize(line, state);
let tokens = this._toBinaryTokens(actualResult.tokens, offsetDelta); let tokens = this._toBinaryTokens(actualResult.tokens, offsetDelta);
......
...@@ -22,6 +22,7 @@ export const enum MonarchBracket { ...@@ -22,6 +22,7 @@ export const enum MonarchBracket {
export interface ILexerMin { export interface ILexerMin {
languageId: string; languageId: string;
includeLF: boolean;
noThrow: boolean; noThrow: boolean;
ignoreCase: boolean; ignoreCase: boolean;
unicode: boolean; unicode: boolean;
......
...@@ -395,6 +395,7 @@ export function compile(languageId: string, json: IMonarchLanguage): monarchComm ...@@ -395,6 +395,7 @@ export function compile(languageId: string, json: IMonarchLanguage): monarchComm
// Create our lexer // Create our lexer
let lexer: monarchCommon.ILexer = <monarchCommon.ILexer>{}; let lexer: monarchCommon.ILexer = <monarchCommon.ILexer>{};
lexer.languageId = languageId; lexer.languageId = languageId;
lexer.includeLF = bool(json.includeLF, false);
lexer.noThrow = false; // raise exceptions during compilation lexer.noThrow = false; // raise exceptions during compilation
lexer.maxStack = 100; lexer.maxStack = 100;
...@@ -411,6 +412,7 @@ export function compile(languageId: string, json: IMonarchLanguage): monarchComm ...@@ -411,6 +412,7 @@ export function compile(languageId: string, json: IMonarchLanguage): monarchComm
// For calling compileAction later on // For calling compileAction later on
let lexerMin: monarchCommon.ILexerMin = <any>json; let lexerMin: monarchCommon.ILexerMin = <any>json;
lexerMin.languageId = languageId; lexerMin.languageId = languageId;
lexerMin.includeLF = lexer.includeLF;
lexerMin.ignoreCase = lexer.ignoreCase; lexerMin.ignoreCase = lexer.ignoreCase;
lexerMin.unicode = lexer.unicode; lexerMin.unicode = lexer.unicode;
lexerMin.noThrow = lexer.noThrow; lexerMin.noThrow = lexer.noThrow;
......
...@@ -231,7 +231,7 @@ class MonarchLineState implements modes.IState { ...@@ -231,7 +231,7 @@ class MonarchLineState implements modes.IState {
interface IMonarchTokensCollector { interface IMonarchTokensCollector {
enterMode(startOffset: number, modeId: string): void; enterMode(startOffset: number, modeId: string): void;
emit(startOffset: number, type: string): void; emit(startOffset: number, type: string): void;
nestedModeTokenize(embeddedModeLine: string, embeddedModeData: EmbeddedModeData, offsetDelta: number): modes.IState; nestedModeTokenize(embeddedModeLine: string, hasEOL: boolean, embeddedModeData: EmbeddedModeData, offsetDelta: number): modes.IState;
} }
class MonarchClassicTokensCollector implements IMonarchTokensCollector { class MonarchClassicTokensCollector implements IMonarchTokensCollector {
...@@ -261,7 +261,7 @@ class MonarchClassicTokensCollector implements IMonarchTokensCollector { ...@@ -261,7 +261,7 @@ class MonarchClassicTokensCollector implements IMonarchTokensCollector {
this._tokens.push(new Token(startOffset, type, this._language!)); this._tokens.push(new Token(startOffset, type, this._language!));
} }
public nestedModeTokenize(embeddedModeLine: string, embeddedModeData: EmbeddedModeData, offsetDelta: number): modes.IState { public nestedModeTokenize(embeddedModeLine: string, hasEOL: boolean, embeddedModeData: EmbeddedModeData, offsetDelta: number): modes.IState {
const nestedModeId = embeddedModeData.modeId; const nestedModeId = embeddedModeData.modeId;
const embeddedModeState = embeddedModeData.state; const embeddedModeState = embeddedModeData.state;
...@@ -272,7 +272,7 @@ class MonarchClassicTokensCollector implements IMonarchTokensCollector { ...@@ -272,7 +272,7 @@ class MonarchClassicTokensCollector implements IMonarchTokensCollector {
return embeddedModeState; return embeddedModeState;
} }
let nestedResult = nestedModeTokenizationSupport.tokenize(embeddedModeLine, embeddedModeState, offsetDelta); let nestedResult = nestedModeTokenizationSupport.tokenize(embeddedModeLine, hasEOL, embeddedModeState, offsetDelta);
this._tokens = this._tokens.concat(nestedResult.tokens); this._tokens = this._tokens.concat(nestedResult.tokens);
this._lastTokenType = null; this._lastTokenType = null;
this._lastTokenLanguage = null; this._lastTokenLanguage = null;
...@@ -345,7 +345,7 @@ class MonarchModernTokensCollector implements IMonarchTokensCollector { ...@@ -345,7 +345,7 @@ class MonarchModernTokensCollector implements IMonarchTokensCollector {
return result; return result;
} }
public nestedModeTokenize(embeddedModeLine: string, embeddedModeData: EmbeddedModeData, offsetDelta: number): modes.IState { public nestedModeTokenize(embeddedModeLine: string, hasEOL: boolean, embeddedModeData: EmbeddedModeData, offsetDelta: number): modes.IState {
const nestedModeId = embeddedModeData.modeId; const nestedModeId = embeddedModeData.modeId;
const embeddedModeState = embeddedModeData.state; const embeddedModeState = embeddedModeData.state;
...@@ -356,7 +356,7 @@ class MonarchModernTokensCollector implements IMonarchTokensCollector { ...@@ -356,7 +356,7 @@ class MonarchModernTokensCollector implements IMonarchTokensCollector {
return embeddedModeState; return embeddedModeState;
} }
let nestedResult = nestedModeTokenizationSupport.tokenize2(embeddedModeLine, embeddedModeState, offsetDelta); let nestedResult = nestedModeTokenizationSupport.tokenize2(embeddedModeLine, hasEOL, embeddedModeState, offsetDelta);
this._prependTokens = MonarchModernTokensCollector._merge(this._prependTokens, this._tokens, nestedResult.tokens); this._prependTokens = MonarchModernTokensCollector._merge(this._prependTokens, this._tokens, nestedResult.tokens);
this._tokens = []; this._tokens = [];
this._currentLanguageId = 0; this._currentLanguageId = 0;
...@@ -456,23 +456,23 @@ export class MonarchTokenizer implements modes.ITokenizationSupport { ...@@ -456,23 +456,23 @@ export class MonarchTokenizer implements modes.ITokenizationSupport {
return MonarchLineStateFactory.create(rootState, null); return MonarchLineStateFactory.create(rootState, null);
} }
public tokenize(line: string, lineState: modes.IState, offsetDelta: number): TokenizationResult { public tokenize(line: string, hasEOL: boolean, lineState: modes.IState, offsetDelta: number): TokenizationResult {
let tokensCollector = new MonarchClassicTokensCollector(); let tokensCollector = new MonarchClassicTokensCollector();
let endLineState = this._tokenize(line, <MonarchLineState>lineState, offsetDelta, tokensCollector); let endLineState = this._tokenize(line, hasEOL, <MonarchLineState>lineState, offsetDelta, tokensCollector);
return tokensCollector.finalize(endLineState); return tokensCollector.finalize(endLineState);
} }
public tokenize2(line: string, lineState: modes.IState, offsetDelta: number): TokenizationResult2 { public tokenize2(line: string, hasEOL: boolean, lineState: modes.IState, offsetDelta: number): TokenizationResult2 {
let tokensCollector = new MonarchModernTokensCollector(this._modeService, this._standaloneThemeService.getColorTheme().tokenTheme); let tokensCollector = new MonarchModernTokensCollector(this._modeService, this._standaloneThemeService.getColorTheme().tokenTheme);
let endLineState = this._tokenize(line, <MonarchLineState>lineState, offsetDelta, tokensCollector); let endLineState = this._tokenize(line, hasEOL, <MonarchLineState>lineState, offsetDelta, tokensCollector);
return tokensCollector.finalize(endLineState); return tokensCollector.finalize(endLineState);
} }
private _tokenize(line: string, lineState: MonarchLineState, offsetDelta: number, collector: IMonarchTokensCollector): MonarchLineState { private _tokenize(line: string, hasEOL: boolean, lineState: MonarchLineState, offsetDelta: number, collector: IMonarchTokensCollector): MonarchLineState {
if (lineState.embeddedModeData) { if (lineState.embeddedModeData) {
return this._nestedTokenize(line, lineState, offsetDelta, collector); return this._nestedTokenize(line, hasEOL, lineState, offsetDelta, collector);
} else { } else {
return this._myTokenize(line, lineState, offsetDelta, collector); return this._myTokenize(line, hasEOL, lineState, offsetDelta, collector);
} }
} }
...@@ -518,24 +518,24 @@ export class MonarchTokenizer implements modes.ITokenizationSupport { ...@@ -518,24 +518,24 @@ export class MonarchTokenizer implements modes.ITokenizationSupport {
return popOffset; return popOffset;
} }
private _nestedTokenize(line: string, lineState: MonarchLineState, offsetDelta: number, tokensCollector: IMonarchTokensCollector): MonarchLineState { private _nestedTokenize(line: string, hasEOL: boolean, lineState: MonarchLineState, offsetDelta: number, tokensCollector: IMonarchTokensCollector): MonarchLineState {
let popOffset = this._findLeavingNestedModeOffset(line, lineState); let popOffset = this._findLeavingNestedModeOffset(line, lineState);
if (popOffset === -1) { if (popOffset === -1) {
// tokenization will not leave nested mode // tokenization will not leave nested mode
let nestedEndState = tokensCollector.nestedModeTokenize(line, lineState.embeddedModeData!, offsetDelta); let nestedEndState = tokensCollector.nestedModeTokenize(line, hasEOL, lineState.embeddedModeData!, offsetDelta);
return MonarchLineStateFactory.create(lineState.stack, new EmbeddedModeData(lineState.embeddedModeData!.modeId, nestedEndState)); return MonarchLineStateFactory.create(lineState.stack, new EmbeddedModeData(lineState.embeddedModeData!.modeId, nestedEndState));
} }
let nestedModeLine = line.substring(0, popOffset); let nestedModeLine = line.substring(0, popOffset);
if (nestedModeLine.length > 0) { if (nestedModeLine.length > 0) {
// tokenize with the nested mode // tokenize with the nested mode
tokensCollector.nestedModeTokenize(nestedModeLine, lineState.embeddedModeData!, offsetDelta); tokensCollector.nestedModeTokenize(nestedModeLine, false, lineState.embeddedModeData!, offsetDelta);
} }
let restOfTheLine = line.substring(popOffset); let restOfTheLine = line.substring(popOffset);
return this._myTokenize(restOfTheLine, lineState, offsetDelta + popOffset, tokensCollector); return this._myTokenize(restOfTheLine, hasEOL, lineState, offsetDelta + popOffset, tokensCollector);
} }
private _safeRuleName(rule: monarchCommon.IRule | null): string { private _safeRuleName(rule: monarchCommon.IRule | null): string {
...@@ -545,9 +545,11 @@ export class MonarchTokenizer implements modes.ITokenizationSupport { ...@@ -545,9 +545,11 @@ export class MonarchTokenizer implements modes.ITokenizationSupport {
return '(unknown)'; return '(unknown)';
} }
private _myTokenize(line: string, lineState: MonarchLineState, offsetDelta: number, tokensCollector: IMonarchTokensCollector): MonarchLineState { private _myTokenize(lineWithoutLF: string, hasEOL: boolean, lineState: MonarchLineState, offsetDelta: number, tokensCollector: IMonarchTokensCollector): MonarchLineState {
tokensCollector.enterMode(offsetDelta, this._modeId); tokensCollector.enterMode(offsetDelta, this._modeId);
const lineWithoutLFLength = lineWithoutLF.length;
const line = (hasEOL && this._lexer.includeLF ? lineWithoutLF + '\n' : lineWithoutLF);
const lineLength = line.length; const lineLength = line.length;
let embeddedModeData = lineState.embeddedModeData; let embeddedModeData = lineState.embeddedModeData;
...@@ -752,8 +754,8 @@ export class MonarchTokenizer implements modes.ITokenizationSupport { ...@@ -752,8 +754,8 @@ export class MonarchTokenizer implements modes.ITokenizationSupport {
if (pos < lineLength) { if (pos < lineLength) {
// there is content from the embedded mode on this line // there is content from the embedded mode on this line
const restOfLine = line.substr(pos); const restOfLine = lineWithoutLF.substr(pos);
return this._nestedTokenize(restOfLine, MonarchLineStateFactory.create(stack, embeddedModeData), offsetDelta + pos, tokensCollector); return this._nestedTokenize(restOfLine, hasEOL, MonarchLineStateFactory.create(stack, embeddedModeData), offsetDelta + pos, tokensCollector);
} else { } else {
return MonarchLineStateFactory.create(stack, embeddedModeData); return MonarchLineStateFactory.create(stack, embeddedModeData);
} }
...@@ -831,7 +833,9 @@ export class MonarchTokenizer implements modes.ITokenizationSupport { ...@@ -831,7 +833,9 @@ export class MonarchTokenizer implements modes.ITokenizationSupport {
tokenType = monarchCommon.sanitize(token); tokenType = monarchCommon.sanitize(token);
} }
tokensCollector.emit(pos0 + offsetDelta, tokenType); if (pos0 < lineWithoutLFLength) {
tokensCollector.emit(pos0 + offsetDelta, tokenType);
}
} }
if (enteringEmbeddedMode !== null) { if (enteringEmbeddedMode !== null) {
......
...@@ -41,6 +41,11 @@ export interface IMonarchLanguage { ...@@ -41,6 +41,11 @@ export interface IMonarchLanguage {
* attach this to every token class (by default '.' + name) * attach this to every token class (by default '.' + name)
*/ */
tokenPostfix?: string; tokenPostfix?: string;
/**
* include line feeds (in the form of a \n character) at the end of lines
* Defaults to false
*/
includeLF?: boolean;
} }
/** /**
......
...@@ -108,10 +108,10 @@ suite('TokenizationSupport2Adapter', () => { ...@@ -108,10 +108,10 @@ suite('TokenizationSupport2Adapter', () => {
const adapter = new TokenizationSupport2Adapter(new MockThemeService(), languageIdentifier, new BadTokensProvider()); const adapter = new TokenizationSupport2Adapter(new MockThemeService(), languageIdentifier, new BadTokensProvider());
const actualClassicTokens = adapter.tokenize('whatever', MockState.INSTANCE, offsetDelta); const actualClassicTokens = adapter.tokenize('whatever', true, MockState.INSTANCE, offsetDelta);
assert.deepEqual(actualClassicTokens.tokens, expectedClassicTokens); assert.deepEqual(actualClassicTokens.tokens, expectedClassicTokens);
const actualModernTokens = adapter.tokenize2('whatever', MockState.INSTANCE, offsetDelta); const actualModernTokens = adapter.tokenize2('whatever', true, MockState.INSTANCE, offsetDelta);
const modernTokens: number[] = []; const modernTokens: number[] = [];
for (let i = 0; i < actualModernTokens.tokens.length; i++) { for (let i = 0; i < actualModernTokens.tokens.length; i++) {
modernTokens[i] = actualModernTokens.tokens[i]; modernTokens[i] = actualModernTokens.tokens[i];
......
...@@ -68,7 +68,7 @@ suite('Monarch', () => { ...@@ -68,7 +68,7 @@ suite('Monarch', () => {
const actualTokens: Token[][] = []; const actualTokens: Token[][] = [];
let state = tokenizer.getInitialState(); let state = tokenizer.getInitialState();
for (const line of lines) { for (const line of lines) {
const result = tokenizer.tokenize(line, state, 0); const result = tokenizer.tokenize(line, true, state, 0);
actualTokens.push(result.tokens); actualTokens.push(result.tokens);
state = result.endState; state = result.endState;
} }
...@@ -143,7 +143,7 @@ suite('Monarch', () => { ...@@ -143,7 +143,7 @@ suite('Monarch', () => {
const actualTokens: Token[][] = []; const actualTokens: Token[][] = [];
let state = tokenizer.getInitialState(); let state = tokenizer.getInitialState();
for (const line of lines) { for (const line of lines) {
const result = tokenizer.tokenize(line, state, 0); const result = tokenizer.tokenize(line, true, state, 0);
actualTokens.push(result.tokens); actualTokens.push(result.tokens);
state = result.endState; state = result.endState;
} }
...@@ -164,4 +164,58 @@ suite('Monarch', () => { ...@@ -164,4 +164,58 @@ suite('Monarch', () => {
]); ]);
}); });
test('microsoft/monaco-editor#2265: Exit a state at end of line', () => {
const modeService = new ModeServiceImpl();
const tokenizer = createMonarchTokenizer(modeService, 'test', {
includeLF: true,
tokenizer: {
root: [
[/^\*/, '', '@inner'],
[/\:\*/, '', '@inner'],
[/[^*:]+/, 'string'],
[/[*:]/, 'string']
],
inner: [
[/\n/, '', '@pop'],
[/\d+/, 'number'],
[/[^\d]+/, '']
]
}
});
const lines = [
`PRINT 10 * 20`,
`*FX200, 3`,
`PRINT 2*3:*FX200, 3`
];
const actualTokens: Token[][] = [];
let state = tokenizer.getInitialState();
for (const line of lines) {
const result = tokenizer.tokenize(line, true, state, 0);
actualTokens.push(result.tokens);
state = result.endState;
}
assert.deepStrictEqual(actualTokens, [
[
new Token(0, 'string.test', 'test'),
],
[
new Token(0, '', 'test'),
new Token(3, 'number.test', 'test'),
new Token(6, '', 'test'),
new Token(8, 'number.test', 'test'),
],
[
new Token(0, 'string.test', 'test'),
new Token(9, '', 'test'),
new Token(13, 'number.test', 'test'),
new Token(16, '', 'test'),
new Token(18, 'number.test', 'test'),
]
]);
});
}); });
...@@ -2406,7 +2406,7 @@ suite('Editor Controller - Regression tests', () => { ...@@ -2406,7 +2406,7 @@ suite('Editor Controller - Regression tests', () => {
const tokenizationSupport: ITokenizationSupport = { const tokenizationSupport: ITokenizationSupport = {
getInitialState: () => NULL_STATE, getInitialState: () => NULL_STATE,
tokenize: undefined!, tokenize: undefined!,
tokenize2: (line: string, state: IState): TokenizationResult2 => { tokenize2: (line: string, hasEOL: boolean, state: IState): TokenizationResult2 => {
return new TokenizationResult2(new Uint32Array(0), state); return new TokenizationResult2(new Uint32Array(0), state);
} }
}; };
......
...@@ -28,7 +28,7 @@ suite('Editor Model - Model Modes 1', () => { ...@@ -28,7 +28,7 @@ suite('Editor Model - Model Modes 1', () => {
const tokenizationSupport: modes.ITokenizationSupport = { const tokenizationSupport: modes.ITokenizationSupport = {
getInitialState: () => NULL_STATE, getInitialState: () => NULL_STATE,
tokenize: undefined!, tokenize: undefined!,
tokenize2: (line: string, state: modes.IState): TokenizationResult2 => { tokenize2: (line: string, hasEOL: boolean, state: modes.IState): TokenizationResult2 => {
calledFor.push(line.charAt(0)); calledFor.push(line.charAt(0));
return new TokenizationResult2(new Uint32Array(0), state); return new TokenizationResult2(new Uint32Array(0), state);
} }
...@@ -181,7 +181,7 @@ suite('Editor Model - Model Modes 2', () => { ...@@ -181,7 +181,7 @@ suite('Editor Model - Model Modes 2', () => {
const tokenizationSupport: modes.ITokenizationSupport = { const tokenizationSupport: modes.ITokenizationSupport = {
getInitialState: () => new ModelState2(''), getInitialState: () => new ModelState2(''),
tokenize: undefined!, tokenize: undefined!,
tokenize2: (line: string, state: modes.IState): TokenizationResult2 => { tokenize2: (line: string, hasEOL: boolean, state: modes.IState): TokenizationResult2 => {
calledFor.push(line); calledFor.push(line);
(<ModelState2>state).prevLineContent = line; (<ModelState2>state).prevLineContent = line;
return new TokenizationResult2(new Uint32Array(0), state); return new TokenizationResult2(new Uint32Array(0), state);
......
...@@ -389,7 +389,7 @@ suite('Editor Model - Words', () => { ...@@ -389,7 +389,7 @@ suite('Editor Model - Words', () => {
this._register(TokenizationRegistry.register(this.getLanguageIdentifier().language, { this._register(TokenizationRegistry.register(this.getLanguageIdentifier().language, {
getInitialState: (): IState => NULL_STATE, getInitialState: (): IState => NULL_STATE,
tokenize: undefined!, tokenize: undefined!,
tokenize2: (line: string, state: IState): TokenizationResult2 => { tokenize2: (line: string, hasEOL: boolean, state: IState): TokenizationResult2 => {
const tokensArr: number[] = []; const tokensArr: number[] = [];
let prevLanguageId: LanguageIdentifier | undefined = undefined; let prevLanguageId: LanguageIdentifier | undefined = undefined;
for (let i = 0; i < line.length; i++) { for (let i = 0; i < line.length; i++) {
......
...@@ -351,7 +351,7 @@ suite('TextModelWithTokens', () => { ...@@ -351,7 +351,7 @@ suite('TextModelWithTokens', () => {
const tokenizationSupport: ITokenizationSupport = { const tokenizationSupport: ITokenizationSupport = {
getInitialState: () => NULL_STATE, getInitialState: () => NULL_STATE,
tokenize: undefined!, tokenize: undefined!,
tokenize2: (line, state) => { tokenize2: (line, hasEOL, state) => {
switch (line) { switch (line) {
case 'function hello() {': { case 'function hello() {': {
const tokens = new Uint32Array([ const tokens = new Uint32Array([
...@@ -446,7 +446,7 @@ suite('TextModelWithTokens regression tests', () => { ...@@ -446,7 +446,7 @@ suite('TextModelWithTokens regression tests', () => {
const tokenizationSupport: ITokenizationSupport = { const tokenizationSupport: ITokenizationSupport = {
getInitialState: () => NULL_STATE, getInitialState: () => NULL_STATE,
tokenize: undefined!, tokenize: undefined!,
tokenize2: (line, state) => { tokenize2: (line, hasEOL, state) => {
let myId = ++_tokenId; let myId = ++_tokenId;
let tokens = new Uint32Array(2); let tokens = new Uint32Array(2);
tokens[0] = 0; tokens[0] = 0;
...@@ -550,7 +550,7 @@ suite('TextModelWithTokens regression tests', () => { ...@@ -550,7 +550,7 @@ suite('TextModelWithTokens regression tests', () => {
const tokenizationSupport: ITokenizationSupport = { const tokenizationSupport: ITokenizationSupport = {
getInitialState: () => NULL_STATE, getInitialState: () => NULL_STATE,
tokenize: undefined!, tokenize: undefined!,
tokenize2: (line, state) => { tokenize2: (line, hasEOL, state) => {
let tokens = new Uint32Array(2); let tokens = new Uint32Array(2);
tokens[0] = 0; tokens[0] = 0;
tokens[1] = ( tokens[1] = (
......
...@@ -287,7 +287,7 @@ class Mode extends MockMode { ...@@ -287,7 +287,7 @@ class Mode extends MockMode {
this._register(TokenizationRegistry.register(this.getId(), { this._register(TokenizationRegistry.register(this.getId(), {
getInitialState: (): IState => null!, getInitialState: (): IState => null!,
tokenize: undefined!, tokenize: undefined!,
tokenize2: (line: string, state: IState): TokenizationResult2 => { tokenize2: (line: string, hasEOL: boolean, state: IState): TokenizationResult2 => {
let tokensArr: number[] = []; let tokensArr: number[] = [];
let prevColor: ColorId = -1; let prevColor: ColorId = -1;
for (let i = 0; i < line.length; i++) { for (let i = 0; i < line.length; i++) {
......
...@@ -333,7 +333,7 @@ suite('SplitLinesCollection', () => { ...@@ -333,7 +333,7 @@ suite('SplitLinesCollection', () => {
const tokenizationSupport: modes.ITokenizationSupport = { const tokenizationSupport: modes.ITokenizationSupport = {
getInitialState: () => NULL_STATE, getInitialState: () => NULL_STATE,
tokenize: undefined!, tokenize: undefined!,
tokenize2: (line: string, state: modes.IState): TokenizationResult2 => { tokenize2: (line: string, hasEOL: boolean, state: modes.IState): TokenizationResult2 => {
let tokens = _tokens[_lineIndex++]; let tokens = _tokens[_lineIndex++];
let result = new Uint32Array(2 * tokens.length); let result = new Uint32Array(2 * tokens.length);
......
...@@ -6449,6 +6449,11 @@ declare namespace monaco.languages { ...@@ -6449,6 +6449,11 @@ declare namespace monaco.languages {
* attach this to every token class (by default '.' + name) * attach this to every token class (by default '.' + name)
*/ */
tokenPostfix?: string; tokenPostfix?: string;
/**
* include line feeds (in the form of a \n character) at the end of lines
* Defaults to false
*/
includeLF?: boolean;
} }
/** /**
......
...@@ -445,11 +445,11 @@ class TMTokenizationSupport implements ITokenizationSupport { ...@@ -445,11 +445,11 @@ class TMTokenizationSupport implements ITokenizationSupport {
return this._actual.getInitialState(); return this._actual.getInitialState();
} }
tokenize(line: string, state: IState, offsetDelta: number): TokenizationResult { tokenize(line: string, hasEOL: boolean, state: IState, offsetDelta: number): TokenizationResult {
throw new Error('Not supported!'); throw new Error('Not supported!');
} }
tokenize2(line: string, state: StackElement, offsetDelta: number): TokenizationResult2 { tokenize2(line: string, hasEOL: boolean, state: StackElement, offsetDelta: number): TokenizationResult2 {
if (offsetDelta !== 0) { if (offsetDelta !== 0) {
throw new Error('Unexpected: offsetDelta should be 0.'); throw new Error('Unexpected: offsetDelta should be 0.');
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册