Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 10 additions & 1 deletion extensions/cpp/language-configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,14 @@
"appendText": "// "
}
},
]
],
"stringConcatenation":{
"excludedPatterns": [
"^\\s*#\\s*include",
"^\\s*#\\s*pragma",
"^\\s*#\\s*error",
"^\\s*#\\s*warning",
"^\\s*#\\s*define",
]
}
Comment on lines +132 to +140
}
14 changes: 13 additions & 1 deletion extensions/csharp/language-configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,5 +126,17 @@
"appendText": "/// "
}
},
]
],
"stringConcatenation":{
"excludedPatterns":[
"^\\s*using",
"^\\s*//",
"^\\s*/\\*",
"^\\s*#\\s*region",
"^\\s*#\\s*pragma",
"^\\s*\\[",
"^\\s*namespace",
"^\\s*throw\\s+new\\s+\\w+\\("
]
}
}
10 changes: 9 additions & 1 deletion extensions/go/language-configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,5 +106,13 @@
"appendText": "// "
}
},
]
],
"stringConcatenation":{
"excludedPatterns":[
"^\\s*import",
"^\\s*package",
"^\\s*//",
"^\\s*/\\*"
]
}
}
12 changes: 11 additions & 1 deletion extensions/java/language-configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,5 +168,15 @@
"appendText": "// "
}
},
]
],
"stringConcatenation":{
"excludedPatterns":[
"^\\s*import",
"^\\s*package",
"^\\s*//",
"^\\s*/\\*",
"^\\s*@",
"^\\s*throw\\s+new\\s+\\w+\\("
]
}
}
12 changes: 11 additions & 1 deletion extensions/javascript/javascript-language-configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -249,5 +249,15 @@
"appendText": "// "
}
},
]
],
"stringConcatenation":{
"excludedPatterns":[
"^\\s*import",
"^\\s*export",
"^\\s*require",
"^\\s*//",
"^\\s*/\\*",
"^\\s*@"
]
}
}
7 changes: 7 additions & 0 deletions extensions/lua/language-configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,12 @@
"indentationRules": {
"increaseIndentPattern": "^((?!(\\-\\-)).)*((\\b(else|function|then|do|repeat)\\b((?!\\b(end|until)\\b).)*)|(\\{\\s*))$",
"decreaseIndentPattern": "^\\s*((\\b(elseif|else|end|until)\\b)|(\\})|(\\)))"
},
"stringConcatenation": {
"excludedPatterns": [
"^\\s*require",
"^\\s*--",
"^\\s*--\\[\\["
]
}
}
11 changes: 10 additions & 1 deletion extensions/python/language-configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,14 @@
"beforeText": "^\\s*(?:def|class|for|if|elif|else|while|try|with|finally|except|async).*?:\\s*$",
"action": { "indent": "indent" }
}
]
],
"stringConcatenation":{
"excludedPatterns":[
"^\\s*import",
"^\\s*from",
"^\\s*#",
"^\\s*@",
"^\\s*raise\\s+\\w+\\("
]
}
}
25 changes: 23 additions & 2 deletions src/vs/editor/common/config/editorOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,14 @@ export interface IEditorOptions {
* Controls whether the accessibility hint should be provided to screen reader users when an inline completion is shown.
*/
inlineCompletionsAccessibilityVerbose?: boolean;

/**
* Enable/disable automatic string concatenation formatting when pressing Enter inside string literal.
* When enabled, pressing Enter inside a string will automatically close the current string and open
* a new one with the appropriate concatenation operator for the current language.
* Defaults to true.
*/
stringConcatenationOnEnter?: boolean;
}

/**
Expand Down Expand Up @@ -5933,7 +5941,8 @@ export const enum EditorOption {
effectiveEditContext,
scrollOnMiddleClick,
effectiveAllowVariableFonts,
doubleClickSelectsBlock
doubleClickSelectsBlock,
stringConcatenationOnEnter
}

export const EditorOptions = {
Expand Down Expand Up @@ -6845,7 +6854,19 @@ export const EditorOptions = {
wrappingIndent: register(new WrappingIndentOption()),
wrappingStrategy: register(new WrappingStrategy()),
effectiveEditContextEnabled: register(new EffectiveEditContextEnabled()),
effectiveAllowVariableFonts: register(new EffectiveAllowVariableFonts())
effectiveAllowVariableFonts: register(new EffectiveAllowVariableFonts()),

stringConcatenationOnEnter: register(new EditorBooleanOption(
EditorOption.stringConcatenationOnEnter,
'stringConcatenationOnEnter',
true,
{
markdownDescription: nls.localize(
'stringConcatenationOnEnter',
'Controls whether pressing `Enter` inside a string literal automatically splits into two concatenated strings using appropriate operator for the current language.'
)
}
)),
};

type EditorOptionsType = typeof EditorOptions;
Expand Down
26 changes: 26 additions & 0 deletions src/vs/editor/common/cursor/cursorTypeEditOperations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { EditorAutoClosingStrategy, EditorAutoIndentStrategy } from '../config/e
import { createScopedLineTokens } from '../languages/supports.js';
import { getIndentActionForType, getIndentForEnter, getInheritIndentForLine } from '../languages/autoIndent.js';
import { getEnterAction } from '../languages/enterAction.js';
import { getStringConcatenation } from '../languages/stringConcatenation.js';
import { CompositionOutcome } from './cursorTypeOperations.js';

export class AutoIndentOperation {
Expand Down Expand Up @@ -532,6 +533,11 @@ export class EnterOperation {
}

private static _enter(config: CursorConfiguration, model: ITextModel, keepPosition: boolean, range: Range): ICommand {

const excludedPatterns = config.stringConcatenation?.excludedPatterns ?? [];

const stringConcatAction = getStringConcatenation(model, range, config.stringConcatenationOnEnter, excludedPatterns,);

if (config.autoIndent === EditorAutoIndentStrategy.None) {
return typeCommand(range, '\n', keepPosition);
}
Expand All @@ -541,6 +547,26 @@ export class EnterOperation {
return typeCommand(range, '\n' + config.normalizeIndentation(indentation), keepPosition);
}
const r = getEnterAction(config.autoIndent, model, range, config.languageConfigurationService);

if (stringConcatAction) {
const lineText = model.getLineContent(range.startLineNumber);
const indentation = strings.getLeadingWhitespace(lineText).substring(0, range.startColumn - 1);

// Expand range to cover the whole line, replacing it entirely
const fullLineRange = new Range(
range.startLineNumber,
1,
range.endLineNumber,
model.getLineMaxColumn(range.endLineNumber)
);

return typeCommand(
fullLineRange,
stringConcatAction.beforeText + '\n' + config.normalizeIndentation(indentation) + stringConcatAction.afterText,
keepPosition
);
Comment on lines +555 to +567
}

if (r) {
if (r.indentAction === IndentAction.None) {
// Nothing special
Expand Down
6 changes: 5 additions & 1 deletion src/vs/editor/common/cursorCommon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ISelection, Selection } from './core/selection.js';
import { ICommand } from './editorCommon.js';
import { IEditorConfiguration } from './config/editorConfiguration.js';
import { PositionAffinity, TextModelResolvedOptions } from './model.js';
import { AutoClosingPairs } from './languages/languageConfiguration.js';
import { AutoClosingPairs, IStringConcatenation } from './languages/languageConfiguration.js';
import { ILanguageConfigurationService } from './languages/languageConfigurationRegistry.js';
import { createScopedLineTokens } from './languages/supports.js';
import { IElectricAction } from './languages/supports/electricCharacter.js';
Expand Down Expand Up @@ -74,12 +74,14 @@ export class CursorConfiguration {
public readonly autoClosingOvertype: EditorAutoClosingEditStrategy;
public readonly autoSurround: EditorAutoSurroundStrategy;
public readonly autoIndent: EditorAutoIndentStrategy;
public readonly stringConcatenationOnEnter: boolean;
public readonly autoClosingPairs: AutoClosingPairs;
public readonly surroundingPairs: CharacterMap;
public readonly blockCommentStartToken: string | null;
public readonly shouldAutoCloseBefore: { quote: (ch: string) => boolean; bracket: (ch: string) => boolean; comment: (ch: string) => boolean };
public readonly wordSegmenterLocales: string[];
public readonly overtypeOnPaste: boolean;
public readonly stringConcatenation: IStringConcatenation | undefined;

private readonly _languageId: string;
private _electricChars: { [key: string]: boolean } | null;
Expand Down Expand Up @@ -142,6 +144,7 @@ export class CursorConfiguration {
this.autoClosingOvertype = options.get(EditorOption.autoClosingOvertype);
this.autoSurround = options.get(EditorOption.autoSurround);
this.autoIndent = options.get(EditorOption.autoIndent);
this.stringConcatenationOnEnter = options.get(EditorOption.stringConcatenationOnEnter);
this.wordSegmenterLocales = options.get(EditorOption.wordSegmenterLocales);
this.overtypeOnPaste = options.get(EditorOption.overtypeOnPaste);

Expand All @@ -164,6 +167,7 @@ export class CursorConfiguration {
}

const commentsConfiguration = this.languageConfigurationService.getLanguageConfiguration(languageId).comments;
this.stringConcatenation = this.languageConfigurationService.getLanguageConfiguration(languageId).stringConcatenation;
this.blockCommentStartToken = commentsConfiguration?.blockCommentStartToken ?? null;
}

Expand Down
30 changes: 30 additions & 0 deletions src/vs/editor/common/languages/languageConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ export interface CommentRule {
blockComment?: CharacterPair | null;
}

/**
* Stores the excluded patterns per-language
* for string concatenation logic
*/
export interface IStringConcatenation {
/**
* RegEx patterns where string concatenation should not be applied
*/
excludedPatterns: string[];
}

/**
* The language configuration interface defines the contract between extensions and
* various editor features, like automatic bracket insertion, automatic indentation etc.
Expand Down Expand Up @@ -103,6 +114,11 @@ export interface LanguageConfiguration {
__electricCharacterSupport?: {
docComment?: IDocComment;
};

/**
* The language's string concatenation excluded patterns.
*/
stringConcatenation?: IStringConcatenation;
}

/**
Expand Down Expand Up @@ -260,6 +276,20 @@ export interface EnterAction {
removeText?: number;
}

/**
* @internal
*/
export interface StringConcatenationOnEnterAction {
/**
* Previous line's text
*/
beforeText: string;
/**
* Concatenated text that has the correspondent per-language operator
*/
afterText: string;
}

/**
* @internal
*/
Expand Down
21 changes: 20 additions & 1 deletion src/vs/editor/common/languages/languageConfigurationRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Disposable, IDisposable, markAsSingleton, toDisposable } from '../../..
import * as strings from '../../../base/common/strings.js';
import { ITextModel } from '../model.js';
import { DEFAULT_WORD_REGEXP, ensureValidWordDefinition } from '../core/wordHelper.js';
import { EnterAction, FoldingRules, IAutoClosingPair, IndentationRule, LanguageConfiguration, AutoClosingPairs, CharacterPair, ExplicitLanguageConfiguration } from './languageConfiguration.js';
import { EnterAction, FoldingRules, IAutoClosingPair, IndentationRule, LanguageConfiguration, AutoClosingPairs, CharacterPair, ExplicitLanguageConfiguration, IStringConcatenation } from './languageConfiguration.js';
import { CharacterPairSupport } from './supports/characterPair.js';
import { BracketElectricCharacterSupport } from './supports/electricCharacter.js';
import { IndentRulesSupport } from './supports/indentRules.js';
Expand Down Expand Up @@ -249,6 +249,7 @@ function combineLanguageConfigurations(configs: LanguageConfiguration[]): Langua
folding: undefined,
colorizedBracketPairs: undefined,
__electricCharacterSupport: undefined,
stringConcatenation: undefined,
};
for (const entry of configs) {
result = {
Expand All @@ -263,6 +264,7 @@ function combineLanguageConfigurations(configs: LanguageConfiguration[]): Langua
folding: entry.folding || result.folding,
colorizedBracketPairs: entry.colorizedBracketPairs || result.colorizedBracketPairs,
__electricCharacterSupport: entry.__electricCharacterSupport || result.__electricCharacterSupport,
stringConcatenation: entry.stringConcatenation || result.stringConcatenation,
};
}

Expand Down Expand Up @@ -360,6 +362,7 @@ export class ResolvedLanguageConfiguration {
public readonly indentationRules: IndentationRule | undefined;
public readonly foldingRules: FoldingRules;
public readonly bracketsNew: LanguageBracketsConfiguration;
public readonly stringConcatenation: IStringConcatenation | undefined;

constructor(
public readonly languageId: string,
Expand Down Expand Up @@ -391,6 +394,7 @@ export class ResolvedLanguageConfiguration {
languageId,
this.underlyingConfig
);
this.stringConcatenation = ResolvedLanguageConfiguration._handleStringConcatenation(this.underlyingConfig);
}

public getWordDefinition(): RegExp {
Expand Down Expand Up @@ -472,6 +476,21 @@ export class ResolvedLanguageConfiguration {

return comments;
}

private static _handleStringConcatenation(
conf: LanguageConfiguration
): IStringConcatenation | undefined {
if (!conf.stringConcatenation) {
return undefined;
}
const excludedPatterns = conf.stringConcatenation.excludedPatterns;
if (!Array.isArray(excludedPatterns)) {
return undefined;
}
return {
excludedPatterns: excludedPatterns.filter(s => typeof s === 'string'),
};
}
}

registerSingleton(ILanguageConfigurationService, LanguageConfigurationService, InstantiationType.Delayed);
Loading