Add conditional use of vcFormat when `formatting` is set to `Default` (#7941)

This commit is contained in:
Colen Garoutte-Carson 2021-08-10 18:13:35 -07:00 коммит произвёл GitHub
Родитель ed64b7cbf2
Коммит a4c1e6a5be
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 291 добавлений и 211 удалений

Просмотреть файл

@ -266,7 +266,7 @@
"%c_cpp.configuration.formatting.Default.description%",
"%c_cpp.configuration.formatting.Disabled.description%"
],
"default": "clangFormat",
"default": "Default",
"description": "%c_cpp.configuration.formatting.description%",
"scope": "resource"
},

Просмотреть файл

@ -24,7 +24,7 @@
"c_cpp.configuration.formatting.description": "Configures the formatting engine",
"c_cpp.configuration.formatting.clangFormat.description": "clang-format will be used to format code.",
"c_cpp.configuration.formatting.vcFormat.description": "The Visual C++ formatting engine will be used to format code.",
"c_cpp.configuration.formatting.Default.description": "clang-format will be used to format code.",
"c_cpp.configuration.formatting.Default.description": "By default, clang-format will be used to format the code. However, the Visual C++ formatting engine will be used if an .editorconfig file with relevant settings is found nearer to the code being formatted and clang_format_style is the default value: 'file'.",
"c_cpp.configuration.formatting.Disabled.description": "Code formatting will be disabled.",
"c_cpp.configuration.vcFormat.indent.braces.description": "Braces are indented by the amount specified in the Editor: Tab Size setting.",
"c_cpp.configuration.vcFormat.indent.multiLineRelativeTo.description": "Determines what new line indentation is relative to",

Просмотреть файл

@ -3,9 +3,8 @@
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import * as vscode from 'vscode';
import { DefaultClient, FormatParams, FormatDocumentRequest, cachedEditorConfigSettings } from '../client';
import { CppSettings } from '../settings';
import * as editorConfig from 'editorconfig';
import { DefaultClient, FormatParams, FormatDocumentRequest } from '../client';
import { CppSettings, getEditorConfigSettings } from '../settings';
export class DocumentFormattingEditProvider implements vscode.DocumentFormattingEditProvider {
private client: DefaultClient;
@ -16,9 +15,12 @@ export class DocumentFormattingEditProvider implements vscode.DocumentFormatting
public async provideDocumentFormattingEdits(document: vscode.TextDocument, options: vscode.FormattingOptions, token: vscode.CancellationToken): Promise<vscode.TextEdit[]> {
await this.client.awaitUntilLanguageClientReady();
const filePath: string = document.uri.fsPath;
const settings: CppSettings = new CppSettings(this.client.RootUri);
const useVcFormat: boolean = settings.useVcFormat(document);
const configCallBack = async (editorConfigSettings: any | undefined) => {
const params: FormatParams = {
settings: { ...editorConfigSettings },
editorConfigSettings: { ...editorConfigSettings },
useVcFormat: useVcFormat,
uri: document.uri.toString(),
insertSpaces: options.insertSpaces,
tabSize: options.tabSize,
@ -70,15 +72,10 @@ export class DocumentFormattingEditProvider implements vscode.DocumentFormatting
}
return results;
};
const settings: CppSettings = new CppSettings();
if (settings.formattingEngine !== "vcFormat") {
if (!useVcFormat) {
return configCallBack(undefined);
} else {
let editorConfigSettings: any = cachedEditorConfigSettings.get(filePath);
if (!editorConfigSettings) {
editorConfigSettings = await editorConfig.parse(filePath);
cachedEditorConfigSettings.set(filePath, editorConfigSettings);
}
const editorConfigSettings: any = getEditorConfigSettings(filePath);
return configCallBack(editorConfigSettings);
}
}

Просмотреть файл

@ -3,9 +3,8 @@
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import * as vscode from 'vscode';
import { DefaultClient, FormatParams, FormatRangeRequest, cachedEditorConfigSettings } from '../client';
import { CppSettings } from '../settings';
import * as editorConfig from 'editorconfig';
import { DefaultClient, FormatParams, FormatRangeRequest } from '../client';
import { CppSettings, getEditorConfigSettings } from '../settings';
export class DocumentRangeFormattingEditProvider implements vscode.DocumentRangeFormattingEditProvider {
private client: DefaultClient;
@ -16,9 +15,12 @@ export class DocumentRangeFormattingEditProvider implements vscode.DocumentRange
public async provideDocumentRangeFormattingEdits(document: vscode.TextDocument, range: vscode.Range, options: vscode.FormattingOptions, token: vscode.CancellationToken): Promise<vscode.TextEdit[]> {
await this.client.awaitUntilLanguageClientReady();
const filePath: string = document.uri.fsPath;
const settings: CppSettings = new CppSettings(this.client.RootUri);
const useVcFormat: boolean = settings.useVcFormat(document);
const configCallBack = async (editorConfigSettings: any | undefined) => {
const params: FormatParams = {
settings: { ...editorConfigSettings },
editorConfigSettings: { ...editorConfigSettings },
useVcFormat: useVcFormat,
uri: document.uri.toString(),
insertSpaces: options.insertSpaces,
tabSize: options.tabSize,
@ -44,15 +46,10 @@ export class DocumentRangeFormattingEditProvider implements vscode.DocumentRange
});
return result;
};
const settings: CppSettings = new CppSettings();
if (settings.formattingEngine !== "vcFormat") {
if (!useVcFormat) {
return configCallBack(undefined);
} else {
let editorConfigSettings: any = cachedEditorConfigSettings.get(filePath);
if (!editorConfigSettings) {
editorConfigSettings = await editorConfig.parse(filePath);
cachedEditorConfigSettings.set(filePath, editorConfigSettings);
}
const editorConfigSettings: any = getEditorConfigSettings(filePath);
return configCallBack(editorConfigSettings);
}
};

Просмотреть файл

@ -3,9 +3,8 @@
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import * as vscode from 'vscode';
import {DefaultClient, FormatParams, FormatOnTypeRequest, cachedEditorConfigSettings} from '../client';
import { CppSettings } from '../settings';
import * as editorConfig from 'editorconfig';
import {DefaultClient, FormatParams, FormatOnTypeRequest} from '../client';
import { CppSettings, getEditorConfigSettings } from '../settings';
export class OnTypeFormattingEditProvider implements vscode.OnTypeFormattingEditProvider {
private client: DefaultClient;
@ -16,9 +15,12 @@ export class OnTypeFormattingEditProvider implements vscode.OnTypeFormattingEdit
public async provideOnTypeFormattingEdits(document: vscode.TextDocument, position: vscode.Position, ch: string, options: vscode.FormattingOptions, token: vscode.CancellationToken): Promise<vscode.TextEdit[]> {
await this.client.awaitUntilLanguageClientReady();
const filePath: string = document.uri.fsPath;
const settings: CppSettings = new CppSettings(this.client.RootUri);
const useVcFormat: boolean = settings.useVcFormat(document);
const configCallBack = async (editorConfigSettings: any | undefined) => {
const params: FormatParams = {
settings: { ...editorConfigSettings },
editorConfigSettings: { ...editorConfigSettings },
useVcFormat: useVcFormat,
uri: document.uri.toString(),
insertSpaces: options.insertSpaces,
tabSize: options.tabSize,
@ -44,8 +46,7 @@ export class OnTypeFormattingEditProvider implements vscode.OnTypeFormattingEdit
});
return result;
};
const settings: CppSettings = new CppSettings();
if (settings.formattingEngine !== "vcFormat") {
if (!useVcFormat) {
// If not using vcFormat, only process on-type requests for ';'
if (ch !== ';') {
const result: vscode.TextEdit[] = [];
@ -54,11 +55,7 @@ export class OnTypeFormattingEditProvider implements vscode.OnTypeFormattingEdit
return configCallBack(undefined);
}
} else {
let editorConfigSettings: any = cachedEditorConfigSettings.get(filePath);
if (!editorConfigSettings) {
editorConfigSettings = await editorConfig.parse(filePath);
cachedEditorConfigSettings.set(filePath, editorConfigSettings);
}
const editorConfigSettings: any = getEditorConfigSettings(filePath);
return configCallBack(editorConfigSettings);
}
}

Просмотреть файл

@ -24,7 +24,7 @@ import { SourceFileConfigurationItem, WorkspaceBrowseConfiguration, SourceFileCo
import { Status, IntelliSenseStatus } from 'vscode-cpptools/out/testApi';
import * as util from '../common';
import * as configs from './configurations';
import { CppSettings, OtherSettings } from './settings';
import { CppSettings, getEditorConfigSettings, OtherSettings } from './settings';
import * as telemetry from '../telemetry';
import { PersistentState, PersistentFolderState } from './persistentState';
import { UI, getUI } from './ui';
@ -42,7 +42,6 @@ import * as os from 'os';
import * as refs from './references';
import * as nls from 'vscode-nls';
import { lookupString, localizedStringCount } from '../nativeStrings';
import * as editorConfig from 'editorconfig';
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
@ -68,6 +67,7 @@ let workspaceDisposables: vscode.Disposable[] = [];
export let workspaceReferences: refs.ReferencesManager;
export const openFileVersions: Map<string, number> = new Map<string, number>();
export const cachedEditorConfigSettings: Map<string, any> = new Map<string, any>();
export const cachedEditorConfigLookups: Map<string, boolean> = new Map<string, boolean>();
export function disposeWorkspaceData(): void {
workspaceDisposables.forEach((d) => d.dispose());
@ -350,7 +350,8 @@ export interface FormatParams {
character: string;
insertSpaces: boolean;
tabSize: number;
settings: any;
editorConfigSettings: any;
useVcFormat: boolean;
}
interface TextEdit {
@ -1021,7 +1022,6 @@ export class DefaultClient implements Client {
const settings_indentAccessSpecifiers: boolean[] = [];
const settings_indentNamespaceContents: boolean[] = [];
const settings_indentPreserveComments: boolean[] = [];
const settings_formattingEngine: (string | undefined)[] = [];
const settings_newLineBeforeOpenBraceNamespace: (string | undefined)[] = [];
const settings_newLineBeforeOpenBraceType: (string | undefined)[] = [];
const settings_newLineBeforeOpenBraceFunction: (string | undefined)[] = [];
@ -1084,7 +1084,6 @@ export class DefaultClient implements Client {
for (const setting of settings) {
settings_clangFormatPath.push(util.resolveVariables(setting.clangFormatPath, this.AdditionalEnvironment));
settings_formattingEngine.push(setting.formattingEngine);
settings_indentBraces.push(setting.vcFormatIndentBraces);
settings_indentWithinParentheses.push(setting.vcFormatIndentWithinParentheses);
settings_indentPreserveWithinParentheses.push(setting.vcFormatIndentPreserveWithinParentheses);
@ -1194,7 +1193,6 @@ export class DefaultClient implements Client {
initializationOptions: {
clang_format_path: settings_clangFormatPath,
clang_format_style: settings_clangFormatStyle,
formatting: settings_formattingEngine,
vcFormat: {
indent: {
braces: settings_indentBraces,
@ -1912,6 +1910,7 @@ export class DefaultClient implements Client {
text: document.getText()
}
};
this.updateActiveDocumentTextOptions();
this.notifyWhenLanguageClientReady(() => this.languageClient.sendNotification(DidOpenNotification, params));
this.trackedDocuments.add(document);
}
@ -2065,10 +2064,15 @@ export class DefaultClient implements Client {
false /* ignoreDeleteEvents */);
this.rootPathFileWatcher.onDidCreate(async (uri) => {
if (path.basename(uri.fsPath).toLowerCase() === ".editorconfig") {
const fileName: string = path.basename(uri.fsPath).toLowerCase();
if (fileName === ".editorconfig") {
cachedEditorConfigSettings.clear();
cachedEditorConfigLookups.clear();
await this.updateActiveDocumentTextOptions();
}
if (fileName === ".clang-format" || fileName === "_clang-format") {
cachedEditorConfigLookups.clear();
}
this.languageClient.sendNotification(FileCreatedNotification, { uri: uri.toString() });
});
@ -2085,12 +2089,12 @@ export class DefaultClient implements Client {
}
this.rootPathFileWatcher.onDidChange(async (uri) => {
const dotIndex: number = uri.fsPath.lastIndexOf('.');
if (path.basename(uri.fsPath).toLowerCase() === ".editorconfig") {
const fileName: string = path.basename(uri.fsPath).toLowerCase();
if (fileName === ".editorconfig") {
cachedEditorConfigSettings.clear();
cachedEditorConfigLookups.clear();
await this.updateActiveDocumentTextOptions();
}
if (dotIndex !== -1) {
const ext: string = uri.fsPath.substr(dotIndex + 1);
if (this.associations_for_did_change?.has(ext)) {
@ -2107,10 +2111,14 @@ export class DefaultClient implements Client {
});
this.rootPathFileWatcher.onDidDelete((uri) => {
if (path.basename(uri.fsPath).toLowerCase() === ".editorconfig") {
const fileName: string = path.basename(uri.fsPath).toLowerCase();
if (fileName === ".editorconfig") {
cachedEditorConfigSettings.clear();
cachedEditorConfigLookups.clear();
}
if (fileName === ".clang-format" || fileName === "_clang-format") {
cachedEditorConfigLookups.clear();
}
this.languageClient.sendNotification(FileDeletedNotification, { uri: uri.toString() });
});
@ -2398,13 +2406,8 @@ export class DefaultClient implements Client {
|| editor.document.languageId === "cuda-cpp")) {
// If using vcFormat, check for a ".editorconfig" file, and apply those text options to the active document.
const settings: CppSettings = new CppSettings(this.RootUri);
if (settings.formattingEngine === "vcFormat") {
const fsPath: string = editor.document.uri.fsPath;
let editorConfigSettings: any = cachedEditorConfigSettings.get(fsPath);
if (!editorConfigSettings) {
editorConfigSettings = await editorConfig.parse(fsPath);
cachedEditorConfigSettings.set(fsPath, editorConfigSettings);
}
if (settings.useVcFormat(editor.document)) {
const editorConfigSettings: any = getEditorConfigSettings(editor.document.uri.fsPath);
if (editorConfigSettings.indent_style === "space" || editorConfigSettings.indent_style === "tab") {
editor.options.insertSpaces = editorConfigSettings.indent_style === "space";
if (editorConfigSettings.indent_size === "tab") {

Просмотреть файл

@ -14,7 +14,7 @@ import { TreeNode, NodeType } from './referencesModel';
import { UI, getUI } from './ui';
import { Client } from './client';
import { ClientCollection } from './clientCollection';
import { CppSettings, generateEditorConfig, OtherSettings } from './settings';
import { CppSettings, OtherSettings } from './settings';
import { PersistentWorkspaceState, PersistentState } from './persistentState';
import { getLanguageConfig } from './languageConfig';
import { getCustomConfigProviders } from './customProviders';
@ -920,9 +920,13 @@ function onEditConfiguration(viewColumn: vscode.ViewColumn = vscode.ViewColumn.A
function onGenerateEditorConfig(): void {
onActivationEvent();
if (!isFolderOpen()) {
generateEditorConfig();
const settings: CppSettings = new CppSettings();
settings.generateEditorConfig();
} else {
selectClient().then(client => generateEditorConfig(client.RootUri));
selectClient().then(client => {
const settings: CppSettings = new CppSettings(client.RootUri);
settings.generateEditorConfig();
});
}
}

Просмотреть файл

@ -11,6 +11,15 @@ import * as os from 'os';
import * as which from 'which';
import { execSync } from 'child_process';
import * as semver from 'semver';
import * as fs from 'fs';
import * as path from 'path';
import { cachedEditorConfigLookups, cachedEditorConfigSettings } from './client';
import * as editorConfig from 'editorconfig';
import { PersistentState } from './persistentState';
import * as nls from 'vscode-nls';
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
function getTarget(): vscode.ConfigurationTarget {
return (vscode.workspace.workspaceFolders) ? vscode.ConfigurationTarget.WorkspaceFolder : vscode.ConfigurationTarget.Global;
@ -23,7 +32,7 @@ class Settings {
* create the Settings object.
* @param resource The path to a resource to which the settings should apply, or undefined if global settings are desired
*/
constructor(section: string, resource?: vscode.Uri) {
constructor(section: string, public resource?: vscode.Uri) {
this.settings = vscode.workspace.getConfiguration(section, resource ? resource : undefined);
}
@ -452,6 +461,224 @@ export class CppSettings extends Settings {
public update<T>(name: string, value: T): void {
super.Section.update(name, value);
}
public populateEditorConfig(document: vscode.TextDocument): void {
// Set up a map of setting names and values. Parse through the document line-by-line, looking for
// existing occurrences to replace. Replaced occurrences are removed from the map. If any remain when
// done, they are added as a new section at the end of the file. The file is opened with unsaved
// edits, so the user may edit or undo if we made a mistake.
const settingMap: Map<string, string> = new Map<string, string>();
settingMap.set("cpp_indent_braces", this.vcFormatIndentBraces.toString());
settingMap.set("cpp_indent_multi_line_relative_to", mapIndentationReferenceToEditorConfig(this.vcFormatIndentMultiLineRelativeTo));
settingMap.set("cpp_indent_within_parentheses", this.vcFormatIndentWithinParentheses.toString());
settingMap.set("cpp_indent_preserve_within_parentheses", this.vcFormatIndentPreserveWithinParentheses.toString());
settingMap.set("cpp_indent_case_labels", this.vcFormatIndentCaseLabels.toString());
settingMap.set("cpp_indent_case_contents", this.vcFormatIndentCaseContents.toString());
settingMap.set("cpp_indent_case_contents_when_block", this.vcFormatIndentCaseContentsWhenBlock.toString());
settingMap.set("cpp_indent_lambda_braces_when_parameter", this.vcFormatIndentLambdaBracesWhenParameter.toString());
settingMap.set("cpp_indent_goto_labels", mapIndentToEditorConfig(this.vcFormatIndentGotoLables));
settingMap.set("cpp_indent_preprocessor", mapIndentToEditorConfig(this.vcFormatIndentPreprocessor));
settingMap.set("cpp_indent_access_specifiers", this.vcFormatIndentAccessSpecifiers.toString());
settingMap.set("cpp_indent_namespace_contents", this.vcFormatIndentNamespaceContents.toString());
settingMap.set("cpp_indent_preserve_comments", this.vcFormatIndentPreserveComments.toString());
settingMap.set("cpp_new_line_before_open_brace_namespace", mapNewOrSameLineToEditorConfig(this.vcFormatNewlineBeforeOpenBraceNamespace));
settingMap.set("cpp_new_line_before_open_brace_type", mapNewOrSameLineToEditorConfig(this.vcFormatNewlineBeforeOpenBraceType));
settingMap.set("cpp_new_line_before_open_brace_function", mapNewOrSameLineToEditorConfig(this.vcFormatNewlineBeforeOpenBraceFunction));
settingMap.set("cpp_new_line_before_open_brace_block", mapNewOrSameLineToEditorConfig(this.vcFormatNewlineBeforeOpenBraceBlock));
settingMap.set("cpp_new_line_before_open_brace_lambda", mapNewOrSameLineToEditorConfig(this.vcFormatNewlineBeforeOpenBraceLambda));
settingMap.set("cpp_new_line_scope_braces_on_separate_lines", this.vcFormatNewlineScopeBracesOnSeparateLines.toString());
settingMap.set("cpp_new_line_close_brace_same_line_empty_type", this.vcFormatNewlineCloseBraceSameLineEmptyType.toString());
settingMap.set("cpp_new_line_close_brace_same_line_empty_function", this.vcFormatNewlineCloseBraceSameLineEmptyFunction.toString());
settingMap.set("cpp_new_line_before_catch", this.vcFormatNewlineBeforeCatch.toString().toString());
settingMap.set("cpp_new_line_before_else", this.vcFormatNewlineBeforeElse.toString().toString());
settingMap.set("cpp_new_line_before_while_in_do_while", this.vcFormatNewlineBeforeWhileInDoWhile.toString());
settingMap.set("cpp_space_before_function_open_parenthesis", this.vcFormatSpaceBeforeFunctionOpenParenthesis.toString());
settingMap.set("cpp_space_within_parameter_list_parentheses", this.vcFormatSpaceWithinParameterListParentheses.toString());
settingMap.set("cpp_space_between_empty_parameter_list_parentheses", this.vcFormatSpaceBetweenEmptyParameterListParentheses.toString());
settingMap.set("cpp_space_after_keywords_in_control_flow_statements", this.vcFormatSpaceAfterKeywordsInControlFlowStatements.toString());
settingMap.set("cpp_space_within_control_flow_statement_parentheses", this.vcFormatSpaceWithinControlFlowStatementParentheses.toString());
settingMap.set("cpp_space_before_lambda_open_parenthesis", this.vcFormatSpaceBeforeLambdaOpenParenthesis.toString());
settingMap.set("cpp_space_within_cast_parentheses", this.vcFormatSpaceWithinCastParentheses.toString());
settingMap.set("cpp_space_after_cast_close_parenthesis", this.vcFormatSpaceAfterCastCloseParenthesis.toString());
settingMap.set("cpp_space_within_expression_parentheses", this.vcFormatSpaceWithinExpressionParentheses.toString());
settingMap.set("cpp_space_before_block_open_brace", this.vcFormatSpaceBeforeBlockOpenBrace.toString());
settingMap.set("cpp_space_between_empty_braces", this.vcFormatSpaceBetweenEmptyBraces.toString());
settingMap.set("cpp_space_before_initializer_list_open_brace", this.vcFormatSpaceBeforeInitializerListOpenBrace.toString());
settingMap.set("cpp_space_within_initializer_list_braces", this.vcFormatSpaceWithinInitializerListBraces.toString());
settingMap.set("cpp_space_preserve_in_initializer_list", this.vcFormatSpacePreserveInInitializerList.toString());
settingMap.set("cpp_space_before_open_square_bracket", this.vcFormatSpaceBeforeOpenSquareBracket.toString());
settingMap.set("cpp_space_within_square_brackets", this.vcFormatSpaceWithinSquareBrackets.toString());
settingMap.set("cpp_space_before_empty_square_brackets", this.vcFormatSpaceBeforeEmptySquareBrackets.toString());
settingMap.set("cpp_space_between_empty_square_brackets", this.vcFormatSpaceBetweenEmptySquareBrackets.toString());
settingMap.set("cpp_space_group_square_brackets", this.vcFormatSpaceGroupSquareBrackets.toString());
settingMap.set("cpp_space_within_lambda_brackets", this.vcFormatSpaceWithinLambdaBrackets.toString());
settingMap.set("cpp_space_between_empty_lambda_brackets", this.vcFormatSpaceBetweenEmptyLambdaBrackets.toString());
settingMap.set("cpp_space_before_comma", this.vcFormatSpaceBeforeComma.toString());
settingMap.set("cpp_space_after_comma", this.vcFormatSpaceAfterComma.toString());
settingMap.set("cpp_space_remove_around_member_operators", this.vcFormatSpaceRemoveAroundMemberOperators.toString());
settingMap.set("cpp_space_before_inheritance_colon", this.vcFormatSpaceBeforeInheritanceColon.toString());
settingMap.set("cpp_space_before_constructor_colon", this.vcFormatSpaceBeforeConstructorColon.toString());
settingMap.set("cpp_space_remove_before_semicolon", this.vcFormatSpaceRemoveBeforeSemicolon.toString());
settingMap.set("cpp_space_after_semicolon", this.vcFormatSpaceInsertAfterSemicolon.toString());
settingMap.set("cpp_space_remove_around_unary_operator", this.vcFormatSpaceRemoveAroundUnaryOperator.toString());
settingMap.set("cpp_space_around_binary_operator", this.vcFormatSpaceAroundBinaryOperator.toString());
settingMap.set("cpp_space_around_assignment_operator", this.vcFormatSpaceAroundAssignmentOperator.toString());
settingMap.set("cpp_space_pointer_reference_alignment", this.vcFormatSpacePointerReferenceAlignment.toString());
settingMap.set("cpp_space_around_ternary_operator", this.vcFormatSpaceAroundTernaryOperator.toString());
settingMap.set("cpp_wrap_preserve_blocks", mapWrapToEditorConfig(this.vcFormatWrapPreserveBlocks));
const edits: vscode.WorkspaceEdit = new vscode.WorkspaceEdit();
let isInWildcardSection: boolean = false;
let trailingBlankLines: number = 0;
// Cycle through lines using document.lineAt(), to avoid issues mapping edits back to lines.
for (let i: number = 0; i < document.lineCount; ++i) {
let textLine: vscode.TextLine = document.lineAt(i);
if (textLine.range.end.character === 0) {
trailingBlankLines++;
continue;
}
trailingBlankLines = 0;
// Keep track of whether we left off in a wildcard section, so we don't output a redundant one.
let text: string = textLine.text.trim();
if (text.startsWith("[")) {
isInWildcardSection = text.startsWith("[*]");
continue;
}
for (const setting of settingMap) {
if (text.startsWith(setting[0])) {
// The next character must be white space or '=', otherwise it's a partial match.
if (text.length > setting[0].length) {
const c: string = text[setting[0].length];
if (c !== '=' && c.trim() !== "") {
continue;
}
}
edits.replace(document.uri, textLine.range, setting[0] + "=" + setting[1]);
// Because we're going to remove this setting from the map,
// scan ahead to update any other sections it may need to be updated in.
for (let j: number = i + 1; j < document.lineCount; ++j) {
textLine = document.lineAt(j);
text = textLine.text.trim();
if (text.startsWith(setting[0])) {
// The next character must be white space or '=', otherwise it's a partial match.
if (text.length > setting[0].length) {
const c: string = text[setting[0].length];
if (c !== '=' && c.trim() !== "") {
continue;
}
}
edits.replace(document.uri, textLine.range, setting[0] + "=" + setting[1]);
}
}
settingMap.delete(setting[0]);
break;
}
}
if (settingMap.size === 0) {
break;
}
}
if (settingMap.size > 0) {
let remainingSettingsText: string = "";
if (document.lineCount > 0) {
while (++trailingBlankLines < 2) {
remainingSettingsText += "\n";
}
}
if (!isInWildcardSection) {
remainingSettingsText += "[*]\n";
}
for (const setting of settingMap) {
remainingSettingsText += setting[0] + "=" + setting[1] + "\n";
}
const lastPosition: vscode.Position = document.lineAt(document.lineCount - 1).range.end;
edits.insert(document.uri, lastPosition, remainingSettingsText);
}
vscode.workspace.applyEdit(edits).then(() => vscode.window.showTextDocument(document));
}
public async generateEditorConfig(): Promise<void> {
let document: vscode.TextDocument;
if (this.resource) {
// If a folder is open and '.editorconfig' exists at the root, use that.
const uri: vscode.Uri = vscode.Uri.joinPath(this.resource, ".editorconfig");
const edits: vscode.WorkspaceEdit = new vscode.WorkspaceEdit();
edits.createFile(uri, { ignoreIfExists: true, overwrite: false });
try {
await vscode.workspace.applyEdit(edits);
document = await vscode.workspace.openTextDocument(uri);
} catch (e) {
document = await vscode.workspace.openTextDocument();
}
} else {
document = await vscode.workspace.openTextDocument();
}
this.populateEditorConfig(document);
}
// If formattingEngine is set to "Default", searches for .editorconfig with vcFormat
// entries, or a .clang-format file, to determine which settings to use.
// This is intentionally not async to avoid races due to multiple entrancy.
public useVcFormat(document: vscode.TextDocument): boolean {
if (this.formattingEngine !== "Default") {
return this.formattingEngine === "vcFormat";
}
if (this.clangFormatStyle !== "file") {
// If a clang-format style other than file is specified, don't try to switch to vcFormat.
return false;
}
const cachedValue: boolean | undefined = cachedEditorConfigLookups.get(document.uri.fsPath);
if (cachedValue !== undefined) {
return cachedValue;
}
let foundEditorConfigWithVcFormatSettings: boolean = false;
const findConfigFile: (parentPath: string) => boolean = (parentPath: string) => {
const editorConfigPath: string = path.join(parentPath, ".editorconfig");
if (fs.existsSync(editorConfigPath)) {
const editorConfigSettings: any = getEditorConfigSettings(document.uri.fsPath);
const keys: string[] = Object.keys(editorConfigSettings);
for (let i: number = 0; i < keys.length; ++i) {
if (keys[i].startsWith("cpp_")) {
foundEditorConfigWithVcFormatSettings = true;
const didEditorConfigNotice: PersistentState<boolean> = new PersistentState<boolean>("Cpp.didEditorConfigNotice", false);
if (!didEditorConfigNotice.Value) {
vscode.window.showInformationMessage(localize("editorconfig.default.behavior",
"Code formatting is using settings from .editorconfig instead of .clang-format. For more information, see the documentation for the `Default` value of the 'C_Cpp.formatting' setting."));
didEditorConfigNotice.Value = true;
}
return true;
}
}
if (editorConfigSettings.root?.toLowerCase() === "true") {
return true;
}
} else {
const clangFormatPath1: string = path.join(parentPath, ".clang-format");
if (fs.existsSync(clangFormatPath1)) {
return true;
} else {
const clangFormatPath2: string = path.join(parentPath, "_clang-format");
if (fs.existsSync(clangFormatPath2)) {
return true;
}
}
}
return false;
};
// Scan parent paths to see which we find first, ".clang-format" or ".editorconfig"
const fsPath: string = document.uri.fsPath;
let parentPath: string = path.dirname(fsPath);
let currentParentPath: string;
do {
currentParentPath = parentPath;
if (findConfigFile(currentParentPath)) {
cachedEditorConfigLookups.set(document.uri.fsPath, foundEditorConfigWithVcFormatSettings);
return foundEditorConfigWithVcFormatSettings;
}
parentPath = path.dirname(parentPath);
} while (parentPath !== currentParentPath);
cachedEditorConfigLookups.set(document.uri.fsPath, false);
return false;
}
}
export interface TextMateRuleSettings {
@ -547,159 +774,14 @@ function mapWrapToEditorConfig(value: string | undefined): string {
return "never";
}
function populateEditorConfig(rootUri: vscode.Uri | undefined, document: vscode.TextDocument): void {
// Set up a map of setting names and values. Parse through the document line-by-line, looking for
// existing occurrences to replace. Replaced occurrences are removed from the map. If any remain when
// done, they are added as a new section at the end of the file. The file is opened with unsaved
// edits, so the user may edit or undo if we made a mistake.
const settings: CppSettings = new CppSettings(rootUri);
const settingMap: Map<string, string> = new Map<string, string>();
settingMap.set("cpp_indent_braces", settings.vcFormatIndentBraces.toString());
settingMap.set("cpp_indent_multi_line_relative_to", mapIndentationReferenceToEditorConfig(settings.vcFormatIndentMultiLineRelativeTo));
settingMap.set("cpp_indent_within_parentheses", settings.vcFormatIndentWithinParentheses.toString());
settingMap.set("cpp_indent_preserve_within_parentheses", settings.vcFormatIndentPreserveWithinParentheses.toString());
settingMap.set("cpp_indent_case_labels", settings.vcFormatIndentCaseLabels.toString());
settingMap.set("cpp_indent_case_contents", settings.vcFormatIndentCaseContents.toString());
settingMap.set("cpp_indent_case_contents_when_block", settings.vcFormatIndentCaseContentsWhenBlock.toString());
settingMap.set("cpp_indent_lambda_braces_when_parameter", settings.vcFormatIndentLambdaBracesWhenParameter.toString());
settingMap.set("cpp_indent_goto_labels", mapIndentToEditorConfig(settings.vcFormatIndentGotoLables));
settingMap.set("cpp_indent_preprocessor", mapIndentToEditorConfig(settings.vcFormatIndentPreprocessor));
settingMap.set("cpp_indent_access_specifiers", settings.vcFormatIndentAccessSpecifiers.toString());
settingMap.set("cpp_indent_namespace_contents", settings.vcFormatIndentNamespaceContents.toString());
settingMap.set("cpp_indent_preserve_comments", settings.vcFormatIndentPreserveComments.toString());
settingMap.set("cpp_new_line_before_open_brace_namespace", mapNewOrSameLineToEditorConfig(settings.vcFormatNewlineBeforeOpenBraceNamespace));
settingMap.set("cpp_new_line_before_open_brace_type", mapNewOrSameLineToEditorConfig(settings.vcFormatNewlineBeforeOpenBraceType));
settingMap.set("cpp_new_line_before_open_brace_function", mapNewOrSameLineToEditorConfig(settings.vcFormatNewlineBeforeOpenBraceFunction));
settingMap.set("cpp_new_line_before_open_brace_block", mapNewOrSameLineToEditorConfig(settings.vcFormatNewlineBeforeOpenBraceBlock));
settingMap.set("cpp_new_line_before_open_brace_lambda", mapNewOrSameLineToEditorConfig(settings.vcFormatNewlineBeforeOpenBraceLambda));
settingMap.set("cpp_new_line_scope_braces_on_separate_lines", settings.vcFormatNewlineScopeBracesOnSeparateLines.toString());
settingMap.set("cpp_new_line_close_brace_same_line_empty_type", settings.vcFormatNewlineCloseBraceSameLineEmptyType.toString());
settingMap.set("cpp_new_line_close_brace_same_line_empty_function", settings.vcFormatNewlineCloseBraceSameLineEmptyFunction.toString());
settingMap.set("cpp_new_line_before_catch", settings.vcFormatNewlineBeforeCatch.toString().toString());
settingMap.set("cpp_new_line_before_else", settings.vcFormatNewlineBeforeElse.toString().toString());
settingMap.set("cpp_new_line_before_while_in_do_while", settings.vcFormatNewlineBeforeWhileInDoWhile.toString());
settingMap.set("cpp_space_before_function_open_parenthesis", settings.vcFormatSpaceBeforeFunctionOpenParenthesis.toString());
settingMap.set("cpp_space_within_parameter_list_parentheses", settings.vcFormatSpaceWithinParameterListParentheses.toString());
settingMap.set("cpp_space_between_empty_parameter_list_parentheses", settings.vcFormatSpaceBetweenEmptyParameterListParentheses.toString());
settingMap.set("cpp_space_after_keywords_in_control_flow_statements", settings.vcFormatSpaceAfterKeywordsInControlFlowStatements.toString());
settingMap.set("cpp_space_within_control_flow_statement_parentheses", settings.vcFormatSpaceWithinControlFlowStatementParentheses.toString());
settingMap.set("cpp_space_before_lambda_open_parenthesis", settings.vcFormatSpaceBeforeLambdaOpenParenthesis.toString());
settingMap.set("cpp_space_within_cast_parentheses", settings.vcFormatSpaceWithinCastParentheses.toString());
settingMap.set("cpp_space_after_cast_close_parenthesis", settings.vcFormatSpaceAfterCastCloseParenthesis.toString());
settingMap.set("cpp_space_within_expression_parentheses", settings.vcFormatSpaceWithinExpressionParentheses.toString());
settingMap.set("cpp_space_before_block_open_brace", settings.vcFormatSpaceBeforeBlockOpenBrace.toString());
settingMap.set("cpp_space_between_empty_braces", settings.vcFormatSpaceBetweenEmptyBraces.toString());
settingMap.set("cpp_space_before_initializer_list_open_brace", settings.vcFormatSpaceBeforeInitializerListOpenBrace.toString());
settingMap.set("cpp_space_within_initializer_list_braces", settings.vcFormatSpaceWithinInitializerListBraces.toString());
settingMap.set("cpp_space_preserve_in_initializer_list", settings.vcFormatSpacePreserveInInitializerList.toString());
settingMap.set("cpp_space_before_open_square_bracket", settings.vcFormatSpaceBeforeOpenSquareBracket.toString());
settingMap.set("cpp_space_within_square_brackets", settings.vcFormatSpaceWithinSquareBrackets.toString());
settingMap.set("cpp_space_before_empty_square_brackets", settings.vcFormatSpaceBeforeEmptySquareBrackets.toString());
settingMap.set("cpp_space_between_empty_square_brackets", settings.vcFormatSpaceBetweenEmptySquareBrackets.toString());
settingMap.set("cpp_space_group_square_brackets", settings.vcFormatSpaceGroupSquareBrackets.toString());
settingMap.set("cpp_space_within_lambda_brackets", settings.vcFormatSpaceWithinLambdaBrackets.toString());
settingMap.set("cpp_space_between_empty_lambda_brackets", settings.vcFormatSpaceBetweenEmptyLambdaBrackets.toString());
settingMap.set("cpp_space_before_comma", settings.vcFormatSpaceBeforeComma.toString());
settingMap.set("cpp_space_after_comma", settings.vcFormatSpaceAfterComma.toString());
settingMap.set("cpp_space_remove_around_member_operators", settings.vcFormatSpaceRemoveAroundMemberOperators.toString());
settingMap.set("cpp_space_before_inheritance_colon", settings.vcFormatSpaceBeforeInheritanceColon.toString());
settingMap.set("cpp_space_before_constructor_colon", settings.vcFormatSpaceBeforeConstructorColon.toString());
settingMap.set("cpp_space_remove_before_semicolon", settings.vcFormatSpaceRemoveBeforeSemicolon.toString());
settingMap.set("cpp_space_after_semicolon", settings.vcFormatSpaceInsertAfterSemicolon.toString());
settingMap.set("cpp_space_remove_around_unary_operator", settings.vcFormatSpaceRemoveAroundUnaryOperator.toString());
settingMap.set("cpp_space_around_binary_operator", settings.vcFormatSpaceAroundBinaryOperator.toString());
settingMap.set("cpp_space_around_assignment_operator", settings.vcFormatSpaceAroundAssignmentOperator.toString());
settingMap.set("cpp_space_pointer_reference_alignment", settings.vcFormatSpacePointerReferenceAlignment.toString());
settingMap.set("cpp_space_around_ternary_operator", settings.vcFormatSpaceAroundTernaryOperator.toString());
settingMap.set("cpp_wrap_preserve_blocks", mapWrapToEditorConfig(settings.vcFormatWrapPreserveBlocks));
const edits: vscode.WorkspaceEdit = new vscode.WorkspaceEdit();
let isInWildcardSection: boolean = false;
let trailingBlankLines: number = 0;
// Cycle through lines using document.lineAt(), to avoid issues mapping edits back to lines.
for (let i: number = 0; i < document.lineCount; ++i) {
let textLine: vscode.TextLine = document.lineAt(i);
if (textLine.range.end.character === 0) {
trailingBlankLines++;
continue;
}
trailingBlankLines = 0;
// Keep track of whether we left off in a wildcard section, so we don't output a redundant one.
let text: string = textLine.text.trim();
if (text.startsWith("[")) {
isInWildcardSection = text.startsWith("[*]");
continue;
}
for (const setting of settingMap) {
if (text.startsWith(setting[0])) {
// The next character must be white space or '=', otherwise it's a partial match.
if (text.length > setting[0].length) {
const c: string = text[setting[0].length];
if (c !== '=' && c.trim() !== "") {
continue;
}
}
edits.replace(document.uri, textLine.range, setting[0] + "=" + setting[1]);
// Because we're going to remove this setting from the map,
// scan ahead to update any other sections it may need to be updated in.
for (let j: number = i + 1; j < document.lineCount; ++j) {
textLine = document.lineAt(j);
text = textLine.text.trim();
if (text.startsWith(setting[0])) {
// The next character must be white space or '=', otherwise it's a partial match.
if (text.length > setting[0].length) {
const c: string = text[setting[0].length];
if (c !== '=' && c.trim() !== "") {
continue;
}
}
edits.replace(document.uri, textLine.range, setting[0] + "=" + setting[1]);
}
}
settingMap.delete(setting[0]);
break;
}
}
if (settingMap.size === 0) {
break;
}
// Look up the appropriate .editorconfig settings for the specified file.
// This is intentionally not async to avoid races due to multiple entrancy.
export function getEditorConfigSettings(fsPath: string): Promise<any> {
let editorConfigSettings: any = cachedEditorConfigSettings.get(fsPath);
if (!editorConfigSettings) {
editorConfigSettings = editorConfig.parseSync(fsPath);
cachedEditorConfigSettings.set(fsPath, editorConfigSettings);
}
if (settingMap.size > 0) {
let remainingSettingsText: string = "";
if (document.lineCount > 0) {
while (++trailingBlankLines < 2) {
remainingSettingsText += "\n";
}
}
if (!isInWildcardSection) {
remainingSettingsText += "[*]\n";
}
for (const setting of settingMap) {
remainingSettingsText += setting[0] + "=" + setting[1] + "\n";
}
const lastPosition: vscode.Position = document.lineAt(document.lineCount - 1).range.end;
edits.insert(document.uri, lastPosition, remainingSettingsText);
}
vscode.workspace.applyEdit(edits).then(() => vscode.window.showTextDocument(document));
return editorConfigSettings;
}
export async function generateEditorConfig(rootUri?: vscode.Uri): Promise<void> {
let document: vscode.TextDocument;
if (rootUri) {
// If a folder is open and '.editorconfig' exists at the root, use that.
const uri: vscode.Uri = vscode.Uri.joinPath(rootUri, ".editorconfig");
const edits: vscode.WorkspaceEdit = new vscode.WorkspaceEdit();
edits.createFile(uri, { ignoreIfExists: true, overwrite: false });
try {
await vscode.workspace.applyEdit(edits);
document = await vscode.workspace.openTextDocument(uri);
} catch (e) {
document = await vscode.workspace.openTextDocument();
}
} else {
document = await vscode.workspace.openTextDocument();
}
populateEditorConfig(rootUri, document);
}