Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove brackets from comments, strings and regexes before evaluating the indentation #210641

Merged
merged 50 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
93c3d0e
wip
aiday-mar Apr 18, 2024
3313082
polishing the code
aiday-mar Apr 18, 2024
cc8ea28
adding code
aiday-mar Apr 18, 2024
42e9b12
adding the language
aiday-mar Apr 18, 2024
31bab16
reshuffling the code to avoid cyclic dependency
aiday-mar Apr 18, 2024
5f15946
polihsing code
aiday-mar Apr 18, 2024
3b93b8e
uncommenting tests
aiday-mar Apr 18, 2024
c5335b7
also adopting the indentation rules within the reindentation operation
aiday-mar Apr 19, 2024
50bb21f
using instead the sliced line tokens instead of the scoped line tokens
aiday-mar Apr 19, 2024
4c178cf
polishing the code
aiday-mar Apr 19, 2024
958a760
using start indices instead
aiday-mar Apr 19, 2024
50fc183
using value everywhere
aiday-mar Apr 19, 2024
8b350c5
using the token data to type the tokens
aiday-mar Apr 19, 2024
f43cb1a
setting to number instead of standard token type
aiday-mar Apr 19, 2024
8f3a9ee
using token data from autoindenttest.ts
aiday-mar Apr 19, 2024
7f4ac61
using same code in both test files
aiday-mar Apr 19, 2024
f871d5b
placing instantiation service into the registerLanguage method
aiday-mar Apr 19, 2024
39e12d0
Merge branch 'main' into near-bandicoot
aiday-mar Apr 19, 2024
1113593
copying object into the node js autoindent.ts
aiday-mar Apr 19, 2024
410428f
combining methods into one method
aiday-mar Apr 22, 2024
cc4da1c
making fields private, adding js docs
aiday-mar Apr 22, 2024
3430099
renaming variables, adding js docs
aiday-mar Apr 22, 2024
8b88b2e
renaming
aiday-mar Apr 22, 2024
9782065
adding return type to the method `_getProcessedPreviousLine`
aiday-mar Apr 22, 2024
45b5be8
placing the indentation addition into the indentation line processor
aiday-mar Apr 23, 2024
7d37ff6
defining a for each method on the line tokens
aiday-mar Apr 23, 2024
51c97b5
polishing the code
aiday-mar Apr 23, 2024
2d20645
trim the text if token offsets overflow
aiday-mar Apr 23, 2024
c4cf3bd
changing the line position
aiday-mar Apr 24, 2024
5593039
polishing the code
aiday-mar Apr 25, 2024
48122ba
polishing the code
aiday-mar Apr 25, 2024
da798e6
removing the 1 from the end character offset calculations
aiday-mar Apr 25, 2024
a447a1f
Merge branch 'main' into near-bandicoot
aiday-mar May 13, 2024
86f1fae
fixing compile errors
aiday-mar May 13, 2024
feb1a31
removing the changes to the autoindent.test.ts file, this will be don…
aiday-mar May 13, 2024
1dd7b59
Merge branch 'main' into near-bandicoot
aiday-mar May 13, 2024
8770945
adding tokenization support into the test
aiday-mar May 13, 2024
0ddda76
adding code in order to include the modified line tokens
aiday-mar May 14, 2024
058f92c
disposing the language service in the tests
aiday-mar May 14, 2024
d5a9fb8
reverting the language service changes and adding the languageIdCodec…
aiday-mar May 14, 2024
33534aa
polishing further the code
aiday-mar May 14, 2024
4ee9475
using tokens directly and not using the method on the scopedLineToken…
aiday-mar May 14, 2024
353b7f7
extracting the virtual model creation into a separate function
aiday-mar May 14, 2024
33d93f0
creating a static constructor directly on the line tokens
aiday-mar May 14, 2024
623d169
adding regexp directly into the bracket configuration
aiday-mar May 14, 2024
f184552
polishing the code
aiday-mar May 14, 2024
903ddf2
adding the global flag on the regex
aiday-mar May 15, 2024
4d74953
adding the text
aiday-mar May 15, 2024
01e76d6
polishing the code and adding test for the indentation context processor
aiday-mar May 22, 2024
b143fda
adding tests
aiday-mar May 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions extensions/javascript/javascript-language-configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,10 @@
},
"indentationRules": {
"decreaseIndentPattern": {
"pattern": "^((?!.*?/\\*).*\\*\/)?\\s*[\\}\\]\\)].*$"
"pattern": "^\\s*[\\}\\]\\)].*$"
},
"increaseIndentPattern": {
"pattern": "^((?!//).)*(\\{([^}\"'`/]*|(\\t|[ ])*//.*)|\\([^)\"'`/]*|\\[[^\\]\"'`/]*)$"
"pattern": "^.*(\\{[^}]*|\\([^)]*|\\[[^\\]]*)$"
},
// e.g. * ...| or */| or *-----*/|
"unIndentedLinePattern": {
Expand Down
4 changes: 2 additions & 2 deletions extensions/typescript-basics/language-configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,10 @@
},
"indentationRules": {
"decreaseIndentPattern": {
"pattern": "^((?!.*?/\\*).*\\*\/)?\\s*[\\}\\]\\)].*$"
"pattern": "^\\s*[\\}\\]\\)].*$"
},
"increaseIndentPattern": {
"pattern": "^((?!//).)*(\\{([^}\"'`/]*|(\\t|[ ])*//.*)|\\([^)\"'`/]*|\\[[^\\]\"'`/]*)$"
"pattern": "^.*(\\{[^}]*|\\([^)]*|\\[[^\\]]*)$"
},
// e.g. * ...| or */| or *-----*/|
"unIndentedLinePattern": {
Expand Down
172 changes: 81 additions & 91 deletions src/vs/editor/common/languages/autoIndent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -85,6 +86,7 @@ export function getInheritIndentForLine(
if (!indentRulesSupport) {
return null;
}
const processedIndentRulesSupport = new ProcessedIndentRulesSupport(model, indentRulesSupport, languageConfigurationService);

if (lineNumber <= 1) {
return {
Expand All @@ -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) {
Expand All @@ -116,14 +118,15 @@ export function getInheritIndentForLine(
};
}

const precedingUnIgnoredLineContent = model.getLineContent(precedingUnIgnoredLine);
if (indentRulesSupport.shouldIncrease(precedingUnIgnoredLineContent) || indentRulesSupport.shouldIndentNextLine(precedingUnIgnoredLineContent)) {
if (processedIndentRulesSupport.shouldIncrease(precedingUnIgnoredLine) || processedIndentRulesSupport.shouldIndentNextLine(precedingUnIgnoredLine)) {
const precedingUnIgnoredLineContent = model.getLineContent(precedingUnIgnoredLine);
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,
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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
};
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -268,7 +270,7 @@ export function getGoodIndentForLine(
indentation = indentConverter.unshiftIndent(indentation);
}

if (indentRulesSupport.shouldDecrease(lineContent)) {
if (processedIndentRulesSupport.shouldDecrease(lineNumber)) {
indentation = indentConverter.unshiftIndent(indentation);
}

Expand All @@ -281,7 +283,7 @@ export function getGoodIndentForLine(
}
}

if (indentRulesSupport.shouldDecrease(lineContent)) {
if (processedIndentRulesSupport.shouldDecrease(lineNumber)) {
if (indent.action === IndentAction.Indent) {
return indent.indentation;
} else {
Expand All @@ -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);

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());
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 = 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
};
}
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}

Loading
Loading