|
|
|
|
@@ -3,7 +3,6 @@
|
|
|
|
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
|
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
import { onUnexpectedError } from 'vs/base/common/errors';
|
|
|
|
|
import { Emitter, Event } from 'vs/base/common/event';
|
|
|
|
|
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
|
|
|
|
import * as strings from 'vs/base/common/strings';
|
|
|
|
|
@@ -12,13 +11,14 @@ import { Range } from 'vs/editor/common/core/range';
|
|
|
|
|
import { ITextModel } from 'vs/editor/common/model';
|
|
|
|
|
import { DEFAULT_WORD_REGEXP, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper';
|
|
|
|
|
import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes';
|
|
|
|
|
import { EnterAction, FoldingRules, IAutoClosingPair, IndentAction, IndentationRule, LanguageConfiguration, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration';
|
|
|
|
|
import { createScopedLineTokens } from 'vs/editor/common/modes/supports';
|
|
|
|
|
import { EnterAction, FoldingRules, IAutoClosingPair, IndentAction, IndentationRule, LanguageConfiguration, StandardAutoClosingPairConditional, CompleteEnterAction } from 'vs/editor/common/modes/languageConfiguration';
|
|
|
|
|
import { createScopedLineTokens, ScopedLineTokens } from 'vs/editor/common/modes/supports';
|
|
|
|
|
import { CharacterPairSupport } from 'vs/editor/common/modes/supports/characterPair';
|
|
|
|
|
import { BracketElectricCharacterSupport, IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter';
|
|
|
|
|
import { IndentConsts, IndentRulesSupport } from 'vs/editor/common/modes/supports/indentRules';
|
|
|
|
|
import { IOnEnterSupportOptions, OnEnterSupport } from 'vs/editor/common/modes/supports/onEnter';
|
|
|
|
|
import { OnEnterSupport } from 'vs/editor/common/modes/supports/onEnter';
|
|
|
|
|
import { RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets';
|
|
|
|
|
import { EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Interface used to support insertion of mode specific comments.
|
|
|
|
|
@@ -48,11 +48,11 @@ export class RichEditSupport {
|
|
|
|
|
private readonly _languageIdentifier: LanguageIdentifier;
|
|
|
|
|
private _brackets: RichEditBrackets | null;
|
|
|
|
|
private _electricCharacter: BracketElectricCharacterSupport | null;
|
|
|
|
|
private readonly _onEnterSupport: OnEnterSupport | null;
|
|
|
|
|
|
|
|
|
|
public readonly comments: ICommentsConfiguration | null;
|
|
|
|
|
public readonly characterPair: CharacterPairSupport;
|
|
|
|
|
public readonly wordDefinition: RegExp;
|
|
|
|
|
public readonly onEnter: OnEnterSupport | null;
|
|
|
|
|
public readonly indentRulesSupport: IndentRulesSupport | null;
|
|
|
|
|
public readonly indentationRules: IndentationRule | undefined;
|
|
|
|
|
public readonly foldingRules: FoldingRules;
|
|
|
|
|
@@ -70,8 +70,7 @@ export class RichEditSupport {
|
|
|
|
|
|
|
|
|
|
this._conf = RichEditSupport._mergeConf(prev, rawConf);
|
|
|
|
|
|
|
|
|
|
this.onEnter = RichEditSupport._handleOnEnter(this._conf);
|
|
|
|
|
|
|
|
|
|
this._onEnterSupport = (this._conf.brackets || this._conf.indentationRules || this._conf.onEnterRules ? new OnEnterSupport(this._conf) : null);
|
|
|
|
|
this.comments = RichEditSupport._handleComments(this._conf);
|
|
|
|
|
|
|
|
|
|
this.characterPair = new CharacterPairSupport(this._conf);
|
|
|
|
|
@@ -102,6 +101,13 @@ export class RichEditSupport {
|
|
|
|
|
return this._electricCharacter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public onEnter(autoIndent: EditorAutoIndentStrategy, oneLineAboveText: string, beforeEnterText: string, afterEnterText: string): EnterAction | null {
|
|
|
|
|
if (!this._onEnterSupport) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return this._onEnterSupport.onEnter(autoIndent, oneLineAboveText, beforeEnterText, afterEnterText);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static _mergeConf(prev: LanguageConfiguration | null, current: LanguageConfiguration): LanguageConfiguration {
|
|
|
|
|
return {
|
|
|
|
|
comments: (prev ? current.comments || prev.comments : current.comments),
|
|
|
|
|
@@ -117,29 +123,6 @@ export class RichEditSupport {
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static _handleOnEnter(conf: LanguageConfiguration): OnEnterSupport | null {
|
|
|
|
|
// on enter
|
|
|
|
|
let onEnter: IOnEnterSupportOptions = {};
|
|
|
|
|
let empty = true;
|
|
|
|
|
|
|
|
|
|
if (conf.brackets) {
|
|
|
|
|
empty = false;
|
|
|
|
|
onEnter.brackets = conf.brackets;
|
|
|
|
|
}
|
|
|
|
|
if (conf.indentationRules) {
|
|
|
|
|
empty = false;
|
|
|
|
|
}
|
|
|
|
|
if (conf.onEnterRules) {
|
|
|
|
|
empty = false;
|
|
|
|
|
onEnter.regExpRules = conf.onEnterRules;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!empty) {
|
|
|
|
|
return new OnEnterSupport(onEnter);
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static _handleComments(conf: LanguageConfiguration): ICommentsConfiguration | null {
|
|
|
|
|
let commentRule = conf.comments;
|
|
|
|
|
if (!commentRule) {
|
|
|
|
|
@@ -351,8 +334,12 @@ export class LanguageConfigurationRegistryImpl {
|
|
|
|
|
*
|
|
|
|
|
* This function only return the inherited indent based on above lines, it doesn't check whether current line should decrease or not.
|
|
|
|
|
*/
|
|
|
|
|
public getInheritIndentForLine(model: IVirtualModel, lineNumber: number, honorIntentialIndent: boolean = true): { indentation: string; action: IndentAction | null; line?: number; } | null {
|
|
|
|
|
let indentRulesSupport = this.getIndentRulesSupport(model.getLanguageIdentifier().id);
|
|
|
|
|
public getInheritIndentForLine(autoIndent: EditorAutoIndentStrategy, model: IVirtualModel, lineNumber: number, honorIntentialIndent: boolean = true): { indentation: string; action: IndentAction | null; line?: number; } | null {
|
|
|
|
|
if (autoIndent < EditorAutoIndentStrategy.Full) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const indentRulesSupport = this.getIndentRulesSupport(model.getLanguageIdentifier().id);
|
|
|
|
|
if (!indentRulesSupport) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
@@ -364,7 +351,7 @@ export class LanguageConfigurationRegistryImpl {
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let precedingUnIgnoredLine = this.getPrecedingValidLine(model, lineNumber, indentRulesSupport);
|
|
|
|
|
const precedingUnIgnoredLine = this.getPrecedingValidLine(model, lineNumber, indentRulesSupport);
|
|
|
|
|
if (precedingUnIgnoredLine < 0) {
|
|
|
|
|
return null;
|
|
|
|
|
} else if (precedingUnIgnoredLine < 1) {
|
|
|
|
|
@@ -374,8 +361,7 @@ export class LanguageConfigurationRegistryImpl {
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let precedingUnIgnoredLineContent = model.getLineContent(precedingUnIgnoredLine);
|
|
|
|
|
|
|
|
|
|
const precedingUnIgnoredLineContent = model.getLineContent(precedingUnIgnoredLine);
|
|
|
|
|
if (indentRulesSupport.shouldIncrease(precedingUnIgnoredLineContent) || indentRulesSupport.shouldIndentNextLine(precedingUnIgnoredLineContent)) {
|
|
|
|
|
return {
|
|
|
|
|
indentation: strings.getLeadingWhitespace(precedingUnIgnoredLineContent),
|
|
|
|
|
@@ -402,9 +388,9 @@ export class LanguageConfigurationRegistryImpl {
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let previousLine = precedingUnIgnoredLine - 1;
|
|
|
|
|
const previousLine = precedingUnIgnoredLine - 1;
|
|
|
|
|
|
|
|
|
|
let previousLineIndentMetadata = indentRulesSupport.getIndentMetadata(model.getLineContent(previousLine));
|
|
|
|
|
const previousLineIndentMetadata = indentRulesSupport.getIndentMetadata(model.getLineContent(previousLine));
|
|
|
|
|
if (!(previousLineIndentMetadata & (IndentConsts.INCREASE_MASK | IndentConsts.DECREASE_MASK)) &&
|
|
|
|
|
(previousLineIndentMetadata & IndentConsts.INDENT_NEXTLINE_MASK)) {
|
|
|
|
|
let stopLine = 0;
|
|
|
|
|
@@ -432,7 +418,7 @@ export class LanguageConfigurationRegistryImpl {
|
|
|
|
|
} else {
|
|
|
|
|
// search from precedingUnIgnoredLine until we find one whose indent is not temporary
|
|
|
|
|
for (let i = precedingUnIgnoredLine; i > 0; i--) {
|
|
|
|
|
let lineContent = model.getLineContent(i);
|
|
|
|
|
const lineContent = model.getLineContent(i);
|
|
|
|
|
if (indentRulesSupport.shouldIncrease(lineContent)) {
|
|
|
|
|
return {
|
|
|
|
|
indentation: strings.getLeadingWhitespace(lineContent),
|
|
|
|
|
@@ -472,27 +458,28 @@ export class LanguageConfigurationRegistryImpl {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getGoodIndentForLine(virtualModel: IVirtualModel, languageId: LanguageId, lineNumber: number, indentConverter: IIndentConverter): string | null {
|
|
|
|
|
let indentRulesSupport = this.getIndentRulesSupport(languageId);
|
|
|
|
|
public getGoodIndentForLine(autoIndent: EditorAutoIndentStrategy, virtualModel: IVirtualModel, languageId: LanguageId, lineNumber: number, indentConverter: IIndentConverter): string | null {
|
|
|
|
|
if (autoIndent < EditorAutoIndentStrategy.Full) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const richEditSupport = this._getRichEditSupport(languageId);
|
|
|
|
|
if (!richEditSupport) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const indentRulesSupport = this.getIndentRulesSupport(languageId);
|
|
|
|
|
if (!indentRulesSupport) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let indent = this.getInheritIndentForLine(virtualModel, lineNumber);
|
|
|
|
|
let lineContent = virtualModel.getLineContent(lineNumber);
|
|
|
|
|
const indent = this.getInheritIndentForLine(autoIndent, virtualModel, lineNumber);
|
|
|
|
|
const lineContent = virtualModel.getLineContent(lineNumber);
|
|
|
|
|
|
|
|
|
|
if (indent) {
|
|
|
|
|
let inheritLine = indent.line;
|
|
|
|
|
const inheritLine = indent.line;
|
|
|
|
|
if (inheritLine !== undefined) {
|
|
|
|
|
let onEnterSupport = this._getOnEnterSupport(languageId);
|
|
|
|
|
let enterResult: EnterAction | null = null;
|
|
|
|
|
try {
|
|
|
|
|
if (onEnterSupport) {
|
|
|
|
|
enterResult = onEnterSupport.onEnter('', virtualModel.getLineContent(inheritLine), '');
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
onUnexpectedError(e);
|
|
|
|
|
}
|
|
|
|
|
const enterResult = richEditSupport.onEnter(autoIndent, '', virtualModel.getLineContent(inheritLine), '');
|
|
|
|
|
|
|
|
|
|
if (enterResult) {
|
|
|
|
|
let indentation = strings.getLeadingWhitespace(virtualModel.getLineContent(inheritLine));
|
|
|
|
|
@@ -539,16 +526,17 @@ export class LanguageConfigurationRegistryImpl {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getIndentForEnter(model: ITextModel, range: Range, indentConverter: IIndentConverter, autoIndent: boolean): { beforeEnter: string, afterEnter: string } | null {
|
|
|
|
|
public getIndentForEnter(autoIndent: EditorAutoIndentStrategy, model: ITextModel, range: Range, indentConverter: IIndentConverter): { beforeEnter: string, afterEnter: string } | null {
|
|
|
|
|
if (autoIndent < EditorAutoIndentStrategy.Full) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
model.forceTokenization(range.startLineNumber);
|
|
|
|
|
let lineTokens = model.getLineTokens(range.startLineNumber);
|
|
|
|
|
|
|
|
|
|
let beforeEnterText;
|
|
|
|
|
let afterEnterText;
|
|
|
|
|
let scopedLineTokens = createScopedLineTokens(lineTokens, range.startColumn - 1);
|
|
|
|
|
let scopedLineText = scopedLineTokens.getLineContent();
|
|
|
|
|
const lineTokens = model.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
|
|
|
|
|
@@ -557,6 +545,7 @@ export class LanguageConfigurationRegistryImpl {
|
|
|
|
|
beforeEnterText = lineTokens.getLineContent().substring(0, range.startColumn - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let afterEnterText: string;
|
|
|
|
|
if (range.isEmpty()) {
|
|
|
|
|
afterEnterText = scopedLineText.substr(range.startColumn - 1 - scopedLineTokens.firstCharOffset);
|
|
|
|
|
} else {
|
|
|
|
|
@@ -564,31 +553,15 @@ export class LanguageConfigurationRegistryImpl {
|
|
|
|
|
afterEnterText = endScopedLineTokens.getLineContent().substr(range.endColumn - 1 - scopedLineTokens.firstCharOffset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let indentRulesSupport = this.getIndentRulesSupport(scopedLineTokens.languageId);
|
|
|
|
|
|
|
|
|
|
const indentRulesSupport = this.getIndentRulesSupport(scopedLineTokens.languageId);
|
|
|
|
|
if (!indentRulesSupport) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let beforeEnterResult = beforeEnterText;
|
|
|
|
|
let beforeEnterIndent = strings.getLeadingWhitespace(beforeEnterText);
|
|
|
|
|
const beforeEnterResult = beforeEnterText;
|
|
|
|
|
const beforeEnterIndent = strings.getLeadingWhitespace(beforeEnterText);
|
|
|
|
|
|
|
|
|
|
if (!autoIndent && !embeddedLanguage) {
|
|
|
|
|
let beforeEnterIndentAction = this.getInheritIndentForLine(model, range.startLineNumber);
|
|
|
|
|
|
|
|
|
|
if (indentRulesSupport.shouldDecrease(beforeEnterText)) {
|
|
|
|
|
if (beforeEnterIndentAction) {
|
|
|
|
|
beforeEnterIndent = beforeEnterIndentAction.indentation;
|
|
|
|
|
if (beforeEnterIndentAction.action !== IndentAction.Indent) {
|
|
|
|
|
beforeEnterIndent = indentConverter.unshiftIndent(beforeEnterIndent);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
beforeEnterResult = beforeEnterIndent + strings.ltrim(strings.ltrim(beforeEnterText, ' '), '\t');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let virtualModel: IVirtualModel = {
|
|
|
|
|
const virtualModel: IVirtualModel = {
|
|
|
|
|
getLineTokens: (lineNumber: number) => {
|
|
|
|
|
return model.getLineTokens(lineNumber);
|
|
|
|
|
},
|
|
|
|
|
@@ -607,10 +580,10 @@ export class LanguageConfigurationRegistryImpl {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let currentLineIndent = strings.getLeadingWhitespace(lineTokens.getLineContent());
|
|
|
|
|
let afterEnterAction = this.getInheritIndentForLine(virtualModel, range.startLineNumber + 1);
|
|
|
|
|
const currentLineIndent = strings.getLeadingWhitespace(lineTokens.getLineContent());
|
|
|
|
|
const afterEnterAction = this.getInheritIndentForLine(autoIndent, virtualModel, range.startLineNumber + 1);
|
|
|
|
|
if (!afterEnterAction) {
|
|
|
|
|
let beforeEnter = embeddedLanguage ? currentLineIndent : beforeEnterIndent;
|
|
|
|
|
const beforeEnter = embeddedLanguage ? currentLineIndent : beforeEnterIndent;
|
|
|
|
|
return {
|
|
|
|
|
beforeEnter: beforeEnter,
|
|
|
|
|
afterEnter: beforeEnter
|
|
|
|
|
@@ -637,18 +610,21 @@ export class LanguageConfigurationRegistryImpl {
|
|
|
|
|
* We should always allow intentional indentation. It means, if users change the indentation of `lineNumber` and the content of
|
|
|
|
|
* this line doesn't match decreaseIndentPattern, we should not adjust the indentation.
|
|
|
|
|
*/
|
|
|
|
|
public getIndentActionForType(model: ITextModel, range: Range, ch: string, indentConverter: IIndentConverter): string | null {
|
|
|
|
|
let scopedLineTokens = this.getScopedLineTokens(model, range.startLineNumber, range.startColumn);
|
|
|
|
|
let indentRulesSupport = this.getIndentRulesSupport(scopedLineTokens.languageId);
|
|
|
|
|
public getIndentActionForType(autoIndent: EditorAutoIndentStrategy, model: ITextModel, range: Range, ch: string, indentConverter: IIndentConverter): string | null {
|
|
|
|
|
if (autoIndent < EditorAutoIndentStrategy.Full) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
const scopedLineTokens = this.getScopedLineTokens(model, range.startLineNumber, range.startColumn);
|
|
|
|
|
const indentRulesSupport = this.getIndentRulesSupport(scopedLineTokens.languageId);
|
|
|
|
|
if (!indentRulesSupport) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let scopedLineText = scopedLineTokens.getLineContent();
|
|
|
|
|
let beforeTypeText = scopedLineText.substr(0, range.startColumn - 1 - scopedLineTokens.firstCharOffset);
|
|
|
|
|
let afterTypeText;
|
|
|
|
|
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 {
|
|
|
|
|
@@ -661,13 +637,12 @@ export class LanguageConfigurationRegistryImpl {
|
|
|
|
|
if (!indentRulesSupport.shouldDecrease(beforeTypeText + afterTypeText) && indentRulesSupport.shouldDecrease(beforeTypeText + ch + afterTypeText)) {
|
|
|
|
|
// after typing `ch`, the content matches decreaseIndentPattern, we should adjust the indent to a good manner.
|
|
|
|
|
// 1. Get inherited indent action
|
|
|
|
|
let r = this.getInheritIndentForLine(model, range.startLineNumber, false);
|
|
|
|
|
const r = this.getInheritIndentForLine(autoIndent, model, range.startLineNumber, false);
|
|
|
|
|
if (!r) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let indentation = r.indentation;
|
|
|
|
|
|
|
|
|
|
if (r.action !== IndentAction.Indent) {
|
|
|
|
|
indentation = indentConverter.unshiftIndent(indentation);
|
|
|
|
|
}
|
|
|
|
|
@@ -679,15 +654,13 @@ export class LanguageConfigurationRegistryImpl {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getIndentMetadata(model: ITextModel, lineNumber: number): number | null {
|
|
|
|
|
let indentRulesSupport = this.getIndentRulesSupport(model.getLanguageIdentifier().id);
|
|
|
|
|
const indentRulesSupport = this.getIndentRulesSupport(model.getLanguageIdentifier().id);
|
|
|
|
|
if (!indentRulesSupport) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lineNumber < 1 || lineNumber > model.getLineCount()) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return indentRulesSupport.getIndentMetadata(model.getLineContent(lineNumber));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -695,34 +668,18 @@ export class LanguageConfigurationRegistryImpl {
|
|
|
|
|
|
|
|
|
|
// begin onEnter
|
|
|
|
|
|
|
|
|
|
private _getOnEnterSupport(languageId: LanguageId): OnEnterSupport | null {
|
|
|
|
|
let value = this._getRichEditSupport(languageId);
|
|
|
|
|
if (!value) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return value.onEnter || null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getRawEnterActionAtPosition(model: ITextModel, lineNumber: number, column: number): EnterAction | null {
|
|
|
|
|
let r = this.getEnterAction(model, new Range(lineNumber, column, lineNumber, column));
|
|
|
|
|
|
|
|
|
|
return r ? r.enterAction : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getEnterAction(model: ITextModel, range: Range): { enterAction: EnterAction; indentation: string; } | null {
|
|
|
|
|
let indentation = this.getIndentationAtPosition(model, range.startLineNumber, range.startColumn);
|
|
|
|
|
|
|
|
|
|
let scopedLineTokens = this.getScopedLineTokens(model, range.startLineNumber, range.startColumn);
|
|
|
|
|
let onEnterSupport = this._getOnEnterSupport(scopedLineTokens.languageId);
|
|
|
|
|
if (!onEnterSupport) {
|
|
|
|
|
public getEnterAction(autoIndent: EditorAutoIndentStrategy, model: ITextModel, range: Range): CompleteEnterAction | null {
|
|
|
|
|
const scopedLineTokens = this.getScopedLineTokens(model, range.startLineNumber, range.startColumn);
|
|
|
|
|
const richEditSupport = this._getRichEditSupport(scopedLineTokens.languageId);
|
|
|
|
|
if (!richEditSupport) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let scopedLineText = scopedLineTokens.getLineContent();
|
|
|
|
|
let beforeEnterText = scopedLineText.substr(0, range.startColumn - 1 - scopedLineTokens.firstCharOffset);
|
|
|
|
|
let afterEnterText;
|
|
|
|
|
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 {
|
|
|
|
|
@@ -730,73 +687,70 @@ export class LanguageConfigurationRegistryImpl {
|
|
|
|
|
afterEnterText = endScopedLineTokens.getLineContent().substr(range.endColumn - 1 - scopedLineTokens.firstCharOffset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let lineNumber = range.startLineNumber;
|
|
|
|
|
let oneLineAboveText = '';
|
|
|
|
|
|
|
|
|
|
if (lineNumber > 1 && scopedLineTokens.firstCharOffset === 0) {
|
|
|
|
|
if (range.startLineNumber > 1 && scopedLineTokens.firstCharOffset === 0) {
|
|
|
|
|
// This is not the first line and the entire line belongs to this mode
|
|
|
|
|
let oneLineAboveScopedLineTokens = this.getScopedLineTokens(model, lineNumber - 1);
|
|
|
|
|
const oneLineAboveScopedLineTokens = this.getScopedLineTokens(model, range.startLineNumber - 1);
|
|
|
|
|
if (oneLineAboveScopedLineTokens.languageId === scopedLineTokens.languageId) {
|
|
|
|
|
// The line above ends with text belonging to the same mode
|
|
|
|
|
oneLineAboveText = oneLineAboveScopedLineTokens.getLineContent();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let enterResult: EnterAction | null = null;
|
|
|
|
|
try {
|
|
|
|
|
enterResult = onEnterSupport.onEnter(oneLineAboveText, beforeEnterText, afterEnterText);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
onUnexpectedError(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const enterResult = richEditSupport.onEnter(autoIndent, oneLineAboveText, beforeEnterText, afterEnterText);
|
|
|
|
|
if (!enterResult) {
|
|
|
|
|
return null;
|
|
|
|
|
} else {
|
|
|
|
|
// Here we add `\t` to appendText first because enterAction is leveraging appendText and removeText to change indentation.
|
|
|
|
|
if (!enterResult.appendText) {
|
|
|
|
|
if (
|
|
|
|
|
(enterResult.indentAction === IndentAction.Indent) ||
|
|
|
|
|
(enterResult.indentAction === IndentAction.IndentOutdent)
|
|
|
|
|
) {
|
|
|
|
|
enterResult.appendText = '\t';
|
|
|
|
|
} else {
|
|
|
|
|
enterResult.appendText = '';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const indentAction = enterResult.indentAction;
|
|
|
|
|
let appendText = enterResult.appendText;
|
|
|
|
|
const removeText = enterResult.removeText || 0;
|
|
|
|
|
|
|
|
|
|
// Here we add `\t` to appendText first because enterAction is leveraging appendText and removeText to change indentation.
|
|
|
|
|
if (!appendText) {
|
|
|
|
|
if (
|
|
|
|
|
(indentAction === IndentAction.Indent) ||
|
|
|
|
|
(indentAction === IndentAction.IndentOutdent)
|
|
|
|
|
) {
|
|
|
|
|
appendText = '\t';
|
|
|
|
|
} else {
|
|
|
|
|
appendText = '';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (enterResult.removeText) {
|
|
|
|
|
indentation = indentation.substring(0, indentation.length - enterResult.removeText);
|
|
|
|
|
let indentation = this.getIndentationAtPosition(model, range.startLineNumber, range.startColumn);
|
|
|
|
|
if (removeText) {
|
|
|
|
|
indentation = indentation.substring(0, indentation.length - removeText);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
enterAction: enterResult,
|
|
|
|
|
indentation: indentation,
|
|
|
|
|
indentAction: indentAction,
|
|
|
|
|
appendText: appendText,
|
|
|
|
|
removeText: removeText,
|
|
|
|
|
indentation: indentation
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getIndentationAtPosition(model: ITextModel, lineNumber: number, column: number): string {
|
|
|
|
|
let lineText = model.getLineContent(lineNumber);
|
|
|
|
|
const lineText = model.getLineContent(lineNumber);
|
|
|
|
|
let indentation = strings.getLeadingWhitespace(lineText);
|
|
|
|
|
if (indentation.length > column - 1) {
|
|
|
|
|
indentation = indentation.substring(0, column - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return indentation;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private getScopedLineTokens(model: ITextModel, lineNumber: number, columnNumber?: number) {
|
|
|
|
|
private getScopedLineTokens(model: ITextModel, lineNumber: number, columnNumber?: number): ScopedLineTokens {
|
|
|
|
|
model.forceTokenization(lineNumber);
|
|
|
|
|
let lineTokens = model.getLineTokens(lineNumber);
|
|
|
|
|
let column = (typeof columnNumber === 'undefined' ? model.getLineMaxColumn(lineNumber) - 1 : columnNumber - 1);
|
|
|
|
|
let scopedLineTokens = createScopedLineTokens(lineTokens, column);
|
|
|
|
|
return scopedLineTokens;
|
|
|
|
|
const lineTokens = model.getLineTokens(lineNumber);
|
|
|
|
|
const column = (typeof columnNumber === 'undefined' ? model.getLineMaxColumn(lineNumber) - 1 : columnNumber - 1);
|
|
|
|
|
return createScopedLineTokens(lineTokens, column);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// end onEnter
|
|
|
|
|
|
|
|
|
|
public getBracketsSupport(languageId: LanguageId): RichEditBrackets | null {
|
|
|
|
|
let value = this._getRichEditSupport(languageId);
|
|
|
|
|
const value = this._getRichEditSupport(languageId);
|
|
|
|
|
if (!value) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|