commit 02515c967504a82c58878ed2a2a9032c522704e5 Author: JOLIMAITRE Matthieu Date: Wed Mar 27 02:21:34 2024 +0100 Init. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ef8e601 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/bin +/deno.lock diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c2098a2 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "linux-gcc-x64", + "includePath": [ + "${workspaceFolder}/**" + ], + "compilerPath": "/usr/bin/gcc", + "cStandard": "${default}", + "cppStandard": "${default}", + "intelliSenseMode": "linux-gcc-x64", + "compilerArgs": [ + "" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..0ee4290 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "C/C++ Runner: Debug Session", + "type": "cppdbg", + "request": "launch", + "args": [], + "stopAtEntry": false, + "externalConsole": false, + "cwd": "/home/mb/Projects/011-epita-proj/tgr/src", + "program": "/home/mb/Projects/011-epita-proj/tgr/src/build/Debug/outDebug", + "MIMode": "gdb", + "miDebuggerPath": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0cc0fad --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,60 @@ +{ + "deno.enable": true, + "C_Cpp_Runner.cCompilerPath": "gcc", + "C_Cpp_Runner.cppCompilerPath": "g++", + "C_Cpp_Runner.debuggerPath": "gdb", + "C_Cpp_Runner.cStandard": "", + "C_Cpp_Runner.cppStandard": "", + "C_Cpp_Runner.msvcBatchPath": "", + "C_Cpp_Runner.useMsvc": false, + "C_Cpp_Runner.warnings": [ + "-Wall", + "-Wextra", + "-Wpedantic", + "-Wshadow", + "-Wformat=2", + "-Wcast-align", + "-Wconversion", + "-Wsign-conversion", + "-Wnull-dereference" + ], + "C_Cpp_Runner.msvcWarnings": [ + "/W4", + "/permissive-", + "/w14242", + "/w14287", + "/w14296", + "/w14311", + "/w14826", + "/w44062", + "/w44242", + "/w14905", + "/w14906", + "/w14263", + "/w44265", + "/w14928" + ], + "C_Cpp_Runner.enableWarnings": true, + "C_Cpp_Runner.warningsAsError": false, + "C_Cpp_Runner.compilerArgs": [], + "C_Cpp_Runner.linkerArgs": [], + "C_Cpp_Runner.includePaths": [], + "C_Cpp_Runner.includeSearch": [ + "*", + "**/*" + ], + "C_Cpp_Runner.excludeSearch": [ + "**/build", + "**/build/**", + "**/.*", + "**/.*/**", + "**/.vscode", + "**/.vscode/**" + ], + "C_Cpp_Runner.useAddressSanitizer": false, + "C_Cpp_Runner.useUndefinedSanitizer": false, + "C_Cpp_Runner.useLeakSanitizer": false, + "C_Cpp_Runner.showCompilationTime": false, + "C_Cpp_Runner.useLinkTimeOptimization": false, + "C_Cpp_Runner.msvcSecureNoWarnings": false +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7de40a8 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# CF Guesser + +Clang Format guesser. + +## Description + +This is a script to find a clang-format configuration similar to the one used to +format a specific source file. + +It works by formatting copies of the provided source file with each variant of each properties and keeping the variant that least modifies the source. + +## Usage + +### Dependencies + +- [deno](https://deno.land/) +- [clang-format](https://clang.llvm.org/docs/ClangFormat.html) (comes with most distribution of clang) + +```sh +./bin/cfguesser --help +# [cfguesser.ts] Usage: cfguesser + +./bin/cfguesser source.c clang-format +# [cfguesser.ts] reading input from 'source.c'. +# [cfguesser.ts] scoring properties. +# [cfguesser.ts] Filtering guesses. +# [cfguesser.ts] Certainty 100 % +# [cfguesser.ts] writing config to 'clang-format' +``` + +### Building + +`./build.sh` diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..70f48a0 --- /dev/null +++ b/build.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -e +cd "$(dirname "$(realpath "$0")")" + +mkdir -p "bin" +rm -f bin/cfguesser +deno compile -A -o bin/cfguesser src/cfguesser.ts diff --git a/deno.json b/deno.json new file mode 100644 index 0000000..38d5831 --- /dev/null +++ b/deno.json @@ -0,0 +1,6 @@ +{ + "fmt": { + "useTabs": true, + "lineWidth": 120 + } +} \ No newline at end of file diff --git a/src/cfguesser.ts b/src/cfguesser.ts new file mode 100755 index 0000000..9e4837c --- /dev/null +++ b/src/cfguesser.ts @@ -0,0 +1,39 @@ +#!/bin/env -S deno run -A + +import { all_properties } from "./lib/all_properties.ts"; +import { ConfigFile } from "./lib/config_file.ts"; +import { Guess } from "./lib/guess.ts"; +import { log_from } from "./lib/utils.ts"; +const log = log_from(import.meta, 0); + +async function main() { + const { input_source_path, output_path } = parse_args(Deno.args); + + log(`reading input from '${input_source_path}'.`); + const source = await Deno.readTextFile(input_source_path); + + log("scoring properties."); + const guess_promises = [] as Promise[]; + for (const property of all_properties()) guess_promises.push(property.guess_from_source(source)); + const guesses = await Promise.all(guess_promises); + + log("Filtering guesses."); + const filtered_guesses = guesses.filter((g) => g !== null) as Guess[]; + const guessed_config = ConfigFile.from_guesses(filtered_guesses); + + const certainty = await guessed_config.calculate_certainty(source); + log("Certainty", certainty * 100, "%"); + log(`writing config to '${output_path}'`); + const output = guessed_config.serialize(); + await Deno.writeTextFile(output_path, output); +} + +function parse_args(args: string[]) { + const bad_usage = () => [log("Usage: cfguesser "), Deno.exit(1)]; + if (args.length < 2) bad_usage(); + for (const arg of args) if (["-h", "--help"].includes(arg)) bad_usage(); + const [input_source_path, output_path] = args; + return { input_source_path, output_path }; +} + +if (import.meta.main) await main(); diff --git a/src/lib/all_properties.ts b/src/lib/all_properties.ts new file mode 100644 index 0000000..398118a --- /dev/null +++ b/src/lib/all_properties.ts @@ -0,0 +1,46 @@ +import { Property } from "./property.ts"; +import { z } from "https://deno.land/x/zod@v3.22.4/mod.ts"; + +import schema from "./data/schema.json" with { type: "json" }; + +function schema_property_parse(input: unknown) { + return z.object({ + type: z.literal("string"), + enum: z.array(z.string()).or(z.undefined()), + }).or(z.object({ + type: z.literal("integer"), + })).or(z.object({ + type: z.literal("boolean"), + })).or(z.object({ + type: z.literal("object"), + properties: z.record(z.string(), z.unknown()), + })).or(z.object({ + type: z.literal("array"), + items: z.unknown(), + })) + .or(z.object({ + oneOf: z.unknown().array(), + })) + .parse(input); +} + +const blacklist = ["Language", "InsertTrailingCommas", "QualifierAlignment"]; + +export function all_properties() { + const result = [] as Property[]; + const schema_properties = schema.properties as Record; + const names = Object.keys(schema.properties) as string[]; + for (const name of names) { + if (blacklist.includes(name)) continue; + const parsed = schema_property_parse(schema_properties[name]); + if ("oneOf" in parsed) continue; + if ((parsed.type === "string") && parsed.enum !== undefined) result.push(new Property(name, parsed.enum)); + if (parsed.type === "boolean") result.push(new Property(name, ["true", "false"])); + if (parsed.type === "integer") result.push(new Property(name, ["1", "2", "4", "8", "16", "80", "120"])); + } + return result; +} + +Deno.test("test_all_properties", () => { + console.log(all_properties()); +}); diff --git a/src/lib/config_file.ts b/src/lib/config_file.ts new file mode 100644 index 0000000..b1fec37 --- /dev/null +++ b/src/lib/config_file.ts @@ -0,0 +1,25 @@ +import { Guess } from "./guess.ts"; +import { clang_format } from "./utils.ts"; +import { source_distance } from "./source_distance.ts"; + +export class ConfigFile { + properties; + + constructor(properties: [string, string][]) { + this.properties = properties; + } + + static from_guesses(guesses: Guess[]) { + return new ConfigFile(guesses.map((g) => [g.property.name, g.value])); + } + + async calculate_certainty(source: string) { + const formatted = await clang_format(source, ...this.properties); + const distance = source_distance(source, formatted); + return (source.length - distance) / source.length; + } + + serialize() { + return this.properties.map(([name, value]) => `${name}: ${value}`).join("\n"); + } +} diff --git a/src/lib/data/schema.json b/src/lib/data/schema.json new file mode 100644 index 0000000..79ebe1b --- /dev/null +++ b/src/lib/data/schema.json @@ -0,0 +1,1188 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://json.schemastore.org/clang-format.json", + "$defs": {}, + "additionalProperties": {}, + "description": "The .clang-format file is a YAML file defining formatting styles used by clang-format.\r\r see https://clang.llvm.org/docs/ClangFormatStyleOptions.html", + "patternProperties": { + "^x-": { + "$comment": "yaml marker for multiple documents in one single yaml file" + } + }, + "properties": { + "BasedOnStyle": { + "$comment": "clang-format ignores cases. For the sake of consistency, use one of following enums", + "description": "The style used for all options not specifically set in the configuration.", + "type": "string", + "enum": [ + "Chromium", + "Google", + "LLVM", + "Mozilla", + "WebKit", + "Microsoft", + "GNU", + "InheritParentConfig", + "chromium", + "google", + "llvm", + "mozilla", + "webkit", + "microsoft", + "gnu", + "inheritparentconfig" + ] + }, + "AccessModifierOffset": { + "description": "clang-format 3.3\r\r The extra indent or outdent of access modifiers, e.g. public:.", + "type": "integer" + }, + "AlignAfterOpenBracket": { + "description": "clang-format 3.8\r\r This applies to round brackets (parentheses), angle brackets and square brackets.", + "type": "string", + "enum": ["Align", "DontAlign", "AlwaysBreak", "BlockIndent"] + }, + "AlignArrayOfStructures": { + "description": "clang-format 13\r\r if not None, when using initialization for an array of structs aligns the fields into columns.\r\r NOTE: In clang-format 15 this option only applied to arrays with equal number of columns per row.", + "type": "string", + "enum": ["None", "Left", "Right"] + }, + "AlignConsecutiveMacros": { + "oneOf": [ + { + "description": "clang-format 9\r\r Style of aligning consecutive macros.", + "type": "string", + "enum": [ + "None", + "Consecutive", + "AcrossEmptyLines", + "AcrossComments", + "AcrossEmptyLinesAndComments" + ] + }, + { + "description": "clang-format 15\r\r Alignment options.", + "type": "object", + "properties": { + "Enabled": { + "description": "Whether aligning is enabled.", + "type": "boolean" + }, + "AcrossEmptyLines": { + "description": "Whether to align across empty lines.", + "type": "boolean" + }, + "AcrossComments": { + "description": "Whether to align across comments.", + "type": "boolean" + } + } + } + ] + }, + "AlignConsecutiveAssignments": { + "oneOf": [ + { + "description": "clang-format 3.8\r\r Style of aligning consecutive assignments.", + "type": "string", + "enum": [ + "None", + "Consecutive", + "AcrossEmptyLines", + "AcrossComments", + "AcrossEmptyLinesAndComments" + ] + }, + { + "description": "clang-format 15\r\r Alignment options.", + "type": "object", + "properties": { + "Enabled": { + "description": "Whether aligning is enabled.", + "type": "boolean" + }, + "AcrossEmptyLines": { + "description": "Whether to align across empty lines.", + "type": "boolean" + }, + "AcrossComments": { + "description": "Whether to align across comments.", + "type": "boolean" + }, + "AlignCompound": { + "description": "Whether compound assignments like += are aligned along with =.", + "type": "boolean" + }, + "PadOperators": { + "description": "Whether short assignment operators are left-padded to the same length as long ones in order to put all assignment operators to the right of the left hand side.", + "type": "boolean" + } + } + } + ] + }, + "AlignConsecutiveBitFields": { + "oneOf": [ + { + "description": "clang-format 11\r\r Style of aligning consecutive bit fields.", + "type": "string", + "enum": [ + "None", + "Consecutive", + "AcrossEmptyLines", + "AcrossComments", + "AcrossEmptyLinesAndComments" + ] + }, + { + "description": "clang-format 15\r\r Alignment options.", + "type": "object", + "properties": { + "Enabled": { + "description": "Whether aligning is enabled.", + "type": "boolean" + }, + "AcrossEmptyLines": { + "description": "Whether to align across empty lines.", + "type": "boolean" + }, + "AcrossComments": { + "description": "Whether to align across comments.", + "type": "boolean" + } + } + } + ] + }, + "AlignConsecutiveDeclarations": { + "oneOf": [ + { + "description": "clang-format 3.8\r\r Style of aligning consecutive declarations.", + "type": "string", + "enum": [ + "None", + "Consecutive", + "AcrossEmptyLines", + "AcrossComments", + "AcrossEmptyLinesAndComments" + ] + }, + { + "description": "clang-format 15\r\r Alignment options.", + "type": "object", + "properties": { + "Enabled": { + "description": "Whether aligning is enabled.", + "type": "boolean" + }, + "AcrossEmptyLines": { + "description": "Whether to align across empty lines.", + "type": "boolean" + }, + "AcrossComments": { + "description": "Whether to align across comments.", + "type": "boolean" + } + } + } + ] + }, + "AlignEscapedNewlines": { + "description": "clang-format 5\r\r Options for aligning backslashes in escaped newlines.", + "type": "string", + "enum": ["Left", "Right", "DontAlign"] + }, + "AlignOperands": { + "description": "clang-format 3.5\r\r If true, horizontally align operands of binary and ternary expressions.", + "type": "string", + "enum": ["Align", "DontAlign", "AlignAfterOperator"] + }, + "AlignTrailingComments": { + "oneOf": [ + { + "description": "clang-format 3.7\r\r Control of trailing comments.\r\r NOTE: In clang-format 16 this option is not a bool but can be set to the options. Conventional bool options still can be parsed as before.", + "type": "boolean" + }, + { + "description": "clang-format 16\r\r Alignment options.", + "type": "object", + "properties": { + "Kind": { + "description": "Specifies the way to align trailing comments.", + "type": "string", + "enum": ["Leave", "Always", "Never"] + }, + "OverEmptyLines": { + "description": "How many empty lines to apply alignment.", + "type": "integer", + "minimum": 0 + } + } + } + ] + }, + "AllowAllArgumentsOnNextLine": { + "description": "clang-format 9\r\r If a function call or braced initializer list doesn't fit on a line, allow putting all arguments onto the next line, even if BinPackArguments is false.", + "type": "boolean" + }, + "AllowAllConstructorInitializersOnNextLine": { + "description": "clang-format 9\r\r This option is deprecated in clang-format 15. See NextLine of PackConstructorInitializers.\r\r If a constructor definition with a member initializer list doesn't fit on a single line, allow putting all member initializers onto the next line, if `ConstructorInitializerAllOnOneLineOrOnePerLine` is true. Note that this parameter has no effect if `ConstructorInitializerAllOnOneLineOrOnePerLine` is false.", + "type": "boolean" + }, + "AllowAllParametersOfDeclarationOnNextLine": { + "description": "clang-format 3.3\r\r If the function declaration doesn't fit on a line, allow putting all parameters of a function declaration onto the next line even if BinPackParameters is false.", + "type": "boolean" + }, + "AllowShortEnumsOnASingleLine": { + "description": "clang-format 11\r\r Allow short enums on a single line.", + "type": "boolean" + }, + "AllowShortBlocksOnASingleLine": { + "description": "clang-format 3.5\r\r Dependent on the value, while (true) { continue; } can be put on a single line.", + "type": "string", + "enum": ["Never", "Empty", "Always"] + }, + "AllowShortCaseLabelsOnASingleLine": { + "description": "clang-format 3.6\r\r If true, short case labels will be contracted to a single line.", + "type": "boolean" + }, + "AllowShortFunctionsOnASingleLine": { + "description": "clang-format 3.5\r\r Dependent on the value, int f() { return 0; } can be put on a single line.", + "type": "string", + "enum": ["All", "None", "Inline", "Empty", "InlineOnly"] + }, + "AllowShortLambdasOnASingleLine": { + "description": "clang-format 9\r\r Dependent on the value, auto lambda []() { return 0; } can be put on a single line.", + "type": "string", + "enum": ["None", "Empty", "Inline", "All"] + }, + "AllowShortIfStatementsOnASingleLine": { + "description": "clang-format 9\r\r If true, if (a) return; can be put on a single line.", + "type": "string", + "enum": ["AllIfsAndElse", "Never", "WithoutElse", "OnlyFirstIf"] + }, + "AllowShortLoopsOnASingleLine": { + "description": "clang-format 3.7\r\r If true, while (true) continue; can be put on a single line.", + "type": "boolean" + }, + "AlwaysBreakAfterDefinitionReturnType": { + "description": "clang-format 3.7\r\r This option is deprecated and is retained for backwards compatibility.\r\r The function definition return type breaking style to use. This option is deprecated and is retained for backwards compatibility", + "type": "string", + "enum": ["None", "All", "TopLevel"] + }, + "AlwaysBreakAfterReturnType": { + "description": "clang-format 3.8\r\r The function declaration return type breaking style to use.", + "type": "string", + "enum": [ + "None", + "All", + "TopLevel", + "AllDefinitions", + "TopLevelDefinitions" + ] + }, + "AlwaysBreakBeforeMultilineStrings": { + "description": "clang-format 3.4\r\r If true, always break before multiline string literals.", + "type": "boolean" + }, + "AlwaysBreakTemplateDeclarations": { + "description": "clang-format 7\r\r The template declaration breaking style to use.", + "type": "string", + "enum": ["Yes", "No", "MultiLine"] + }, + "AttributeMacros": { + "description": "clang-format 12\r\r A vector of strings that should be interpreted as attributes/qualifiers instead of identifiers. This can be useful for language extensions or static analyzer annotations.", + "type": "array", + "items": { + "type": "string", + "examples": ["__capability", "__output", "__ununsed"] + } + }, + "BinPackArguments": { + "description": "clang-format 3.7\r\r If false, a function call's arguments will either be all on the same line or will have one line each.", + "type": "boolean" + }, + "BinPackParameters": { + "description": "clang-format 3.7\r\r If false, a function declaration's or function definition's parameters will either all be on the same line or will have one line each.", + "type": "boolean" + }, + "BitFieldColonSpacing": { + "description": "clang-format 12\r\r The BitFieldColonSpacingStyle to use for bitfields.", + "type": "string", + "enum": ["Both", "None", "Before", "After"] + }, + "BraceWrapping": { + "description": "clang-format 3.8\r\r Control of individual brace wrapping cases.If BreakBeforeBraces is set to BS_Custom, use this to specify how each individual brace case should be handled. Otherwise, this is ignored.", + "type": "object", + "properties": { + "AfterCaseLabel": { + "description": "Wrap case labels.", + "type": "boolean" + }, + "AfterClass": { + "description": "Wrap class definitions.", + "type": "boolean" + }, + "AfterControlStatement": { + "description": "Wrap control statements (if/for/while/switch/...).", + "type": "string", + "enum": ["Never", "MultiLine", "Always"] + }, + "AfterEnum": { + "description": "Wrap enum definitions.", + "type": "boolean" + }, + "AfterFunction": { + "description": "Wrap function definitions.", + "type": "boolean" + }, + "AfterNamespace": { + "description": "Wrap namespace definitions.", + "type": "boolean" + }, + "AfterObjCDeclaration": { + "description": "Wrap ObjC definitions (interfaces, implementations…). @autoreleasepool and @synchronized blocks are wrapped according to AfterControlStatement flag.", + "type": "boolean" + }, + "AfterStruct": { + "description": "Wrap struct definitions.", + "type": "boolean" + }, + "AfterUnion": { + "description": "Wrap union definitions.", + "type": "boolean" + }, + "AfterExternBlock": { + "description": "Wrap extern blocks.", + "type": "boolean" + }, + "BeforeCatch": { + "description": "Wrap before catch.", + "type": "boolean" + }, + "BeforeElse": { + "description": "Wrap before else.", + "type": "boolean" + }, + "BeforeLambdaBody": { + "description": "Wrap lambda block.", + "type": "boolean" + }, + "BeforeWhile": { + "description": "Wrap before while.", + "type": "boolean" + }, + "IndentBraces": { + "description": "Indent the wrapped braces themselves.", + "type": "boolean" + }, + "SplitEmptyFunction": { + "description": "If false, empty function body can be put on a single line. This option is used only if the opening brace of the function has already been wrapped, i.e. the AfterFunction brace wrapping mode is set, and the function could/should not be put on a single line (as per AllowShortFunctionsOnASingleLine and constructor formatting options).", + "type": "boolean" + }, + "SplitEmptyRecord": { + "description": "If false, empty record (e.g. class, struct or union) body can be put on a single line. This option is used only if the opening brace of the record has already been wrapped, i.e. the AfterClass (for classes) brace wrapping mode is set.", + "type": "boolean" + }, + "SplitEmptyNamespace": { + "description": "If false, empty namespace body can be put on a single line. This option is used only if the opening brace of the namespace has already been wrapped, i.e. the AfterNamespace brace wrapping mode is set.", + "type": "boolean" + } + } + }, + "BreakAfterAttributes": { + "description": "clang-format 16\r\r Break after a group of C++11 attributes before a function declaration/definition name.", + "type": "string", + "enum": ["Always", "Leave", "Never"] + }, + "BreakAfterJavaFieldAnnotations": { + "description": "clang-format 3.8\r\r Break after each annotation on a field in Java files.", + "type": "boolean" + }, + "BreakArrays": { + "description": "clang-format 16\r\r If true, clang-format will always break after a Json array [ otherwise it will scan until the closing ] to determine if it should add newlines between elements (prettier compatible).", + "type": "boolean" + }, + "BreakBeforeInlineASMColon": { + "description": "clang-format 16\r\r The inline ASM colon style to use..", + "type": "string", + "enum": ["Never", "OnlyMultiline", "Always"] + }, + "BreakBeforeBinaryOperators": { + "description": "clang-format 3.6\r\r The way to wrap binary operators.", + "type": "string", + "enum": ["None", "NonAssignment", "All"] + }, + "BreakBeforeBraces": { + "description": "clang-format 3.7\r\r The brace breaking style to use.", + "type": "string", + "enum": [ + "Attach", + "Linux", + "Mozilla", + "Stroustrup", + "Allman", + "Whitesmiths", + "GNU", + "WebKit", + "Custom" + ] + }, + "BreakBeforeConceptDeclarations": { + "oneOf": [ + { + "type": "boolean" + }, + { + "description": "clang-format 12\r\r The concept declaration style to use.", + "type": "string", + "enum": ["Never", "Allowed", "Always"] + } + ] + }, + "BreakBeforeInheritanceComma": { + "description": "clnag-format 5\r\r If true, in the class inheritance expression clang-format will break before : and , if there is multiple inheritance.", + "type": "boolean" + }, + "BreakBeforeTernaryOperators": { + "description": "clang-format 3.7\r\r If true, ternary operators will be placed after line breaks.", + "type": "boolean" + }, + "BreakConstructorInitializers": { + "description": "clang-format 5\r\r The constructor initializers style to use.", + "type": "string", + "enum": ["BeforeColon", "BeforeComma", "AfterColon"] + }, + "BreakInheritanceList": { + "description": "clang-format 7\r\r The inheritance list style to use.", + "type": "string", + "enum": ["BeforeColon", "BeforeComma", "AfterColon", "AfterComma"] + }, + "BreakStringLiterals": { + "description": "clang-format 3.9\r\r Allow breaking string literals when formatting.", + "type": "boolean" + }, + "BreakConstructorInitializersBeforeComma": { + "description": "clang-format 3.8\r\r Always break constructor initializers before commas and align the commas with the colon.", + "type": "boolean" + }, + "ColumnLimit": { + "description": "clang-format 3.7\r\r The column limit.", + "type": "integer", + "minimum": 0 + }, + "CommentPragmas": { + "description": "clang-format 3.7\r\r A regular expression that describes comments with special meaning, which should not be split into lines or otherwise changed.", + "type": "string" + }, + "CompactNamespaces": { + "description": "clang-format 5\r\r If true, consecutive namespace declarations will be on the same line. If false, each namespace is declared on a new line.", + "type": "boolean" + }, + "ConstructorInitializerAllOnOneLineOrOnePerLine": { + "description": "clang-format 3.7\r\r This option is deprecated. See CurrentLine of PackConstructorInitializers.", + "type": "boolean" + }, + "ConstructorInitializerIndentWidth": { + "description": "clang-format 3.7\r\r The number of characters to use for indentation of constructor initializer lists as well as inheritance lists.", + "type": "integer", + "minimum": 0 + }, + "ContinuationIndentWidth": { + "description": "clang-format 3.7\r\r Indent width for line continuations.", + "type": "integer", + "minimum": 0 + }, + "Cpp11BracedListStyle": { + "description": "clang-format 3.4\r\r If true, format braced lists as best suited for C++11 braced lists.", + "type": "boolean" + }, + "DeriveLineEnding": { + "description": "clang-format 10\r\r Analyze the formatted file for the most used line ending (\r\n or \n). UseCRLF is only used as a fallback if none can be derived.\r\r This option is deprecated in clang-format 16. See DeriveLF and DeriveCRLF of LineEnding.", + "type": "boolean" + }, + "DerivePointerAlignment": { + "description": "clang-format 3.7\r\r If true, analyze the formatted file for the most common alignment of & and *. Pointer and reference alignment styles are going to be updated according to the preferences found in the file. PointerAlignment is then used only as fallback.", + "type": "boolean" + }, + "DisableFormat": { + "description": "clang-format 3.7\r\r Disables formatting completely.", + "type": "boolean" + }, + "EmptyLineAfterAccessModifier": { + "description": "clang-format 13\r\r Defines when to put an empty line after access modifiers. EmptyLineBeforeAccessModifier configuration handles the number of empty lines between two access modifiers.", + "type": "string", + "enum": ["Never", "Leave", "Always"] + }, + "EmptyLineBeforeAccessModifier": { + "description": "clang-format 12\r\r Defines in which cases to put empty line before access modifiers.", + "type": "string", + "enum": ["LogicalBlock", "Never", "Leave", "Always"] + }, + "ExperimentalAutoDetectBinPacking": { + "description": "clang-format 3.7\r\r If true, clang-format detects whether function calls and definitions are formatted with one parameter per line.", + "type": "boolean" + }, + "FixNamespaceComments": { + "description": "clang-format 5\r\r If true, clang-format adds missing namespace end comments for short namespaces and fixes invalid existing ones. This doesn't affect short namespaces, which are controlled by \"ShortNamespaceLines\".", + "type": "boolean" + }, + "ForEachMacros": { + "description": "clang-format 3.7\r\r A vector of macros that should be interpreted as foreach loops instead of as function calls.", + "type": "array", + "items": { + "type": "string", + "examples": ["foreach", "Q_FOREACH", "BOOST_FOREACH"] + } + }, + "IfMacros": { + "description": "clang-format 13\r\r A vector of macros that should be interpreted as conditionals instead of as function calls.", + "type": "array", + "items": { + "type": "string", + "examples": ["KJ_IF_MAYBE"] + } + }, + "IncludeBlocks": { + "description": "clang-format 7\r\r Dependent on the value, multiple #include blocks can be sorted as one and divided based on category.", + "type": "string", + "enum": ["Regroup", "Preserve", "Merge"] + }, + "IncludeCategories": { + "description": "clang-format 7\r\r Regular expressions denoting the different #include categories used for ordering #includes. If none of the regular expressions match, INT_MAX is assigned as category.The main header for a source file automatically gets category 0. so that it is generally kept at the beginning of the #includes (https://llvm.org/docs/CodingStandards.html#include-style). However, you can also assign negative priorities if you have certain headers that always need to be first. \r There is a third and optional field SortPriority which can used while IncludeBlocks = IBS_Regroup to define the priority in which #includes should be ordered. The value of Priority defines the order of #include blocks and also allows the grouping of #includes of different priority. SortPriority is set to the value of Priority as default if it is not assigned.", + "type": "array", + "items": { + "type": "object", + "properties": { + "Regex": { + "type": "string" + }, + "Priority": { + "type": "integer", + "minimum": 0 + }, + "SortPriority": { + "type": "integer", + "minimum": 0 + }, + "CaseSensitive": { + "type": "boolean" + } + }, + "required": ["Regex", "Priority"] + } + }, + "IncludeIsMainRegex": { + "description": "clang-format 7\r\r Specify a regular expression of suffixes that are allowed in the file-to-main-include mapping.\rWhen guessing whether a #include is the \"main\" include (to assign category 0, see above), use this regex of allowed suffixes to the header stem. A partial match is done, so that: - \"\" means \"arbitrary suffix\" - \"$\" means \"no suffix\"\rFor example, if configured to \"(_test)?$\", then a header a.h would be seen as the \"main\" include in both a.cc and a_test.cc.", + "type": "string" + }, + "IncludeIsMainSourceRegex": { + "description": "clang-format 7\r\r Specify a regular expression for files being formatted that are allowed to be considered \"main\" in the file-to-main-include mapping.\rBy default, clang-format considers files as \"main\" only when they end with: .c, .cc, .cpp, .c++, .cxx, .m or .mm extensions. For these files a guessing of \"main\" include takes place (to assign category 0, see above). This config option allows for additional suffixes and extensions for files to be considered as \"main\".\rBy default, clang-format considers files as \"main\" only when they end with: .c, .cc, .cpp, .c++, .cxx, .m or .mm extensions. For these files a guessing of \"main\" include takes place (to assign category 0, see above). This config option allows for additional suffixes and extensions for files to be considered as \"main\". ", + "type": "string" + }, + "IndentAccessModifiers": { + "description": "clang-format 13\r\r Specify whether access modifiers should have their own indentation level.\rWhen false, access modifiers are indented (or outdented) relative to the record members, respecting the AccessModifierOffset. Record members are indented one level below the record. When true, access modifiers get their own indentation level. As a consequence, record members are always indented 2 levels below the record, regardless of the access modifier presence. Value of the AccessModifierOffset is ignored.", + "type": "boolean" + }, + "IndentCaseBlocks": { + "description": "clang-format 11\r\r Indent case label blocks one level from the case label.\rWhen false, the block following the case label uses the same indentation level as for the case label, treating the case label the same as an if-statement. When true, the block gets indented as a scope block.", + "type": "boolean" + }, + "IndentCaseLabels": { + "description": "clang-format 3.3\r\r Indent case labels one level from the switch statement.\rWhen false, use the same indentation level as for the switch statement. Switch statement body is always indented one level more than case labels (except the first block following the case label, which itself indents the code - unless IndentCaseBlocks is enabled).", + "type": "boolean" + }, + "IndentExternBlock": { + "description": "clang-format 11\r\r IndentExternBlockStyle is the type of indenting of extern blocks.", + "type": "string", + "enum": ["AfterExternBlock", "NoIndent", "Indent"] + }, + "IndentGotoLabels": { + "description": "clang-format 10\r\r Indent goto labels.\rWhen false, goto labels are flushed left.", + "type": "boolean" + }, + "IndentPPDirectives": { + "description": "clang-format 6\r\r The preprocessor directive indenting style to use.", + "type": "string", + "enum": ["None", "AfterHash", "BeforeHash"] + }, + "IndentRequiresClause": { + "description": "clang-format 15\r\r Indent the requires clause in a template. This only applies when RequiresClausePosition is OwnLine, or WithFollowing.", + "type": "boolean" + }, + "IndentRequires": { + "description": "clang-format 12-14\r\r Indent the requires clause in a template. This only applies when RequiresClausePosition is OwnLine, or WithFollowing. In clang-format 12, 13 and 14 it was named IndentRequires.", + "type": "boolean" + }, + "IndentWidth": { + "description": "clang-format 3.7\r\r The number of columns to use for indentation.", + "type": "integer", + "minimum": 0 + }, + "IndentWrappedFunctionNames": { + "description": "clang-format 3.7\r\r Indent if a function definition or declaration is wrapped after the type.", + "type": "boolean" + }, + "InsertBraces": { + "description": "clang-fomrat 15\r Insert braces after control statements (if, else, for, do, and while) in C++ unless the control statements are inside macro definitions or the braces would enclose preprocessor directives.\r\r Warning: Setting this option to true could lead to incorrect code formatting due to clang-format's lack of complete semantic information. As such, extra care should be taken to review code changes made by this option.", + "type": "boolean" + }, + "InsertNewlineAtEOF": { + "description": "clang-fomrat 16\r Insert a newline at end of file if missing.", + "type": "boolean" + }, + "InsertTrailingCommas": { + "description": "clang-fromat 11\r\r If set to TCS_Wrapped will insert trailing commas in container literals (arrays and objects) that wrap across multiple lines. It is currently only available for JavaScript and disabled by default TCS_None. InsertTrailingCommas cannot be used together with BinPackArguments as inserting the comma disables bin-packing.", + "type": "string", + "enum": ["None", "Wrapped"] + }, + "IntegerLiteralSeparator": { + "description": "clang-format 16\r\r Format integer literal separators (' for C++ and _ for C#, Java, and JavaScript).\r\r Separator format of integer literals of different bases. If negative, remove separators. If 0, leave the literal as is. If positive, insert separators between digits starting from the rightmost digit.\r\r You can also specify a minimum number of digits (BinaryMinDigits, DecimalMinDigits, and HexMinDigits) the integer literal must have in order for the separators to be inserted.", + "type": "object", + "properties": { + "Binary": { + "description": "Format separators in binary literals.", + "type": "integer" + }, + "BinaryMinDigits": { + "description": "Format separators in binary literals with a minimum number of digits.", + "type": "integer" + }, + "Decimal": { + "description": "Format separators in decimal literals.", + "type": "integer" + }, + "DecimalMinDigits": { + "description": "Format separators in decimal literals with a minimum number of digits.", + "type": "integer" + }, + "Hex": { + "description": "Format separators in hexadecimal literals", + "type": "integer" + }, + "HexMinDigits": { + "description": "Format separators in hexadecimal literals with a minimum number of digits.", + "type": "integer" + } + } + }, + "JavaImportGroup": { + "description": "clang-format 8\r\r The JavaScriptQuoteStyle to use for JavaScript strings.", + "type": "array" + }, + "JavaScriptQuotes": { + "description": "clang-fromat 3.9\r\r The JavaScriptQuoteStyle to use for JavaScript strings.", + "type": "string", + "enum": ["Leave", "Single", "Double"] + }, + "JavaScriptWrapImports": { + "description": "clang-format 3.9\r\r Whether to wrap JavaScript import/export statements.", + "type": "boolean" + }, + "KeepEmptyLinesAtTheStartOfBlocks": { + "description": "clang-format 3.7\r\r If true, the empty line at the start of blocks is kept.", + "type": "boolean" + }, + "LambdaBodyIndentation": { + "description": "clang-format 13\r\r The indentation style of lambda bodies. Signature (the default) causes the lambda body to be indented one additional level relative to the indentation level of the signature. OuterScope forces the lambda body to be indented one additional level relative to the parent scope containing the lambda signature. For callback-heavy code, it may improve readability to have the signature indented two levels and to use OuterScope. The KJ style guide requires OuterScope. KJ style guide", + "type": "string", + "enum": ["Signature", "OuterScope"] + }, + "Language": { + "description": "clang-format 3.5\r\r Language, this format style is targeted at.", + "type": "string", + "enum": [ + "None", + "Cpp", + "ObjC", + "CSharp", + "Java", + "JavaScript", + "Json", + "Proto", + "TableGen", + "TextProto", + "Verilog" + ] + }, + "LineEnding": { + "description": "clang-format 16\r\r Line ending style (\\n or \\r\\n) to use.", + "type": "string", + "enum": ["LF", "CRLF", "DeriveLF", "DeriveCRLF"] + }, + "MacroBlockBegin": { + "description": "clang-format 3.7\r\r A regular expression matching macros that start a block.", + "type": "string", + "examples": ["^NS_MAP_BEGIN|NS_TABLE_HEAD$"] + }, + "MacroBlockEnd": { + "description": "clang-format 3.7\r\r A regular expression matching macros that end a block.", + "type": "string", + "examples": ["^NS_MAP_END|NS_TABLE_.*_END$"] + }, + "MaxEmptyLinesToKeep": { + "description": "clang-format 3.7\r\r The maximum number of consecutive empty lines to keep.", + "type": "integer", + "default": 1, + "minimum": 0 + }, + "NamespaceIndentation": { + "description": "clnag-format 3.7\r\r The indentation used for namespaces.", + "type": "string", + "enum": ["None", "Inner", "All"] + }, + "NamespaceMacros": { + "description": "clnag-format 9\r\r A vector of macros which are used to open namespace blocks.", + "type": "array", + "items": { + "type": "string", + "examples": ["TESTSUITE"] + } + }, + "ObjCBinPackProtocolList": { + "description": "clang-format 7\r\r Controls bin-packing Objective-C protocol conformance list items into as few lines as possible when they go over ColumnLimit.If Auto (the default), delegates to the value in BinPackParameters. If that is true, bin-packs Objective-C protocol conformance list items into as few lines as possible whenever they go over ColumnLimit.\rIf Always, always bin-packs Objective-C protocol conformance list items into as few lines as possible whenever they go over ColumnLimit.\rIf Never, lays out Objective-C protocol conformance list items onto individual lines whenever they go over ColumnLimit.", + "type": "string", + "default": "Auto", + "enum": ["Never", "Auto", "Always"] + }, + "ObjCBlockIndentWidth": { + "description": "clang-format 3.7\r\r The number of characters to use for indentation of ObjC blocks.", + "type": "integer", + "minimum": 0 + }, + "ObjCBreakBeforeNestedBlockParam": { + "description": "clang-format 11\r\r Break parameters list into lines when there is nested block parameters in a function call.", + "type": "boolean" + }, + "ObjCSpaceAfterProperty": { + "description": "clang-format 3.7\r\r Add a space after @property in Objective-C, i.e. use @property (readonly) instead of @property(readonly).", + "type": "boolean" + }, + "ObjCSpaceBeforeProtocolList": { + "description": "clang-format 3.7\r\r Add a space in front of an Objective-C protocol list, i.e. use Foo instead of Foo.", + "type": "boolean" + }, + "PPIndentWidth": { + "description": "clang-format 13\r\r The number of columns to use for indentation of preprocessor statements. When set to -1 (default) IndentWidth is used also for preprocessor statements.", + "type": "integer", + "default": -1 + }, + "PackConstructorInitializers": { + "description": "clang-format 14\r\r The pack constructor initializers style to use.", + "type": "string", + "enum": ["Never", "BinPack", "CurrentLine", "NextLine"] + }, + "PenaltyBreakAssignment": { + "description": "clang-format 5\r\r The penalty for breaking around an assignment operator.", + "type": "integer", + "minimum": 0 + }, + "PenaltyBreakBeforeFirstCallParameter": { + "description": "clang-format 3.7\r\r The penalty for breaking a function call after call(.", + "type": "integer", + "minimum": 0 + }, + "PenaltyBreakComment": { + "description": "clang-format 3.7\r\r The penalty for each line break introduced inside a comment.", + "type": "integer", + "minimum": 0 + }, + "PenaltyBreakFirstLessLess": { + "description": "clang-format 3.7\r\r The penalty for breaking before the first <<.", + "type": "integer", + "minimum": 0 + }, + "PenaltyBreakOpenParenthesis": { + "description": "clang-format 14\r\r The penalty for breaking after (.", + "type": "integer", + "minimum": 0 + }, + "PenaltyBreakString": { + "description": "clang-format 3.7\r\r The penalty for each line break introduced inside a string literal.", + "type": "integer", + "minimum": 0 + }, + "PenaltyBreakTemplateDeclaration": { + "description": "clang-format 7\r\r The penalty for breaking after template declaration.", + "type": "integer", + "minimum": 0 + }, + "PenaltyExcessCharacter": { + "description": "clang-format 3.7\r\r The penalty for each character outside of the column limit.", + "type": "integer", + "minimum": 0 + }, + "PenaltyIndentedWhitespace": { + "description": "clang-format 12\r\r Penalty for each character of whitespace indentation (counted relative to leading non-whitespace column).", + "type": "integer", + "minimum": 0 + }, + "PenaltyReturnTypeOnItsOwnLine": { + "description": "clang-format 3.7\r\r Penalty for putting the return type of a function onto its own line.", + "type": "integer", + "minimum": 0 + }, + "PointerAlignment": { + "description": "clang-format 3.7\r\r Pointer and reference alignment style.", + "type": "string", + "enum": ["Left", "Right", "Middle"] + }, + "QualifierAlignment": { + "description": "clang-format 14\r\r Different ways to arrange specifiers and qualifiers (e.g. const/volatile).\r\r Warning:Setting QualifierAlignment to something other than Leave, COULD lead to incorrect code formatting due to incorrect decisions made due to clang-formats lack of complete semantic information.\r\r As such extra care should be taken to review code changes made by the use of this option.", + "type": "string", + "enum": ["Leave", "Left", "Right", "Custom"] + }, + "QualifierOrder": { + "description": "clang-format 14\r\r The order in which the qualifiers appear. Order is an array that can contain any of the following. \r\r Note: it MUST contain 'type'. Items to the left of 'type' will be placed to the left of the type and aligned in the order supplied. Items to the right of 'type' will be placed to the right of the type and aligned in the order supplied.", + "type": "array", + "contains": { + "type": "string", + "enum": ["type"] + }, + "items": { + "type": "string", + "enum": [ + "const", + "inline", + "static", + "constexpr", + "volatile", + "restrict", + "type" + ] + } + }, + "RawStringFormats": { + "description": "clang-format 6\r\r Defines hints for detecting supported languages code blocks in raw strings.", + "type": "array", + "items": { + "type": "object", + "properties": { + "Language": { + "$ref": "#/properties/Language" + }, + "Delimiters": { + "type": "array", + "items": { + "type": "string" + } + }, + "CanonicalDelimiter": { + "type": "string" + }, + "BasedOnStyle": { + "$ref": "#/properties/BasedOnStyle" + }, + "EnclosingFunctions": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "examples": [ + { + "Language": "Cpp", + "Delimiters": ["cc", "CC", "cpp", "Cpp", "CPP", "c++", "C++"], + "CanonicalDelimiter": "", + "BasedOnStyle": "google" + }, + { + "Language": "TextProto", + "Delimiters": ["pb", "PB", "proto", "PROTO"], + "EnclosingFunctions": [ + "EqualsProto", + "EquivToProto", + "PARSE_PARTIAL_TEXT_PROTO", + "PARSE_TEST_PROTO", + "PARSE_TEXT_PROTO", + "ParseTextOrDie", + "ParseTextProtoOrDie", + "ParseTestProto", + "ParsePartialTestProto" + ], + "CanonicalDelimiter": "pb", + "BasedOnStyle": "google" + } + ] + } + }, + "ReferenceAlignment": { + "description": "clang-format 13\r\r Reference alignment style (overrides PointerAlignment for references).", + "type": "string", + "enum": ["Pointer", "Left", "Right", "Middle"] + }, + "ReflowComments": { + "description": "clang-format 4\r\r If true, clang-format will attempt to re-flow comments. That is it will touch a comment and reflow long comments into new lines, trying to obey the ColumnLimit.", + "type": "boolean" + }, + "RemoveSemicolon": { + "description": "clang-format 16\r\r Remove semicolons after the closing brace of a non-empty function.\r\r Warning: Setting this option to true could lead to incorrect code formatting due to clang-format's lack of complete semantic information. As such, extra care should be taken to review code changes made by this option.", + "type": "boolean" + }, + "RemoveBracesLLVM": { + "description": "clang-format 14\r\r Remove optional braces of control statements (if, else, for, and while) in C++ according to the LLVM coding style. \r\r Warning: This option will be renamed and expanded to support other styles.\r\r Setting this option to true could lead to incorrect code formatting due to clang-format's lack of complete semantic information. As such, extra care should be taken to review code changes made by this option.", + "type": "boolean" + }, + "RequiresClausePosition": { + "description": "clang-format 15\r\r The position of the requires clause.", + "type": "string", + "enum": ["OwnLine", "WithPreceding", "WithFollowing", "SingleLine"] + }, + "RequiresExpressionIndentation": { + "description": "clang-format 16 \r\r The indentation used for requires expression bodies.", + "type": "string", + "enum": ["OuterScope", "Keyword"] + }, + "SeparateDefinitionBlocks": { + "description": "clang-format 14\r\r Specifies the use of empty lines to separate definition blocks, including classes, structs, enums, and functions.", + "type": "string", + "enum": ["Leave", "Always", "Never"] + }, + "ShortNamespaceLines": { + "description": "clang-format 13\r\r The maximal number of unwrapped lines that a short namespace spans. Defaults to 1.", + "type": "integer", + "default": 1, + "minimum": 0 + }, + "SortIncludes": { + "description": "clang-format 4\r\r Controls if and how clang-format will sort #includes. If Never, includes are never sorted. If CaseInsensitive, includes are sorted in an ASCIIbetical or case insensitive fashion. If CaseSensitive, includes are sorted in an alphabetical or case sensitive fashion.", + "type": "string", + "enum": ["CaseSensitive", "CaseInsensitive", "Never"] + }, + "SortJavaStaticImport": { + "description": "clang-format 12\r\r When sorting Java imports, by default static imports are placed before non-static imports. If JavaStaticImportAfterImport is After, static imports are placed after non-static imports.", + "type": "string", + "enum": ["Before", "After"] + }, + "SortUsingDeclarations": { + "oneOf": [ + { + "description": "clang-format 5\r\r If true, clang-format will sort using declarations.", + "type": "boolean" + }, + { + "description": "clang-format 16\r\r Controls if and how clang-format will sort using declarations.", + "type": "string", + "enum": ["Never", "Lexicographic", "LexicographicNumeric"] + } + ] + }, + "SpaceAfterCStyleCast": { + "description": "clang-format 3.5\r\r If true, a space is inserted after C style casts.", + "type": "boolean" + }, + "SpaceAfterLogicalNot": { + "description": "clang-format 9\r\r If true, a space is inserted after the logical not operator (!).", + "type": "boolean" + }, + "SpaceAfterTemplateKeyword": { + "description": "clang-format 4\r\r If true, a space will be inserted after the 'template' keyword.", + "type": "boolean" + }, + "SpaceAroundPointerQualifiers": { + "description": "clang-format 12\r\r Defines in which cases to put a space before or after pointer qualifiers", + "type": "string", + "enum": ["Default", "Before", "After", "Both"] + }, + "SpaceBeforeAssignmentOperators": { + "description": "clang-format 3.7\r\r If false, spaces will be removed before assignment operators.", + "type": "boolean" + }, + "SpaceBeforeCaseColon": { + "description": "clang-format 12\r\r If false, spaces will be removed before case colon.", + "type": "boolean" + }, + "SpaceBeforeCpp11BracedList": { + "description": "clang-format 7\r\r If true, a space will be inserted before a C++11 braced list used to initialize an object (after the preceding identifier or type).", + "type": "boolean" + }, + "SpaceBeforeCtorInitializerColon": { + "description": "clang-format 7\r\r If false, spaces will be removed before constructor initializer colon.", + "type": "boolean" + }, + "SpaceBeforeInheritanceColon": { + "description": "clang-format 7\r\r If false, spaces will be removed before inheritance colon.", + "type": "boolean" + }, + "SpaceBeforeParens": { + "description": "clang-format 3.5\r\r Defines in which cases to put a space before opening parentheses.", + "type": "string", + "enum": [ + "ControlStatements", + "Never", + "ControlStatementsExceptControlMacros", + "NonEmptyParentheses", + "Always", + "Custom" + ] + }, + "SpaceBeforeParensOptions": { + "description": "clang-format 14\r\r Control of individual space before parentheses.\r\r If SpaceBeforeParens is set to Custom, use this to specify how each individual space before parentheses case should be handled. Otherwise, this is ignored.", + "type": "object", + "properties": { + "AfterControlStatements": { + "description": "If true, put space between control statement keywords (for/if/while…) and opening parentheses.", + "type": "boolean" + }, + "AfterForeachMacros": { + "description": "If true, put space between foreach macros and opening parentheses.", + "type": "boolean" + }, + "AfterFunctionDeclarationName": { + "description": "If true, put a space between function declaration name and opening parentheses.", + "type": "boolean" + }, + "AfterFunctionDefinitionName": { + "description": "If true, put a space between function definition name and opening parentheses.", + "type": "boolean" + }, + "AfterIfMacros": { + "description": "If true, put space between if macros and opening parentheses.", + "type": "boolean" + }, + "AfterOverloadedOperator": { + "description": "If true, put a space between operator overloading and opening parentheses.", + "type": "boolean" + }, + "AfterRequiresInClause": { + "description": "If true, put space between requires keyword in a requires clause and opening parentheses, if there is one.", + "type": "boolean" + }, + "AfterRequiresInExpression": { + "description": "If true, put space between requires keyword in a requires expression and opening parentheses.", + "type": "boolean" + }, + "BeforeNonEmptyParentheses": { + "description": "If true, put a space before opening parentheses only if the parentheses are not empty.", + "type": "boolean" + } + } + }, + "SpaceBeforeRangeBasedForLoopColon": { + "description": "clang-format 7\r\r If false, spaces will be removed before range-based for loop colon.", + "type": "boolean" + }, + "SpaceBeforeSquareBrackets": { + "description": "clang-format 10\r\r If true, spaces will be before [. Lambdas will not be affected. Only the first [ will get a space added.", + "type": "boolean" + }, + "SpaceInEmptyBlock": { + "description": "clang-format 10\r\r If true, spaces will be inserted into {}.", + "type": "boolean" + }, + "SpaceInEmptyParentheses": { + "description": "clang-format 3.7\r\r If true, spaces may be inserted into ().", + "type": "boolean" + }, + "SpacesBeforeTrailingComments": { + "description": "clang-format 3.7\r\r The number of spaces before trailing line comments (// - comments).\r\rThis does not affect trailing block comments (/* - comments) as those commonly have different usage patterns and a number of special cases.", + "type": "integer", + "minimum": 0 + }, + "SpacesInAngles": { + "description": "clang-format 3.4\r\r The SpacesInAnglesStyle to use for template argument lists.", + "type": "string", + "enum": ["Never", "Always", "Leave"] + }, + "SpacesInCStyleCastParentheses": { + "description": "clang-format 3.7\r\r If true, spaces may be inserted into C style casts.", + "type": "boolean" + }, + "SpacesInConditionalStatement": { + "description": "clang-format 10\r\r If true, spaces will be inserted around if/for/switch/while conditions.", + "type": "boolean" + }, + "SpacesInContainerLiterals": { + "description": "clang-format 3.7\r\r If true, spaces are inserted inside container literals (e.g. ObjC and Javascript array and dict literals).", + "type": "boolean" + }, + "SpacesInLineCommentPrefix": { + "description": "clang-format 13\r\r How many spaces are allowed at the start of a line comment. To disable the maximum set it to -1, apart from that the maximum takes precedence over the minimum.\r\r In clang-format 16, his option has only effect if ReflowComments is set to true.", + "type": "object", + "properties": { + "Minimum": { + "type": "integer", + "default": 1 + }, + "Maximum": { + "type": "integer", + "default": 1 + } + } + }, + "SpacesInParentheses": { + "description": "clang-format 3.7\r\r If true, spaces will be inserted after ( and before ).", + "type": "boolean" + }, + "SpacesInSquareBrackets": { + "description": "clang-format 3.7\r\r If true, spaces will be inserted after [ and before ]. Lambdas without arguments or unspecified size array declarations will not be affected.", + "type": "boolean" + }, + "Standard": { + "description": "clang-format 3.7\r\r Parse and format C++ constructs compatible with this standard.", + "type": "string", + "enum": ["Auto", "Latest", "c++03", "c++11", "c++14", "c++17", "c++20"] + }, + "StatementAttributeLikeMacros": { + "description": "clang-format 12\r\r Macros which are ignored in front of a statement, as if they were an attribute. So that they are not parsed as identifier, for example for Qts emit.", + "type": "array", + "items": { + "type": "string", + "examples": ["Q_EMIT"] + } + }, + "StatementMacros": { + "description": "clnag-format 8\r\r A vector of macros that should be interpreted as complete statements.\r\r Typical macros are expressions, and require a semi-colon to be added; sometimes this is not the case, and this allows to make clang-format aware of such cases.", + "type": "array", + "items": { + "type": "string", + "examples": ["Q_UNUSED", "QT_REQUIRE_VERSION"] + } + }, + "TabWidth": { + "description": "clang-format 3.7\r\r The number of columns used for tab stops.", + "type": "integer", + "minimum": 0 + }, + "TypenameMacros": { + "description": "clang-format 9\r\r A vector of macros that should be interpreted as type declarations instead of as function calls.\r\rFor example: OpenSSL STACK_OF, BSD LIST_ENTRY.", + "type": "array", + "items": { + "type": "string", + "examples": ["STACK_OF", "LIST_ENTRY"] + } + }, + "UseCRLF": { + "description": "clang-format 10\r\r Use \r\n instead of \n for line breaks. Also used as fallback if DeriveLineEnding is true.\r\r This option is deprecated in clang-format 16. See LF and CRLF of LineEnding.", + "type": "boolean" + }, + "UseTab": { + "description": "clang-format 3.7\r\r The way to use tab characters in the resulting file.", + "type": "string", + "enum": [ + "Never", + "ForIndentation", + "ForContinuationAndIndentation", + "AlignWithSpaces", + "Always" + ] + }, + "WhitespaceSensitiveMacros": { + "description": "clang-format 11\r\r A vector of macros which are whitespace-sensitive and should not be touched.\r\r For example: BOOST_PP_STRINGIZE", + "type": "array", + "items": { + "type": "string", + "examples": [ + "STRINGIZE", + "PP_STRINGIZE", + "BOOST_PP_STRINGIZE", + "NS_SWIFT_NAME", + "CF_SWIFT_NAME" + ] + } + } + }, + "title": ".clang-format config", + "type": "object" +} diff --git a/src/lib/data/schema_update.sh b/src/lib/data/schema_update.sh new file mode 100755 index 0000000..70656b4 --- /dev/null +++ b/src/lib/data/schema_update.sh @@ -0,0 +1,5 @@ +#!/bin/sh +set -e +cd "$(dirname "$(realpath "$0")")" + +wget -O ./schema.json https://json.schemastore.org/clang-format.json diff --git a/src/lib/guess.ts b/src/lib/guess.ts new file mode 100644 index 0000000..966ef1a --- /dev/null +++ b/src/lib/guess.ts @@ -0,0 +1,24 @@ +import { source_distance } from "./source_distance.ts"; +import { Property } from "./property.ts"; +import { clang_format, log_from } from "./utils.ts"; +export const log = log_from(import.meta); + +export class Guess { + property; + value; + + constructor(property: Property, value: string) { + this.property = property; + this.value = value; + } + + async test(source: string) { + const original = source; + const formatted = await clang_format(source, [this.property.name, this.value]); + log("Scoring", this.property.name, ":", this.value); + const distance = source_distance(original, formatted); + const score = -1 * distance; + log("Scored to", score); + return score; + } +} diff --git a/src/lib/lib.ts b/src/lib/lib.ts new file mode 100644 index 0000000..15fb6cc --- /dev/null +++ b/src/lib/lib.ts @@ -0,0 +1,2 @@ +import { log_from } from "./utils.ts"; +export const log = log_from(import.meta); diff --git a/src/lib/property.ts b/src/lib/property.ts new file mode 100644 index 0000000..f9b06ca --- /dev/null +++ b/src/lib/property.ts @@ -0,0 +1,33 @@ +import { Guess } from "./guess.ts"; +import { indexed } from "./utils.ts"; + +export class Property { + name; + possible_values; + + constructor(name: string, possible_values: string[]) { + this.name = name; + this.possible_values = possible_values; + } + + async guess_from_source(source: string) { + const score_promises = [] as Promise<[number, Guess, number]>[]; + for (const [index, value] of indexed(this.possible_values)) { + const guess = new Guess(this, value); + score_promises.push((async () => [index, guess, await guess.test(source)])()); + } + const scored_guesses = await Promise.all(score_promises); + if (all_equals(scored_guesses.map(([_, __, s]) => s))) return null; + // note : Sorts by score (highest first) then by index (lowest first). + const sorted_gueses = scored_guesses.toSorted(([ia, _, a], [ib, __, b]) => (b - a) == 0 ? (ia - ib) : (b - a)); + const [_, guess, __] = sorted_gueses[0]; + return guess; + } +} + +function all_equals(arr: T[]) { + if (arr.length === 0) return true; + const [first] = arr; + for (const item of arr) if (item !== first) return false; + return true; +} diff --git a/src/lib/source_distance.ts b/src/lib/source_distance.ts new file mode 100644 index 0000000..ffb2f23 --- /dev/null +++ b/src/lib/source_distance.ts @@ -0,0 +1,84 @@ +import { assertEquals } from "https://deno.land/std@0.220.1/assert/mod.ts"; +import { range } from "./utils.ts"; + +export function source_distance(a: string, b: string) { + // return source_distance_impl(a, b); + return source_distance_wrap(a, b); +} + +import { distance } from "https://deno.land/x/fastest_levenshtein@1.0.10/mod.ts"; +export function source_distance_wrap(a: string, b: string) { + a = unify_indents(a); + b = unify_indents(b); + return distance(a, b); +} + +export function source_distance_impl(a: string, b: string) { + a = unify_indents(a); + b = unify_indents(b); + const [rest_a, rest_b] = remove_common_fragments(a, b, 6); + const len_a = rest_a.replaceAll(sep_seq, "").length; + const len_b = rest_b.replaceAll(sep_seq, "").length; + return len_a + len_b; +} + +const sep_seq = "🥖"; + +function unify_indents(text: string) { + const after = text.replaceAll("\t", " ").replaceAll(" ", " "); + if (after === text) return text; + else return unify_indents(after); +} + +function remove_common_fragments(a: string, b: string, min_frag_size: number) { + const result = find_fragment_location(a, b, min_frag_size); + if (result === null) return [a, b]; + const [ia, ib, size] = result; + const new_a = remove_fragment(a, ia, size); + const new_b = remove_fragment(b, ib, size); + return remove_common_fragments(new_a, new_b, min_frag_size); +} + +function find_fragment_location(a: string, b: string, min_frag_size: number) { + for (const start_a of range(0, a.length)) { + const slice_a = a.slice(start_a); + for (const start_b of range(0, b.length)) { + const slice_b = b.slice(start_b); + const common_count = common_length(slice_a, slice_b); + if (common_count < min_frag_size) continue; + return [start_a, start_b, common_count] as const; + } + } + return null; +} + +function common_length(a: string, b: string) { + let result = 0; + for (const index of range(0, Math.min(a.length, b.length))) { + if (a[index] != b[index]) break; + result += 1; + } + return result; +} + +function remove_fragment(text: string, start: number, size: number) { + const prefix = text.slice(0, start); + const suffix = text.slice(start + size); + return prefix + sep_seq + suffix; +} + +Deno.test("test_source_distance", () => { + const source_a = ` +int main() { + if (a) b; +}`; + const source_b = ` +int main() +{ + if (a) + { + b; + } +}`; + assertEquals(source_distance(source_a, source_b), 19); +}); diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..f4f1ece --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,57 @@ +import { assert, assertEquals } from "https://deno.land/std@0.220.1/assert/mod.ts"; +import * as path from "https://deno.land/std@0.220.1/path/mod.ts"; + +export function* range(from: number, to: number) { + while (from < to) yield from++; +} + +export function* indexed(source: Iterable) { + let index = 0; + for (const item of source) yield [index++, item] as const; +} + +const LOG_LEVEL = 0; +export function log_from(meta: ImportMeta, level = 1) { + const filename = path.basename(new URL(meta.url).pathname); + return (...args: unknown[]) => { + if (LOG_LEVEL < level) return; + console.log(`[${filename}]`, ...args); + }; +} + +export function promise_split() { + let resolver = null as ((item: T) => void) | null; + let resolved = false; + const promise = new Promise((res) => resolver = res); + const resolve = (item: T) => { + resolved = true; + assert(resolver !== null); + resolver(item); + }; + return { promise, resolve, resolved }; +} + +export function sleep(ms: number) { + return new Promise((resolver) => setTimeout(resolver, ms)); +} + +export async function clang_format(source: string, ...style_properties: [string, string][]) { + const encoded_input = new TextEncoder().encode(source); + const style_arg = `--style={${style_properties.map(([n, v]) => `${n}: ${v}`).join(",")}}`; + const command = new Deno.Command("clang-format", { args: ["-", style_arg], stdin: "piped", stdout: "piped" }); + const process = command.spawn(); + const writer = process.stdin.getWriter(); + await writer.write(encoded_input); + await writer.close(); + const result = await process.output(); + if (!result.success) throw new Error(`clang-format failed with style_arg: '${style_arg}'`); + const encoded_output = result.stdout; + const output = new TextDecoder().decode(encoded_output); + return output; +} + +Deno.test("try_clang_format", async () => { + const input = "int a = 3;"; + const output = await clang_format(input); + assertEquals(input, output); +});