├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── LICENCE.md ├── README.md ├── front-end ├── .gitignore ├── package-lock.json ├── package.json ├── public │ ├── activateArrayIcon.svg │ ├── activeFieldIcon.svg │ ├── addIcon.svg │ ├── deactivateArrayIcon.svg │ ├── deleteIcon.svg │ ├── favicon.ico │ ├── favicon.svg │ ├── inactiveFieldIcon.svg │ ├── index.html │ ├── loadingFailedIcon.svg │ ├── loadingFinishedIcon.svg │ ├── loadingIcon.gif │ ├── loadingIcon.svg │ ├── manifest.json │ ├── questionIcon.svg │ └── robots.txt └── src │ ├── API.js │ ├── App.jsx │ ├── Yaml&ConfigStuff.js │ ├── code_snippets │ ├── cppCode.cpp │ └── javaCode.java │ ├── components │ ├── ArraySelector.jsx │ ├── ConfigUiPage.jsx │ ├── ConfigUiPage.module.css │ ├── Editor.jsx │ ├── Editor.module.css │ ├── Error.jsx │ ├── Error.module.css │ ├── Header.jsx │ ├── Header.module.css │ ├── IncludeCategoriesSelector.jsx │ ├── LoadingIcon.jsx │ ├── LoadingIcon.module.css │ ├── MapSelector.jsx │ ├── Option.jsx │ ├── Option.module.css │ ├── OptionList.jsx │ ├── RawStringFormatsSelector.jsx │ ├── Selector.module.css │ ├── SingleSelector.jsx │ └── ace.css │ ├── config.json │ ├── index.css │ ├── index.js │ └── reportWebVitals.js ├── llvm ├── .gitignore ├── Dockerfile ├── build-config.py ├── prepare.sh └── requirements.txt └── server ├── Dockerfile ├── cmd └── clang-format-configurator │ └── main.go ├── config.json ├── debug-script.sh ├── debug.Dockerfile ├── gen-service.sh ├── go.mod ├── go.sum └── internal └── app ├── config └── config.go ├── formatter └── formatter.go └── server ├── server.go ├── server_test.go └── testing_data.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | .vscode/* 18 | !.vscode/launch.json 19 | !.vsode/tasks.json 20 | 21 | server/bin 22 | server/third-party/* 23 | server/*.service 24 | 25 | front-end/.prettierrc -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Golang Remote Debug", 6 | "type": "go", 7 | "request": "attach", 8 | "mode": "remote", 9 | "port": 2345, 10 | "host": "127.0.0.1", 11 | "apiVersion": 2, 12 | "trace": "verbose", 13 | "showLog": true, 14 | "preLaunchTask": "start_server_remote_debugger" 15 | }, 16 | { 17 | "name": "Golang Local Debug", 18 | "type": "go", 19 | "request": "attach", 20 | "mode": "remote", 21 | "port": 2345, 22 | "host": "127.0.0.1", 23 | "apiVersion": 2, 24 | "trace": "verbose", 25 | "showLog": true, 26 | "preLaunchTask": "start_server_local_debugger" 27 | }, 28 | { 29 | "name": "Debug React App", 30 | "request": "launch", 31 | "type": "chrome", 32 | "preLaunchTask": "start_npm_dev_server", 33 | "url": "http://localhost:3000", 34 | "webRoot": "${workspaceFolder}" 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "start_server_remote_debugger", 6 | "command": "./debug-script.sh", 7 | "args": [ 8 | "--all" 9 | ], 10 | "isBackground": true, 11 | "problemMatcher": { 12 | "owner": "custom", 13 | "pattern": [ 14 | { 15 | "regexp": "(.*):(\\d+:\\d+):(.*)", 16 | "file": 1, 17 | "location": 2, 18 | "message": 3 19 | } 20 | ], 21 | "background": { 22 | "activeOnStart": false, 23 | "beginsPattern": "Sending build.*", 24 | "endsPattern": "API server listening at.*" 25 | } 26 | }, 27 | "type": "shell", 28 | "options": { 29 | "cwd": "${workspaceFolder}/server/" 30 | } 31 | }, 32 | { 33 | "label": "start_server_local_debugger", 34 | "command": "./debug-script.sh", 35 | "args": [ 36 | "--run-local" 37 | ], 38 | "type": "shell", 39 | "isBackground": true, 40 | "problemMatcher": { 41 | "owner": "custom", 42 | "pattern": [ 43 | { 44 | "regexp": "(.*):(\\d+:\\d+):(.*)", 45 | "file": 1, 46 | "location": 2, 47 | "message": 3 48 | } 49 | ], 50 | "background": { 51 | "activeOnStart": false, 52 | "beginsPattern": "Starting local debugger", 53 | "endsPattern": "API server listening at.*" 54 | } 55 | }, 56 | "options": { 57 | "cwd": "${workspaceFolder}/server/" 58 | } 59 | }, 60 | { 61 | "label": "stop_container", 62 | "command": "./debug-script.sh", 63 | "args": [ 64 | "--stop" 65 | ], 66 | "type": "shell", 67 | "options": { 68 | "cwd": "${workspaceFolder}/server/" 69 | } 70 | }, 71 | { 72 | "type": "npm", 73 | "script": "start", 74 | "path": "front-end", 75 | "isBackground": true, 76 | "label": "start_npm_dev_server", 77 | "problemMatcher": { 78 | "owner": "custom", 79 | "pattern": [ 80 | { 81 | "regexp": "(.*):(\\d+:\\d+):(.*)", 82 | "file": 1, 83 | "location": 2, 84 | "message": 3 85 | } 86 | ], 87 | "background": { 88 | "activeOnStart": false, 89 | "beginsPattern": "Starting the development server*", 90 | "endsPattern": "compiled" 91 | } 92 | }, 93 | "detail": "BROWSER=chrome react-scripts start", 94 | "options": { 95 | "cwd": "${workspaceFolder}/front-end/" 96 | } 97 | 98 | } 99 | ] 100 | } -------------------------------------------------------------------------------- /LICENCE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Wirena 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clang-format-configurator-v2 2 | 3 | Interactively create a clang-format configuration while watching how the changes affect your code. 4 | 5 | Check it out at https://clang-format-configurator.site/ 6 | 7 | Clang-format-configurator-v2 is a written from scratch successor of [clang-format-configurator](https://github.com/zed0/clang-format-configurator) with following bugs fixed and new features implemented: 8 | 9 | - Newer clang-format versions are available 10 | - Options show default values instead of "Default" when BasedOnStyle is selected 11 | - Support for complex options such as arrays, BraceWrapping, IncludeCategories, RawStringFormats etc 12 | - Readable error description on invalid option value instead of "Bad Request" 13 | - Support for multiple programming languages 14 | - For complex options config file is correctly(?) generated now 15 | - Incomprehensible frontend shitcode and idiotic config generation algorithm 16 | - Fix of critical "Not invented here" bug 17 | 18 | ## User guide 19 | 20 | ### Basic info 21 | 22 | Clang-format tool defaults all unset options to values from selected BasedOnStyle when doing formatting, this configurator follows such behaviour: every time `BasedOnStyle` is changed, all options are filled with values from selected style, when user uploads their .clang-format config file, configurator also automatically fills all unset options to match style from user's config. 23 | 24 | Default values for enum options in dropdown selectors are marked with asterisk (*) on Firefox and with different color and italic font style on other browsers. Default values for strings and numbers are set as a placeholder (can be seen when input field is empty). Setting dropdown selectors to blank does the same thing as setting them to current style's default value. 25 | 26 | String selectors have "Unset" button to the right of an edit field and work this way: 27 | 28 | - Unset option marked with "Option unset" text and is, well, unset. It defaults to selected style 29 | - Active option with empty string in it is set to an empty string 30 | 31 | Array selectors have big red X buttons and big blue + buttons. First ones delete element above them, second ones append new empty element to the end. There are no empty arrays, because clang-format treats them the same way as unset option. 32 | 33 | "Autoformat on changes" checkbox is quite self explanatory, works both for code changes and option changes 34 | 35 | Deprecated options are placed at the bottom of the list and have strikethrough title style 36 | 37 | Red "overridden by X" warning means that this option value is overridden by X option value. Currently, works only for `BraceWrapping` and `SpaceBeforeParensOptions`. If you know other options that override something, please let me know by creating an issue. 38 | 39 | Legacy values (`None, Consecutive, AcrossEmptyLines, AcrossComments, AcrossEmptyLinesAndComments`) for `AlignConsecutive*` are not supported by configurator. 40 | 41 | ### Config File page 42 | 43 | Well, "Upload"/"Download" buttons are for uploading/downloading config file, editor is for editing or copypasting config file contents in-place. "Close" button disgards all changes to config file, "Load Config" buttons applies them. 44 | 45 | "Remove duplicates with BasedOnStyle (Not tested)" checkbox is for removing options, which values match selected BasedOnStyle style. This feature is not properly tested. 46 | 47 | ### Diff mode 48 | 49 | Configurator formats code on the left/top panel, code on the right/bottom panel is left unchanged. 50 | 51 | Btw, if you want to make formatting as close as possible to existing formatted code, then try [clang-unformat](https://github.com/alandefreitas/clang-unformat) 52 | 53 | 54 | ## Build and Development 55 | 56 | ### Build and Run 57 | 58 | Requirements: docker, nodejs, linux or wsl 59 | 60 | 1. Build clang-format binaries and frontend config 61 | 62 | ``llvm/prepare.sh`` script creates debian docker image, builds clang-format from source, dumps styles and copy artifacts outside of container, then launches ``llvm/build-config.py`` script 63 | 64 | Whole process takes around 30 minutes on Intel i5 12600 65 | 66 | ``` 67 | cd llvm 68 | ./prepare.sh --all 69 | ``` 70 | Clang-format binaries are placed in ``server/third-party/clang-format`` direcotry, docs and defaults are in ``llvm/docs/`` and ``llvm/defaults/`` respectively and config is written into ``llvm/config.json``. ``front-end/src/config.json`` is a soft link to ``llvm/config.json`` 71 | 72 | Install JS dependencies 73 | 74 | ``` 75 | cd front-end 76 | npm install 77 | ``` 78 | 79 | 2. Configure server and client 80 | 81 | - Set URL of Format endpoint in ``front-end/src/config.json`` using ``FormatApiUrl`` key, or keep ``http://localhost:8080/format?`` by default 82 | - Set server bind address in ``server/config.json`` using key ``bind-addr`` 83 | - Set path to TLS key and certificate in ``server/config.json`` using keys ``certificate-file`` and ``key-file`` or leave it empty to run plain HTTP server without encryption 84 | 85 | 3. Run and Debug with VS Code 86 | 87 | - Run "Debug React App" to debug front-end 88 | - Run "Golang Remote Debug" to debug server in docker container 89 | - Run "Golang Local Debug" to debug server locally if you have golang installed 90 | 91 | ### Clang-format version list modification 92 | 93 | - List of clang-format versions is written in [TAGS](https://github.com/Wirena/clang-format-configurator-v2/blob/master/llvm/prepare.sh#L146) variable in ``llvm/prepare.sh`` script. This array holds git tag names, tag format must follow this pattern: "llvmorg-x.x.x", because ``get-version-from-tag`` extracts version string from tag name. If you want to support a version, which tag name does not follow this pattern, then modify ``get-version-from-tag`` function. Unless it works as is, idk 94 | 95 | - Run ``llvm/prepare.sh`` to build new version binaries and build frontend config file. 96 | 97 | - Then, manually modify "versions" array in backend config file ``server/config.json``. 98 | 99 | ### Config file format 100 | 101 | Config file format is not documented. It's generated by ``llvm/build-config.py`` script, and this script is utter shitcode. Even I don't understand how it works anymore. 102 | 103 | ## TODOs: 104 | - Preserve comments - [feature](https://github.com/Wirena/clang-format-configurator-v2/issues/21#issuecomment-1567945533) 105 | - Better C++ and Java code examples that show effect of selecting different formatting options 106 | - Code examples for Protobuf, C# and Objective C 107 | 108 | -------------------------------------------------------------------------------- /front-end/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /front-end/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "front-end", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.1", 7 | "@testing-library/react": "^12.1.2", 8 | "@testing-library/user-event": "^13.5.0", 9 | "ace-builds": "^1.7.1", 10 | "file-saver": "^2.0.5", 11 | "html-webpack-plugin": "^5.5.0", 12 | "lodash": "^4.17.21", 13 | "lodash.clonedeepwith": "^4.5.0", 14 | "re-resizable": "^6.9.1", 15 | "react": "^17.0.2", 16 | "react-ace": "^9.5.0", 17 | "react-collapsible": "^2.8.4", 18 | "react-cookie": "^4.1.1", 19 | "react-dom": "^17.0.2", 20 | "react-scripts": "5.0.0", 21 | "reactjs-popup": "^2.0.5", 22 | "web-vitals": "^2.1.4", 23 | "yaml": "^2.2.2" 24 | }, 25 | "scripts": { 26 | "start": "BROWSER=none react-scripts start", 27 | "build": "react-scripts build", 28 | "test": "react-scripts test", 29 | "eject": "react-scripts eject" 30 | }, 31 | "eslintConfig": { 32 | "extends": [ 33 | "react-app", 34 | "react-app/jest" 35 | ] 36 | }, 37 | "browserslist": { 38 | "production": [ 39 | ">0.2%", 40 | "not dead", 41 | "not op_mini all" 42 | ], 43 | "development": [ 44 | "last 1 chrome version", 45 | "last 1 firefox version", 46 | "last 1 safari version" 47 | ] 48 | }, 49 | "devDependencies": { 50 | "@babel/plugin-proposal-private-property-in-object": "^7.21.11", 51 | "style-loader": "^3.3.1" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /front-end/public/activateArrayIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 45 | 46 | 48 | 49 | 51 | image/svg+xml 52 | 54 | 55 | 56 | 57 | 58 | 64 | 72 | 80 | 85 | 90 | 91 | 97 | 98 | -------------------------------------------------------------------------------- /front-end/public/activeFieldIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 36 | 40 | 45 | 46 | 47 | 66 | 72 | 73 | 75 | 76 | 78 | image/svg+xml 79 | 81 | 82 | 83 | 84 | 85 | 91 | 99 | 100 | 104 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /front-end/public/addIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 45 | 46 | 48 | 49 | 51 | image/svg+xml 52 | 54 | 55 | 56 | 57 | 58 | 64 | 72 | 80 | 81 | 87 | 92 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /front-end/public/deactivateArrayIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 45 | 46 | 48 | 49 | 51 | image/svg+xml 52 | 54 | 55 | 56 | 57 | 58 | 64 | 72 | 80 | 88 | 89 | 95 | 99 | 104 | 105 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /front-end/public/deleteIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 45 | 46 | 48 | 49 | 51 | image/svg+xml 52 | 54 | 55 | 56 | 57 | 58 | 64 | 72 | 80 | 81 | 87 | 93 | 96 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /front-end/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wirena/clang-format-configurator-v2/30a75fe2ddc8392185cce27842c00573b65f8bcd/front-end/public/favicon.ico -------------------------------------------------------------------------------- /front-end/public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 35 | 37 | 43 | 44 | 48 | 56 | 57 | 62 | 67 | 78 | CF 90 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /front-end/public/inactiveFieldIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 36 | 40 | 45 | 46 | 47 | 66 | 72 | 73 | 75 | 76 | 78 | image/svg+xml 79 | 81 | 82 | 83 | 84 | 85 | 91 | 99 | 100 | 104 | 112 | 113 | 117 | 123 | 124 | 129 | 135 | 136 | 140 | 147 | 152 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /front-end/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 12 | 13 | Clang-format configurator 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /front-end/public/loadingFailedIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 36 | 38 | 42 | 47 | 55 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /front-end/public/loadingFinishedIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 36 | 38 | 42 | 47 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /front-end/public/loadingIcon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wirena/clang-format-configurator-v2/30a75fe2ddc8392185cce27842c00573b65f8bcd/front-end/public/loadingIcon.gif -------------------------------------------------------------------------------- /front-end/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /front-end/public/questionIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 29 | 34 | 35 | 42 | 43 | 63 | 65 | 66 | 68 | image/svg+xml 69 | 71 | 72 | 73 | 74 | 75 | 81 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /front-end/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /front-end/src/API.js: -------------------------------------------------------------------------------- 1 | import config from "./config.json"; 2 | 3 | export function Format(code, yamlStyle, version, language, onFormat, onError) { 4 | let filext = ""; 5 | switch (language) { 6 | case "c_cpp": 7 | filext = ".cpp" 8 | break; 9 | case "java": 10 | filext = ".java" 11 | break 12 | } 13 | const body = { code: code, style: yamlStyle } 14 | fetch(config.FormatApiUrl + new URLSearchParams({ 15 | version: version, 16 | filext: filext 17 | }), { 18 | body: JSON.stringify(body), 19 | mode: 'cors', 20 | method: 'POST' 21 | }) 22 | .then(response => { 23 | if (response.ok) 24 | response.text().then(onFormat) 25 | else 26 | response.text().then(onError) 27 | }).catch((errorObject) => {onError(errorObject.toString())}) 28 | } -------------------------------------------------------------------------------- /front-end/src/App.jsx: -------------------------------------------------------------------------------- 1 | import Header from "./components/Header"; 2 | import OptionList from "./components/OptionList"; 3 | import { Resizable } from "re-resizable"; 4 | import Editor from "./components/Editor"; 5 | import Error from "./components/Error"; 6 | import LoadingIcon from "./components/LoadingIcon"; 7 | import { useEffect, useState, useRef } from "react"; 8 | import config from "./config.json"; 9 | import { buildYamlCmdString } from "./Yaml&ConfigStuff" 10 | import { Format } from "./API" 11 | import { useCookies } from "react-cookie"; 12 | import Popup from 'reactjs-popup'; 13 | 14 | import { debounce } from "lodash"; 15 | import ConfigUiPage from "./components/ConfigUiPage"; 16 | 17 | 18 | const App = () => { 19 | const [cookie, setCookie] = useCookies([]) 20 | 21 | const [configPageOpened, setConfigPageOpened] = useState(false) 22 | 23 | const errorText = useRef("") 24 | const [activeErrorPopup, setActiveErrorPopup] = useState(false) 25 | const [darkThemeActive, setDarkThemeActive] = useState(cookie.theme === "dark" || cookie.theme === "light" ? cookie.theme === "dark" : window.matchMedia("(prefers-color-scheme: dark)").matches) 26 | useEffect(() => { if (darkThemeActive) document.body.className = "dark"; else document.body.className = "" }, [darkThemeActive]) 27 | const [options, setOptions] = useState( 28 | { selectedVersion: config.Versions.values[0].arg_val_enum.includes(cookie.version) ? cookie.version : config.Versions.values[0].arg_val_enum[0], BasedOnStyle: undefined }); 29 | // code editor text 30 | const [text, setText] = useState(""); 31 | // autoupdate formatting on option or code change 32 | const [autoUpdateFormatting, setAutoUpdateFormatting] = useState(true); 33 | const [currentLang, setCurrentLang] = useState("c_cpp") 34 | /* 35 | List of options that were modified manually 36 | Used to ease removing options, which values match default values fro selected style 37 | */ 38 | const modifiedOptionTitles = useRef([]) 39 | /* 40 | Options object with defaults for selected style set, without user input modifications 41 | */ 42 | const unmodifiedOptions = useRef({}) 43 | 44 | 45 | function formatCode(options, text, currentLang) { 46 | const yamlStr = buildYamlCmdString(options, config) 47 | Format(text, yamlStr, options.selectedVersion, currentLang, (code) => { if (code !== "") { setText(code); window.loadingIcon.setLoadingState("success") } }, 48 | (errortext) => { 49 | window.loadingIcon.setLoadingState("error") 50 | errorText.current = errortext 51 | setActiveErrorPopup(true) 52 | }) 53 | }; 54 | 55 | useEffect(() => { if (autoUpdateFormatting) { formatCode(options, text, currentLang) } }, [text]); 56 | 57 | const editorTabWidth = (options.TabWidth) ? options.TabWidth : 4; 58 | 59 | // Another useeffect for options because of debouncing 60 | const formatCodeDebounced = useRef(debounce(formatCode, 1000, { leading: false, trailing: true })).current 61 | useEffect(() => { 62 | if (autoUpdateFormatting) { 63 | window.loadingIcon.setLoadingState("loading"); 64 | formatCodeDebounced(options, text, currentLang) 65 | } 66 | }, [options]); 67 | return ( 68 |
69 |
{ const newDarkThemeStatus = !darkThemeActive; setCookie("theme", newDarkThemeStatus ? "dark" : "light", { path: "/" }); setDarkThemeActive(newDarkThemeStatus) }} 73 | onUpdate={() => { window.loadingIcon.setLoadingState("loading"); formatCode(options, text, currentLang) }} 74 | onConfigFile={() => { setConfigPageOpened(true) }} 75 | onAutoFormatChange={setAutoUpdateFormatting} 76 | /> 77 | setActiveErrorPopup(false)}> 85 | 88 | 89 | setConfigPageOpened(false)} 93 | modal={true} 94 | closeOnEscape={false} 95 | closeOnDocumentClick={false} 96 | position="center center" 97 | modifiedOptionTitles={modifiedOptionTitles} 98 | unmodifiedOptions={unmodifiedOptions} 99 | 100 | > 101 | setConfigPageOpened(false)} 103 | onLoaded={({ newOptions, _unmodifiedOptions, _modifiedOptionTitles }) => { 104 | if (newOptions === undefined) 105 | return 106 | modifiedOptionTitles.current = _modifiedOptionTitles 107 | unmodifiedOptions.current = _unmodifiedOptions 108 | setOptions(newOptions) 109 | setConfigPageOpened(false) 110 | }} 111 | onError={(errorDescription) => { 112 | errorText.current = errorDescription 113 | setActiveErrorPopup(true) 114 | }} 115 | darkTheme={darkThemeActive} 116 | options={options} 117 | modifiedOptionTitles={modifiedOptionTitles} 118 | unmodifiedOptions={unmodifiedOptions} 119 | 120 | /> 121 | 122 | 123 |
124 | 139 |
140 | { unmodifiedOptions.current = options }} 144 | llvmVersionOption={config.Versions} 145 | onOptionChange={setOptions} 146 | updateModifiedList={(title) => { 147 | if (title === undefined) 148 | modifiedOptionTitles.current = [] 149 | else { 150 | if (!modifiedOptionTitles.current.includes(title)) modifiedOptionTitles.current.push(title) 151 | } 152 | }} 153 | /> 154 |
155 |
156 |
157 | } 165 | columnLimitLine={options.ColumnLimit ? Number(options.ColumnLimit) : 80} 166 | /> 167 | 168 |
169 |
170 |
171 | ); 172 | } 173 | 174 | 175 | export default App; 176 | -------------------------------------------------------------------------------- /front-end/src/Yaml&ConfigStuff.js: -------------------------------------------------------------------------------- 1 | import yaml from "yaml" 2 | import { cloneDeepWith, cloneDeep, isEmpty, isEqual, isNumber, isObject } from "lodash"; 3 | 4 | export class ValidationError extends Error { } 5 | 6 | 7 | function mapAlignConsecutive(value, version) { 8 | switch (value) { 9 | case "None": 10 | if (version >= 18) 11 | return { 12 | Enabled: false, 13 | AcrossEmptyLines: false, 14 | AcrossComments: false, 15 | AlignCompound: false, 16 | AlignFunctionPointers: false, 17 | PadOperators: true 18 | } 19 | else 20 | return { 21 | Enabled: false, 22 | AcrossEmptyLines: false, 23 | AcrossComments: false, 24 | AlignCompound: false, 25 | PadOperators: true 26 | } 27 | case "Consecutive": 28 | if (version >= 18) 29 | return { 30 | Enabled: true, 31 | AcrossEmptyLines: false, 32 | AcrossComments: false, 33 | AlignCompound: false, 34 | AlignFunctionPointers: false, 35 | PadOperators: true 36 | } 37 | else 38 | return { 39 | Enabled: true, 40 | AcrossEmptyLines: false, 41 | AcrossComments: false, 42 | AlignCompound: false, 43 | PadOperators: true 44 | } 45 | case "AcrossEmptyLines": 46 | if (version >= 18) 47 | return { 48 | Enabled: true, 49 | AcrossEmptyLines: true, 50 | AcrossComments: false, 51 | AlignCompound: false, 52 | AlignFunctionPointers: false, 53 | PadOperators: true 54 | } 55 | else 56 | return { 57 | Enabled: true, 58 | AcrossEmptyLines: true, 59 | AcrossComments: false, 60 | AlignCompound: false, 61 | PadOperators: true 62 | } 63 | case "AcrossComments": 64 | if (version >= 18) 65 | return { 66 | Enabled: true, 67 | AcrossEmptyLines: false, 68 | AcrossComments: true, 69 | AlignCompound: false, 70 | AlignFunctionPointers: false, 71 | PadOperators: true 72 | } 73 | else return { 74 | Enabled: true, 75 | AcrossEmptyLines: false, 76 | AcrossComments: true, 77 | AlignCompound: false, 78 | PadOperators: true 79 | } 80 | case "AcrossEmptyLinesAndComments": 81 | if (version >= 18) 82 | return { 83 | Enabled: true, 84 | AcrossEmptyLines: true, 85 | AcrossComments: true, 86 | AlignCompound: false, 87 | AlignFunctionPointers: false, 88 | PadOperators: true 89 | } 90 | else 91 | return { 92 | Enabled: true, 93 | AcrossEmptyLines: true, 94 | AcrossComments: true, 95 | AlignCompound: false, 96 | PadOperators: true 97 | } 98 | case "true": 99 | if (version >= 18) 100 | return { 101 | Enabled: true, 102 | AcrossEmptyLines: false, 103 | AcrossComments: false, 104 | AlignCompound: false, 105 | AlignFunctionPointers: false, 106 | PadOperators: true 107 | } 108 | else 109 | return { 110 | Enabled: true, 111 | AcrossEmptyLines: false, 112 | AcrossComments: false, 113 | AlignCompound: false, 114 | PadOperators: true 115 | } 116 | case "false": 117 | if (version >= 18) 118 | return { 119 | Enabled: false, 120 | AcrossEmptyLines: false, 121 | AcrossComments: false, 122 | AlignCompound: false, 123 | AlignFunctionPointers: false, 124 | PadOperators: true 125 | } 126 | else 127 | return { 128 | Enabled: false, 129 | AcrossEmptyLines: false, 130 | AcrossComments: false, 131 | AlignCompound: false, 132 | PadOperators: true 133 | } 134 | default: 135 | return undefined 136 | } 137 | } 138 | 139 | 140 | 141 | export function convertLegacyAlignConsectutiveOptions(yamlString, version) { 142 | let options = yaml.parse(yamlString) 143 | const optionsList = Object.keys(options) 144 | for (let i = 0; i < optionsList.length; i++) { 145 | const isString = typeof options[optionsList[i]] === "string"; 146 | const isBoolean = typeof options[optionsList[i]] === "boolean"; 147 | if (optionsList[i].startsWith("AlignConsecutive") && (isBoolean || isString)) { 148 | const currentVal = options[optionsList[i]]; 149 | options[optionsList[i]] = mapAlignConsecutive(isString ? currentVal : currentVal.toString(), version) 150 | } 151 | } 152 | const doc = new yaml.Document(); 153 | doc.contents = options 154 | const YamlOptions = { 155 | directives: true 156 | } 157 | return doc.toString(YamlOptions) 158 | } 159 | 160 | function removeOptionsDuplicatingStyleDefs(options, modifiedOptionTitles, unmodifiedOptions) { 161 | for (const [key1, value1] of Object.entries(options)) { 162 | 163 | if (key1 === "BasedOnStyle") continue 164 | 165 | if (key1 === "BraceWrapping") { 166 | if (options["BreakBeforeBraces"] !== "Custom") { 167 | delete options[key1] 168 | } 169 | continue 170 | } 171 | 172 | if (!modifiedOptionTitles.includes(key1)) { 173 | delete options[key1] 174 | continue 175 | } 176 | 177 | if (Array.isArray(value1)) { 178 | if (isEqual(value1, unmodifiedOptions[key1])) 179 | delete options[key1] 180 | continue 181 | } 182 | 183 | // if object, compare its properties and delete those that match unmodified defaults 184 | if (isObject(value1)) { 185 | for (const [key2, value2] of Object.entries(value1)) 186 | if (value2 === unmodifiedOptions[key1][key2]) 187 | delete options[key1][key2] 188 | if (Object.entries(options[key1]).length === 0) 189 | delete options[key1] 190 | continue 191 | } 192 | 193 | if (value1 === unmodifiedOptions[key1]) 194 | delete options[key1] 195 | } 196 | return options 197 | } 198 | 199 | 200 | 201 | export function buildYamlConfigFile(chosenOptions, removeDuplicates, modifiedOptionTitles, unmodifiedOptions) { 202 | let options = cloneDeep(chosenOptions) 203 | if (options.BasedOnStyle !== undefined && removeDuplicates) 204 | options = removeOptionsDuplicatingStyleDefs(options, modifiedOptionTitles, unmodifiedOptions) 205 | 206 | delete options.selectedVersion 207 | const doc = new yaml.Document(); 208 | doc.contents = options 209 | const YamlOptions = { 210 | directives: true 211 | } 212 | return doc.toString(YamlOptions) 213 | } 214 | 215 | 216 | 217 | function removeEmpty(obj) { 218 | Object.entries(obj).filter(([k, v]) => isObject(v)).forEach(([k, v]) => { 219 | if (Array.isArray(v)) { 220 | //remove empty objects from array 221 | obj[k] = v.filter(n => Object.entries(Object.fromEntries( 222 | Object.entries(n).filter(([_, v]) => v != null))).length) 223 | if (obj[k].length === 0) 224 | delete obj[k] 225 | } else if (isEmpty(v)) obj[k] = undefined; else v = removeEmpty(v) 226 | }) 227 | return obj 228 | } 229 | 230 | 231 | 232 | export function buildYamlCmdString(chosenOptions, config) { 233 | //clone object not to interfere with App's state 234 | const customizer = (value, key, object) => { 235 | if (object) { 236 | /* 237 | This part is kinda cringe: clang-format cant parse flow style yaml file 238 | when integer value is not followed by comma, i.e. any last integer value will give an error, 239 | Example: 240 | { 241 | Regex: '^', 242 | Priority: 2, 243 | SortPriority: 0, <--- OK 244 | CaseSensitive: 'false' 245 | }, 246 | { 247 | Regex: '^<.*\.h>', 248 | Priority: '1', 249 | SortPriority: 0 <---- Apparently not OK 250 | }, 251 | 252 | 253 | So we turn all number into strings. Genius 254 | */ 255 | if (isNumber(value)) 256 | return value.toString(); 257 | 258 | // And also remove empty objects. Clang-format does not like them either 259 | if (isEmpty(value) && isObject(value)) 260 | return null 261 | } 262 | return undefined; 263 | } 264 | 265 | const doc = new yaml.Document(); 266 | doc.contents = removeEmpty(cloneDeepWith(chosenOptions, customizer)) 267 | delete doc.contents.selectedVersion 268 | const YamlOptions = { 269 | collectionStyle: 'flow', indentSeq: false, 270 | // false, true and other values have to be strings for the same reason as numbers 271 | falseStr: "'false'", trueStr: "'true'", defaultStringType: 'QUOTE_SINGLE', 272 | defaultKeyType: 'PLAIN', 273 | } 274 | return doc.toString(YamlOptions) 275 | } 276 | 277 | 278 | export function loadOptionsFromString(optionsStr, config, selectedVersion, onLoaded) { 279 | const options = yaml.parse(optionsStr) 280 | if (typeof (options) == "string" || Array.isArray(options) || options === null) 281 | throw new Error("Looks like invalid yaml file") 282 | 283 | manuallyValidate(options, selectedVersion) 284 | 285 | const BasedOnStyle = options.BasedOnStyle 286 | if (BasedOnStyle === undefined) { 287 | const modifiedOptionTitles = Object.entries(options).map(([k, v]) => { return k }) 288 | options.selectedVersion = selectedVersion 289 | onLoaded({ 290 | newOptions: options, _unmodifiedOptions: undefined, 291 | _modifiedOptionTitles: modifiedOptionTitles 292 | }) 293 | return 294 | } 295 | 296 | // check if config is compatible with selected version 297 | // values might also differ for same key from version to version but this solution is ok for now 298 | 299 | const listOfOptionTitles = config[selectedVersion].map(el => el.title) 300 | Object.entries(options).forEach(([k, v]) => { 301 | if (!listOfOptionTitles.includes(k)) { 302 | throw new Error("Config contains key that is incompatible with selected clang-format version:\n" + k) 303 | } 304 | }) 305 | 306 | // fill optionsWithDefaults with default values for selected style 307 | let unmodifiedOptions = { 308 | selectedVersion: selectedVersion, 309 | BasedOnStyle: BasedOnStyle 310 | } 311 | 312 | config[unmodifiedOptions.selectedVersion] 313 | .slice(1).forEach((option) => { 314 | if ( 315 | option.values.length === 1 && 316 | option.values[0].defaults[BasedOnStyle] !== undefined 317 | ) { 318 | unmodifiedOptions[option.title] = 319 | option.values[0].defaults[BasedOnStyle].value; 320 | } else { 321 | // set all option values to selected style defaults 322 | // inluding nested ones 323 | // filter out options without defaults for this style 324 | option.values.filter((element) => element.defaults[BasedOnStyle] !== undefined) 325 | .forEach((nestedOption) => { 326 | if (unmodifiedOptions[option.title] == undefined) 327 | unmodifiedOptions[option.title] = {} 328 | unmodifiedOptions[option.title][nestedOption.title] = 329 | nestedOption.defaults[BasedOnStyle].value 330 | } 331 | ); 332 | } 333 | }); 334 | let modifedOptions = cloneDeep(unmodifiedOptions) 335 | let modifiedOptionTitles = [] 336 | Object.entries(options).forEach(([k, v]) => { 337 | if (isObject(modifedOptions[k])) 338 | /*if value is object such as BraceWrapping i.e. has nested options 339 | then simply doing modifedOptions[k] = v; will undefine those properties 340 | which were not declared in user selected .clang-format file 341 | */ 342 | Object.assign(modifedOptions[k], v) 343 | else 344 | modifedOptions[k] = v; 345 | modifiedOptionTitles.push(k) 346 | }) 347 | onLoaded({ 348 | newOptions: modifedOptions, _unmodifiedOptions: unmodifiedOptions, 349 | _modifiedOptionTitles: modifiedOptionTitles 350 | }) 351 | 352 | } 353 | 354 | 355 | 356 | export function loadOptionsFromFile(fileName, config, selectedVersion, onError, onLoaded) { 357 | const reader = new FileReader(); 358 | reader.onload = (e) => { 359 | try { 360 | const options = yaml.parse(e.target.result) 361 | if (typeof (options) == "string") 362 | throw new Error("Looks like invalid yaml file") 363 | const BasedOnStyle = options.BasedOnStyle 364 | 365 | if (BasedOnStyle === undefined) { 366 | const modifiedOptionTitles = Object.entries(options).map(([k, v]) => { return k }) 367 | options.selectedVersion = selectedVersion 368 | onLoaded({ 369 | newOptions: options, _unmodifiedOptions: undefined, 370 | _modifiedOptionTitles: modifiedOptionTitles 371 | }) 372 | return 373 | } 374 | 375 | // check if config is compatible with selected version 376 | // values might also differ for same key from version to version but this solution is ok for now 377 | 378 | const listOfOptionTitles = config[selectedVersion].map(el => el.title) 379 | Object.entries(options).forEach(([k, v]) => { 380 | if (!listOfOptionTitles.includes(k)) { 381 | throw new Error("Config contains key that is incompatible with selected clang-format version:\n" + k) 382 | } 383 | }) 384 | 385 | // fill optionsWithDefaults with default values for selected style 386 | let unmodifiedOptions = { 387 | selectedVersion: selectedVersion, 388 | BasedOnStyle: BasedOnStyle 389 | } 390 | 391 | config[unmodifiedOptions.selectedVersion] 392 | .slice(1).forEach((option) => { 393 | if ( 394 | option.values.length === 1 && 395 | option.values[0].defaults[BasedOnStyle] !== undefined 396 | ) { 397 | unmodifiedOptions[option.title] = 398 | option.values[0].defaults[BasedOnStyle].value; 399 | } else { 400 | // set all option values to selected style defaults 401 | // inluding nested ones 402 | // filter out options without defaults for this style 403 | option.values.filter((element) => element.defaults[BasedOnStyle] !== undefined) 404 | .forEach((nestedOption) => { 405 | if (unmodifiedOptions[option.title] == undefined) 406 | unmodifiedOptions[option.title] = {} 407 | unmodifiedOptions[option.title][nestedOption.title] = 408 | nestedOption.defaults[BasedOnStyle].value 409 | } 410 | ); 411 | } 412 | }); 413 | let modifedOptions = cloneDeep(unmodifiedOptions) 414 | let modifiedOptionTitles = [] 415 | Object.entries(options).forEach(([k, v]) => { 416 | if (isObject(modifedOptions[k])) 417 | /*if value is object such as BraceWrapping i.e. has nested options 418 | then simply doing modifedOptions[k] = v; will undefine those properties 419 | which were not declared in user selected .clang-format file 420 | */ 421 | Object.assign(modifedOptions[k], v) 422 | else 423 | modifedOptions[k] = v; 424 | modifiedOptionTitles.push(k) 425 | }) 426 | onLoaded({ 427 | newOptions: modifedOptions, _unmodifiedOptions: unmodifiedOptions, 428 | _modifiedOptionTitles: modifiedOptionTitles 429 | }) 430 | } catch (e) { 431 | onError(e.message) 432 | } 433 | } 434 | reader.readAsText(fileName) 435 | } 436 | 437 | 438 | 439 | export function manuallyValidate(config, version) { 440 | // Check AlignConsecutive legacy values 441 | if (parseInt(version) >= 15) { 442 | const optionsList = Object.keys(config) 443 | for (let i = 0; i < optionsList.length; i++) { 444 | const isString = typeof config[optionsList[i]] === "string"; 445 | const isBoolean = typeof config[optionsList[i]] === "boolean"; 446 | if (optionsList[i].startsWith("AlignConsecutive") && (isBoolean || isString)) { 447 | throw new ValidationError() 448 | } 449 | } 450 | } 451 | } 452 | 453 | -------------------------------------------------------------------------------- /front-end/src/code_snippets/cppCode.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | // IndentPPDirectives 3 | #ifdef WIN32 4 | #include 5 | #endif 6 | // SortIncludes 7 | #include "AnotherHeader.h" 8 | #include "MyHeader.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | // AlignConsecutiveMacros 18 | #define SHORT_NAME 42 19 | #define LONGER_NAME 0x007f 20 | #define EVEN_LONGER_NAME (2) 21 | #define fooo(x) (x * x) 22 | #define baar(y, z) (y + z) 23 | 24 | // AlignEscapedNewlines 25 | #define PPP \ 26 | int aaaa; \ 27 | int b; \ 28 | int dddddddddd; 29 | 30 | namespace LevelOneNamespace { 31 | namespace LevelTwoNamespace { 32 | 33 | struct AAAAAAAAAAAAAAAAAAAA { 34 | // AlignConsecutiveDeclarations 35 | int a; 36 | int bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb; 37 | std::string cccccccccccccccccc; 38 | }; 39 | 40 | // AlwaysBreakTemplateDeclarations 41 | template T foo() {} 42 | 43 | template 44 | T foo(int aaaaaaaaaaaaaaaaaaaaa, int bbbbbbbbbbbbbbbbbbbbb) {} 45 | 46 | // AllowShortEnumsOnASingleLine 47 | enum : unsigned int { AA = 0, BB } myEnum; 48 | 49 | // SpaceBeforeInheritanceColon 50 | class B : public E { 51 | private: 52 | // AlignArrayOfStructures 53 | struct AAAAAAAAAAAAAAAAAAAA test[3] = { 54 | {56, 23, "hello"}, {-1, 93463, "world"}, {7, 5, "!!"}}; 55 | 56 | // AlignTrailingComments, AlignConsecutiveDeclarations, QualifierOrder, 57 | // QualifierAlignment, AlignTrailingComments 58 | static char const *variable; // very important variable 59 | void *const *x = nullptr; // not so important variable 60 | char const *anotherVariable; // another comment 61 | int a = 1; // another variable 62 | // used for this, this, and that 63 | int longComplicatedName = 4; 64 | int b = 3; 65 | 66 | protected: 67 | // AlwaysBreakAfterReturnType, QualifierAlignment 68 | constexpr static inline int function(int a, int b) { return (a + b) / 2; } 69 | 70 | // AllowShortFunctionsOnASingleLine 71 | static bool shortFilter(AAAAAAAAAAAAAAAAAAAA v) { return v.a != 4; } 72 | 73 | void empty() {} 74 | 75 | // IndentWrappedFunctionNames 76 | std::map, std::vector>> 77 | func(AAAAAAAAAAAAAAAAAAAA *v); 78 | 79 | public: 80 | // SpaceBeforeCtorInitializerColon 81 | explicit B() : a(9){}; 82 | 83 | // PackConstructorInitializers 84 | explicit B(int _a, int _b, int _c, std::vector str) 85 | : a(_a), b(_b), longComplicatedName(_c), anotherVariable(str[0].c_str()) { 86 | // AllowShortIfStatementsOnASingleLine, SpaceBeforeParens 87 | if (_c) 88 | anotherVariable = nullptr; 89 | if (_a) 90 | anotherVariable = "baz"; 91 | else 92 | anotherVariable = "bar"; 93 | } 94 | 95 | // AllowAllParametersOfDeclarationOnNextLine BinPackParameters 96 | int myFunction(int aaaaaaaaaaaaa, int bbbbbbbbbbbbbbbbbbbbbbb, 97 | int ccccccccccccc, int d, int e) { 98 | int myvar = aaaaaaaaaaaaa / 10; 99 | long anothervaw = d % 2; 100 | // comment 101 | char *msg = "Hello all"; 102 | 103 | // AlignOperands 104 | myvar = bbbbbbbbbbbbbbbbbbbbbbb + ccccccccccccc + aaaaaaaaaaaaa; 105 | 106 | // AllowShortCaseLabelsOnASingleLine, SpaceBeforeParens 107 | switch (e) { 108 | case 1: 109 | return e; 110 | case 2: 111 | return 2; 112 | }; 113 | 114 | // AllowShortBlocksOnASingleLine, SpaceBeforeParens 115 | while (true) { 116 | } 117 | while (true) { 118 | continue; 119 | } 120 | } 121 | 122 | // AlignAfterOpenBracket, BinPackParameters, 123 | void loooonFunctionIsVeryLongButNotAsLongAsJavaTypeNames( 124 | std::vector const &inputVector, 125 | std::map *outputMap) { 126 | std::vector bar; 127 | std::copy_if(inputVector.begin(), inputVector.end(), 128 | std::back_inserter(bar), &shortFilter); 129 | // AllowShortLambdasOnASingleLine 130 | std::sort(inputVector.begin(), inputVector.end(), 131 | [](auto v) { return v.a < v.b; }); 132 | std::transform(inputVector.begin(), inputVector.end(), 133 | std::inserter(*outputMap, outputMap->end()), 134 | [](const AAAAAAAAAAAAAAAAAAAA &element) { 135 | // LambdaBodyIndentation 136 | return std::make_pair( 137 | element.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, 138 | element.cccccccccccccccccc); 139 | }); 140 | }; 141 | int notInline(AAAAAAAAAAAAAAAAAAAA *v); 142 | }; 143 | 144 | // AllowShortFunctionsOnASingleLine 145 | int notInline(AAAAAAAAAAAAAAAAAAAA *v) { return v->a + 1; } 146 | 147 | } // namespace LevelTwoNamespace 148 | } // namespace LevelOneNamespace 149 | 150 | int main() { 151 | // ReflowComments 152 | // veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of 153 | // information 154 | /* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty 155 | * of information */ 156 | 157 | // SortUsingDeclarations 158 | using std::cin; 159 | using std::cout; 160 | 161 | int aaaaaaaaaaaaaaaaaaa, bbbbbbbbbbb, ppppppppppp, eeeee; 162 | // AlignConsecutiveAssignments 163 | aaaaaaaaaaaaaaaaaaa = 6; 164 | bbbbbbbbbbb = 5; 165 | ppppppppppp = 10; 166 | LevelOneNamespace::LevelTwoNamespace::B b{ 167 | 1, 3, 4, 168 | // SpaceBeforeCpp11BracedList 169 | std::vector{"aaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbb", 170 | "cccccccccccccccccccccccccccc"}}; 171 | // AllowShortLoopsOnASingleLine 172 | for (int i = 0; i < 10; i++) 173 | cout << i; 174 | LevelOneNamespace::LevelTwoNamespace::AAAAAAAAAAAAAAAAAAAA 175 | ddddddddddddddddddddddddd{5, 5, "ff"}; 176 | b.notInline(ddddddddddddddddddddddddd); 177 | // SpaceAfterCStyleCast, AllowAllArgumentsOnNextLine 178 | cout << (bool)b.myFunction(aaaaaaaaaaaaaaaaaaa, bbbbbbbbbbb, ppppppppppp, 179 | eeeee, 0); 180 | 181 | return 0; 182 | } 183 | -------------------------------------------------------------------------------- /front-end/src/code_snippets/javaCode.java: -------------------------------------------------------------------------------- 1 | //Who uses clang-format to format Java code? 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.io.Reader; 7 | import java.nio.charset.Charset; 8 | import java.util.Arrays; 9 | import java.util.LinkedHashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.function.Function; 13 | import java.util.stream.Collectors; 14 | import java.util.stream.Stream; 15 | import static java.util.stream.Collectors.toList; 16 | 17 | public class Main { 18 | 19 | @Partial 20 | @Mock 21 | DataLoad loader; 22 | 23 | /** 24 | * Merges two given sorted arrays into one 25 | * 26 | * @param a1 first sorted array 27 | * @param a2 second sorted array 28 | * @return new array containing all elements from a1 and a2, sorted 29 | */ 30 | public static int[] mergeArrays(int[] a1, int[] a2) { 31 | int[] res = new int[a1.length + a2.length]; 32 | int it1 = 0, it2 = 0; 33 | for (int i = 0; i < res.length; ++i) { 34 | if ((it1 < a1.length) && (it2 < a2.length)) { 35 | if (a1[it1] <= a2[it2]) { 36 | res[i] = a1[it1]; 37 | it1++; 38 | } else { 39 | res[i] = a2[it2]; 40 | it2++; 41 | } 42 | } else if (it1 == a1.length) { 43 | System.arraycopy(a2, it2, res, i, 1); 44 | it2++; 45 | } else { 46 | System.arraycopy(a1, it1, res, i, 1); 47 | it1++; 48 | } 49 | } 50 | return res; 51 | } 52 | 53 | @Annotation 54 | @AnotherAnnotation 55 | @MoreAnnotations 56 | public static void top10FrequencyWords()throws IOException{ 57 | Charset charset = Charset.forName("UTF-8"); 58 | Reader reader = new InputStreamReader(System.in, charset); 59 | BufferedReader bufferedReader = new BufferedReader(reader); 60 | Stream stream = Arrays.stream(bufferedReader.readLine().toLowerCase().split("[^\\p{L}\\p{Digit}_]+")); 61 | Map result = stream.collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); 62 | Map finalMap = new LinkedHashMap<>(); 63 | result.entrySet().stream().sorted(Map.Entry.comparingByValue().reversed()).forEachOrdered(e -> finalMap.put(e.getKey(), e.getValue())); 64 | List sortedEntries = finalMap.entrySet().stream().sorted(Map.Entry.comparingByValue().reversed().thenComparing(Map.Entry::getKey)).collect(toList()); 65 | int cnt = 0; 66 | for (Map.Entry sortedEntry : sortedEntries) { 67 | if(cnt>=10) 68 | break; 69 | System.out.println(sortedEntry.getKey()); 70 | cnt++; 71 | } 72 | System.out.flush(); 73 | } 74 | 75 | 76 | public static void main(String[] args) { 77 | 78 | try{ 79 | top10FrequencyWords(); 80 | } 81 | catch(Exception e) { 82 | System.out.println("Exception"); 83 | } 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /front-end/src/components/ArraySelector.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "./Selector.module.css"; 3 | import SingleSelector from "./SingleSelector"; 4 | import RawStringFormatsSelector from "./RawStringFormatsSelector" 5 | import IncludeCategoriesSelector from "./IncludeCategoriesSelector"; 6 | import Popup from "reactjs-popup" 7 | 8 | 9 | const ArraySelector = ({ selectorInfo, onChange, currentStyle, currentOptionValue }) => { 10 | const defaultValue = selectorInfo.defaults[currentStyle] === undefined ? 11 | undefined : selectorInfo.defaults[currentStyle].value 12 | 13 | if (currentOptionValue === undefined) 14 | 15 | return ( 16 | 17 | 22 | Add element to the array { 27 | const newArr = [] 28 | newArr.push("") 29 | onChange(newArr) 30 | }} 31 | /> 32 | 33 | } 34 | on={['hover']} 35 | position="right center" closeOnDocumentClick> 36 | {"Add element to the array"} 37 | 38 | 39 | ) 40 | 41 | return ( 42 | 43 | {currentOptionValue.map((value, index) => { 44 | let element = {} 45 | switch (selectorInfo.argument_type) { 46 | case "std::vector": 47 | element = ( { 52 | let newOptValue = [...currentOptionValue] 53 | newOptValue[index] = newValue; 54 | onChange(newOptValue) 55 | }} 56 | />) 57 | break 58 | case "std::vector": 59 | element = ( { 64 | let newOptValue = [...currentOptionValue] 65 | newOptValue[index] = newValue; 66 | onChange(newOptValue) 67 | }} 68 | />) 69 | break 70 | default: 71 | element = { 77 | let newOptValue = [...currentOptionValue] 78 | newOptValue[index] = newValue; 79 | onChange(newOptValue) 80 | }} 81 | /> 82 | break 83 | } 84 | return ( 85 |
86 | {element} 87 | 92 | Delete array element { 97 | if (currentOptionValue.length === 1) 98 | onChange(undefined) 99 | else 100 | onChange(currentOptionValue.filter((val, filterIndex) => 101 | index !== filterIndex)) 102 | }} 103 | /> 104 | 105 | } 106 | on={['hover']} 107 | position="right center" closeOnDocumentClick> 108 | {"Delete array element"} 109 | 110 |
111 | ) 112 | })} 113 | 118 | Add element to the array { 123 | if (currentOptionValue === undefined) 124 | onChange([""]) 125 | else { 126 | const newArr = [...currentOptionValue] 127 | newArr.push("") 128 | onChange(newArr) 129 | } 130 | 131 | }} 132 | /> 133 | 134 | } 135 | on={['hover']} 136 | position="right center" closeOnDocumentClick> 137 | {"Add element to the array"} 138 | 139 |
140 | ) 141 | } 142 | 143 | export default ArraySelector; 144 | -------------------------------------------------------------------------------- /front-end/src/components/ConfigUiPage.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import AceEditor from "react-ace"; 3 | import { saveAs } from 'file-saver'; 4 | import styles from "./ConfigUiPage.module.css" 5 | import "./ace.css" 6 | import "ace-builds/src-noconflict/mode-yaml"; 7 | import "ace-builds/src-noconflict/snippets/yaml"; 8 | import "ace-builds/src-noconflict/theme-textmate"; 9 | import "ace-builds/src-noconflict/theme-clouds_midnight"; 10 | import "ace-builds/src-noconflict/ext-language_tools"; 11 | import "ace-builds/src-noconflict/ext-beautify" 12 | import "ace-builds/src-min-noconflict/ext-searchbox"; 13 | import { buildYamlConfigFile, loadOptionsFromString, convertLegacyAlignConsectutiveOptions, ValidationError } from "../Yaml&ConfigStuff" 14 | import config from "../config.json"; 15 | import { useCookies } from "react-cookie"; 16 | 17 | 18 | 19 | const ConfigUiPage = ({ options, modifiedOptionTitles, unmodifiedOptions, onLoaded, onError, onClose, darkTheme }) => { 20 | const [cookie, setCookie] = useCookies([]) 21 | const [removeDuplicates, setRemoveDuplicates] = React.useState(cookie["remove-duplicates"] === "true" ? true : false) // bruh 22 | 23 | const [showAlignConsecutiveErrPage, setShowAlignConsecuriveErrPage] = React.useState(false) 24 | 25 | const [optionsText, setOptionsText] = React.useState(buildYamlConfigFile(options, removeDuplicates, modifiedOptionTitles.current, unmodifiedOptions.current)) 26 | const downloadConfigFile = React.useCallback(() => { 27 | const blob = new Blob([optionsText], { type: 'text/plain;charset=utf-8' }) 28 | saveAs(blob, ".clang-format") 29 | }, [optionsText]) 30 | 31 | const onUploadFileToStr = React.useCallback(() => { 32 | const input = document.createElement('input'); 33 | input.type = 'file'; 34 | input.onchange = (e) => { 35 | const reader = new FileReader(); 36 | reader.onload = (e) => { 37 | setOptionsText(e.target.result) 38 | } 39 | reader.readAsText(e.target.files[0]) 40 | } 41 | input.click() 42 | }, [setOptionsText]) 43 | 44 | const loadConfigTxtToOptions = React.useCallback((optionsStr) => { 45 | try { 46 | loadOptionsFromString(optionsStr, config, options.selectedVersion, (res) => { 47 | onLoaded(res) 48 | }) 49 | } catch (err) { 50 | if (err instanceof ValidationError) { 51 | setShowAlignConsecuriveErrPage(true) 52 | } else 53 | onError(err.message) 54 | } 55 | }, [onError, onLoaded, options.selectedVersion, setShowAlignConsecuriveErrPage]) 56 | if (showAlignConsecutiveErrPage) 57 | return (
58 |

59 | Failed to load config 60 |

61 |

62 | {/* Bruh */} 63 | {"Legacy values "} None{", "} Consecutive{", "} AcrossEmptyLines{", "}AcrossComments{", "} 64 | AcrossEmptyLinesAndComments{" or booleans for "}AlignConsecutive*{" options are not supported for clang-format versions >=15.0"} 65 |

66 |
67 | More info 68 |

69 | {"Since version 15.0 clang-format "} 70 | {"reads"}{" "}AlignConsecutive* 72 | {" options both as a list of nested flags, which was added to improve flexibility, and "} 73 | {"as a single value for compatibility with older config files."} 74 |

75 |

76 | {"For example, these three snippets are valid and describe the same style"} 77 |

78 |
    79 |
  •  80 |                         {`AlignConsecutiveMacros: Consecutive`}
     81 |                     
  • 82 |
  •  83 |                         {`AlignConsecutiveMacros: true`}
     84 |                     
  • 85 |
  •  86 |                         {/* Bruh */
     87 |                             `AlignConsecutiveMacros:
     88 |     Enabled: true
     89 |     AcrossEmptyLines: false
     90 |     AcrossComments: false
     91 |     AlignCompound: false
     92 |     AlignFunctionPointers: false
     93 |     PadOperators: true`}
     94 |                     
  • 95 |
96 |

97 | {"So, due to technical reasons"} 98 | {"this configurator does not support the first two ways of setting AlignConsecutive options, the third one works fine"} 99 |

100 |

101 | {"Conversion to modern style is done according to "} 102 | {"this"}{" piece of code from LLVM repo"} 105 |

106 | 107 |
108 |

109 | Convert legacy values to modern style? 110 |

111 |
112 | 120 | 121 |
122 |
) 123 | else 124 | return ( 125 |
126 |
127 |

128 | Edit your .clang-format config file in place or use Upload and Download buttons to open File Explorer 129 |

130 |
131 |
132 |
133 | 149 |
150 |
151 |
152 |
153 | { 158 | setCookie("remove-duplicates", !removeDuplicates); 159 | setRemoveDuplicates(!removeDuplicates); 160 | setOptionsText(buildYamlConfigFile(options, !removeDuplicates, modifiedOptionTitles.current, unmodifiedOptions.current)) 161 | }} />{" "} 162 | 165 |
166 | 167 | 168 | 171 | 175 | 176 | 177 | 180 | 183 | 184 | 185 |
186 |
187 | 188 | ) 189 | 190 | 191 | }; 192 | 193 | export default ConfigUiPage 194 | -------------------------------------------------------------------------------- /front-end/src/components/ConfigUiPage.module.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --top-height: 30px; 3 | --bottom-height: 50px; 4 | } 5 | 6 | :global(.dark) a { 7 | color: #99c3ff; 8 | } 9 | 10 | :global(.dark) a:visited { 11 | color: #c58af9; 12 | } 13 | 14 | 15 | .config_ui_page { 16 | padding: 0px; 17 | margin: 0px; 18 | height: 96%; 19 | } 20 | 21 | 22 | :global(.dark) .config_ui_page { 23 | background-color: #313131; 24 | border-color: #5b5b5b; 25 | } 26 | 27 | 28 | .top_panel { 29 | padding: 10px 5px 0px 15px; 30 | margin: 0px; 31 | height: var(--top-height); 32 | } 33 | 34 | .page_title_text { 35 | font-family: sans-serif; 36 | font-weight: normal; 37 | font-size: 17px; 38 | overflow: hidden; 39 | margin: 0px; 40 | padding: 0px; 41 | } 42 | 43 | :global(.dark) .page_title_text { 44 | color: #cfcec4; 45 | } 46 | 47 | .middle_panel { 48 | width: 100%; 49 | height: calc(100% - var(--bottom-height) - 5px - var(--top-height)); 50 | } 51 | 52 | .bottom_panel { 53 | padding: 10px; 54 | height: var(--bottom-height); 55 | 56 | } 57 | 58 | 59 | .config_option_description { 60 | font-family: sans-serif; 61 | font-weight: normal; 62 | font-size: 15px; 63 | overflow: hidden; 64 | margin: 0px; 65 | padding: 0px; 66 | } 67 | 68 | :global(.dark) .config_option_description { 69 | color: #cfcec4; 70 | } 71 | 72 | 73 | .bottom_control_buttons { 74 | float: right; 75 | } 76 | 77 | .editor_container { 78 | height: 100%; 79 | margin-left: 15px; 80 | margin-right: 15px; 81 | width: 98%; 82 | float: left; 83 | border: 1px solid; 84 | border-color: black; 85 | } 86 | 87 | 88 | .aligncons_error_ui_page { 89 | height: 100%; 90 | max-height: 100%; 91 | padding: 10px; 92 | margin: 0px; 93 | overflow: hidden; 94 | } 95 | 96 | .aligncons_error_main_msg { 97 | text-align: center; 98 | font-family: sans-serif; 99 | font-weight: bold; 100 | font-size: 30px; 101 | padding: 0px; 102 | } 103 | 104 | :global(.dark) .aligncons_error_main_msg { 105 | color: #cfcec4; 106 | } 107 | 108 | 109 | .aligncons_error_detailed_msg { 110 | font-family: sans-serif; 111 | font-weight: normal; 112 | font-size: 20px; 113 | margin: 20px; 114 | padding: 0px; 115 | } 116 | 117 | 118 | :global(.dark) .aligncons_error_detailed_msg { 119 | color: #cfcec4; 120 | } 121 | 122 | 123 | .aligncons_error_shitcode_msg { 124 | font-family: sans-serif; 125 | font-weight: lighter; 126 | font-size: 8px; 127 | margin: 0px; 128 | padding: 0px; 129 | } 130 | 131 | 132 | :global(.dark) .aligncons_error_shitcode_msg { 133 | color: #cfcec4; 134 | } 135 | 136 | .aligncons_error_spoiler { 137 | max-height: 50%; 138 | overflow: auto; 139 | border-style: solid; 140 | border-width: 1px; 141 | border-radius: 5px; 142 | margin: 5px; 143 | padding: 5px; 144 | } 145 | 146 | 147 | .aligncons_error_spoiler_summary { 148 | font-family: sans-serif; 149 | font-weight: normal; 150 | font-style: italic; 151 | font-size: 18px; 152 | margin: 0px; 153 | padding: 0px; 154 | } 155 | 156 | 157 | :global(.dark) .aligncons_error_spoiler_summary { 158 | color: #cfcec4; 159 | } 160 | 161 | .aligncons_error_spoiler_content { 162 | font-family: sans-serif; 163 | font-weight: normal; 164 | font-style: normal; 165 | font-size: 18px; 166 | margin: 20px; 167 | padding: 0px; 168 | } 169 | 170 | 171 | :global(.dark) .aligncons_error_spoiler_content { 172 | color: #cfcec4; 173 | } 174 | 175 | .aligncons_error_spoiler_code_snippets_list { 176 | margin-left: 20px; 177 | margin-right: 20px; 178 | } 179 | 180 | .aligncons_error_spoiler_code_snippet { 181 | font-family: sans-serif; 182 | font-weight: normal; 183 | font-style: normal; 184 | font-size: 18px; 185 | overflow: hidden; 186 | padding: 7px; 187 | 188 | } 189 | 190 | 191 | :global(.dark) .aligncons_error_spoiler_code_snippet { 192 | color: #cfcec4; 193 | } 194 | 195 | 196 | .aligncons_error_convert_prompt { 197 | text-align: center; 198 | font-family: sans-serif; 199 | font-weight: bold; 200 | font-size: 25px; 201 | overflow: hidden; 202 | margin: 20px; 203 | margin-bottom: 10px; 204 | padding: 0px; 205 | } 206 | 207 | 208 | :global(.dark) .aligncons_error_convert_prompt { 209 | color: #cfcec4; 210 | } 211 | 212 | 213 | .aligncons_error_button_container { 214 | display: flex; 215 | justify-content: center; 216 | align-items: center; 217 | } 218 | -------------------------------------------------------------------------------- /front-end/src/components/Editor.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { diff as DiffEditor } from "react-ace"; 3 | import AceEditor from "react-ace"; 4 | import styles from './Editor.module.css' 5 | import cppCode from '../code_snippets/cppCode.cpp' 6 | import javaCode from '../code_snippets/javaCode.java' 7 | import "./ace.css" 8 | import "ace-builds/src-noconflict/mode-java"; 9 | import "ace-builds/src-noconflict/mode-c_cpp"; 10 | import "ace-builds/src-noconflict/snippets/c_cpp"; 11 | import "ace-builds/src-noconflict/snippets/java"; 12 | import "ace-builds/src-noconflict/theme-textmate"; 13 | import "ace-builds/src-noconflict/theme-clouds_midnight"; 14 | import "ace-builds/src-noconflict/ext-language_tools"; 15 | import "ace-builds/src-noconflict/ext-beautify" 16 | import "ace-builds/src-min-noconflict/ext-searchbox"; 17 | import { debounce } from "lodash"; 18 | 19 | const Editor = ({ editorText, onTextChange, currentLang, setCurrentLang, tabWidth, darkTheme, loadingIcon, columnLimitLine }) => { 20 | const cppCodeString = React.useRef("") 21 | const javaCodeString = React.useRef("") 22 | const [editorDiffMode, setEditorDiffMode] = React.useState(false) 23 | const [orientationBeside, setOrientationBeside] = React.useState(true) 24 | 25 | const [leftPanelText, setLeftPanelText] = React.useState(editorText) 26 | const [rightPanelText, setRightPanelText] = React.useState("") 27 | React.useEffect(() => { if (editorDiffMode) setLeftPanelText(editorText) }, [editorDiffMode, editorText]) 28 | React.useEffect(() => { if (editorDiffMode) setRightPanelText(editorText) }, [editorDiffMode]) 29 | 30 | const onTextChangeDebounced = React.useRef(debounce(onTextChange, 1000, { leading: false, trailing: true }), []) 31 | 32 | React.useEffect(() => { 33 | //Load Cpp snippet 34 | fetch(cppCode) 35 | .then((response) => response.text()) 36 | .then((textContent) => { 37 | cppCodeString.current = textContent; 38 | onTextChange(cppCodeString.current) 39 | }) 40 | //Load Java snippet 41 | fetch(javaCode) 42 | .then((response) => response.text()) 43 | .then((textContent) => { 44 | javaCodeString.current = textContent; 45 | }) 46 | }, []) 47 | 48 | const editor = (editorDiffMode ? ( 49 | { 62 | if (leftPanelText !== editorText) 63 | window.loadingIcon.setLoadingState("loading") 64 | setLeftPanelText(leftPanelText) 65 | setRightPanelText(rightPanelText) 66 | onTextChangeDebounced.current(leftPanelText) 67 | }} 68 | value={[leftPanelText, rightPanelText]} 69 | setOptions={{ 70 | printMargin: columnLimitLine, 71 | enableBasicAutocompletion: true, 72 | enableLiveAutocompletion: true, 73 | enableSnippets: true, 74 | showGutter: true, 75 | }} 76 | /> 77 | ) : ( { 90 | if (text !== editorText) 91 | window.loadingIcon.setLoadingState("loading"); 92 | onTextChangeDebounced.current(text) 93 | }} 94 | setOptions={{ 95 | printMargin: columnLimitLine, 96 | enableBasicAutocompletion: true, 97 | enableLiveAutocompletion: true, 98 | enableSnippets: true, 99 | }} 100 | />)) 101 | 102 | return ( 103 | 104 | 105 | 106 | {loadingIcon} 107 | 115 | 116 | 124 | {editorDiffMode ? 126 | Format works only for left/top panel 127 | : null} 128 | 129 | 130 | 131 | {editorDiffMode ? () : null} 137 | 144 | 145 | 146 | {editor} 147 | 148 | ) 149 | }; 150 | 151 | export default Editor 152 | -------------------------------------------------------------------------------- /front-end/src/components/Editor.module.css: -------------------------------------------------------------------------------- 1 | .tab_container { 2 | display: flex; 3 | justify-content: space-between; 4 | align-items: center; 5 | box-sizing: border-box; 6 | } 7 | 8 | .tab_button { 9 | margin-bottom: 0px; 10 | padding-bottom: 0px; 11 | border-bottom-width: 0px; 12 | border-bottom-style: none; 13 | border-bottom-left-radius: 0px; 14 | border-bottom-right-radius: 0px; 15 | } 16 | 17 | #mode_button { 18 | width: 110px; 19 | } 20 | 21 | .codeMarker { 22 | background: #fff677; 23 | position: absolute; 24 | z-index: 20; 25 | } 26 | 27 | .diffmode_guide { 28 | font-family: sans-serif; 29 | font-size: 17px; 30 | padding: 15px; 31 | margin: 10px; 32 | overflow: hidden; 33 | } 34 | 35 | .diffmode_guide { 36 | font-family: sans-serif; 37 | font-size: 17px; 38 | padding: 15px; 39 | margin: 10px; 40 | overflow: hidden; 41 | } 42 | 43 | :global(.dark) .diffmode_guide { 44 | color: #cfcec4; 45 | } 46 | -------------------------------------------------------------------------------- /front-end/src/components/Error.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "./Error.module.css" 3 | 4 | 5 | const Error = ({ errorText }) => { 6 | return ( 7 |
8 |

Something went wrong:

9 |

{errorText}

10 |
11 | ) 12 | } 13 | 14 | export default Error -------------------------------------------------------------------------------- /front-end/src/components/Error.module.css: -------------------------------------------------------------------------------- 1 | .error{ 2 | padding: 0px; 3 | margin: 0px; 4 | } 5 | 6 | :global(.dark) .error{ 7 | background-color: #313131; 8 | color: rgb(226, 226, 226); 9 | } 10 | 11 | .error_header{ 12 | text-align: center; 13 | font-weight: 400; 14 | margin: 5px; 15 | } 16 | 17 | .error_message{ 18 | white-space: pre-wrap; 19 | text-align: center; 20 | font-weight: 400; 21 | font-family: monospace; 22 | margin: 5px; 23 | } -------------------------------------------------------------------------------- /front-end/src/components/Header.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './Header.module.css' 3 | 4 | 5 | 6 | const Header = ({ autoFormat, onAutoFormatChange, darkTheme, onDarkThemeChange, onUpdate, onConfigFile }) => { 7 | return ( 8 |
9 |

clang-format configurator v2

10 | 11 | onAutoFormatChange(event.currentTarget.checked)} 15 | /> 16 | 17 | 20 | 23 | 28 | 31 | 36 | 37 |
38 | ) 39 | } 40 | 41 | export default Header; -------------------------------------------------------------------------------- /front-end/src/components/Header.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | background-color: rgb(38, 38, 38); 3 | overflow: hidden; 4 | padding: 5px; 5 | display: flex; 6 | justify-content: space-between; 7 | align-items: center; 8 | box-sizing: border-box; 9 | height: 44px; 10 | } 11 | 12 | :global(.dark) .header { 13 | background-color: rgb(0, 0, 0); 14 | } 15 | 16 | .title_text { 17 | color: #cecdcd; 18 | font-size: medium; 19 | padding-left: 5px; 20 | } 21 | 22 | :global(.dark) button { 23 | background: #393939; 24 | color: rgb(228, 228, 228); 25 | border-color: #7f7f7f; 26 | } 27 | 28 | .checkbox { 29 | -ms-transform: scale(1.3); 30 | -moz-transform: scale(1.3); 31 | -webkit-transform: scale(1.3); 32 | -o-transform: scale(1.3); 33 | transform: scale(1.3); 34 | margin-top: 10px; 35 | padding: 4px 12px; 36 | border-radius: 5px; 37 | } 38 | 39 | button { 40 | margin: 2px; 41 | padding: 4px 12px; 42 | border-radius: 5px; 43 | } 44 | 45 | 46 | .label { 47 | color: #cecdcd; 48 | margin: 3px; 49 | } 50 | 51 | #theme_button { 52 | width: 120px; 53 | } -------------------------------------------------------------------------------- /front-end/src/components/IncludeCategoriesSelector.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "./Selector.module.css"; 3 | import SingleSelector from "./SingleSelector"; 4 | 5 | 6 | const IncludeCategoriesSelector = ({ selectorInfo, onChange, currentStyle, 7 | currentOptionValue }) => { 8 | return ( 9 |
11 |
Regex
12 | { 17 | let val = { ...currentOptionValue } 18 | val.Regex = newValue 19 | onChange(val) 20 | }} 21 | /> 22 |
Priority
23 | { 28 | let val = { ...currentOptionValue } 29 | val.Priority = newValue 30 | onChange(val) 31 | }} 32 | /> 33 |
CaseSensitive
34 | { 39 | let val = { ...currentOptionValue } 40 | val.CaseSensitive = newValue 41 | onChange(val) 42 | }} 43 | /> 44 |
SortPriority
45 | { 50 | let val = { ...currentOptionValue } 51 | val.SortPriority = newValue 52 | onChange(val) 53 | }} 54 | /> 55 | 56 | 57 |
) 58 | } 59 | 60 | 61 | export default IncludeCategoriesSelector; -------------------------------------------------------------------------------- /front-end/src/components/LoadingIcon.jsx: -------------------------------------------------------------------------------- 1 | import styles from "./LoadingIcon.module.css" 2 | import React from "react"; 3 | 4 | class LoadingIcon extends React.Component { 5 | 6 | constructor() { 7 | super(); 8 | this.loadingState = "loading"; 9 | window.loadingIcon = this; 10 | } 11 | 12 | setLoadingState(state) { 13 | this.loadingState = state 14 | this.forceUpdate() 15 | } 16 | 17 | render() { 18 | let image; 19 | if (this.loadingState === "loading") 20 | image = (loading); 21 | else if (this.loadingState === "success") 22 | image = (loading); 23 | else if (this.loadingState === "error") 24 | image = (loading); 25 | else { 26 | console.error("LoadingIcon.jsx: Invalid state value - " + this.loadingState) 27 | image = () 28 | } 29 | return ({image}) 30 | } 31 | } 32 | 33 | 34 | 35 | export default LoadingIcon; -------------------------------------------------------------------------------- /front-end/src/components/LoadingIcon.module.css: -------------------------------------------------------------------------------- 1 | .loading_icon { 2 | width: 20px; 3 | height: 20px; 4 | padding-left: 2px; 5 | } -------------------------------------------------------------------------------- /front-end/src/components/MapSelector.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "./Selector.module.css"; 3 | import SingleSelector from "./SingleSelector"; 4 | 5 | const MapSelector = ({ selectorInfo, onChange, currentStyle, currentOptionValue }) => { 6 | const currentOptionValueDefined = currentOptionValue !== undefined; 7 | return ( 8 | 9 | {selectorInfo.map((value) => { 10 | return ( 11 |
12 | {value.title} 13 | { 16 | let newOptionValue = currentOptionValueDefined ? currentOptionValue : {} 17 | newOptionValue[value.title] = changedValue; 18 | onChange(newOptionValue) 19 | }} 20 | defaultValue={value.defaults[currentStyle]} 21 | currentOptionValue={currentOptionValueDefined ? currentOptionValue[value.title] : ""} 22 | currentStyle={currentStyle} 23 | /> 24 |
) 25 | })} 26 |
27 | ) 28 | } 29 | 30 | export default MapSelector; 31 | 32 | -------------------------------------------------------------------------------- /front-end/src/components/Option.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Collapsible from "react-collapsible"; 3 | import styles from "./Option.module.css"; 4 | import SingleSelector from "./SingleSelector"; 5 | import MapSelector from "./MapSelector"; 6 | import ArraySelector from "./ArraySelector"; 7 | import { over } from "lodash"; 8 | 9 | 10 | const Option = ({ optionInfo, 11 | currentOptionValue, 12 | currentStyle, 13 | onChange, 14 | overridenBy: overriddenBy 15 | }) => { 16 | const [showDocString, setShowDocString] = React.useState(false); 17 | 18 | const placeSelector = () => { 19 | const onChangeFunc = (newOptionValue) => { onChange(optionInfo.title, newOptionValue) } 20 | switch (optionInfo.title) { 21 | 22 | case "AlignTrailingComments": 23 | if (optionInfo.values.length === 1) 24 | return () 31 | // else fallthrough 32 | case "BraceWrapping": 33 | case "SpaceBeforeParensOptions": 34 | case "SpacesInLineCommentPrefix": 35 | return () 41 | default: 42 | if (optionInfo.values[0].argument_type.includes("std::vector<")) 43 | return () 49 | 50 | if (optionInfo.values.length > 2) 51 | return () 57 | 58 | return () 65 | 66 | } 67 | } 68 | 69 | const overrideWarningText = overriddenBy === undefined ? "" : " overridden by " + overriddenBy 70 | 71 | return ( 72 |
73 | 74 | 85 | {optionInfo.title} 86 | {overrideWarningText} 87 | 88 | 95 | 96 | 97 | {placeSelector()} 98 |
99 | ); 100 | } 101 | 102 | 103 | export default Option; 104 | -------------------------------------------------------------------------------- /front-end/src/components/Option.module.css: -------------------------------------------------------------------------------- 1 | .option { 2 | margin: 5px; 3 | padding: 3px 0px; 4 | display: flex; 5 | justify-content: space-between; 6 | } 7 | 8 | .title { 9 | font-family: sans-serif; 10 | font-weight: bold; 11 | font-size: 17px; 12 | overflow: hidden; 13 | } 14 | 15 | .title_deprecated { 16 | font-family: sans-serif; 17 | font-size: 17px; 18 | overflow: hidden; 19 | text-decoration: line-through 20 | } 21 | 22 | 23 | :global(.dark) .title { 24 | color: #cfcec4; 25 | } 26 | 27 | :global(.dark) .title_deprecated { 28 | color: #cfcec4; 29 | } 30 | 31 | .title_container { 32 | max-width: calc(100% - 235px); 33 | padding: 3px; 34 | } 35 | 36 | .overridden_waring { 37 | font-family: sans-serif; 38 | font-size: 10px; 39 | overflow: hidden; 40 | color: red; 41 | } 42 | 43 | :global(.dark) .overridden_waring { 44 | color: #c98787; 45 | } 46 | 47 | 48 | 49 | .docstring { 50 | min-height: 20px; 51 | padding: 15px; 52 | margin: 10px; 53 | padding-top: 5px; 54 | background-color: #f5f5f5; 55 | border: 1px solid #e3e3e3; 56 | border-radius: 4px; 57 | font-size: 14px; 58 | } 59 | 60 | :global(.dark) .docstring { 61 | background-color: #2b2e32; 62 | border-color: #141414; 63 | color: #d0cec5; 64 | } 65 | 66 | code { 67 | padding: 2px 4px; 68 | font-size: 90%; 69 | color: #c7254e; 70 | background-color: #f9f2f4; 71 | border-radius: 4px; 72 | font-family: Menlo, Monaco, Consolas, "Courier New", monospace; 73 | } 74 | 75 | :global(.dark) code { 76 | background-color: rgb(79, 44, 53); 77 | color: rgb(245, 95, 133); 78 | } 79 | 80 | pre { 81 | overflow-x: scroll; 82 | max-width: 100%; 83 | padding-bottom: 8px; 84 | font-size: inherit; 85 | color: inherit; 86 | border-style: solid; 87 | border: 1px solid #e3e3e3; 88 | background-color: #f8f8f8; 89 | border-radius: 4px; 90 | white-space: pre; 91 | padding-top: 10px; 92 | font-weight: 200; 93 | font-family: Menlo, Monaco, Consolas, "Courier New", monospace; 94 | } 95 | 96 | :global(.dark) pre { 97 | background-color: #434343; 98 | scrollbar-color: dark; 99 | border-color: #1a1a1a; 100 | } 101 | 102 | .info_button { 103 | display: inline-block; 104 | width: 13px; 105 | height: 13px; 106 | padding: 0; 107 | border: none; 108 | background: none; 109 | transition-duration: 100ms; 110 | opacity: 0.9; 111 | } 112 | 113 | .info_button:hover { 114 | opacity: 0.8; 115 | transition-duration: 100ms; 116 | } 117 | 118 | .info_button:active { 119 | opacity: 1; 120 | transition-duration: 20ms; 121 | } 122 | -------------------------------------------------------------------------------- /front-end/src/components/OptionList.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Option from "./Option"; 3 | import { cloneDeep } from "lodash"; 4 | import { useCookies } from "react-cookie"; 5 | 6 | const OptionList = ({ config, options, llvmVersionOption, onOptionChange, updateModifiedList, onFreshGeneratedOptions }) => { 7 | const [_, setCookie] = useCookies([]); // ultra mega bruh 8 | 9 | const deprecatedOptions = [] 10 | const nonDeprecatedOptions = config[options.selectedVersion].slice(1).map((option) => { 11 | 12 | const checkIfOverridden = (currentOptionName, options) => { 13 | switch (currentOptionName) { 14 | case "BraceWrapping": 15 | if (options["BreakBeforeBraces"] !== "Custom") 16 | return "BreakBeforeBraces" 17 | else 18 | return undefined 19 | case "SpaceBeforeParensOptions": 20 | if (options["SpaceBeforeParens"] !== "Custom") 21 | return "SpaceBeforeParens" 22 | else 23 | return undefined 24 | default: return undefined 25 | } 26 | } 27 | 28 | const optionWgt = (