mirror of
https://github.com/Microsoft/vscode
synced 2024-10-01 08:50:48 +00:00
Remove brackets from comments, strings and regexes before evaluating the indentation (#210641)
* wip * polishing the code * adding code * adding the language * reshuffling the code to avoid cyclic dependency * polihsing code * uncommenting tests * also adopting the indentation rules within the reindentation operation * using instead the sliced line tokens instead of the scoped line tokens * polishing the code * using start indices instead * using value everywhere * using the token data to type the tokens * setting to number instead of standard token type * using token data from autoindenttest.ts * using same code in both test files * placing instantiation service into the registerLanguage method * copying object into the node js autoindent.ts
This commit is contained in:
parent
e3b7a27662
commit
d309e11579
|
@ -111,10 +111,10 @@
|
|||
},
|
||||
"indentationRules": {
|
||||
"decreaseIndentPattern": {
|
||||
"pattern": "^((?!.*?/\\*).*\\*\/)?\\s*[\\}\\]\\)].*$"
|
||||
"pattern": "^\\s*[\\}\\]\\)].*$"
|
||||
},
|
||||
"increaseIndentPattern": {
|
||||
"pattern": "^((?!//).)*(\\{([^}\"'`/]*|(\\t|[ ])*//.*)|\\([^)\"'`/]*|\\[[^\\]\"'`/]*)$"
|
||||
"pattern": "^.*(\\{[^}]*|\\([^)]*|\\[[^\\]]*)$"
|
||||
},
|
||||
// e.g. * ...| or */| or *-----*/|
|
||||
"unIndentedLinePattern": {
|
||||
|
|
|
@ -129,10 +129,10 @@
|
|||
},
|
||||
"indentationRules": {
|
||||
"decreaseIndentPattern": {
|
||||
"pattern": "^((?!.*?/\\*).*\\*\/)?\\s*[\\}\\]\\)].*$"
|
||||
"pattern": "^\\s*[\\}\\]\\)].*$"
|
||||
},
|
||||
"increaseIndentPattern": {
|
||||
"pattern": "^((?!//).)*(\\{([^}\"'`/]*|(\\t|[ ])*//.*)|\\([^)\"'`/]*|\\[[^\\]\"'`/]*)$"
|
||||
"pattern": "^.*(\\{[^}]*|\\([^)]*|\\[[^\\]]*)$"
|
||||
},
|
||||
// e.g. * ...| or */| or *-----*/|
|
||||
"unIndentedLinePattern": {
|
||||
|
|
|
@ -7,17 +7,18 @@ import * as strings from 'vs/base/common/strings';
|
|||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IndentAction } from 'vs/editor/common/languages/languageConfiguration';
|
||||
import { createScopedLineTokens } from 'vs/editor/common/languages/supports';
|
||||
import { IndentConsts, IndentRulesSupport } from 'vs/editor/common/languages/supports/indentRules';
|
||||
import { IndentConsts } from 'vs/editor/common/languages/supports/indentRules';
|
||||
import { EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions';
|
||||
import { getScopedLineTokens, ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry';
|
||||
import { LineTokens } from 'vs/editor/common/tokens/lineTokens';
|
||||
import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry';
|
||||
import { IViewLineTokens } from 'vs/editor/common/tokens/lineTokens';
|
||||
import { IndentationContextProcessor, isLanguageDifferentFromLineStart, ProcessedIndentRulesSupport } from 'vs/editor/common/languages/supports/indentationLineProcessor';
|
||||
|
||||
export interface IVirtualModel {
|
||||
tokenization: {
|
||||
getLineTokens(lineNumber: number): LineTokens;
|
||||
getLineTokens(lineNumber: number): IViewLineTokens;
|
||||
getLanguageId(): string;
|
||||
getLanguageIdAtPosition(lineNumber: number, column: number): string;
|
||||
forceTokenization?(lineNumber: number): void;
|
||||
};
|
||||
getLineContent(lineNumber: number): string;
|
||||
}
|
||||
|
@ -35,7 +36,7 @@ export interface IIndentConverter {
|
|||
* 0: every line above are invalid
|
||||
* else: nearest preceding line of the same language
|
||||
*/
|
||||
function getPrecedingValidLine(model: IVirtualModel, lineNumber: number, indentRulesSupport: IndentRulesSupport) {
|
||||
function getPrecedingValidLine(model: IVirtualModel, lineNumber: number, processedIndentRulesSupport: ProcessedIndentRulesSupport) {
|
||||
const languageId = model.tokenization.getLanguageIdAtPosition(lineNumber, 0);
|
||||
if (lineNumber > 1) {
|
||||
let lastLineNumber: number;
|
||||
|
@ -46,7 +47,7 @@ function getPrecedingValidLine(model: IVirtualModel, lineNumber: number, indentR
|
|||
return resultLineNumber;
|
||||
}
|
||||
const text = model.getLineContent(lastLineNumber);
|
||||
if (indentRulesSupport.shouldIgnore(text) || /^\s+$/.test(text) || text === '') {
|
||||
if (processedIndentRulesSupport.shouldIgnore(lastLineNumber) || /^\s+$/.test(text) || text === '') {
|
||||
resultLineNumber = lastLineNumber;
|
||||
continue;
|
||||
}
|
||||
|
@ -85,6 +86,7 @@ export function getInheritIndentForLine(
|
|||
if (!indentRulesSupport) {
|
||||
return null;
|
||||
}
|
||||
const processedIndentRulesSupport = new ProcessedIndentRulesSupport(model, indentRulesSupport, languageConfigurationService);
|
||||
|
||||
if (lineNumber <= 1) {
|
||||
return {
|
||||
|
@ -106,7 +108,7 @@ export function getInheritIndentForLine(
|
|||
}
|
||||
}
|
||||
|
||||
const precedingUnIgnoredLine = getPrecedingValidLine(model, lineNumber, indentRulesSupport);
|
||||
const precedingUnIgnoredLine = getPrecedingValidLine(model, lineNumber, processedIndentRulesSupport);
|
||||
if (precedingUnIgnoredLine < 0) {
|
||||
return null;
|
||||
} else if (precedingUnIgnoredLine < 1) {
|
||||
|
@ -116,14 +118,15 @@ export function getInheritIndentForLine(
|
|||
};
|
||||
}
|
||||
|
||||
if (processedIndentRulesSupport.shouldIncrease(precedingUnIgnoredLine) || processedIndentRulesSupport.shouldIndentNextLine(precedingUnIgnoredLine)) {
|
||||
const precedingUnIgnoredLineContent = model.getLineContent(precedingUnIgnoredLine);
|
||||
if (indentRulesSupport.shouldIncrease(precedingUnIgnoredLineContent) || indentRulesSupport.shouldIndentNextLine(precedingUnIgnoredLineContent)) {
|
||||
return {
|
||||
indentation: strings.getLeadingWhitespace(precedingUnIgnoredLineContent),
|
||||
action: IndentAction.Indent,
|
||||
line: precedingUnIgnoredLine
|
||||
};
|
||||
} else if (indentRulesSupport.shouldDecrease(precedingUnIgnoredLineContent)) {
|
||||
} else if (processedIndentRulesSupport.shouldDecrease(precedingUnIgnoredLine)) {
|
||||
const precedingUnIgnoredLineContent = model.getLineContent(precedingUnIgnoredLine);
|
||||
return {
|
||||
indentation: strings.getLeadingWhitespace(precedingUnIgnoredLineContent),
|
||||
action: null,
|
||||
|
@ -150,7 +153,7 @@ export function getInheritIndentForLine(
|
|||
(previousLineIndentMetadata & IndentConsts.INDENT_NEXTLINE_MASK)) {
|
||||
let stopLine = 0;
|
||||
for (let i = previousLine - 1; i > 0; i--) {
|
||||
if (indentRulesSupport.shouldIndentNextLine(model.getLineContent(i))) {
|
||||
if (processedIndentRulesSupport.shouldIndentNextLine(i)) {
|
||||
continue;
|
||||
}
|
||||
stopLine = i;
|
||||
|
@ -173,17 +176,16 @@ export function getInheritIndentForLine(
|
|||
} else {
|
||||
// search from precedingUnIgnoredLine until we find one whose indent is not temporary
|
||||
for (let i = precedingUnIgnoredLine; i > 0; i--) {
|
||||
const lineContent = model.getLineContent(i);
|
||||
if (indentRulesSupport.shouldIncrease(lineContent)) {
|
||||
if (processedIndentRulesSupport.shouldIncrease(i)) {
|
||||
return {
|
||||
indentation: strings.getLeadingWhitespace(lineContent),
|
||||
indentation: strings.getLeadingWhitespace(model.getLineContent(i)),
|
||||
action: IndentAction.Indent,
|
||||
line: i
|
||||
};
|
||||
} else if (indentRulesSupport.shouldIndentNextLine(lineContent)) {
|
||||
} else if (processedIndentRulesSupport.shouldIndentNextLine(i)) {
|
||||
let stopLine = 0;
|
||||
for (let j = i - 1; j > 0; j--) {
|
||||
if (indentRulesSupport.shouldIndentNextLine(model.getLineContent(i))) {
|
||||
if (processedIndentRulesSupport.shouldIndentNextLine(i)) {
|
||||
continue;
|
||||
}
|
||||
stopLine = j;
|
||||
|
@ -195,9 +197,9 @@ export function getInheritIndentForLine(
|
|||
action: null,
|
||||
line: stopLine + 1
|
||||
};
|
||||
} else if (indentRulesSupport.shouldDecrease(lineContent)) {
|
||||
} else if (processedIndentRulesSupport.shouldDecrease(i)) {
|
||||
return {
|
||||
indentation: strings.getLeadingWhitespace(lineContent),
|
||||
indentation: strings.getLeadingWhitespace(model.getLineContent(i)),
|
||||
action: null,
|
||||
line: i
|
||||
};
|
||||
|
@ -235,8 +237,8 @@ export function getGoodIndentForLine(
|
|||
return null;
|
||||
}
|
||||
|
||||
const processedIndentRulesSupport = new ProcessedIndentRulesSupport(virtualModel, indentRulesSupport, languageConfigurationService);
|
||||
const indent = getInheritIndentForLine(autoIndent, virtualModel, lineNumber, undefined, languageConfigurationService);
|
||||
const lineContent = virtualModel.getLineContent(lineNumber);
|
||||
|
||||
if (indent) {
|
||||
const inheritLine = indent.line;
|
||||
|
@ -268,7 +270,7 @@ export function getGoodIndentForLine(
|
|||
indentation = indentConverter.unshiftIndent(indentation);
|
||||
}
|
||||
|
||||
if (indentRulesSupport.shouldDecrease(lineContent)) {
|
||||
if (processedIndentRulesSupport.shouldDecrease(lineNumber)) {
|
||||
indentation = indentConverter.unshiftIndent(indentation);
|
||||
}
|
||||
|
||||
|
@ -281,7 +283,7 @@ export function getGoodIndentForLine(
|
|||
}
|
||||
}
|
||||
|
||||
if (indentRulesSupport.shouldDecrease(lineContent)) {
|
||||
if (processedIndentRulesSupport.shouldDecrease(lineNumber)) {
|
||||
if (indent.action === IndentAction.Indent) {
|
||||
return indent.indentation;
|
||||
} else {
|
||||
|
@ -308,80 +310,44 @@ export function getIndentForEnter(
|
|||
if (autoIndent < EditorAutoIndentStrategy.Full) {
|
||||
return null;
|
||||
}
|
||||
model.tokenization.forceTokenization(range.startLineNumber);
|
||||
const lineTokens = model.tokenization.getLineTokens(range.startLineNumber);
|
||||
const scopedLineTokens = createScopedLineTokens(lineTokens, range.startColumn - 1);
|
||||
const scopedLineText = scopedLineTokens.getLineContent();
|
||||
|
||||
let embeddedLanguage = false;
|
||||
let beforeEnterText: string;
|
||||
if (scopedLineTokens.firstCharOffset > 0 && lineTokens.getLanguageId(0) !== scopedLineTokens.languageId) {
|
||||
// we are in the embeded language content
|
||||
embeddedLanguage = true; // if embeddedLanguage is true, then we don't touch the indentation of current line
|
||||
beforeEnterText = scopedLineText.substr(0, range.startColumn - 1 - scopedLineTokens.firstCharOffset);
|
||||
} else {
|
||||
beforeEnterText = lineTokens.getLineContent().substring(0, range.startColumn - 1);
|
||||
}
|
||||
|
||||
let afterEnterText: string;
|
||||
if (range.isEmpty()) {
|
||||
afterEnterText = scopedLineText.substr(range.startColumn - 1 - scopedLineTokens.firstCharOffset);
|
||||
} else {
|
||||
const endScopedLineTokens = getScopedLineTokens(model, range.endLineNumber, range.endColumn);
|
||||
afterEnterText = endScopedLineTokens.getLineContent().substr(range.endColumn - 1 - scopedLineTokens.firstCharOffset);
|
||||
}
|
||||
|
||||
const indentRulesSupport = languageConfigurationService.getLanguageConfiguration(scopedLineTokens.languageId).indentRulesSupport;
|
||||
const languageId = model.getLanguageIdAtPosition(range.startLineNumber, range.startColumn);
|
||||
const indentRulesSupport = languageConfigurationService.getLanguageConfiguration(languageId).indentRulesSupport;
|
||||
if (!indentRulesSupport) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const beforeEnterResult = beforeEnterText;
|
||||
const beforeEnterIndent = strings.getLeadingWhitespace(beforeEnterText);
|
||||
model.tokenization.forceTokenization(range.startLineNumber);
|
||||
const indentationContextProcessor = new IndentationContextProcessor(model, languageConfigurationService);
|
||||
const processedContextTokens = indentationContextProcessor.getProcessedTokenContextAroundRange(range);
|
||||
const afterEnterProcessedTokens = processedContextTokens.afterRangeProcessedTokens;
|
||||
const beforeEnterProcessedTokens = processedContextTokens.beforeRangeProcessedTokens;
|
||||
const beforeEnterIndent = strings.getLeadingWhitespace(beforeEnterProcessedTokens.getLineContent());
|
||||
|
||||
const virtualModel: IVirtualModel = {
|
||||
tokenization: {
|
||||
getLineTokens: (lineNumber: number) => {
|
||||
return model.tokenization.getLineTokens(lineNumber);
|
||||
},
|
||||
getLanguageId: () => {
|
||||
return model.getLanguageId();
|
||||
},
|
||||
getLanguageIdAtPosition: (lineNumber: number, column: number) => {
|
||||
return model.getLanguageIdAtPosition(lineNumber, column);
|
||||
},
|
||||
},
|
||||
getLineContent: (lineNumber: number) => {
|
||||
if (lineNumber === range.startLineNumber) {
|
||||
return beforeEnterResult;
|
||||
} else {
|
||||
return model.getLineContent(lineNumber);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const currentLineIndent = strings.getLeadingWhitespace(lineTokens.getLineContent());
|
||||
const virtualModel = createVirtualModelWithModifiedTokensAtLine(model, range.startLineNumber, beforeEnterProcessedTokens);
|
||||
const languageIsDifferentFromLineStart = isLanguageDifferentFromLineStart(model, range.getStartPosition());
|
||||
const currentLine = model.getLineContent(range.startLineNumber);
|
||||
const currentLineIndent = strings.getLeadingWhitespace(currentLine);
|
||||
const afterEnterAction = getInheritIndentForLine(autoIndent, virtualModel, range.startLineNumber + 1, undefined, languageConfigurationService);
|
||||
if (!afterEnterAction) {
|
||||
const beforeEnter = embeddedLanguage ? currentLineIndent : beforeEnterIndent;
|
||||
const beforeEnter = languageIsDifferentFromLineStart ? currentLineIndent : beforeEnterIndent;
|
||||
return {
|
||||
beforeEnter: beforeEnter,
|
||||
afterEnter: beforeEnter
|
||||
};
|
||||
}
|
||||
|
||||
let afterEnterIndent = embeddedLanguage ? currentLineIndent : afterEnterAction.indentation;
|
||||
let afterEnterIndent = languageIsDifferentFromLineStart ? currentLineIndent : afterEnterAction.indentation;
|
||||
|
||||
if (afterEnterAction.action === IndentAction.Indent) {
|
||||
afterEnterIndent = indentConverter.shiftIndent(afterEnterIndent);
|
||||
}
|
||||
|
||||
if (indentRulesSupport.shouldDecrease(afterEnterText)) {
|
||||
if (indentRulesSupport.shouldDecrease(afterEnterProcessedTokens.getLineContent())) {
|
||||
afterEnterIndent = indentConverter.unshiftIndent(afterEnterIndent);
|
||||
}
|
||||
|
||||
return {
|
||||
beforeEnter: embeddedLanguage ? currentLineIndent : beforeEnterIndent,
|
||||
beforeEnter: languageIsDifferentFromLineStart ? currentLineIndent : beforeEnterIndent,
|
||||
afterEnter: afterEnterIndent
|
||||
};
|
||||
}
|
||||
|
@ -401,33 +367,28 @@ export function getIndentActionForType(
|
|||
if (autoIndent < EditorAutoIndentStrategy.Full) {
|
||||
return null;
|
||||
}
|
||||
const scopedLineTokens = getScopedLineTokens(model, range.startLineNumber, range.startColumn);
|
||||
|
||||
if (scopedLineTokens.firstCharOffset) {
|
||||
const languageIsDifferentFromLineStart = isLanguageDifferentFromLineStart(model, range.getStartPosition());
|
||||
if (languageIsDifferentFromLineStart) {
|
||||
// this line has mixed languages and indentation rules will not work
|
||||
return null;
|
||||
}
|
||||
|
||||
const indentRulesSupport = languageConfigurationService.getLanguageConfiguration(scopedLineTokens.languageId).indentRulesSupport;
|
||||
const languageId = model.getLanguageIdAtPosition(range.startLineNumber, range.startColumn);
|
||||
const indentRulesSupport = languageConfigurationService.getLanguageConfiguration(languageId).indentRulesSupport;
|
||||
if (!indentRulesSupport) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const scopedLineText = scopedLineTokens.getLineContent();
|
||||
const beforeTypeText = scopedLineText.substr(0, range.startColumn - 1 - scopedLineTokens.firstCharOffset);
|
||||
|
||||
// selection support
|
||||
let afterTypeText: string;
|
||||
if (range.isEmpty()) {
|
||||
afterTypeText = scopedLineText.substr(range.startColumn - 1 - scopedLineTokens.firstCharOffset);
|
||||
} else {
|
||||
const endScopedLineTokens = getScopedLineTokens(model, range.endLineNumber, range.endColumn);
|
||||
afterTypeText = endScopedLineTokens.getLineContent().substr(range.endColumn - 1 - scopedLineTokens.firstCharOffset);
|
||||
}
|
||||
const indentationContextProcessor = new IndentationContextProcessor(model, languageConfigurationService);
|
||||
const processedContextTokens = indentationContextProcessor.getProcessedTokenContextAroundRange(range);
|
||||
const beforeRangeText = processedContextTokens.beforeRangeProcessedTokens.getLineContent();
|
||||
const afterRangeText = processedContextTokens.afterRangeProcessedTokens.getLineContent();
|
||||
const textAroundRange = beforeRangeText + afterRangeText;
|
||||
const textAroundRangeWithCharacter = beforeRangeText + ch + afterRangeText;
|
||||
|
||||
// If previous content already matches decreaseIndentPattern, it means indentation of this line should already be adjusted
|
||||
// Users might change the indentation by purpose and we should honor that instead of readjusting.
|
||||
if (!indentRulesSupport.shouldDecrease(beforeTypeText + afterTypeText) && indentRulesSupport.shouldDecrease(beforeTypeText + ch + afterTypeText)) {
|
||||
if (!indentRulesSupport.shouldDecrease(textAroundRange) && indentRulesSupport.shouldDecrease(textAroundRangeWithCharacter)) {
|
||||
// after typing `ch`, the content matches decreaseIndentPattern, we should adjust the indent to a good manner.
|
||||
// 1. Get inherited indent action
|
||||
const r = getInheritIndentForLine(autoIndent, model, range.startLineNumber, false, languageConfigurationService);
|
||||
|
@ -460,3 +421,32 @@ export function getIndentMetadata(
|
|||
}
|
||||
return indentRulesSupport.getIndentMetadata(model.getLineContent(lineNumber));
|
||||
}
|
||||
|
||||
function createVirtualModelWithModifiedTokensAtLine(model: ITextModel, modifiedLineNumber: number, modifiedTokens: IViewLineTokens): IVirtualModel {
|
||||
const virtualModel: IVirtualModel = {
|
||||
tokenization: {
|
||||
getLineTokens: (lineNumber: number): IViewLineTokens => {
|
||||
if (lineNumber === modifiedLineNumber) {
|
||||
return modifiedTokens;
|
||||
} else {
|
||||
return model.tokenization.getLineTokens(lineNumber);
|
||||
}
|
||||
},
|
||||
getLanguageId: (): string => {
|
||||
return model.getLanguageId();
|
||||
},
|
||||
getLanguageIdAtPosition: (lineNumber: number, column: number): string => {
|
||||
return model.getLanguageIdAtPosition(lineNumber, column);
|
||||
},
|
||||
},
|
||||
getLineContent: (lineNumber: number): string => {
|
||||
if (lineNumber === modifiedLineNumber) {
|
||||
return modifiedTokens.getLineContent();
|
||||
} else {
|
||||
return model.getLineContent(lineNumber);
|
||||
}
|
||||
}
|
||||
};
|
||||
return virtualModel;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@ import { Range } from 'vs/editor/common/core/range';
|
|||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IndentAction, CompleteEnterAction } from 'vs/editor/common/languages/languageConfiguration';
|
||||
import { EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions';
|
||||
import { getIndentationAtPosition, getScopedLineTokens, ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry';
|
||||
import { getIndentationAtPosition, ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry';
|
||||
import { IndentationContextProcessor } from 'vs/editor/common/languages/supports/indentationLineProcessor';
|
||||
|
||||
export function getEnterAction(
|
||||
autoIndent: EditorAutoIndentStrategy,
|
||||
|
@ -15,33 +16,17 @@ export function getEnterAction(
|
|||
range: Range,
|
||||
languageConfigurationService: ILanguageConfigurationService
|
||||
): CompleteEnterAction | null {
|
||||
const scopedLineTokens = getScopedLineTokens(model, range.startLineNumber, range.startColumn);
|
||||
const richEditSupport = languageConfigurationService.getLanguageConfiguration(scopedLineTokens.languageId);
|
||||
model.tokenization.forceTokenization(range.startLineNumber);
|
||||
const languageId = model.getLanguageIdAtPosition(range.startLineNumber, range.startColumn);
|
||||
const richEditSupport = languageConfigurationService.getLanguageConfiguration(languageId);
|
||||
if (!richEditSupport) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const scopedLineText = scopedLineTokens.getLineContent();
|
||||
const beforeEnterText = scopedLineText.substr(0, range.startColumn - 1 - scopedLineTokens.firstCharOffset);
|
||||
|
||||
// selection support
|
||||
let afterEnterText: string;
|
||||
if (range.isEmpty()) {
|
||||
afterEnterText = scopedLineText.substr(range.startColumn - 1 - scopedLineTokens.firstCharOffset);
|
||||
} else {
|
||||
const endScopedLineTokens = getScopedLineTokens(model, range.endLineNumber, range.endColumn);
|
||||
afterEnterText = endScopedLineTokens.getLineContent().substr(range.endColumn - 1 - scopedLineTokens.firstCharOffset);
|
||||
}
|
||||
|
||||
let previousLineText = '';
|
||||
if (range.startLineNumber > 1 && scopedLineTokens.firstCharOffset === 0) {
|
||||
// This is not the first line and the entire line belongs to this mode
|
||||
const oneLineAboveScopedLineTokens = getScopedLineTokens(model, range.startLineNumber - 1);
|
||||
if (oneLineAboveScopedLineTokens.languageId === scopedLineTokens.languageId) {
|
||||
// The line above ends with text belonging to the same mode
|
||||
previousLineText = oneLineAboveScopedLineTokens.getLineContent();
|
||||
}
|
||||
}
|
||||
const indentationContextProcessor = new IndentationContextProcessor(model, languageConfigurationService);
|
||||
const processedContextTokens = indentationContextProcessor.getProcessedTokenContextAroundRange(range);
|
||||
const previousLineText = processedContextTokens.previousLineProcessedTokens.getLineContent();
|
||||
const beforeEnterText = processedContextTokens.beforeRangeProcessedTokens.getLineContent();
|
||||
const afterEnterText = processedContextTokens.afterRangeProcessedTokens.getLineContent();
|
||||
|
||||
const enterResult = richEditSupport.onEnter(autoIndent, previousLineText, beforeEnterText, afterEnterText);
|
||||
if (!enterResult) {
|
||||
|
|
|
@ -9,7 +9,6 @@ import * as strings from 'vs/base/common/strings';
|
|||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { DEFAULT_WORD_REGEXP, ensureValidWordDefinition } from 'vs/editor/common/core/wordHelper';
|
||||
import { EnterAction, FoldingRules, IAutoClosingPair, IndentationRule, LanguageConfiguration, AutoClosingPairs, CharacterPair, ExplicitLanguageConfiguration } from 'vs/editor/common/languages/languageConfiguration';
|
||||
import { createScopedLineTokens, ScopedLineTokens } from 'vs/editor/common/languages/supports';
|
||||
import { CharacterPairSupport } from 'vs/editor/common/languages/supports/characterPair';
|
||||
import { BracketElectricCharacterSupport } from 'vs/editor/common/languages/supports/electricCharacter';
|
||||
import { IndentRulesSupport } from 'vs/editor/common/languages/supports/indentRules';
|
||||
|
@ -181,13 +180,6 @@ export function getIndentationAtPosition(model: ITextModel, lineNumber: number,
|
|||
return indentation;
|
||||
}
|
||||
|
||||
export function getScopedLineTokens(model: ITextModel, lineNumber: number, columnNumber?: number): ScopedLineTokens {
|
||||
model.tokenization.forceTokenization(lineNumber);
|
||||
const lineTokens = model.tokenization.getLineTokens(lineNumber);
|
||||
const column = (typeof columnNumber === 'undefined' ? model.getLineMaxColumn(lineNumber) - 1 : columnNumber - 1);
|
||||
return createScopedLineTokens(lineTokens, column);
|
||||
}
|
||||
|
||||
class ComposedLanguageConfiguration {
|
||||
private readonly _entries: LanguageConfigurationContribution[];
|
||||
private _order: number;
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { LineTokens } from 'vs/editor/common/tokens/lineTokens';
|
||||
import { IViewLineTokens, LineTokens } from 'vs/editor/common/tokens/lineTokens';
|
||||
import { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes';
|
||||
import { ILanguageIdCodec } from 'vs/editor/common/languages';
|
||||
|
||||
export function createScopedLineTokens(context: LineTokens, offset: number): ScopedLineTokens {
|
||||
const tokenCount = context.getCount();
|
||||
|
@ -34,6 +35,7 @@ export function createScopedLineTokens(context: LineTokens, offset: number): Sco
|
|||
export class ScopedLineTokens {
|
||||
_scopedLineTokensBrand: void = undefined;
|
||||
|
||||
public readonly languageIdCodec: ILanguageIdCodec;
|
||||
public readonly languageId: string;
|
||||
private readonly _actual: LineTokens;
|
||||
private readonly _firstTokenIndex: number;
|
||||
|
@ -55,6 +57,7 @@ export class ScopedLineTokens {
|
|||
this._lastTokenIndex = lastTokenIndex;
|
||||
this.firstCharOffset = firstCharOffset;
|
||||
this._lastCharOffset = lastCharOffset;
|
||||
this.languageIdCodec = actual.languageIdCodec;
|
||||
}
|
||||
|
||||
public getLineContent(): string {
|
||||
|
@ -62,6 +65,10 @@ export class ScopedLineTokens {
|
|||
return actualLineContent.substring(this.firstCharOffset, this._lastCharOffset);
|
||||
}
|
||||
|
||||
public getLineLength(): number {
|
||||
return this._lastCharOffset - this.firstCharOffset;
|
||||
}
|
||||
|
||||
public getActualLineContentBefore(offset: number): string {
|
||||
const actualLineContent = this._actual.getLineContent();
|
||||
return actualLineContent.substring(0, this.firstCharOffset + offset);
|
||||
|
@ -78,6 +85,10 @@ export class ScopedLineTokens {
|
|||
public getStandardTokenType(tokenIndex: number): StandardTokenType {
|
||||
return this._actual.getStandardTokenType(tokenIndex + this._firstTokenIndex);
|
||||
}
|
||||
|
||||
public toIViewLineTokens(): IViewLineTokens {
|
||||
return this._actual.sliceAndInflate(this.firstCharOffset, this._lastCharOffset, 0);
|
||||
}
|
||||
}
|
||||
|
||||
const enum IgnoreBracketsInTokens {
|
||||
|
|
|
@ -0,0 +1,236 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry';
|
||||
import { createScopedLineTokens, ScopedLineTokens } from 'vs/editor/common/languages/supports';
|
||||
import { IVirtualModel } from 'vs/editor/common/languages/autoIndent';
|
||||
import { IViewLineTokens, LineTokens } from 'vs/editor/common/tokens/lineTokens';
|
||||
import { IndentRulesSupport } from 'vs/editor/common/languages/supports/indentRules';
|
||||
import { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
|
||||
/**
|
||||
* This class is a wrapper class around {@link IndentRulesSupport}.
|
||||
* It processes the lines by removing the language configuration brackets from the regex, string and comment tokens.
|
||||
* It then calls into the {@link IndentRulesSupport} to validate the indentation conditions.
|
||||
*/
|
||||
export class ProcessedIndentRulesSupport {
|
||||
|
||||
private readonly _indentRulesSupport: IndentRulesSupport;
|
||||
private readonly _indentationLineProcessor: IndentationLineProcessor;
|
||||
|
||||
constructor(
|
||||
model: IVirtualModel,
|
||||
indentRulesSupport: IndentRulesSupport,
|
||||
languageConfigurationService: ILanguageConfigurationService
|
||||
) {
|
||||
this._indentRulesSupport = indentRulesSupport;
|
||||
this._indentationLineProcessor = new IndentationLineProcessor(model, languageConfigurationService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the new indentation and return whether the indentation level should be increased after the given line number
|
||||
*/
|
||||
public shouldIncrease(lineNumber: number, newIndentation?: string): boolean {
|
||||
const processedLine = this._indentationLineProcessor.getProcessedLine(lineNumber, newIndentation);
|
||||
return this._indentRulesSupport.shouldIncrease(processedLine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the new indentation and return whether the indentation level should be decreased after the given line number
|
||||
*/
|
||||
public shouldDecrease(lineNumber: number, newIndentation?: string): boolean {
|
||||
const processedLine = this._indentationLineProcessor.getProcessedLine(lineNumber, newIndentation);
|
||||
return this._indentRulesSupport.shouldDecrease(processedLine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the new indentation and return whether the indentation level should remain unchanged at the given line number
|
||||
*/
|
||||
public shouldIgnore(lineNumber: number, newIndentation?: string): boolean {
|
||||
const processedLine = this._indentationLineProcessor.getProcessedLine(lineNumber, newIndentation);
|
||||
return this._indentRulesSupport.shouldIgnore(processedLine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the new indentation and return whether the indentation level should increase on the line after the given line number
|
||||
*/
|
||||
public shouldIndentNextLine(lineNumber: number, newIndentation?: string): boolean {
|
||||
const processedLine = this._indentationLineProcessor.getProcessedLine(lineNumber, newIndentation);
|
||||
return this._indentRulesSupport.shouldIndentNextLine(processedLine);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This class fetches the processed text around a range which can be used for indentation evaluation.
|
||||
* It returns:
|
||||
* - The processed text before the given range and on the same start line
|
||||
* - The processed text after the given range and on the same end line
|
||||
* - The processed text on the previous line
|
||||
*/
|
||||
export class IndentationContextProcessor {
|
||||
|
||||
private readonly model: ITextModel;
|
||||
private readonly indentationLineProcessor: IndentationLineProcessor;
|
||||
|
||||
constructor(
|
||||
model: ITextModel,
|
||||
languageConfigurationService: ILanguageConfigurationService
|
||||
) {
|
||||
this.model = model;
|
||||
this.indentationLineProcessor = new IndentationLineProcessor(model, languageConfigurationService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the processed text, stripped from the language configuration brackets within the string, comment and regex tokens, around the given range
|
||||
*/
|
||||
getProcessedTokenContextAroundRange(range: Range): {
|
||||
beforeRangeProcessedTokens: IViewLineTokens;
|
||||
afterRangeProcessedTokens: IViewLineTokens;
|
||||
previousLineProcessedTokens: IViewLineTokens;
|
||||
} {
|
||||
const beforeRangeProcessedTokens = this._getProcessedTokensBeforeRange(range);
|
||||
const afterRangeProcessedTokens = this._getProcessedTokensAfterRange(range);
|
||||
const previousLineProcessedTokens = this._getProcessedPreviousLineTokens(range);
|
||||
return { beforeRangeProcessedTokens, afterRangeProcessedTokens, previousLineProcessedTokens };
|
||||
}
|
||||
|
||||
private _getProcessedTokensBeforeRange(range: Range): IViewLineTokens {
|
||||
this.model.tokenization.forceTokenization(range.startLineNumber);
|
||||
const lineTokens = this.model.tokenization.getLineTokens(range.startLineNumber);
|
||||
const scopedLineTokens = createScopedLineTokens(lineTokens, range.startColumn - 1);
|
||||
let slicedTokens: IViewLineTokens;
|
||||
if (isLanguageDifferentFromLineStart(this.model, range.getStartPosition())) {
|
||||
const columnIndexWithinScope = (range.startColumn - 1) - scopedLineTokens.firstCharOffset;
|
||||
const firstCharacterOffset = scopedLineTokens.firstCharOffset;
|
||||
const lastCharacterOffset = firstCharacterOffset + columnIndexWithinScope;
|
||||
slicedTokens = lineTokens.sliceAndInflate(firstCharacterOffset, lastCharacterOffset, 0);
|
||||
} else {
|
||||
const columnWithinLine = range.startColumn - 1;
|
||||
slicedTokens = lineTokens.sliceAndInflate(0, columnWithinLine, 0);
|
||||
}
|
||||
const processedTokens = this.indentationLineProcessor.getProcessedTokens(slicedTokens);
|
||||
return processedTokens;
|
||||
}
|
||||
|
||||
private _getProcessedTokensAfterRange(range: Range): IViewLineTokens {
|
||||
const position: Position = range.isEmpty() ? range.getStartPosition() : range.getEndPosition();
|
||||
this.model.tokenization.forceTokenization(position.lineNumber);
|
||||
const lineTokens = this.model.tokenization.getLineTokens(position.lineNumber);
|
||||
const scopedLineTokens = createScopedLineTokens(lineTokens, position.column - 1);
|
||||
const columnIndexWithinScope = position.column - 1 - scopedLineTokens.firstCharOffset;
|
||||
const firstCharacterOffset = scopedLineTokens.firstCharOffset + columnIndexWithinScope;
|
||||
const lastCharacterOffset = scopedLineTokens.firstCharOffset + scopedLineTokens.getLineLength();
|
||||
const slicedTokens = lineTokens.sliceAndInflate(firstCharacterOffset, lastCharacterOffset, 0);
|
||||
const processedTokens = this.indentationLineProcessor.getProcessedTokens(slicedTokens);
|
||||
return processedTokens;
|
||||
}
|
||||
|
||||
private _getProcessedPreviousLineTokens(range: Range): IViewLineTokens {
|
||||
const getScopedLineTokensAtEndColumnOfLine = (lineNumber: number): ScopedLineTokens => {
|
||||
this.model.tokenization.forceTokenization(lineNumber);
|
||||
const lineTokens = this.model.tokenization.getLineTokens(lineNumber);
|
||||
const endColumnOfLine = this.model.getLineMaxColumn(lineNumber) - 1;
|
||||
const scopedLineTokensAtEndColumn = createScopedLineTokens(lineTokens, endColumnOfLine);
|
||||
return scopedLineTokensAtEndColumn;
|
||||
};
|
||||
|
||||
this.model.tokenization.forceTokenization(range.startLineNumber);
|
||||
const lineTokens = this.model.tokenization.getLineTokens(range.startLineNumber);
|
||||
const scopedLineTokens = createScopedLineTokens(lineTokens, range.startColumn - 1);
|
||||
const emptyTokens = LineTokens.createEmpty('', scopedLineTokens.languageIdCodec);
|
||||
const previousLineNumber = range.startLineNumber - 1;
|
||||
const isFirstLine = previousLineNumber === 0;
|
||||
if (isFirstLine) {
|
||||
return emptyTokens;
|
||||
}
|
||||
const canScopeExtendOnPreviousLine = scopedLineTokens.firstCharOffset === 0;
|
||||
if (!canScopeExtendOnPreviousLine) {
|
||||
return emptyTokens;
|
||||
}
|
||||
const scopedLineTokensAtEndColumnOfPreviousLine = getScopedLineTokensAtEndColumnOfLine(previousLineNumber);
|
||||
const doesLanguageContinueOnPreviousLine = scopedLineTokens.languageId === scopedLineTokensAtEndColumnOfPreviousLine.languageId;
|
||||
if (!doesLanguageContinueOnPreviousLine) {
|
||||
return emptyTokens;
|
||||
}
|
||||
const previousSlicedLineTokens = scopedLineTokensAtEndColumnOfPreviousLine.toIViewLineTokens();
|
||||
const processedTokens = this.indentationLineProcessor.getProcessedTokens(previousSlicedLineTokens);
|
||||
return processedTokens;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class performs the actual processing of the indentation lines.
|
||||
* The brackets of the language configuration are removed from the regex, string and comment tokens.
|
||||
*/
|
||||
class IndentationLineProcessor {
|
||||
|
||||
constructor(
|
||||
private readonly model: IVirtualModel,
|
||||
private readonly languageConfigurationService: ILanguageConfigurationService
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Get the processed line for the given line number and potentially adjust the indentation level.
|
||||
* Remove the language configuration brackets from the regex, string and comment tokens.
|
||||
*/
|
||||
getProcessedLine(lineNumber: number, newIndentation?: string): string {
|
||||
const replaceIndentation = (line: string, newIndentation: string): string => {
|
||||
const currentIndentation = strings.getLeadingWhitespace(line);
|
||||
const adjustedLine = newIndentation + line.substring(currentIndentation.length);
|
||||
return adjustedLine;
|
||||
};
|
||||
|
||||
this.model.tokenization.forceTokenization?.(lineNumber);
|
||||
const tokens = this.model.tokenization.getLineTokens(lineNumber);
|
||||
let processedLine = this.getProcessedTokens(tokens).getLineContent();
|
||||
if (newIndentation !== undefined) {
|
||||
processedLine = replaceIndentation(processedLine, newIndentation);
|
||||
}
|
||||
return processedLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the line with the given tokens, remove the language configuration brackets from the regex, string and comment tokens.
|
||||
*/
|
||||
getProcessedTokens(tokens: IViewLineTokens): IViewLineTokens {
|
||||
|
||||
const shouldRemoveBracketsFromTokenType = (tokenType: StandardTokenType): boolean => {
|
||||
return tokenType === StandardTokenType.String
|
||||
|| tokenType === StandardTokenType.RegEx
|
||||
|| tokenType === StandardTokenType.Comment;
|
||||
};
|
||||
|
||||
const languageId = tokens.getLanguageId(0);
|
||||
const bracketsConfiguration = this.languageConfigurationService.getLanguageConfiguration(languageId).bracketsNew;
|
||||
const bracketsRegExp = bracketsConfiguration.getBracketRegExp({ global: true });
|
||||
const textAndMetadata: { text: string; metadata: number }[] = [];
|
||||
tokens.forEach((tokenIndex: number) => {
|
||||
const tokenType = tokens.getStandardTokenType(tokenIndex);
|
||||
let text = tokens.getTokenText(tokenIndex);
|
||||
if (shouldRemoveBracketsFromTokenType(tokenType)) {
|
||||
text = text.replace(bracketsRegExp, '');
|
||||
}
|
||||
const metadata = tokens.getMetadata(tokenIndex);
|
||||
textAndMetadata.push({ text, metadata });
|
||||
});
|
||||
const processedLineTokens = LineTokens.createFromTextAndMetadata(textAndMetadata, tokens.languageIdCodec);
|
||||
return processedLineTokens;
|
||||
}
|
||||
}
|
||||
|
||||
export function isLanguageDifferentFromLineStart(model: ITextModel, position: Position): boolean {
|
||||
model.tokenization.forceTokenization(position.lineNumber);
|
||||
const lineTokens = model.tokenization.getLineTokens(position.lineNumber);
|
||||
const scopedLineTokens = createScopedLineTokens(lineTokens, position.column - 1);
|
||||
const doesScopeStartAtOffsetZero = scopedLineTokens.firstCharOffset === 0;
|
||||
const isScopedLanguageEqualToFirstLanguageOnLine = lineTokens.getLanguageId(0) === scopedLineTokens.languageId;
|
||||
const languageIsDifferentFromLineStart = !doesScopeStartAtOffsetZero && !isScopedLanguageEqualToFirstLanguageOnLine;
|
||||
return languageIsDifferentFromLineStart;
|
||||
}
|
|
@ -4,7 +4,9 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CachedFunction } from 'vs/base/common/cache';
|
||||
import { RegExpOptions } from 'vs/base/common/strings';
|
||||
import { LanguageConfiguration } from 'vs/editor/common/languages/languageConfiguration';
|
||||
import { createBracketOrRegExp } from 'vs/editor/common/languages/supports/richEditBrackets';
|
||||
|
||||
/**
|
||||
* Captures all bracket related configurations for a single language.
|
||||
|
@ -91,6 +93,11 @@ export class LanguageBracketsConfiguration {
|
|||
public getBracketInfo(bracketText: string): BracketKind | undefined {
|
||||
return this.getOpeningBracketInfo(bracketText) || this.getClosingBracketInfo(bracketText);
|
||||
}
|
||||
|
||||
public getBracketRegExp(options?: RegExpOptions): RegExp {
|
||||
const brackets = Array.from([...this._openingBrackets.keys(), ...this._closingBrackets.keys()]);
|
||||
return createBracketOrRegExp(brackets, options);
|
||||
}
|
||||
}
|
||||
|
||||
function filterValidBrackets(bracketPairs: [string, string][]): [string, string][] {
|
||||
|
|
|
@ -7,6 +7,7 @@ import * as strings from 'vs/base/common/strings';
|
|||
import * as stringBuilder from 'vs/editor/common/core/stringBuilder';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { CharacterPair } from 'vs/editor/common/languages/languageConfiguration';
|
||||
import { RegExpOptions } from 'vs/base/common/strings';
|
||||
|
||||
interface InternalBracket {
|
||||
open: string[];
|
||||
|
@ -408,9 +409,9 @@ function prepareBracketForRegExp(str: string): string {
|
|||
return (insertWordBoundaries ? `\\b${str}\\b` : str);
|
||||
}
|
||||
|
||||
function createBracketOrRegExp(pieces: string[]): RegExp {
|
||||
export function createBracketOrRegExp(pieces: string[], options?: RegExpOptions): RegExp {
|
||||
const regexStr = `(${pieces.map(prepareBracketForRegExp).join(')|(')})`;
|
||||
return strings.createRegExp(regexStr, true);
|
||||
return strings.createRegExp(regexStr, true, options);
|
||||
}
|
||||
|
||||
const toReversedString = (function () {
|
||||
|
|
|
@ -7,8 +7,10 @@ import { ILanguageIdCodec } from 'vs/editor/common/languages';
|
|||
import { FontStyle, ColorId, StandardTokenType, MetadataConsts, TokenMetadata, ITokenPresentation } from 'vs/editor/common/encodedTokenAttributes';
|
||||
|
||||
export interface IViewLineTokens {
|
||||
languageIdCodec: ILanguageIdCodec;
|
||||
equals(other: IViewLineTokens): boolean;
|
||||
getCount(): number;
|
||||
getStandardTokenType(tokenIndex: number): StandardTokenType;
|
||||
getForeground(tokenIndex: number): ColorId;
|
||||
getEndOffset(tokenIndex: number): number;
|
||||
getClassName(tokenIndex: number): string;
|
||||
|
@ -18,6 +20,8 @@ export interface IViewLineTokens {
|
|||
getLineContent(): string;
|
||||
getMetadata(tokenIndex: number): number;
|
||||
getLanguageId(tokenIndex: number): string;
|
||||
getTokenText(tokenIndex: number): string;
|
||||
forEach(callback: (tokenIndex: number) => void): void;
|
||||
}
|
||||
|
||||
export class LineTokens implements IViewLineTokens {
|
||||
|
@ -26,7 +30,8 @@ export class LineTokens implements IViewLineTokens {
|
|||
private readonly _tokens: Uint32Array;
|
||||
private readonly _tokensCount: number;
|
||||
private readonly _text: string;
|
||||
private readonly _languageIdCodec: ILanguageIdCodec;
|
||||
|
||||
public readonly languageIdCodec: ILanguageIdCodec;
|
||||
|
||||
public static defaultTokenMetadata = (
|
||||
(FontStyle.None << MetadataConsts.FONT_STYLE_OFFSET)
|
||||
|
@ -44,11 +49,23 @@ export class LineTokens implements IViewLineTokens {
|
|||
return new LineTokens(tokens, lineContent, decoder);
|
||||
}
|
||||
|
||||
public static createFromTextAndMetadata(data: { text: string; metadata: number }[], decoder: ILanguageIdCodec): LineTokens {
|
||||
let offset: number = 0;
|
||||
let fullText: string = '';
|
||||
const tokens = new Array<number>();
|
||||
for (const { text, metadata } of data) {
|
||||
tokens.push(offset + text.length, metadata);
|
||||
offset += text.length;
|
||||
fullText += text;
|
||||
}
|
||||
return new LineTokens(new Uint32Array(tokens), fullText, decoder);
|
||||
}
|
||||
|
||||
constructor(tokens: Uint32Array, text: string, decoder: ILanguageIdCodec) {
|
||||
this._tokens = tokens;
|
||||
this._tokensCount = (this._tokens.length >>> 1);
|
||||
this._text = text;
|
||||
this._languageIdCodec = decoder;
|
||||
this.languageIdCodec = decoder;
|
||||
}
|
||||
|
||||
public equals(other: IViewLineTokens): boolean {
|
||||
|
@ -98,7 +115,7 @@ export class LineTokens implements IViewLineTokens {
|
|||
public getLanguageId(tokenIndex: number): string {
|
||||
const metadata = this._tokens[(tokenIndex << 1) + 1];
|
||||
const languageId = TokenMetadata.getLanguageId(metadata);
|
||||
return this._languageIdCodec.decodeLanguageId(languageId);
|
||||
return this.languageIdCodec.decodeLanguageId(languageId);
|
||||
}
|
||||
|
||||
public getStandardTokenType(tokenIndex: number): StandardTokenType {
|
||||
|
@ -225,7 +242,21 @@ export class LineTokens implements IViewLineTokens {
|
|||
}
|
||||
}
|
||||
|
||||
return new LineTokens(new Uint32Array(newTokens), text, this._languageIdCodec);
|
||||
return new LineTokens(new Uint32Array(newTokens), text, this.languageIdCodec);
|
||||
}
|
||||
|
||||
public getTokenText(tokenIndex: number): string {
|
||||
const startOffset = this.getStartOffset(tokenIndex);
|
||||
const endOffset = this.getEndOffset(tokenIndex);
|
||||
const text = this._text.substring(startOffset, endOffset);
|
||||
return text;
|
||||
}
|
||||
|
||||
public forEach(callback: (tokenIndex: number) => void): void {
|
||||
const tokenCount = this.getCount();
|
||||
for (let tokenIndex = 0; tokenIndex < tokenCount; tokenIndex++) {
|
||||
callback(tokenIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,12 +270,15 @@ class SliceLineTokens implements IViewLineTokens {
|
|||
private readonly _firstTokenIndex: number;
|
||||
private readonly _tokensCount: number;
|
||||
|
||||
public readonly languageIdCodec: ILanguageIdCodec;
|
||||
|
||||
constructor(source: LineTokens, startOffset: number, endOffset: number, deltaOffset: number) {
|
||||
this._source = source;
|
||||
this._startOffset = startOffset;
|
||||
this._endOffset = endOffset;
|
||||
this._deltaOffset = deltaOffset;
|
||||
this._firstTokenIndex = source.findTokenIndexAtOffset(startOffset);
|
||||
this.languageIdCodec = source.languageIdCodec;
|
||||
|
||||
this._tokensCount = 0;
|
||||
for (let i = this._firstTokenIndex, len = source.getCount(); i < len; i++) {
|
||||
|
@ -284,6 +318,10 @@ class SliceLineTokens implements IViewLineTokens {
|
|||
return this._tokensCount;
|
||||
}
|
||||
|
||||
public getStandardTokenType(tokenIndex: number): StandardTokenType {
|
||||
return this._source.getStandardTokenType(this._firstTokenIndex + tokenIndex);
|
||||
}
|
||||
|
||||
public getForeground(tokenIndex: number): ColorId {
|
||||
return this._source.getForeground(this._firstTokenIndex + tokenIndex);
|
||||
}
|
||||
|
@ -308,4 +346,24 @@ class SliceLineTokens implements IViewLineTokens {
|
|||
public findTokenIndexAtOffset(offset: number): number {
|
||||
return this._source.findTokenIndexAtOffset(offset + this._startOffset - this._deltaOffset) - this._firstTokenIndex;
|
||||
}
|
||||
|
||||
public getTokenText(tokenIndex: number): string {
|
||||
const adjustedTokenIndex = this._firstTokenIndex + tokenIndex;
|
||||
const tokenStartOffset = this._source.getStartOffset(adjustedTokenIndex);
|
||||
const tokenEndOffset = this._source.getEndOffset(adjustedTokenIndex);
|
||||
let text = this._source.getTokenText(adjustedTokenIndex);
|
||||
if (tokenStartOffset < this._startOffset) {
|
||||
text = text.substring(this._startOffset - tokenStartOffset);
|
||||
}
|
||||
if (tokenEndOffset > this._endOffset) {
|
||||
text = text.substring(0, text.length - (tokenEndOffset - this._endOffset));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
public forEach(callback: (tokenIndex: number) => void): void {
|
||||
for (let tokenIndex = 0; tokenIndex < this.getCount(); tokenIndex++) {
|
||||
callback(tokenIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,29 +9,26 @@ import { EditOperation, ISingleEditOperation } from 'vs/editor/common/core/editO
|
|||
import { normalizeIndentation } from 'vs/editor/common/core/indentation';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry';
|
||||
import { ProcessedIndentRulesSupport } from 'vs/editor/common/languages/supports/indentationLineProcessor';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
|
||||
export function getReindentEditOperations(model: ITextModel, languageConfigurationService: ILanguageConfigurationService, startLineNumber: number, endLineNumber: number, inheritedIndent?: string): ISingleEditOperation[] {
|
||||
export function getReindentEditOperations(model: ITextModel, languageConfigurationService: ILanguageConfigurationService, startLineNumber: number, endLineNumber: number): ISingleEditOperation[] {
|
||||
if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) {
|
||||
// Model is empty
|
||||
return [];
|
||||
}
|
||||
|
||||
const indentationRules = languageConfigurationService.getLanguageConfiguration(model.getLanguageId()).indentationRules;
|
||||
if (!indentationRules) {
|
||||
const indentationRulesSupport = languageConfigurationService.getLanguageConfiguration(model.getLanguageId()).indentRulesSupport;
|
||||
if (!indentationRulesSupport) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const processedIndentRulesSupport = new ProcessedIndentRulesSupport(model, indentationRulesSupport, languageConfigurationService);
|
||||
endLineNumber = Math.min(endLineNumber, model.getLineCount());
|
||||
|
||||
// Skip `unIndentedLinePattern` lines
|
||||
while (startLineNumber <= endLineNumber) {
|
||||
if (!indentationRules.unIndentedLinePattern) {
|
||||
break;
|
||||
}
|
||||
|
||||
const text = model.getLineContent(startLineNumber);
|
||||
if (!indentationRules.unIndentedLinePattern.test(text)) {
|
||||
if (!processedIndentRulesSupport.shouldIgnore(startLineNumber)) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -54,37 +51,19 @@ export function getReindentEditOperations(model: ITextModel, languageConfigurati
|
|||
const indentEdits: ISingleEditOperation[] = [];
|
||||
|
||||
// indentation being passed to lines below
|
||||
let globalIndent: string;
|
||||
|
||||
// Calculate indentation for the first line
|
||||
// If there is no passed-in indentation, we use the indentation of the first line as base.
|
||||
const currentLineText = model.getLineContent(startLineNumber);
|
||||
let adjustedLineContent = currentLineText;
|
||||
if (inheritedIndent !== undefined && inheritedIndent !== null) {
|
||||
globalIndent = inheritedIndent;
|
||||
const oldIndentation = strings.getLeadingWhitespace(currentLineText);
|
||||
|
||||
adjustedLineContent = globalIndent + currentLineText.substring(oldIndentation.length);
|
||||
if (indentationRules.decreaseIndentPattern && indentationRules.decreaseIndentPattern.test(adjustedLineContent)) {
|
||||
globalIndent = unshiftIndent(globalIndent);
|
||||
adjustedLineContent = globalIndent + currentLineText.substring(oldIndentation.length);
|
||||
|
||||
}
|
||||
if (currentLineText !== adjustedLineContent) {
|
||||
indentEdits.push(EditOperation.replaceMove(new Selection(startLineNumber, 1, startLineNumber, oldIndentation.length + 1), normalizeIndentation(globalIndent, indentSize, insertSpaces)));
|
||||
}
|
||||
} else {
|
||||
globalIndent = strings.getLeadingWhitespace(currentLineText);
|
||||
}
|
||||
|
||||
let globalIndent = strings.getLeadingWhitespace(currentLineText);
|
||||
// idealIndentForNextLine doesn't equal globalIndent when there is a line matching `indentNextLinePattern`.
|
||||
let idealIndentForNextLine: string = globalIndent;
|
||||
|
||||
if (indentationRules.increaseIndentPattern && indentationRules.increaseIndentPattern.test(adjustedLineContent)) {
|
||||
if (processedIndentRulesSupport.shouldIncrease(startLineNumber)) {
|
||||
idealIndentForNextLine = shiftIndent(idealIndentForNextLine);
|
||||
globalIndent = shiftIndent(globalIndent);
|
||||
}
|
||||
else if (indentationRules.indentNextLinePattern && indentationRules.indentNextLinePattern.test(adjustedLineContent)) {
|
||||
else if (processedIndentRulesSupport.shouldIndentNextLine(startLineNumber)) {
|
||||
idealIndentForNextLine = shiftIndent(idealIndentForNextLine);
|
||||
}
|
||||
|
||||
|
@ -94,9 +73,9 @@ export function getReindentEditOperations(model: ITextModel, languageConfigurati
|
|||
for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
|
||||
const text = model.getLineContent(lineNumber);
|
||||
const oldIndentation = strings.getLeadingWhitespace(text);
|
||||
const adjustedLineContent = idealIndentForNextLine + text.substring(oldIndentation.length);
|
||||
const currentIdealIndent = idealIndentForNextLine;
|
||||
|
||||
if (indentationRules.decreaseIndentPattern && indentationRules.decreaseIndentPattern.test(adjustedLineContent)) {
|
||||
if (processedIndentRulesSupport.shouldDecrease(lineNumber, currentIdealIndent)) {
|
||||
idealIndentForNextLine = unshiftIndent(idealIndentForNextLine);
|
||||
globalIndent = unshiftIndent(globalIndent);
|
||||
}
|
||||
|
@ -106,14 +85,14 @@ export function getReindentEditOperations(model: ITextModel, languageConfigurati
|
|||
}
|
||||
|
||||
// calculate idealIndentForNextLine
|
||||
if (indentationRules.unIndentedLinePattern && indentationRules.unIndentedLinePattern.test(text)) {
|
||||
if (processedIndentRulesSupport.shouldIgnore(lineNumber)) {
|
||||
// In reindent phase, if the line matches `unIndentedLinePattern` we inherit indentation from above lines
|
||||
// but don't change globalIndent and idealIndentForNextLine.
|
||||
continue;
|
||||
} else if (indentationRules.increaseIndentPattern && indentationRules.increaseIndentPattern.test(adjustedLineContent)) {
|
||||
} else if (processedIndentRulesSupport.shouldIncrease(lineNumber, currentIdealIndent)) {
|
||||
globalIndent = shiftIndent(globalIndent);
|
||||
idealIndentForNextLine = globalIndent;
|
||||
} else if (indentationRules.indentNextLinePattern && indentationRules.indentNextLinePattern.test(adjustedLineContent)) {
|
||||
} else if (processedIndentRulesSupport.shouldIndentNextLine(lineNumber, currentIdealIndent)) {
|
||||
idealIndentForNextLine = shiftIndent(idealIndentForNextLine);
|
||||
} else {
|
||||
idealIndentForNextLine = globalIndent;
|
||||
|
|
|
@ -24,7 +24,7 @@ import { TypeOperations } from 'vs/editor/common/cursor/cursorTypeOperations';
|
|||
import { cppBracketRules, goBracketRules, htmlBracketRules, latexBracketRules, luaBracketRules, phpBracketRules, rubyBracketRules, typescriptBracketRules, vbBracketRules } from 'vs/editor/test/common/modes/supports/bracketRules';
|
||||
import { latexAutoClosingPairsRules } from 'vs/editor/test/common/modes/supports/autoClosingPairsRules';
|
||||
|
||||
enum Language {
|
||||
export enum Language {
|
||||
TypeScript = 'ts-test',
|
||||
Ruby = 'ruby-test',
|
||||
PHP = 'php-test',
|
||||
|
@ -44,7 +44,7 @@ function testIndentationToTabsCommand(lines: string[], selection: Selection, tab
|
|||
testCommand(lines, null, selection, (accessor, sel) => new IndentationToTabsCommand(sel, tabSize), expectedLines, expectedSelection);
|
||||
}
|
||||
|
||||
function registerLanguage(instantiationService: TestInstantiationService, language: Language): IDisposable {
|
||||
export function registerLanguage(instantiationService: TestInstantiationService, language: Language): IDisposable {
|
||||
const disposables = new DisposableStore();
|
||||
const languageService = instantiationService.get(ILanguageService);
|
||||
disposables.add(registerLanguageConfiguration(instantiationService, language));
|
||||
|
@ -52,7 +52,7 @@ function registerLanguage(instantiationService: TestInstantiationService, langua
|
|||
return disposables;
|
||||
}
|
||||
|
||||
function registerLanguageConfiguration(instantiationService: TestInstantiationService, language: Language): IDisposable {
|
||||
export function registerLanguageConfiguration(instantiationService: TestInstantiationService, language: Language): IDisposable {
|
||||
const languageConfigurationService = instantiationService.get(ILanguageConfigurationService);
|
||||
switch (language) {
|
||||
case Language.TypeScript:
|
||||
|
@ -110,12 +110,12 @@ function registerLanguageConfiguration(instantiationService: TestInstantiationSe
|
|||
}
|
||||
}
|
||||
|
||||
interface StandardTokenTypeData {
|
||||
export interface StandardTokenTypeData {
|
||||
startIndex: number;
|
||||
standardTokenType: StandardTokenType;
|
||||
}
|
||||
|
||||
function registerTokenizationSupport(instantiationService: TestInstantiationService, tokens: StandardTokenTypeData[][], languageId: string): IDisposable {
|
||||
export function registerTokenizationSupport(instantiationService: TestInstantiationService, tokens: StandardTokenTypeData[][], languageId: string): IDisposable {
|
||||
let lineIndex = 0;
|
||||
const languageService = instantiationService.get(ILanguageService);
|
||||
const tokenizationSupport: ITokenizationSupport = {
|
||||
|
@ -1007,9 +1007,7 @@ suite('Auto Indent On Type - TypeScript/JavaScript', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// Failing tests...
|
||||
|
||||
test.skip('issue #208232: incorrect indentation inside of comments', () => {
|
||||
test('issue #208232: incorrect indentation inside of comments', () => {
|
||||
|
||||
// https://github.com/microsoft/vscode/issues/208232
|
||||
|
||||
|
@ -1023,6 +1021,12 @@ suite('Auto Indent On Type - TypeScript/JavaScript', () => {
|
|||
withTestCodeEditor(model, { autoIndent: "full" }, (editor, viewModel, instantiationService) => {
|
||||
|
||||
disposables.add(registerLanguage(instantiationService, languageId));
|
||||
const tokens: StandardTokenTypeData[][] = [
|
||||
[{ startIndex: 0, standardTokenType: StandardTokenType.Comment }],
|
||||
[{ startIndex: 0, standardTokenType: StandardTokenType.Comment }],
|
||||
[{ startIndex: 0, standardTokenType: StandardTokenType.Comment }]
|
||||
];
|
||||
disposables.add(registerTokenizationSupport(instantiationService, tokens, languageId));
|
||||
editor.setSelection(new Selection(2, 23, 2, 23));
|
||||
viewModel.type("\n", 'keyboard');
|
||||
assert.strictEqual(model.getValue(), [
|
||||
|
@ -1034,6 +1038,8 @@ suite('Auto Indent On Type - TypeScript/JavaScript', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// Failing tests...
|
||||
|
||||
test.skip('issue #43244: indent after equal sign is detected', () => {
|
||||
|
||||
// https://github.com/microsoft/vscode/issues/43244
|
||||
|
@ -1398,24 +1404,28 @@ suite('Auto Indent On Type - PHP', () => {
|
|||
|
||||
ensureNoDisposablesAreLeakedInTestSuite();
|
||||
|
||||
test('temp issue because there should be at least one passing test in a suite', () => {
|
||||
assert.ok(true);
|
||||
});
|
||||
|
||||
test.skip('issue #199050: should not indent after { detected in a string', () => {
|
||||
test('issue #199050: should not indent after { detected in a string', () => {
|
||||
|
||||
// https://github.com/microsoft/vscode/issues/199050
|
||||
|
||||
const model = createTextModel("$phrase = preg_replace('#(\{1|%s).*#su', '', $phrase);", languageId, {});
|
||||
const model = createTextModel("preg_replace('{');", languageId, {});
|
||||
disposables.add(model);
|
||||
|
||||
withTestCodeEditor(model, { autoIndent: "full" }, (editor, viewModel, instantiationService) => {
|
||||
|
||||
disposables.add(registerLanguage(instantiationService, languageId));
|
||||
const tokens: StandardTokenTypeData[][] = [
|
||||
[
|
||||
{ startIndex: 0, standardTokenType: StandardTokenType.Other },
|
||||
{ startIndex: 13, standardTokenType: StandardTokenType.String },
|
||||
{ startIndex: 16, standardTokenType: StandardTokenType.Other },
|
||||
]
|
||||
];
|
||||
disposables.add(registerTokenizationSupport(instantiationService, tokens, languageId));
|
||||
editor.setSelection(new Selection(1, 54, 1, 54));
|
||||
viewModel.type("\n", 'keyboard');
|
||||
assert.strictEqual(model.getValue(), [
|
||||
"$phrase = preg_replace('#(\{1|%s).*#su', '', $phrase);",
|
||||
"preg_replace('{');",
|
||||
""
|
||||
].join('\n'));
|
||||
});
|
||||
|
|
|
@ -0,0 +1,236 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as assert from 'assert';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';
|
||||
import { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes';
|
||||
import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry';
|
||||
import { IndentationContextProcessor, ProcessedIndentRulesSupport } from 'vs/editor/common/languages/supports/indentationLineProcessor';
|
||||
import { Language, registerLanguage, registerTokenizationSupport, StandardTokenTypeData } from 'vs/editor/contrib/indentation/test/browser/indentation.test';
|
||||
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
|
||||
import { createTextModel } from 'vs/editor/test/common/testTextModel';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
|
||||
suite('Indentation Context Processor - TypeScript/JavaScript', () => {
|
||||
|
||||
const languageId = Language.TypeScript;
|
||||
let disposables: DisposableStore;
|
||||
|
||||
setup(() => {
|
||||
disposables = new DisposableStore();
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
disposables.dispose();
|
||||
});
|
||||
|
||||
ensureNoDisposablesAreLeakedInTestSuite();
|
||||
|
||||
test('brackets inside of string', () => {
|
||||
|
||||
const model = createTextModel([
|
||||
'const someVar = "{some text}"',
|
||||
].join('\n'), languageId, {});
|
||||
disposables.add(model);
|
||||
|
||||
withTestCodeEditor(model, { autoIndent: "full" }, (editor, viewModel, instantiationService) => {
|
||||
const tokens: StandardTokenTypeData[][] = [[
|
||||
{ startIndex: 0, standardTokenType: StandardTokenType.Other },
|
||||
{ startIndex: 16, standardTokenType: StandardTokenType.String },
|
||||
{ startIndex: 28, standardTokenType: StandardTokenType.String }
|
||||
]];
|
||||
disposables.add(registerLanguage(instantiationService, languageId));
|
||||
disposables.add(registerTokenizationSupport(instantiationService, tokens, languageId));
|
||||
const languageConfigurationService = instantiationService.get(ILanguageConfigurationService);
|
||||
const indentationContextProcessor = new IndentationContextProcessor(model, languageConfigurationService);
|
||||
const processedContext = indentationContextProcessor.getProcessedTokenContextAroundRange(new Range(1, 23, 1, 23));
|
||||
assert.strictEqual(processedContext.beforeRangeProcessedTokens.getLineContent(), 'const someVar = "some');
|
||||
assert.strictEqual(processedContext.afterRangeProcessedTokens.getLineContent(), ' text"');
|
||||
assert.strictEqual(processedContext.previousLineProcessedTokens.getLineContent(), '');
|
||||
});
|
||||
});
|
||||
|
||||
test('brackets inside of comment', () => {
|
||||
|
||||
const model = createTextModel([
|
||||
'const someVar2 = /*(a])*/',
|
||||
'const someVar = /* [()] some other t{e}xt() */ "some text"',
|
||||
].join('\n'), languageId, {});
|
||||
disposables.add(model);
|
||||
|
||||
withTestCodeEditor(model, { autoIndent: "full" }, (editor, viewModel, instantiationService) => {
|
||||
const tokens: StandardTokenTypeData[][] = [
|
||||
[
|
||||
{ startIndex: 0, standardTokenType: StandardTokenType.Other },
|
||||
{ startIndex: 17, standardTokenType: StandardTokenType.Comment },
|
||||
],
|
||||
[
|
||||
{ startIndex: 0, standardTokenType: StandardTokenType.Other },
|
||||
{ startIndex: 16, standardTokenType: StandardTokenType.Comment },
|
||||
{ startIndex: 46, standardTokenType: StandardTokenType.Other },
|
||||
{ startIndex: 47, standardTokenType: StandardTokenType.String }
|
||||
]];
|
||||
disposables.add(registerLanguage(instantiationService, languageId));
|
||||
disposables.add(registerTokenizationSupport(instantiationService, tokens, languageId));
|
||||
const languageConfigurationService = instantiationService.get(ILanguageConfigurationService);
|
||||
const indentationContextProcessor = new IndentationContextProcessor(model, languageConfigurationService);
|
||||
const processedContext = indentationContextProcessor.getProcessedTokenContextAroundRange(new Range(2, 29, 2, 35));
|
||||
assert.strictEqual(processedContext.beforeRangeProcessedTokens.getLineContent(), 'const someVar = /* some');
|
||||
assert.strictEqual(processedContext.afterRangeProcessedTokens.getLineContent(), ' text */ "some text"');
|
||||
assert.strictEqual(processedContext.previousLineProcessedTokens.getLineContent(), 'const someVar2 = /*a*/');
|
||||
});
|
||||
});
|
||||
|
||||
test('brackets inside of regex', () => {
|
||||
|
||||
const model = createTextModel([
|
||||
'const someRegex2 = /(()))]/;',
|
||||
'const someRegex = /()a{h}{s}[(a}87(9a9()))]/;',
|
||||
].join('\n'), languageId, {});
|
||||
disposables.add(model);
|
||||
|
||||
withTestCodeEditor(model, { autoIndent: "full" }, (editor, viewModel, instantiationService) => {
|
||||
const tokens: StandardTokenTypeData[][] = [
|
||||
[
|
||||
{ startIndex: 0, standardTokenType: StandardTokenType.Other },
|
||||
{ startIndex: 19, standardTokenType: StandardTokenType.RegEx },
|
||||
{ startIndex: 27, standardTokenType: StandardTokenType.Other },
|
||||
],
|
||||
[
|
||||
{ startIndex: 0, standardTokenType: StandardTokenType.Other },
|
||||
{ startIndex: 18, standardTokenType: StandardTokenType.RegEx },
|
||||
{ startIndex: 44, standardTokenType: StandardTokenType.Other },
|
||||
]
|
||||
];
|
||||
disposables.add(registerLanguage(instantiationService, languageId));
|
||||
disposables.add(registerTokenizationSupport(instantiationService, tokens, languageId));
|
||||
const languageConfigurationService = instantiationService.get(ILanguageConfigurationService);
|
||||
const indentationContextProcessor = new IndentationContextProcessor(model, languageConfigurationService);
|
||||
const processedContext = indentationContextProcessor.getProcessedTokenContextAroundRange(new Range(1, 25, 2, 33));
|
||||
assert.strictEqual(processedContext.beforeRangeProcessedTokens.getLineContent(), 'const someRegex2 = /');
|
||||
assert.strictEqual(processedContext.afterRangeProcessedTokens.getLineContent(), '879a9/;');
|
||||
assert.strictEqual(processedContext.previousLineProcessedTokens.getLineContent(), '');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('Processed Indent Rules Support - TypeScript/JavaScript', () => {
|
||||
|
||||
const languageId = Language.TypeScript;
|
||||
let disposables: DisposableStore;
|
||||
|
||||
setup(() => {
|
||||
disposables = new DisposableStore();
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
disposables.dispose();
|
||||
});
|
||||
|
||||
ensureNoDisposablesAreLeakedInTestSuite();
|
||||
|
||||
test('should increase', () => {
|
||||
|
||||
const model = createTextModel([
|
||||
'const someVar = {',
|
||||
'const someVar2 = "{"',
|
||||
'const someVar3 = /*{*/'
|
||||
].join('\n'), languageId, {});
|
||||
disposables.add(model);
|
||||
|
||||
withTestCodeEditor(model, { autoIndent: "full" }, (editor, viewModel, instantiationService) => {
|
||||
const tokens: StandardTokenTypeData[][] = [
|
||||
[
|
||||
{ startIndex: 0, standardTokenType: StandardTokenType.Other }
|
||||
],
|
||||
[
|
||||
{ startIndex: 0, standardTokenType: StandardTokenType.Other },
|
||||
{ startIndex: 17, standardTokenType: StandardTokenType.String },
|
||||
],
|
||||
[
|
||||
{ startIndex: 0, standardTokenType: StandardTokenType.Other },
|
||||
{ startIndex: 17, standardTokenType: StandardTokenType.Comment },
|
||||
]
|
||||
];
|
||||
disposables.add(registerLanguage(instantiationService, languageId));
|
||||
disposables.add(registerTokenizationSupport(instantiationService, tokens, languageId));
|
||||
const languageConfigurationService = instantiationService.get(ILanguageConfigurationService);
|
||||
const indentationRulesSupport = languageConfigurationService.getLanguageConfiguration(languageId).indentRulesSupport;
|
||||
if (!indentationRulesSupport) {
|
||||
assert.fail('indentationRulesSupport should be defined');
|
||||
}
|
||||
const processedIndentRulesSupport = new ProcessedIndentRulesSupport(model, indentationRulesSupport, languageConfigurationService);
|
||||
assert.strictEqual(processedIndentRulesSupport.shouldIncrease(1), true);
|
||||
assert.strictEqual(processedIndentRulesSupport.shouldIncrease(2), false);
|
||||
assert.strictEqual(processedIndentRulesSupport.shouldIncrease(3), false);
|
||||
});
|
||||
});
|
||||
|
||||
test('should decrease', () => {
|
||||
|
||||
const model = createTextModel([
|
||||
'}',
|
||||
'"])some text}"',
|
||||
'])*/'
|
||||
].join('\n'), languageId, {});
|
||||
disposables.add(model);
|
||||
|
||||
withTestCodeEditor(model, { autoIndent: "full" }, (editor, viewModel, instantiationService) => {
|
||||
const tokens: StandardTokenTypeData[][] = [
|
||||
[{ startIndex: 0, standardTokenType: StandardTokenType.Other }],
|
||||
[{ startIndex: 0, standardTokenType: StandardTokenType.String }],
|
||||
[{ startIndex: 0, standardTokenType: StandardTokenType.Comment }]
|
||||
];
|
||||
disposables.add(registerLanguage(instantiationService, languageId));
|
||||
disposables.add(registerTokenizationSupport(instantiationService, tokens, languageId));
|
||||
const languageConfigurationService = instantiationService.get(ILanguageConfigurationService);
|
||||
const indentationRulesSupport = languageConfigurationService.getLanguageConfiguration(languageId).indentRulesSupport;
|
||||
if (!indentationRulesSupport) {
|
||||
assert.fail('indentationRulesSupport should be defined');
|
||||
}
|
||||
const processedIndentRulesSupport = new ProcessedIndentRulesSupport(model, indentationRulesSupport, languageConfigurationService);
|
||||
assert.strictEqual(processedIndentRulesSupport.shouldDecrease(1), true);
|
||||
assert.strictEqual(processedIndentRulesSupport.shouldDecrease(2), false);
|
||||
assert.strictEqual(processedIndentRulesSupport.shouldDecrease(3), false);
|
||||
});
|
||||
});
|
||||
|
||||
test('should increase next line', () => {
|
||||
|
||||
const model = createTextModel([
|
||||
'if()',
|
||||
'const someString = "if()"',
|
||||
'const someRegex = /if()/'
|
||||
].join('\n'), languageId, {});
|
||||
disposables.add(model);
|
||||
|
||||
withTestCodeEditor(model, { autoIndent: "full" }, (editor, viewModel, instantiationService) => {
|
||||
const tokens: StandardTokenTypeData[][] = [
|
||||
[
|
||||
{ startIndex: 0, standardTokenType: StandardTokenType.Other }
|
||||
],
|
||||
[
|
||||
{ startIndex: 0, standardTokenType: StandardTokenType.Other },
|
||||
{ startIndex: 19, standardTokenType: StandardTokenType.String }
|
||||
],
|
||||
[
|
||||
{ startIndex: 0, standardTokenType: StandardTokenType.Other },
|
||||
{ startIndex: 18, standardTokenType: StandardTokenType.RegEx }
|
||||
]
|
||||
];
|
||||
disposables.add(registerLanguage(instantiationService, languageId));
|
||||
disposables.add(registerTokenizationSupport(instantiationService, tokens, languageId));
|
||||
const languageConfigurationService = instantiationService.get(ILanguageConfigurationService);
|
||||
const indentationRulesSupport = languageConfigurationService.getLanguageConfiguration(languageId).indentRulesSupport;
|
||||
if (!indentationRulesSupport) {
|
||||
assert.fail('indentationRulesSupport should be defined');
|
||||
}
|
||||
const processedIndentRulesSupport = new ProcessedIndentRulesSupport(model, indentationRulesSupport, languageConfigurationService);
|
||||
assert.strictEqual(processedIndentRulesSupport.shouldIndentNextLine(1), true);
|
||||
assert.strictEqual(processedIndentRulesSupport.shouldIndentNextLine(2), false);
|
||||
assert.strictEqual(processedIndentRulesSupport.shouldIndentNextLine(3), false);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -4,7 +4,8 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IViewLineTokens } from 'vs/editor/common/tokens/lineTokens';
|
||||
import { ColorId, TokenMetadata, ITokenPresentation } from 'vs/editor/common/encodedTokenAttributes';
|
||||
import { ColorId, TokenMetadata, ITokenPresentation, StandardTokenType } from 'vs/editor/common/encodedTokenAttributes';
|
||||
import { ILanguageIdCodec } from 'vs/editor/common/languages';
|
||||
|
||||
/**
|
||||
* A token on a line.
|
||||
|
@ -22,6 +23,10 @@ export class TestLineToken {
|
|||
this._metadata = metadata;
|
||||
}
|
||||
|
||||
public getStandardTokenType(): StandardTokenType {
|
||||
return TokenMetadata.getTokenType(this._metadata);
|
||||
}
|
||||
|
||||
public getForeground(): ColorId {
|
||||
return TokenMetadata.getForeground(this._metadata);
|
||||
}
|
||||
|
@ -79,6 +84,10 @@ export class TestLineTokens implements IViewLineTokens {
|
|||
return this._actual.length;
|
||||
}
|
||||
|
||||
public getStandardTokenType(tokenIndex: number): StandardTokenType {
|
||||
return this._actual[tokenIndex].getStandardTokenType();
|
||||
}
|
||||
|
||||
public getForeground(tokenIndex: number): ColorId {
|
||||
return this._actual[tokenIndex].getForeground();
|
||||
}
|
||||
|
@ -114,6 +123,18 @@ export class TestLineTokens implements IViewLineTokens {
|
|||
public getLanguageId(tokenIndex: number): string {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
public getTokenText(tokenIndex: number): string {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
public forEach(callback: (tokenIndex: number) => void): void {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
public get languageIdCodec(): ILanguageIdCodec {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
}
|
||||
|
||||
export class TestLineTokenFactory {
|
||||
|
|
Loading…
Reference in a new issue