14 Commits

Author SHA1 Message Date
f5bfff80b7 🚨 Format code 2025-09-24 21:44:42 +08:00
1462d8a753 🐛 Fixed docker prettier plugin issue 2025-09-24 19:14:10 +08:00
39ee2d14f3 Optimize font size and add more fonts 2025-09-24 00:44:41 +08:00
e536cdd9ba 🐛 Fixed docker、shell prettier plugin issue 2025-09-23 19:43:26 +08:00
dc4b73406d 🐛 Fixed prettier plugin parser configuration issue 2025-09-21 23:23:43 +08:00
0012a5dc19 Added loading animation switch 2025-09-21 22:17:41 +08:00
3cda88371e 🐛 Fixed manual Chunks issue 2025-09-21 21:54:39 +08:00
0338351680 Improve app launch speed 2025-09-21 03:04:23 +08:00
e372a0dd7c 🎨 Modify the rendering logic of the checkbox 2025-09-21 01:14:33 +08:00
d597f379ff 🚚 Refactor directory structure 2025-09-21 00:11:40 +08:00
9222a52d91 Modified sql prettier plugin 2025-09-20 19:18:33 +08:00
c3670bb8cd Added dockerfile、lua prettier plugin 2025-09-20 01:30:16 +08:00
2ea3456ff7 Added dart prettier plugin 2025-09-19 19:41:48 +08:00
c9379f0edb Added python prettier plugin 2025-09-19 19:35:48 +08:00
324 changed files with 13032 additions and 20797 deletions

View File

@@ -484,6 +484,12 @@ export class GeneralConfig {
*/
"globalHotkey": HotkeyCombo;
/**
* 界面设置
* 是否启用加载动画
*/
"enableLoadingAnimation": boolean;
/** Creates a new GeneralConfig instance. */
constructor($$source: Partial<GeneralConfig> = {}) {
if (!("alwaysOnTop" in $$source)) {
@@ -507,6 +513,9 @@ export class GeneralConfig {
if (!("globalHotkey" in $$source)) {
this["globalHotkey"] = (new HotkeyCombo());
}
if (!("enableLoadingAnimation" in $$source)) {
this["enableLoadingAnimation"] = false;
}
Object.assign(this, $$source);
}

View File

@@ -50,7 +50,11 @@ export default defineConfig([
'.local',
'/bin',
'Dockerfile',
'**/bindings/'
'**/bindings/',
'*.js',
'**/*.js',
'**/*.cjs',
'**/*.mjs',
],
}
]);

View File

@@ -9,5 +9,6 @@
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<script src="/math.js"></script>
</body>
</html>

View File

@@ -41,7 +41,6 @@
"@lezer/highlight": "^1.2.1",
"@lezer/lr": "^1.4.2",
"@prettier/plugin-xml": "^3.4.2",
"@reteps/dockerfmt": "^0.3.6",
"@toml-tools/lexer": "^1.0.0",
"@toml-tools/parser": "^1.0.0",
"codemirror": "^6.0.2",
@@ -52,18 +51,14 @@
"groovy-beautify": "^0.0.17",
"hsl-matcher": "^1.2.4",
"java-parser": "^3.0.1",
"jinx-rust": "^0.1.6",
"jsox": "^1.2.123",
"linguist-languages": "^9.0.0",
"node-sql-parser": "^5.3.12",
"php-parser": "^3.2.5",
"pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.5.0",
"prettier": "^3.6.2",
"remarkable": "^2.0.1",
"sass": "^1.92.1",
"sh-syntax": "^0.5.8",
"sql-formatter": "^15.6.9",
"vue": "^3.5.21",
"vue-i18n": "^11.1.12",
"vue-pick-colors": "^1.8.0",
@@ -80,7 +75,6 @@
"eslint": "^9.35.0",
"eslint-plugin-vue": "^10.4.0",
"globals": "^16.4.0",
"rollup-plugin-visualizer": "^6.0.3",
"typescript": "^5.9.2",
"typescript-eslint": "^8.43.0",
"unplugin-vue-components": "^29.0.0",
@@ -1881,15 +1875,6 @@
"prettier": "^3.0.0"
}
},
"node_modules/@reteps/dockerfmt": {
"version": "0.3.6",
"resolved": "https://registry.npmmirror.com/@reteps/dockerfmt/-/dockerfmt-0.3.6.tgz",
"integrity": "sha512-Tb5wIMvBf/nLejTQ61krK644/CEMB/cpiaIFXqGApfGqO3GwcR3qnI0DbmkFVCl2OyEp8LnLX3EkucoL0+tbFg==",
"license": "MIT",
"engines": {
"node": "^v12.20.0 || ^14.13.0 || >=16.0.0"
}
},
"node_modules/@rolldown/pluginutils": {
"version": "1.0.0-beta.29",
"resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.29.tgz",
@@ -2279,12 +2264,6 @@
"undici-types": "~7.10.0"
}
},
"node_modules/@types/pegjs": {
"version": "0.10.6",
"resolved": "https://registry.npmmirror.com/@types/pegjs/-/pegjs-0.10.6.tgz",
"integrity": "sha512-eLYXDbZWXh2uxf+w8sXS8d6KSoXTswfps6fvCUuVAGN8eRpfe7h9eSRydxiSJvo9Bf+GzifsDOr9TMQlmJdmkw==",
"license": "MIT"
},
"node_modules/@types/remarkable": {
"version": "2.0.8",
"resolved": "https://registry.npmmirror.com/@types/remarkable/-/remarkable-2.0.8.tgz",
@@ -2850,16 +2829,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz",
@@ -2894,6 +2863,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"license": "Python-2.0"
},
"node_modules/asn1.js": {
@@ -2982,15 +2952,6 @@
],
"license": "MIT"
},
"node_modules/big-integer": {
"version": "1.6.52",
"resolved": "https://registry.npmmirror.com/big-integer/-/big-integer-1.6.52.tgz",
"integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==",
"license": "Unlicense",
"engines": {
"node": ">=0.6"
}
},
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz",
@@ -3414,21 +3375,6 @@
"consola": "^3.2.3"
}
},
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dev": true,
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/codemirror": {
"version": "6.0.2",
"resolved": "https://registry.npmmirror.com/codemirror/-/codemirror-6.0.2.tgz",
@@ -3507,12 +3453,6 @@
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"license": "MIT"
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz",
@@ -3769,16 +3709,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/define-lazy-prop": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
"integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/define-properties": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/define-properties/-/define-properties-1.2.1.tgz",
@@ -3852,12 +3782,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/discontinuous-range": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
"integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==",
"license": "MIT"
},
"node_modules/domain-browser": {
"version": "4.22.0",
"resolved": "https://registry.npmmirror.com/domain-browser/-/domain-browser-4.22.0.tgz",
@@ -3923,13 +3847,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
"license": "MIT"
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
@@ -4024,16 +3941,6 @@
"@esbuild/win32-x64": "0.25.2"
}
},
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -4443,16 +4350,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true,
"license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
@@ -4830,22 +4727,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-docker": {
"version": "2.2.1",
"resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-2.2.1.tgz",
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
"dev": true,
"license": "MIT",
"bin": {
"is-docker": "cli.js"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -4856,16 +4737,6 @@
"node": ">=0.10.0"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/is-generator-function": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/is-generator-function/-/is-generator-function-1.1.0.tgz",
@@ -4972,19 +4843,6 @@
"url": "https://github.com/sponsors/mesqueeb"
}
},
"node_modules/is-wsl": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-2.2.0.tgz",
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-docker": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/isarray": {
"version": "2.0.5",
"resolved": "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz",
@@ -5020,12 +4878,6 @@
"lodash": "4.17.21"
}
},
"node_modules/jinx-rust": {
"version": "0.1.6",
"resolved": "https://registry.npmmirror.com/jinx-rust/-/jinx-rust-0.1.6.tgz",
"integrity": "sha512-qP+wtQL1PrDDFwtPKhNGtjWOmijCrKdfUHWTV2G/ikxfjrh+cjdvkQTmny9RAsVF0jiui9m+F0INWu4cuRcZeQ==",
"license": "MIT"
},
"node_modules/jiti": {
"version": "2.4.2",
"resolved": "https://registry.npmmirror.com/jiti/-/jiti-2.4.2.tgz",
@@ -5340,12 +5192,6 @@
"pathe": "^2.0.1"
}
},
"node_modules/moo": {
"version": "0.5.2",
"resolved": "https://registry.npmmirror.com/moo/-/moo-0.5.2.tgz",
"integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==",
"license": "BSD-3-Clause"
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
@@ -5395,28 +5241,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/nearley": {
"version": "2.20.1",
"resolved": "https://registry.npmmirror.com/nearley/-/nearley-2.20.1.tgz",
"integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==",
"license": "MIT",
"dependencies": {
"commander": "^2.19.0",
"moo": "^0.5.0",
"railroad-diagrams": "^1.0.0",
"randexp": "0.4.6"
},
"bin": {
"nearley-railroad": "bin/nearley-railroad.js",
"nearley-test": "bin/nearley-test.js",
"nearley-unparse": "bin/nearley-unparse.js",
"nearleyc": "bin/nearleyc.js"
},
"funding": {
"type": "individual",
"url": "https://nearley.js.org/#give-to-nearley"
}
},
"node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-7.1.1.tgz",
@@ -5432,19 +5256,6 @@
"optional": true,
"peer": true
},
"node_modules/node-sql-parser": {
"version": "5.3.12",
"resolved": "https://registry.npmmirror.com/node-sql-parser/-/node-sql-parser-5.3.12.tgz",
"integrity": "sha512-GQBwA2e44qjbK0MzFwh5bNYefniV6cKT4KfjNDpuh/2EWipUEK1BCMc//moSidp8EF6fHB/EqJwUH9GCh9MAPg==",
"license": "Apache-2.0",
"dependencies": {
"@types/pegjs": "^0.10.0",
"big-integer": "^1.6.48"
},
"engines": {
"node": ">=8"
}
},
"node_modules/node-stdlib-browser": {
"version": "1.3.1",
"resolved": "https://registry.npmmirror.com/node-stdlib-browser/-/node-stdlib-browser-1.3.1.tgz",
@@ -5604,24 +5415,6 @@
"optional": true,
"peer": true
},
"node_modules/open": {
"version": "8.4.2",
"resolved": "https://registry.npmmirror.com/open/-/open-8.4.2.tgz",
"integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"define-lazy-prop": "^2.0.0",
"is-docker": "^2.1.1",
"is-wsl": "^2.2.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz",
@@ -6103,25 +5896,6 @@
],
"license": "MIT"
},
"node_modules/railroad-diagrams": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
"integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==",
"license": "CC0-1.0"
},
"node_modules/randexp": {
"version": "0.4.6",
"resolved": "https://registry.npmmirror.com/randexp/-/randexp-0.4.6.tgz",
"integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
"license": "MIT",
"dependencies": {
"discontinuous-range": "1.0.0",
"ret": "~0.1.10"
},
"engines": {
"node": ">=0.12"
}
},
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz",
@@ -6214,16 +5988,6 @@
"sprintf-js": "~1.0.2"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/resolve": {
"version": "1.22.10",
"resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.10.tgz",
@@ -6255,15 +6019,6 @@
"node": ">=4"
}
},
"node_modules/ret": {
"version": "0.1.15",
"resolved": "https://registry.npmmirror.com/ret/-/ret-0.1.15.tgz",
"integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
"license": "MIT",
"engines": {
"node": ">=0.12"
}
},
"node_modules/reusify": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz",
@@ -6332,50 +6087,6 @@
"fsevents": "~2.3.2"
}
},
"node_modules/rollup-plugin-visualizer": {
"version": "6.0.3",
"resolved": "https://registry.npmmirror.com/rollup-plugin-visualizer/-/rollup-plugin-visualizer-6.0.3.tgz",
"integrity": "sha512-ZU41GwrkDcCpVoffviuM9Clwjy5fcUxlz0oMoTXTYsK+tcIFzbdacnrr2n8TXcHxbGKKXtOdjxM2HUS4HjkwIw==",
"dev": true,
"license": "MIT",
"dependencies": {
"open": "^8.0.0",
"picomatch": "^4.0.2",
"source-map": "^0.7.4",
"yargs": "^17.5.1"
},
"bin": {
"rollup-plugin-visualizer": "dist/bin/cli.js"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"rolldown": "1.x || ^1.0.0-beta",
"rollup": "2.x || 3.x || 4.x"
},
"peerDependenciesMeta": {
"rolldown": {
"optional": true
},
"rollup": {
"optional": true
}
}
},
"node_modules/rollup-plugin-visualizer/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -6505,21 +6216,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/sh-syntax": {
"version": "0.5.8",
"resolved": "https://registry.npmmirror.com/sh-syntax/-/sh-syntax-0.5.8.tgz",
"integrity": "sha512-JfVoxf4FxQI5qpsPbkHhZo+n6N9YMJobyl4oGEUBb/31oQYlgTjkXQD8PBiafS2UbWoxrTO0Z5PJUBXEPAG1Zw==",
"license": "MIT",
"dependencies": {
"tslib": "^2.8.1"
},
"engines": {
"node": ">=16.0.0"
},
"funding": {
"url": "https://opencollective.com/sh-syntax"
}
},
"node_modules/sha.js": {
"version": "2.4.12",
"resolved": "https://registry.npmmirror.com/sha.js/-/sha.js-2.4.12.tgz",
@@ -6640,16 +6336,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/source-map": {
"version": "0.7.6",
"resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.7.6.tgz",
"integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">= 12"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -6674,19 +6360,6 @@
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
"license": "BSD-3-Clause"
},
"node_modules/sql-formatter": {
"version": "15.6.9",
"resolved": "https://registry.npmmirror.com/sql-formatter/-/sql-formatter-15.6.9.tgz",
"integrity": "sha512-r9VKnkRfKW7jbhTgytwbM+JqmFclQYN9L58Z3UTktuy9V1f1Y+rGK3t70Truh2wIOJzvZkzobAQ2PwGjjXsr6Q==",
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1",
"nearley": "^2.20.1"
},
"bin": {
"sql-formatter": "bin/sql-formatter-cli.cjs"
}
},
"node_modules/std-env": {
"version": "3.9.0",
"resolved": "https://registry.npmmirror.com/std-env/-/std-env-3.9.0.tgz",
@@ -6729,34 +6402,6 @@
"safe-buffer": "~5.2.0"
}
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -7671,24 +7316,6 @@
"node": ">=0.10.0"
}
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/xml-name-validator": {
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
@@ -7709,45 +7336,6 @@
"node": ">=0.4"
}
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=10"
}
},
"node_modules/yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
"dev": true,
"license": "MIT",
"dependencies": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.1.1"
},
"engines": {
"node": ">=12"
}
},
"node_modules/yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz",

View File

@@ -45,7 +45,6 @@
"@lezer/highlight": "^1.2.1",
"@lezer/lr": "^1.4.2",
"@prettier/plugin-xml": "^3.4.2",
"@reteps/dockerfmt": "^0.3.6",
"@toml-tools/lexer": "^1.0.0",
"@toml-tools/parser": "^1.0.0",
"codemirror": "^6.0.2",
@@ -56,18 +55,14 @@
"groovy-beautify": "^0.0.17",
"hsl-matcher": "^1.2.4",
"java-parser": "^3.0.1",
"jinx-rust": "^0.1.6",
"jsox": "^1.2.123",
"linguist-languages": "^9.0.0",
"node-sql-parser": "^5.3.12",
"php-parser": "^3.2.5",
"pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.5.0",
"prettier": "^3.6.2",
"remarkable": "^2.0.1",
"sass": "^1.92.1",
"sh-syntax": "^0.5.8",
"sql-formatter": "^15.6.9",
"vue": "^3.5.21",
"vue-i18n": "^11.1.12",
"vue-pick-colors": "^1.8.0",
@@ -84,7 +79,6 @@
"eslint": "^9.35.0",
"eslint-plugin-vue": "^10.4.0",
"globals": "^16.4.0",
"rollup-plugin-visualizer": "^6.0.3",
"typescript": "^5.9.2",
"typescript-eslint": "^8.43.0",
"unplugin-vue-components": "^29.0.0",

Binary file not shown.

View File

@@ -1,7 +1,5 @@
importScripts("guesslang.min.js")
const LANGUAGES = ["json", "py", "html", "sql", "md", "java", "php", "css", "xml", "cpp", "rs", "cs", "rb", "sh", "yaml", "toml", "go", "clj", "ex", "erl", "js", "ts", "swift", "kt", "groovy", "ps1", "dart", "scala"]
const guessLang = new self.GuessLang()
function sendResult(language, confidence, idx) {
@@ -27,20 +25,13 @@ onmessage = (event) => {
guessLang.runModel(content).then((result) => {
if (result.length > 0) {
const lang = result[0]
if (LANGUAGES.includes(lang.languageId) && lang.confidence > 0.15) {
sendResult(lang.languageId, lang.confidence, idx)
// 返回置信度最高的结果
const bestResult = result[0]
if (bestResult.confidence > 0.15) {
sendResult(bestResult.languageId, bestResult.confidence, idx)
return
}
}
for (let lang of result) {
if (LANGUAGES.includes(lang.languageId) && lang.confidence > 0.5) {
sendResult(lang.languageId, lang.confidence, idx)
return
}
}
sendResult("text", 0.0, idx)
}).catch(() => {
sendResult("text", 0.0, idx)

3
frontend/public/math.js Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,254 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
鸿蒙字体压缩工具
使用 fonttools 库压缩 TTF 字体文件,减小文件大小
"""
import os
import sys
import subprocess
import shutil
from pathlib import Path
from typing import List, Tuple
def check_dependencies():
"""检查必要的依赖是否已安装"""
missing_packages = []
# 检查 fonttools
try:
import fontTools
except ImportError:
missing_packages.append('fonttools')
# 检查 brotli
try:
import brotli
except ImportError:
missing_packages.append('brotli')
# 检查 pyftsubset 命令是否可用
try:
result = subprocess.run(['pyftsubset', '--help'], capture_output=True, text=True)
if result.returncode != 0:
missing_packages.append('fonttools[subset]')
except FileNotFoundError:
if 'fonttools' not in missing_packages:
missing_packages.append('fonttools[subset]')
if missing_packages:
print(f"缺少必要的依赖包: {', '.join(missing_packages)}")
print("请运行以下命令安装:")
print(f"pip install {' '.join(missing_packages)}")
return False
return True
def get_file_size(file_path: str) -> int:
"""获取文件大小(字节)"""
return os.path.getsize(file_path)
def format_file_size(size_bytes: int) -> str:
"""格式化文件大小显示"""
if size_bytes < 1024:
return f"{size_bytes} B"
elif size_bytes < 1024 * 1024:
return f"{size_bytes / 1024:.2f} KB"
else:
return f"{size_bytes / (1024 * 1024):.2f} MB"
def compress_font(input_path: str, output_path: str, compression_level: str = "basic") -> bool:
"""
压缩单个字体文件
Args:
input_path: 输入字体文件路径
output_path: 输出字体文件路径
compression_level: 压缩级别 ("basic", "medium", "aggressive")
Returns:
bool: 压缩是否成功
"""
try:
# 基础压缩参数
base_args = [
"pyftsubset", input_path,
"--output-file=" + output_path,
"--flavor=woff2", # 输出为 WOFF2 格式,压缩率更高
"--with-zopfli", # 使用 Zopfli 算法进一步压缩
]
# 根据压缩级别设置不同的参数
if compression_level == "basic":
# 基础压缩:保留常用字符和功能
args = base_args + [
"--unicodes=U+0020-007F,U+00A0-00FF,U+2000-206F,U+2070-209F,U+20A0-20CF", # 基本拉丁字符、标点符号等
"--layout-features=*", # 保留所有布局特性
"--glyph-names", # 保留字形名称
"--symbol-cmap", # 保留符号映射
"--legacy-cmap", # 保留传统字符映射
"--notdef-glyph", # 保留 .notdef 字形
"--recommended-glyphs", # 保留推荐字形
"--name-IDs=*", # 保留所有名称ID
"--name-legacy", # 保留传统名称
]
elif compression_level == "medium":
# 中等压缩:移除一些不常用的功能
args = base_args + [
"--unicodes=U+0020-007F,U+00A0-00FF,U+2000-206F", # 减少字符范围
"--layout-features=kern,liga,clig", # 只保留关键布局特性
"--no-glyph-names", # 移除字形名称
"--notdef-glyph",
"--name-IDs=1,2,3,4,5,6", # 只保留基本名称ID
]
else: # aggressive
# 激进压缩:最大程度减小文件大小
args = base_args + [
"--unicodes=U+0020-007F", # 只保留基本ASCII字符
"--no-layout-features", # 移除所有布局特性
"--no-glyph-names", # 移除字形名称
"--no-symbol-cmap", # 移除符号映射
"--no-legacy-cmap", # 移除传统映射
"--notdef-glyph",
"--name-IDs=1,2", # 只保留最基本的名称
"--desubroutinize", # 去子程序化可能减小CFF字体大小
]
# 执行压缩命令
result = subprocess.run(args, capture_output=True, text=True)
if result.returncode == 0:
return True
else:
print(f"压缩失败: {result.stderr}")
return False
except Exception as e:
print(f"压缩过程中出现错误: {str(e)}")
return False
def find_font_files(directory: str) -> List[str]:
"""查找目录中的所有字体文件"""
font_extensions = ['.ttf', '.otf', '.woff', '.woff2']
font_files = []
for root, dirs, files in os.walk(directory):
for file in files:
if any(file.lower().endswith(ext) for ext in font_extensions):
font_files.append(os.path.join(root, file))
return font_files
def compress_fonts_batch(font_directory: str, compression_level: str = "basic"):
"""
批量压缩字体文件
Args:
font_directory: 字体文件目录
compression_level: 压缩级别
"""
if not os.path.exists(font_directory):
print(f"错误: 目录 {font_directory} 不存在")
return
# 查找所有字体文件
font_files = find_font_files(font_directory)
if not font_files:
print("未找到字体文件")
return
print(f"找到 {len(font_files)} 个字体文件")
print(f"压缩级别: {compression_level}")
print(f"压缩后的文件将与源文件放在同一目录,扩展名为 .woff2")
print("-" * 60)
total_original_size = 0
total_compressed_size = 0
successful_compressions = 0
for i, font_file in enumerate(font_files, 1):
print(f"[{i}/{len(font_files)}] 处理: {os.path.basename(font_file)}")
# 获取原始文件大小
original_size = get_file_size(font_file)
total_original_size += original_size
# 生成输出文件名(保持原文件名,只改变扩展名)
file_dir = os.path.dirname(font_file)
base_name = os.path.splitext(os.path.basename(font_file))[0]
output_file = os.path.join(file_dir, f"{base_name}.woff2")
# 压缩字体
if compress_font(font_file, output_file, compression_level):
if os.path.exists(output_file):
compressed_size = get_file_size(output_file)
total_compressed_size += compressed_size
successful_compressions += 1
# 计算压缩率
compression_ratio = (1 - compressed_size / original_size) * 100
print(f" ✓ 成功: {format_file_size(original_size)}{format_file_size(compressed_size)} "
f"(压缩 {compression_ratio:.1f}%)")
else:
print(f" ✗ 失败: 输出文件未生成")
else:
print(f" ✗ 失败: 压缩过程出错")
print()
# 显示总结
print("=" * 60)
print("压缩完成!")
print(f"成功压缩: {successful_compressions}/{len(font_files)} 个文件")
if successful_compressions > 0:
total_compression_ratio = (1 - total_compressed_size / total_original_size) * 100
print(f"总大小: {format_file_size(total_original_size)}{format_file_size(total_compressed_size)}")
print(f"总压缩率: {total_compression_ratio:.1f}%")
print(f"节省空间: {format_file_size(total_original_size - total_compressed_size)}")
def main():
"""主函数"""
print("鸿蒙字体压缩工具")
print("=" * 60)
# 检查依赖
if not check_dependencies():
return
# 获取当前脚本所在目录
current_dir = os.path.dirname(os.path.abspath(__file__))
# 设置默认字体目录
font_directory = current_dir
print(f"字体目录: {font_directory}")
# 让用户选择压缩级别
print("\n请选择压缩级别:")
print("1. 基础压缩 (保留大部分功能,适合网页使用)")
print("2. 中等压缩 (平衡文件大小和功能)")
print("3. 激进压缩 (最小文件大小,可能影响显示效果)")
while True:
choice = input("\n请输入选择 (1-3): ").strip()
if choice == "1":
compression_level = "basic"
break
elif choice == "2":
compression_level = "medium"
break
elif choice == "3":
compression_level = "aggressive"
break
else:
print("无效选择,请输入 1、2 或 3")
# 开始批量压缩
compress_fonts_batch(font_directory, compression_level=compression_level)
if __name__ == "__main__":
main()

View File

@@ -1,146 +0,0 @@
/* HarmonyOS Sans 字体定义 */
/* HarmonyOS Sans Regular */
@font-face {
font-family: 'HarmonyOS Sans';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_Sans/HarmonyOS_Sans_Regular.ttf') format('truetype');
font-weight: 400;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans Light */
@font-face {
font-family: 'HarmonyOS Sans';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_Sans/HarmonyOS_Sans_Light.ttf') format('truetype');
font-weight: 300;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans Medium */
@font-face {
font-family: 'HarmonyOS Sans';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_Sans/HarmonyOS_Sans_Medium.ttf') format('truetype');
font-weight: 500;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans Semibold */
@font-face {
font-family: 'HarmonyOS Sans';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_Sans/HarmonyOS_Sans_Semibold.ttf') format('truetype');
font-weight: 600;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans Bold */
@font-face {
font-family: 'HarmonyOS Sans';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_Sans/HarmonyOS_Sans_Bold.ttf') format('truetype');
font-weight: 700;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans Black */
@font-face {
font-family: 'HarmonyOS Sans';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_Sans/HarmonyOS_Sans_Black.ttf') format('truetype');
font-weight: 900;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans Thin */
@font-face {
font-family: 'HarmonyOS Sans';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_Sans/HarmonyOS_Sans_Thin.ttf') format('truetype');
font-weight: 100;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans SC 简体中文字体 */
/* HarmonyOS Sans SC Regular */
@font-face {
font-family: 'HarmonyOS Sans SC';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_SansSC/HarmonyOS_SansSC_Regular.ttf') format('truetype');
font-weight: 400;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans SC Light */
@font-face {
font-family: 'HarmonyOS Sans SC';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_SansSC/HarmonyOS_SansSC_Light.ttf') format('truetype');
font-weight: 300;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans SC Medium */
@font-face {
font-family: 'HarmonyOS Sans SC';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_SansSC/HarmonyOS_SansSC_Medium.ttf') format('truetype');
font-weight: 500;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans SC Semibold */
@font-face {
font-family: 'HarmonyOS Sans SC';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_SansSC/HarmonyOS_SansSC_Semibold.ttf') format('truetype');
font-weight: 600;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans SC Bold */
@font-face {
font-family: 'HarmonyOS Sans SC';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_SansSC/HarmonyOS_SansSC_Bold.ttf') format('truetype');
font-weight: 700;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans SC Black */
@font-face {
font-family: 'HarmonyOS Sans SC';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_SansSC/HarmonyOS_SansSC_Black.ttf') format('truetype');
font-weight: 900;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Sans SC Thin */
@font-face {
font-family: 'HarmonyOS Sans SC';
src: url('../fonts/HarmonyOS Sans/HarmonyOS_SansSC/HarmonyOS_SansSC_Thin.ttf') format('truetype');
font-weight: 100;
font-style: normal;
font-display: swap;
}
/* 字体加载优化 */
.font-loading {
font-family: system-ui, -apple-system, sans-serif;
}
.font-loaded {
font-family: 'HarmonyOS Sans SC', 'HarmonyOS Sans', 'Microsoft YaHei', 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
}
/* CodeMirror 专用字体类 */
.cm-harmonyos-font {
font-family: 'HarmonyOS Sans SC', 'HarmonyOS Sans', 'Microsoft YaHei', 'PingFang SC', 'Helvetica Neue', Arial, sans-serif !important;
font-feature-settings: 'liga' 1, 'calt' 1;
font-variant-ligatures: contextual;
text-rendering: optimizeLegibility;
}

View File

@@ -0,0 +1,31 @@
@font-face {
font-family: 'Hack';
src: url('../fonts/Hack/hack-regular.woff2') format('woff2'),
url('../fonts/Hack/hack-regular.woff') format('woff');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'Hack';
src: url('../fonts/Hack/hack-bold.woff2') format('woff2'),
url('../fonts/Hack/hack-bold.woff') format('woff');
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: 'Hack';
src: url('../fonts/Hack/hack-italic.woff2') format('woff2'),
url('../fonts/Hack/hack-italic.woff') format('woff');
font-weight: 400;
font-style: italic;
}
@font-face {
font-family: 'Hack';
src: url('../fonts/Hack/hack-bolditalic.woff2') format('woff2'),
url('../fonts/Hack/hack-bolditalic.woff') format('woff');
font-weight: 700;
font-style: italic;
}

View File

@@ -0,0 +1,134 @@
/* HarmonyOS 字体定义 */
/* HarmonyOS Regular */
@font-face {
font-family: 'HarmonyOS';
src: url('../fonts/HarmonyOS/HarmonyOS_Sans/HarmonyOS_Sans_Regular.woff2') format('truetype');
font-weight: 400;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Light */
@font-face {
font-family: 'HarmonyOS';
src: url('../fonts/HarmonyOS/HarmonyOS_Sans/HarmonyOS_Sans_Light.woff2') format('truetype');
font-weight: 300;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Medium */
@font-face {
font-family: 'HarmonyOS';
src: url('../fonts/HarmonyOS/HarmonyOS_Sans/HarmonyOS_Sans_Medium.woff2') format('truetype');
font-weight: 500;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Semibold */
@font-face {
font-family: 'HarmonyOS';
src: url('../fonts/HarmonyOS/HarmonyOS_Sans/HarmonyOS_Sans_Semibold.woff2') format('truetype');
font-weight: 600;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Bold */
@font-face {
font-family: 'HarmonyOS';
src: url('../fonts/HarmonyOS/HarmonyOS_Sans/HarmonyOS_Sans_Bold.woff2') format('truetype');
font-weight: 700;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Black */
@font-face {
font-family: 'HarmonyOS';
src: url('../fonts/HarmonyOS/HarmonyOS_Sans/HarmonyOS_Sans_Black.woff2') format('truetype');
font-weight: 900;
font-style: normal;
font-display: swap;
}
/* HarmonyOS Thin */
@font-face {
font-family: 'HarmonyOS';
src: url('../fonts/HarmonyOS/HarmonyOS_Sans/HarmonyOS_Sans_Thin.woff2') format('truetype');
font-weight: 100;
font-style: normal;
font-display: swap;
}
/* HarmonyOS SC 简体中文字体 */
/* HarmonyOS SC Regular */
@font-face {
font-family: 'HarmonyOS';
src: url('../fonts/HarmonyOS/HarmonyOS_SansSC/HarmonyOS_SansSC_Regular.woff2') format('truetype');
font-weight: 400;
font-style: normal;
font-display: swap;
}
/* HarmonyOS SC Light */
@font-face {
font-family: 'HarmonyOS';
src: url('../fonts/HarmonyOS/HarmonyOS_SansSC/HarmonyOS_SansSC_Light.woff2') format('truetype');
font-weight: 300;
font-style: normal;
font-display: swap;
}
/* HarmonyOS SC Medium */
@font-face {
font-family: 'HarmonyOS';
src: url('../fonts/HarmonyOS/HarmonyOS_SansSC/HarmonyOS_SansSC_Medium.woff2') format('truetype');
font-weight: 500;
font-style: normal;
font-display: swap;
}
/* HarmonyOS SC Semibold */
@font-face {
font-family: 'HarmonyOS';
src: url('../fonts/HarmonyOS/HarmonyOS_SansSC/HarmonyOS_SansSC_Semibold.woff2') format('truetype');
font-weight: 600;
font-style: normal;
font-display: swap;
}
/* HarmonyOS SC Bold */
@font-face {
font-family: 'HarmonyOS';
src: url('../fonts/HarmonyOS/HarmonyOS_SansSC/HarmonyOS_SansSC_Bold.woff2') format('truetype');
font-weight: 700;
font-style: normal;
font-display: swap;
}
/* HarmonyOS SC Black */
@font-face {
font-family: 'HarmonyOS';
src: url('../fonts/HarmonyOS/HarmonyOS_SansSC/HarmonyOS_SansSC_Black.woff2') format('truetype');
font-weight: 900;
font-style: normal;
font-display: swap;
}
/* HarmonyOS SC Thin */
@font-face {
font-family: 'HarmonyOS';
src: url('../fonts/HarmonyOS/HarmonyOS_SansSC/HarmonyOS_SansSC_Thin.woff2') format('truetype');
font-weight: 100;
font-style: normal;
font-display: swap;
}
/* 字体加载优化 */
.font-loading {
font-family: system-ui, -apple-system, sans-serif;
}

View File

@@ -1,5 +1,7 @@
/* 导入所有CSS文件 */
@import 'normalize.css';
@import 'variables.css';
@import "fonts.css";
@import "harmony_fonts.css";
@import 'scrollbar.css';
@import 'hack_fonts.css';
@import 'opensans_fonts.css';

View File

@@ -0,0 +1,80 @@
/* BEGIN Light */
@font-face {
font-family: 'Open Sans';
src: url("../fonts/OpenSans/Light/OpenSans-Light.woff2?v=1.1.0") format("woff2"), url("../fonts/OpenSans/Light/OpenSans-Light.woff?v=1.1.0") format("woff");
font-weight: 300;
font-style: normal;
}
/* END Light */
/* BEGIN Light Italic */
@font-face {
font-family: 'Open Sans';
src: url("../fonts/OpenSans/LightItalic/OpenSans-LightItalic.woff2?v=1.1.0") format("woff2"), url("../fonts/OpenSans/LightItalic/OpenSans-LightItalic.woff?v=1.1.0") format("woff");
font-weight: 300;
font-style: italic;
}
/* END Light Italic */
/* BEGIN Regular */
@font-face {
font-family: 'Open Sans';
src: url("../fonts/OpenSans/Regular/OpenSans-Regular.woff2?v=1.1.0") format("woff2"), url("../fonts/OpenSans/Regular/OpenSans-Regular.woff?v=1.1.0") format("woff");
font-weight: normal;
font-style: normal;
}
/* END Regular */
/* BEGIN Italic */
@font-face {
font-family: 'Open Sans';
src: url("../fonts/OpenSans/Italic/OpenSans-Italic.woff2?v=1.1.0") format("woff2"), url("../fonts/OpenSans/Italic/OpenSans-Italic.woff?v=1.1.0") format("woff");
font-weight: normal;
font-style: italic;
}
/* END Italic */
/* BEGIN Semibold */
@font-face {
font-family: 'Open Sans';
src: url("../fonts/OpenSans/Semibold/OpenSans-Semibold.woff2?v=1.1.0") format("woff2"), url("../fonts/OpenSans/Semibold/OpenSans-Semibold.woff?v=1.1.0") format("woff");
font-weight: 600;
font-style: normal;
}
/* END Semibold */
/* BEGIN Semibold Italic */
@font-face {
font-family: 'Open Sans';
src: url("../fonts/OpenSans/SemiboldItalic/OpenSans-SemiboldItalic.woff2?v=1.1.0") format("woff2"), url("../fonts/OpenSans/SemiboldItalic/OpenSans-SemiboldItalic.woff?v=1.1.0") format("woff");
font-weight: 600;
font-style: italic;
}
/* END Semibold Italic */
/* BEGIN Bold */
@font-face {
font-family: 'Open Sans';
src: url("../fonts/OpenSans/Bold/OpenSans-Bold.woff2?v=1.1.0") format("woff2"), url("../fonts/OpenSans/Bold/OpenSans-Bold.woff?v=1.1.0") format("woff");
font-weight: bold;
font-style: normal;
}
/* END Bold */
/* BEGIN Bold Italic */
@font-face {
font-family: 'Open Sans';
src: url("../fonts/OpenSans/BoldItalic/OpenSans-BoldItalic.woff2?v=1.1.0") format("woff2"), url("../fonts/OpenSans/BoldItalic/OpenSans-BoldItalic.woff?v=1.1.0") format("woff");
font-weight: bold;
font-style: italic;
}
/* END Bold Italic */
/* BEGIN Extrabold */
@font-face {
font-family: 'Open Sans';
src: url("../fonts/OpenSans/ExtraBold/OpenSans-ExtraBold.woff2?v=1.1.0") format("woff2"), url("../fonts/OpenSans/ExtraBold/OpenSans-ExtraBold.woff?v=1.1.0") format("woff");
font-weight: 800;
font-style: normal;
}
/* END Extrabold */
/* BEGIN Extrabold Italic */
@font-face {
font-family: 'Open Sans';
src: url("../fonts/OpenSans/ExtraBoldItalic/OpenSans-ExtraBoldItalic.woff2?v=1.1.0") format("woff2"), url("../fonts/OpenSans/ExtraBoldItalic/OpenSans-ExtraBoldItalic.woff?v=1.1.0") format("woff");
font-weight: 800;
font-style: italic;
}
/* END Extrabold Italic */

View File

@@ -0,0 +1,164 @@
import {
AppConfig,
AppearanceConfig,
EditingConfig,
GeneralConfig,
LanguageType,
SystemThemeType,
TabType,
UpdatesConfig,
UpdateSourceType,
GitBackupConfig,
AuthMethod
} from '@/../bindings/voidraft/internal/models/models';
import {FONT_OPTIONS} from './fonts';
// 配置键映射和限制的类型定义
export type GeneralConfigKeyMap = {
readonly [K in keyof GeneralConfig]: string;
};
export type EditingConfigKeyMap = {
readonly [K in keyof EditingConfig]: string;
};
export type AppearanceConfigKeyMap = {
readonly [K in keyof AppearanceConfig]: string;
};
export type UpdatesConfigKeyMap = {
readonly [K in keyof UpdatesConfig]: string;
};
export type BackupConfigKeyMap = {
readonly [K in keyof GitBackupConfig]: string;
};
export type NumberConfigKey = 'fontSize' | 'tabSize' | 'lineHeight';
// 配置键映射
export const GENERAL_CONFIG_KEY_MAP: GeneralConfigKeyMap = {
alwaysOnTop: 'general.alwaysOnTop',
dataPath: 'general.dataPath',
enableSystemTray: 'general.enableSystemTray',
startAtLogin: 'general.startAtLogin',
enableGlobalHotkey: 'general.enableGlobalHotkey',
globalHotkey: 'general.globalHotkey',
enableWindowSnap: 'general.enableWindowSnap',
enableLoadingAnimation: 'general.enableLoadingAnimation',
} as const;
export const EDITING_CONFIG_KEY_MAP: EditingConfigKeyMap = {
fontSize: 'editing.fontSize',
fontFamily: 'editing.fontFamily',
fontWeight: 'editing.fontWeight',
lineHeight: 'editing.lineHeight',
enableTabIndent: 'editing.enableTabIndent',
tabSize: 'editing.tabSize',
tabType: 'editing.tabType',
autoSaveDelay: 'editing.autoSaveDelay'
} as const;
export const APPEARANCE_CONFIG_KEY_MAP: AppearanceConfigKeyMap = {
language: 'appearance.language',
systemTheme: 'appearance.systemTheme'
} as const;
export const UPDATES_CONFIG_KEY_MAP: UpdatesConfigKeyMap = {
version: 'updates.version',
autoUpdate: 'updates.autoUpdate',
primarySource: 'updates.primarySource',
backupSource: 'updates.backupSource',
backupBeforeUpdate: 'updates.backupBeforeUpdate',
updateTimeout: 'updates.updateTimeout',
github: 'updates.github',
gitea: 'updates.gitea'
} as const;
export const BACKUP_CONFIG_KEY_MAP: BackupConfigKeyMap = {
enabled: 'backup.enabled',
repo_url: 'backup.repo_url',
auth_method: 'backup.auth_method',
username: 'backup.username',
password: 'backup.password',
token: 'backup.token',
ssh_key_path: 'backup.ssh_key_path',
ssh_key_passphrase: 'backup.ssh_key_passphrase',
backup_interval: 'backup.backup_interval',
auto_backup: 'backup.auto_backup',
} as const;
// 配置限制
export const CONFIG_LIMITS = {
fontSize: {min: 12, max: 28, default: 13},
tabSize: {min: 2, max: 8, default: 4},
lineHeight: {min: 1.0, max: 3.0, default: 1.5},
tabType: {values: [TabType.TabTypeSpaces, TabType.TabTypeTab], default: TabType.TabTypeSpaces}
} as const;
// 默认配置
export const DEFAULT_CONFIG: AppConfig = {
general: {
alwaysOnTop: false,
dataPath: '',
enableSystemTray: true,
startAtLogin: false,
enableGlobalHotkey: false,
globalHotkey: {
ctrl: false,
shift: false,
alt: true,
win: false,
key: 'X'
},
enableWindowSnap: true,
enableLoadingAnimation: true,
},
editing: {
fontSize: CONFIG_LIMITS.fontSize.default,
fontFamily: FONT_OPTIONS[0].value,
fontWeight: 'normal',
lineHeight: CONFIG_LIMITS.lineHeight.default,
enableTabIndent: true,
tabSize: CONFIG_LIMITS.tabSize.default,
tabType: CONFIG_LIMITS.tabType.default,
autoSaveDelay: 5000
},
appearance: {
language: LanguageType.LangZhCN,
systemTheme: SystemThemeType.SystemThemeAuto
},
updates: {
version: "1.0.0",
autoUpdate: true,
primarySource: UpdateSourceType.UpdateSourceGithub,
backupSource: UpdateSourceType.UpdateSourceGitea,
backupBeforeUpdate: true,
updateTimeout: 30,
github: {
owner: "landaiqing",
repo: "voidraft",
},
gitea: {
baseURL: "https://git.landaiqing.cn",
owner: "landaiqing",
repo: "voidraft",
}
},
backup: {
enabled: false,
repo_url: "",
auth_method: AuthMethod.UserPass,
username: "",
password: "",
token: "",
ssh_key_path: "",
ssh_key_passphrase: "",
backup_interval: 60,
auto_backup: true,
},
metadata: {
version: '1.0.0',
lastUpdated: new Date().toString(),
}
};

View File

@@ -0,0 +1,101 @@
// Font options with popular programming and common fonts
export const FONT_OPTIONS = [
// Custom fonts
{
label: 'HarmonyOS',
value: 'HarmonyOS'
},
{
label: 'Hack',
value: 'Hack'
},
{
label: 'Open Sans',
value: '"Open Sans"'
},
// Common system fonts
{
label: 'Arial',
value: 'Arial'
},
{
label: 'Helvetica',
value: 'Helvetica'
},
{
label: 'Times New Roman',
value: '"Times New Roman"'
},
{
label: 'Georgia',
value: 'Georgia'
},
{
label: 'Verdana',
value: 'Verdana'
},
{
label: 'Tahoma',
value: 'Tahoma'
},
{
label: 'Segoe UI',
value: '"Segoe UI"'
},
{
label: 'System UI',
value: 'system-ui'
},
// Chinese fonts
{
label: 'Microsoft YaHei',
value: '"Microsoft YaHei"'
},
{
label: 'PingFang SC',
value: '"PingFang SC"'
},
// Popular programming fonts
{
label: 'JetBrains Mono',
value: '"JetBrains Mono"'
},
{
label: 'Fira Code',
value: '"Fira Code"'
},
{
label: 'Source Code Pro',
value: '"Source Code Pro"'
},
{
label: 'Cascadia Code',
value: '"Cascadia Code"'
},
{
label: 'Consolas',
value: 'Consolas'
},
{
label: 'Monaco',
value: 'Monaco'
},
{
label: 'Menlo',
value: 'Menlo'
},
{
label: 'Roboto Mono',
value: '"Roboto Mono"'
},
{
label: 'Inconsolata',
value: 'Inconsolata'
},
{
label: 'Ubuntu Mono',
value: '"Ubuntu Mono"'
}
];

View File

@@ -0,0 +1,13 @@
export type SupportedLocaleType = 'zh-CN' | 'en-US';
// 支持的语言列表
export const SUPPORTED_LOCALES = [
{
code: 'zh-CN' as SupportedLocaleType,
name: '简体中文'
},
{
code: 'en-US' as SupportedLocaleType,
name: 'English'
}
] as const;

View File

@@ -1,23 +1,31 @@
/**
* Prettier Plugin for C/C++ formatting using clang-format WebAssembly
* Prettier Plugin for C/C++/C#/Java/Protobuf formatting using clang-format WebAssembly
*
* This plugin provides support for formatting C/C++ files using the clang-format WASM implementation.
* It supports various C/C++ file extensions and common clang-format styles.
* This plugin provides support for formatting multiple languages using the clang-format WASM implementation.
* Supported languages:
* - C / C++
* - Objective-C / Objective-C++
* - C#
* - Java
* - Protocol Buffer (Protobuf)
*
* It supports various file extensions and common clang-format styles.
*/
import type { Plugin, Parser, Printer } from 'prettier';
// Import the clang-format WASM module
import clangFormatInit, { format } from './clang-format-vite.js';
const parserName = 'clang';
const parserName = 'clang-format';
// Language configuration
const languages = [
{
name: 'C',
aliases: ['c'],
parsers: [parserName],
parsers: ['c'],
extensions: ['.c', '.h'],
filenames: ['*.c', '*.h'],
aceMode: 'c_cpp',
tmScope: 'source.c',
linguistLanguageId: 50,
@@ -26,8 +34,9 @@ const languages = [
{
name: 'C++',
aliases: ['cpp', 'cxx', 'cc'],
parsers: [parserName],
parsers: ['cpp'],
extensions: ['.cpp', '.cxx', '.cc', '.hpp', '.hxx', '.hh', '.C', '.H'],
filenames: ['*.cpp', '*.cxx', '*.cc', '*.hpp', '*.hxx', '*.hh', '*.C', '*.H'],
aceMode: 'c_cpp',
tmScope: 'source.cpp',
linguistLanguageId: 43,
@@ -36,12 +45,57 @@ const languages = [
{
name: 'Objective-C',
aliases: ['objc', 'objectivec'],
parsers: [parserName],
extensions: ['.m', '.mm'],
parsers: ['objective-c'],
extensions: ['.m'],
filenames: ['*.m'],
aceMode: 'objectivec',
tmScope: 'source.objc',
linguistLanguageId: 259,
vscodeLanguageIds: ['objective-c']
},
{
name: 'Objective-C++',
aliases: ['objcpp', 'objectivecpp'],
parsers: ['objective-cpp'],
extensions: ['.mm'],
filenames: ['*.mm'],
aceMode: 'objectivec',
tmScope: 'source.objcpp',
linguistLanguageId: 260,
vscodeLanguageIds: ['objective-cpp']
},
{
name: 'C#',
aliases: ['csharp', 'cs'],
parsers: ['cs'],
extensions: ['.cs'],
filenames: ['*.cs'],
aceMode: 'csharp',
tmScope: 'source.cs',
linguistLanguageId: 42,
vscodeLanguageIds: ['csharp']
},
{
name: 'Java',
aliases: ['java'],
parsers: ['java'],
extensions: ['.java'],
filenames: ['*.java'],
aceMode: 'java',
tmScope: 'source.java',
linguistLanguageId: 181,
vscodeLanguageIds: ['java']
},
{
name: 'Protocol Buffer',
aliases: ['protobuf', 'proto'],
parsers: ['proto'],
extensions: ['.proto'],
filenames: ['*.proto'],
aceMode: 'protobuf',
tmScope: 'source.proto',
linguistLanguageId: 297,
vscodeLanguageIds: ['proto']
}
];
@@ -53,39 +107,44 @@ const clangParser: Parser<string> = {
locEnd: (node: string) => node.length,
};
// Initialize clang-format WASM module
// Lazy initialize clang-format WASM module
let initPromise: Promise<void> | null = null;
let isInitialized = false;
function initClangFormat(): Promise<void> {
if (initPromise) {
return initPromise;
if (isInitialized) {
return Promise.resolve();
}
initPromise = (async () => {
if (!isInitialized) {
await clangFormatInit();
isInitialized = true;
}
})();
if (!initPromise) {
initPromise = (async () => {
try {
await clangFormatInit();
isInitialized = true;
} catch (error) {
console.warn('Failed to initialize clang-format WASM module:', error);
initPromise = null;
throw error;
}
})();
}
return initPromise;
}
// Printer configuration
const clangPrinter: Printer<string> = {
print: (path, options) => {
// @ts-expect-error -- Support async printer like shell plugin
async print(path, options) {
try {
if (!isInitialized) {
console.warn('clang-format WASM module not initialized, returning original text');
return (path as any).getValue ? (path as any).getValue() : path.node;
}
// Wait for initialization to complete
await initClangFormat();
const text = (path as any).getValue ? (path as any).getValue() : path.node;
const style = getClangStyle(options);
// Format using clang-format (synchronous call)
const formatted = format(text, undefined, style);
const formatted = format(text, options.filename, style);
return formatted.trim();
} catch (error) {
@@ -129,6 +188,13 @@ const options = {
{ value: 'Microsoft', description: "Microsoft's style guide" },
{ value: 'GNU', description: 'GNU coding standards' }
]
},
filename: {
// since: '0.1.0',
category: 'Config',
type: 'string',
default: undefined,
description: 'Custom filename to use for web_fmt processing (affects language detection)',
}
};
@@ -141,14 +207,9 @@ const clangPlugin: Plugin = {
printers: {
[parserName]: clangPrinter,
},
options,
...options,
};
// Initialize WASM module when plugin loads
initClangFormat().catch(error => {
console.warn('Failed to initialize clang-format WASM module:', error);
});
export default clangPlugin;
export { languages };
export const parsers = clangPlugin.parsers;

View File

@@ -0,0 +1,105 @@
cmake_minimum_required(VERSION 3.20)
project(clang-format-wasm)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Set Emscripten flags for WASM
if(EMSCRIPTEN)
set(CMAKE_EXECUTABLE_SUFFIX ".js")
# Common Emscripten flags
set(EMSCRIPTEN_FLAGS
-O3
-sWASM=1
-sEXPORTED_RUNTIME_METHODS=['ccall','cwrap']
-sALLOW_MEMORY_GROWTH=1
-sMODULARIZE=1
-sEXPORT_NAME='Module'
-sENVIRONMENT=web,webview,worker
-sUSE_ES6_IMPORT_META=0
--no-entry
)
# Library-specific flags
set(LIB_EMSCRIPTEN_FLAGS
${EMSCRIPTEN_FLAGS}
-sEXPORTED_FUNCTIONS=['_malloc','_free']
--bind
)
# CLI-specific flags
set(CLI_EMSCRIPTEN_FLAGS
${EMSCRIPTEN_FLAGS}
-sEXPORTED_FUNCTIONS=['_main']
-sINVOKE_RUN=0
-sNODERAWFS=1
)
endif()
# Find LLVM
find_package(LLVM REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
# Include LLVM headers
include_directories(${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})
# Find Clang
find_package(Clang REQUIRED CONFIG)
# Get LLVM components
llvm_map_components_to_libnames(llvm_libs support core)
# Define source files
set(LIB_SOURCES
lib.cc
lib.h
binding.cc
)
set(CLI_SOURCES
cli.cc
)
# Create library target
add_executable(clang-format-wasm ${LIB_SOURCES})
# Link against Clang and LLVM libraries
target_link_libraries(clang-format-wasm
clangFormat
clangToolingCore
clangBasic
clangRewrite
${llvm_libs}
)
# Create CLI target
add_executable(clang-format-cli ${CLI_SOURCES})
target_link_libraries(clang-format-cli
clangFormat
clangToolingCore
clangBasic
clangRewrite
${llvm_libs}
)
# Set Emscripten flags
if(EMSCRIPTEN)
# Configure library target
set_target_properties(clang-format-wasm PROPERTIES
COMPILE_FLAGS "${LIB_EMSCRIPTEN_FLAGS}"
LINK_FLAGS "${LIB_EMSCRIPTEN_FLAGS}"
OUTPUT_NAME "clang-format-esm"
)
# Configure CLI target
set_target_properties(clang-format-cli PROPERTIES
COMPILE_FLAGS "${CLI_EMSCRIPTEN_FLAGS}"
LINK_FLAGS "${CLI_EMSCRIPTEN_FLAGS}"
OUTPUT_NAME "clang-format-cli"
)
endif()

View File

@@ -0,0 +1,100 @@
# Clang Format WASM Plugin
这是一个基于 clang-format WebAssembly 的 Prettier 插件,支持格式化 C/C++/C#/Java/Protobuf 代码。
## 目录结构
```
clang/
├── src/ # 源码目录
│ ├── scripts/ # 构建和工具脚本
│ │ ├── build.sh # 主构建脚本
│ │ ├── gen_patch.sh # 补丁生成脚本
│ │ └── cli.patch # CLI 修改补丁
│ ├── *.cc # C++ 源文件
│ ├── *.h # C++ 头文件
│ ├── CMakeLists.txt # CMake 构建配置
│ ├── package.json # NPM 包配置
│ ├── clang-format.d.ts # TypeScript 类型定义
│ ├── template.js # JavaScript 模板
│ └── clang-format-diff.py # Python 差异工具
├── *.js # 编译后的 JavaScript 文件
├── *.wasm # 编译后的 WebAssembly 文件
├── *.cjs # CommonJS 格式的 CLI 工具
├── git-clang-format # Git 集成工具
└── index.ts # 插件入口文件
```
## 构建说明
### 前提条件
- Install LLVM and Clang (version 18 or later)
- Install CMake (version 3.27 or later)
- Install Ninja (version 1.11 or later)
### 构建步骤
1. Clone this repository
2. 进入源码目录:
```bash
cd src
```
3. 运行构建脚本:
```bash
./scripts/build.sh
```
构建脚本会:
- 创建 `build` 目录并编译源码
- 将编译结果复制到上级目录(插件目录)
- 生成 WebAssembly 文件和 JavaScript 绑定
- 复制必要的工具和类型定义文件
### 输出文件
构建完成后,插件目录下会包含:
- `clang-format.wasm` - WebAssembly 库文件
- `clang-format.js` - JavaScript 绑定文件
- `clang-format-cli.cjs` - CLI 工具
- `clang-format-cli.wasm` - CLI WebAssembly 文件
- `git-clang-format` - Git 集成工具
- `clang-format-diff.py` - 差异工具
## 开发说明
### 修改源码
- C++ 源文件位于 `src/` 目录下
- 修改后运行 `./scripts/build.sh` 重新构建
- 类型定义文件 `src/clang-format.d.ts` 需要与实际 API 保持同步
### 生成补丁
如果修改了 CLI 相关代码,可以使用:
```bash
./scripts/gen_patch.sh
```
生成补丁文件 `scripts/cli.patch`。
## 使用说明
插件会自动加载编译后的 WebAssembly 文件,支持以下语言:
- C/C++
- Objective-C/C++
- C#
- Java
- Protocol Buffer
支持的 clang-format 样式:
- LLVM
- Google
- Chromium
- Mozilla
- WebKit
- Microsoft
- GNU
- 自定义样式

View File

@@ -0,0 +1,42 @@
set -Eeo pipefail
cd $(dirname $0)/..
project_root=$(pwd)
rm -rf pkg
mkdir -p pkg build
cd build
export CC=$(which clang)
export CXX=$(which clang++)
emcmake cmake -G Ninja ..
ninja clang-format-wasm
cd $project_root
if [[ ! -z "${WASM_OPT}" ]]; then
wasm-opt -Os build/clang-format-esm.wasm -o build/clang-format-esm-Os.wasm
wasm-opt -Oz build/clang-format-esm.wasm -o build/clang-format-esm-Oz.wasm
fi
SMALLEST_WASM=$(ls -Sr build/clang-format-e*.wasm | head -1)
cp $SMALLEST_WASM pkg/clang-format.wasm
cat src/template.js build/clang-format-esm.js >pkg/clang-format.js
# add shebang
echo '#!/usr/bin/env node' | cat - ./build/clang-format-cli.js >./pkg/clang-format-cli.cjs
cp ./build/clang-format-cli.wasm ./pkg/
cp ./src/clang-format.d.ts src/clang-format-*.js ./pkg/
cp ./package.json LICENSE README.md .npmignore ./pkg/
# copy git-clang-format and clang-format-diff.py
cp ./build/_deps/llvm_project-src/clang/tools/clang-format/git-clang-format ./pkg/
cp ./build/_deps/llvm_project-src/clang/tools/clang-format/clang-format-diff.py ./pkg/
ls -lh ./pkg
# make sure repo is clean
# git diff --exit-code

View File

@@ -0,0 +1,95 @@
diff --git a/src/cli.cc b/src/cli.cc
index 2861005..69ec009 100644
--- a/src/cli.cc
+++ b/src/cli.cc
@@ -12,7 +12,7 @@
///
//===----------------------------------------------------------------------===//
-#include "../../lib/Format/MatchFilePath.h"
+#include "clang/../../lib/Format/MatchFilePath.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileManager.h"
@@ -27,6 +27,8 @@
#include "llvm/Support/Process.h"
#include <fstream>
+#include "CustomFileSystem.h"
+
using namespace llvm;
using clang::tooling::Replacements;
@@ -448,9 +450,12 @@ static bool format(StringRef FileName, bool ErrorOnIncompleteFormat = false) {
return true;
}
+ auto RealFS = vfs::getRealFileSystem();
+ auto CustomFS = new vfs::CustomFileSystem(RealFS);
+ IntrusiveRefCntPtr<vfs::FileSystem> CustomFSPtr(CustomFS);
Expected<FormatStyle> FormatStyle =
getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(),
- nullptr, WNoErrorList.isSet(WNoError::Unknown));
+ CustomFSPtr.get(), WNoErrorList.isSet(WNoError::Unknown));
if (!FormatStyle) {
llvm::errs() << toString(FormatStyle.takeError()) << "\n";
return true;
@@ -571,10 +576,15 @@ static int dumpConfig() {
}
Code = std::move(CodeOrErr.get());
}
+
+ auto RealFS = vfs::getRealFileSystem();
+ auto CustomFS = new vfs::CustomFileSystem(RealFS);
+ IntrusiveRefCntPtr<vfs::FileSystem> CustomFSPtr(CustomFS);
+
Expected<clang::format::FormatStyle> FormatStyle = clang::format::getStyle(
Style,
FileNames.empty() || FileNames[0] == "-" ? AssumeFileName : FileNames[0],
- FallbackStyle, Code ? Code->getBuffer() : "");
+ FallbackStyle, Code ? Code->getBuffer() : "", CustomFSPtr.get());
if (!FormatStyle) {
llvm::errs() << toString(FormatStyle.takeError()) << "\n";
return 1;
@@ -607,24 +617,26 @@ static bool isIgnored(StringRef FilePath) {
String Path;
String AbsPath{FilePath};
+ auto PathStyle = vfs::getPathStyle();
+
using namespace llvm::sys::path;
- make_absolute(AbsPath);
- remove_dots(AbsPath, /*remove_dot_dot=*/true);
+ vfs::make_absolute(AbsPath);
+ remove_dots(AbsPath, /*remove_dot_dot=*/true, PathStyle);
- if (StringRef Dir{parent_path(AbsPath)}; PrevDir != Dir) {
+ if (StringRef Dir{parent_path(AbsPath, PathStyle)}; PrevDir != Dir) {
PrevDir = Dir;
for (;;) {
Path = Dir;
- append(Path, ".clang-format-ignore");
+ append(Path, PathStyle, ".clang-format-ignore");
if (is_regular_file(Path))
break;
- Dir = parent_path(Dir);
+ Dir = parent_path(Dir, PathStyle);
if (Dir.empty())
return false;
}
- IgnoreDir = convert_to_slash(Dir);
+ IgnoreDir = convert_to_slash(Dir, PathStyle);
std::ifstream IgnoreFile{Path.c_str()};
if (!IgnoreFile.good())
@@ -644,7 +656,7 @@ static bool isIgnored(StringRef FilePath) {
if (IgnoreDir.empty())
return false;
- const auto Pathname{convert_to_slash(AbsPath)};
+ const auto Pathname{convert_to_slash(AbsPath, PathStyle)};
for (const auto &Pat : Patterns) {
const bool IsNegated = Pat[0] == '!';
StringRef Pattern{Pat};

View File

@@ -0,0 +1,26 @@
current_dir=$(pwd)
tmp_dir=$(mktemp -d)
cd $tmp_dir
git init
cp $current_dir/build/_deps/llvm_project-src/clang/tools/clang-format/ClangFormat.cpp ./cli.cc
git add -f .
git commit -m "init"
cp $current_dir/src/cli.cc ./cli.cc
git add -f .
git diff \
--cached \
--no-color \
--ignore-space-at-eol \
--no-ext-diff \
--src-prefix=a/src/ \
--dst-prefix=b/src/ \
>$current_dir/scripts/cli.patch || true
rm -rf $tmp_dir

View File

@@ -0,0 +1,32 @@
export function format(input: string, filename: string, config?: LayoutConfig): string;
interface LayoutConfig {
line_width?: number;
line_ending?: "lf" | "crlf";
language_version?: string;
}
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
export type InitOutput = unknown;
// export type SyncInitInput = BufferSource | WebAssembly.Module;
// /**
// * Instantiates the given `module`, which can either be bytes or
// * a precompiled `WebAssembly.Module`.
// *
// * @param {SyncInitInput} module
// *
// * @returns {InitOutput}
// */
// export function initSync(module: SyncInitInput): InitOutput;
/**
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {InitInput | Promise<InitInput>} module_or_path
*
* @returns {Promise<InitOutput>}
*/
export default function init(module_or_path?: InitInput | Promise<InitInput>): Promise<InitOutput>;

View File

@@ -0,0 +1,84 @@
import { format as dart_fmt, instantiate, invoke } from "./dart_fmt.mjs";
let wasm;
function get_imports() {}
function init_memory() {}
function normalize(module) {
if (!(module instanceof WebAssembly.Module)) {
return new WebAssembly.Module(module);
}
return module;
}
export default async function (input) {
if (wasm !== undefined) return wasm;
if (typeof input === "undefined") {
input = new URL("dart_fmt.wasm", import.meta.url);
}
const imports = get_imports();
if (
typeof input === "string" ||
(typeof Request === "function" && input instanceof Request) ||
(typeof URL === "function" && input instanceof URL)
) {
input = fetch(input);
}
init_memory(imports);
wasm = await load(await input)
.then(normalize)
.then(instantiate);
invoke(wasm);
return wasm;
}
async function load(module) {
if (typeof Response === "function" && module instanceof Response) {
if ("compileStreaming" in WebAssembly) {
try {
return await WebAssembly.compileStreaming(module);
} catch (e) {
if (module.headers.get("Content-Type") != "application/wasm") {
console.warn(
"`WebAssembly.compileStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",
e,
);
} else {
throw e;
}
}
}
return module.arrayBuffer();
}
return module;
}
export function format(source, filename = "stdin.dart", config = {}) {
const options = { lineEnding: "\n" };
if (config.line_width) {
options.pageWidth = config.line_width;
}
if (options.line_ending === "crlf") {
options.lineEnding = "\r\n";
}
if(options.language_version) {
options.languageVersion = options.language_version;
}
const result = dart_fmt(source, filename, JSON.stringify(options));
const err = result[0] === "x";
const output = result.slice(1);
if (err) {
throw new Error(output);
}
return output;
}

View File

@@ -0,0 +1,350 @@
// `modulePromise` is a promise to the `WebAssembly.module` object to be
// instantiated.
// `importObjectPromise` is a promise to an object that contains any additional
// imports needed by the module that aren't provided by the standard runtime.
// The fields on this object will be merged into the importObject with which
// the module will be instantiated.
// This function returns a promise to the instantiated module.
export const instantiate = async (modulePromise, importObjectPromise) => {
let dartInstance;
// Prints to the console
function printToConsole(value) {
if (typeof dartPrint == "function") {
dartPrint(value);
return;
}
if (typeof console == "object" && typeof console.log != "undefined") {
console.log(value);
return;
}
if (typeof print == "function") {
print(value);
return;
}
throw "Unable to print message: " + js;
}
// Converts a Dart List to a JS array. Any Dart objects will be converted, but
// this will be cheap for JSValues.
function arrayFromDartList(constructor, list) {
const exports = dartInstance.exports;
const read = exports.$listRead;
const length = exports.$listLength(list);
const array = new constructor(length);
for (let i = 0; i < length; i++) {
array[i] = read(list, i);
}
return array;
}
// A special symbol attached to functions that wrap Dart functions.
const jsWrappedDartFunctionSymbol = Symbol("JSWrappedDartFunction");
function finalizeWrapper(dartFunction, wrapped) {
wrapped.dartFunction = dartFunction;
wrapped[jsWrappedDartFunctionSymbol] = true;
return wrapped;
}
// Imports
const dart2wasm = {
_49: v => v.toString(),
_64: s => {
if (!/^\s*[+-]?(?:Infinity|NaN|(?:\.\d+|\d+(?:\.\d*)?)(?:[eE][+-]?\d+)?)\s*$/.test(s)) {
return NaN;
}
return parseFloat(s);
},
_65: () => {
let stackString = new Error().stack.toString();
let frames = stackString.split('\n');
let drop = 2;
if (frames[0] === 'Error') {
drop += 1;
}
return frames.slice(drop).join('\n');
},
_69: () => {
// On browsers return `globalThis.location.href`
if (globalThis.location != null) {
return globalThis.location.href;
}
return null;
},
_70: () => {
return typeof process != "undefined" &&
Object.prototype.toString.call(process) == "[object process]" &&
process.platform == "win32";
},
_85: s => JSON.stringify(s),
_86: s => printToConsole(s),
_87: a => a.join(''),
_90: (s, t) => s.split(t),
_91: s => s.toLowerCase(),
_92: s => s.toUpperCase(),
_93: s => s.trim(),
_97: (s, p, i) => s.indexOf(p, i),
_98: (s, p, i) => s.lastIndexOf(p, i),
_100: Object.is,
_101: s => s.toUpperCase(),
_102: s => s.toLowerCase(),
_103: (a, i) => a.push(i),
_113: (a, b) => a == b ? 0 : (a > b ? 1 : -1),
_114: a => a.length,
_116: (a, i) => a[i],
_117: (a, i, v) => a[i] = v,
_120: (o, start, length) => new Uint8Array(o.buffer, o.byteOffset + start, length),
_121: (o, start, length) => new Int8Array(o.buffer, o.byteOffset + start, length),
_122: (o, start, length) => new Uint8ClampedArray(o.buffer, o.byteOffset + start, length),
_123: (o, start, length) => new Uint16Array(o.buffer, o.byteOffset + start, length),
_124: (o, start, length) => new Int16Array(o.buffer, o.byteOffset + start, length),
_125: (o, start, length) => new Uint32Array(o.buffer, o.byteOffset + start, length),
_126: (o, start, length) => new Int32Array(o.buffer, o.byteOffset + start, length),
_129: (o, start, length) => new Float32Array(o.buffer, o.byteOffset + start, length),
_130: (o, start, length) => new Float64Array(o.buffer, o.byteOffset + start, length),
_133: (o) => new DataView(o.buffer, o.byteOffset, o.byteLength),
_137: Function.prototype.call.bind(Object.getOwnPropertyDescriptor(DataView.prototype, 'byteLength').get),
_138: (b, o) => new DataView(b, o),
_140: Function.prototype.call.bind(DataView.prototype.getUint8),
_141: Function.prototype.call.bind(DataView.prototype.setUint8),
_142: Function.prototype.call.bind(DataView.prototype.getInt8),
_143: Function.prototype.call.bind(DataView.prototype.setInt8),
_144: Function.prototype.call.bind(DataView.prototype.getUint16),
_145: Function.prototype.call.bind(DataView.prototype.setUint16),
_146: Function.prototype.call.bind(DataView.prototype.getInt16),
_147: Function.prototype.call.bind(DataView.prototype.setInt16),
_148: Function.prototype.call.bind(DataView.prototype.getUint32),
_149: Function.prototype.call.bind(DataView.prototype.setUint32),
_150: Function.prototype.call.bind(DataView.prototype.getInt32),
_151: Function.prototype.call.bind(DataView.prototype.setInt32),
_156: Function.prototype.call.bind(DataView.prototype.getFloat32),
_157: Function.prototype.call.bind(DataView.prototype.setFloat32),
_158: Function.prototype.call.bind(DataView.prototype.getFloat64),
_159: Function.prototype.call.bind(DataView.prototype.setFloat64),
_165: x0 => format = x0,
_166: f => finalizeWrapper(f, function(x0,x1,x2) { return dartInstance.exports._166(f,arguments.length,x0,x1,x2); }),
_184: (c) =>
queueMicrotask(() => dartInstance.exports.$invokeCallback(c)),
_187: (s, m) => {
try {
return new RegExp(s, m);
} catch (e) {
return String(e);
}
},
_188: (x0,x1) => x0.exec(x1),
_189: (x0,x1) => x0.test(x1),
_190: (x0,x1) => x0.exec(x1),
_191: (x0,x1) => x0.exec(x1),
_192: x0 => x0.pop(),
_198: o => o === undefined,
_199: o => typeof o === 'boolean',
_200: o => typeof o === 'number',
_202: o => typeof o === 'string',
_205: o => o instanceof Int8Array,
_206: o => o instanceof Uint8Array,
_207: o => o instanceof Uint8ClampedArray,
_208: o => o instanceof Int16Array,
_209: o => o instanceof Uint16Array,
_210: o => o instanceof Int32Array,
_211: o => o instanceof Uint32Array,
_212: o => o instanceof Float32Array,
_213: o => o instanceof Float64Array,
_214: o => o instanceof ArrayBuffer,
_215: o => o instanceof DataView,
_216: o => o instanceof Array,
_217: o => typeof o === 'function' && o[jsWrappedDartFunctionSymbol] === true,
_220: o => o instanceof RegExp,
_221: (l, r) => l === r,
_222: o => o,
_223: o => o,
_224: o => o,
_225: b => !!b,
_226: o => o.length,
_229: (o, i) => o[i],
_230: f => f.dartFunction,
_231: l => arrayFromDartList(Int8Array, l),
_232: (data, length) => {
const jsBytes = new Uint8Array(length);
const getByte = dartInstance.exports.$uint8ListGet;
for (let i = 0; i < length; i++) {
jsBytes[i] = getByte(data, i);
}
return jsBytes;
},
_233: l => arrayFromDartList(Uint8ClampedArray, l),
_234: l => arrayFromDartList(Int16Array, l),
_235: l => arrayFromDartList(Uint16Array, l),
_236: l => arrayFromDartList(Int32Array, l),
_237: l => arrayFromDartList(Uint32Array, l),
_238: l => arrayFromDartList(Float32Array, l),
_239: l => arrayFromDartList(Float64Array, l),
_240: (data, length) => {
const read = dartInstance.exports.$byteDataGetUint8;
const view = new DataView(new ArrayBuffer(length));
for (let i = 0; i < length; i++) {
view.setUint8(i, read(data, i));
}
return view;
},
_241: l => arrayFromDartList(Array, l),
_242: (s, length) => {
if (length == 0) return '';
const read = dartInstance.exports.$stringRead1;
let result = '';
let index = 0;
const chunkLength = Math.min(length - index, 500);
let array = new Array(chunkLength);
while (index < length) {
const newChunkLength = Math.min(length - index, 500);
for (let i = 0; i < newChunkLength; i++) {
array[i] = read(s, index++);
}
if (newChunkLength < chunkLength) {
array = array.slice(0, newChunkLength);
}
result += String.fromCharCode(...array);
}
return result;
}
,
_243: (s, length) => {
if (length == 0) return '';
const read = dartInstance.exports.$stringRead2;
let result = '';
let index = 0;
const chunkLength = Math.min(length - index, 500);
let array = new Array(chunkLength);
while (index < length) {
const newChunkLength = Math.min(length - index, 500);
for (let i = 0; i < newChunkLength; i++) {
array[i] = read(s, index++);
}
if (newChunkLength < chunkLength) {
array = array.slice(0, newChunkLength);
}
result += String.fromCharCode(...array);
}
return result;
}
,
_244: (s) => {
let length = s.length;
let range = 0;
for (let i = 0; i < length; i++) {
range |= s.codePointAt(i);
}
const exports = dartInstance.exports;
if (range < 256) {
if (length <= 10) {
if (length == 1) {
return exports.$stringAllocate1_1(s.codePointAt(0));
}
if (length == 2) {
return exports.$stringAllocate1_2(s.codePointAt(0), s.codePointAt(1));
}
if (length == 3) {
return exports.$stringAllocate1_3(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2));
}
if (length == 4) {
return exports.$stringAllocate1_4(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3));
}
if (length == 5) {
return exports.$stringAllocate1_5(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4));
}
if (length == 6) {
return exports.$stringAllocate1_6(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4), s.codePointAt(5));
}
if (length == 7) {
return exports.$stringAllocate1_7(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4), s.codePointAt(5), s.codePointAt(6));
}
if (length == 8) {
return exports.$stringAllocate1_8(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4), s.codePointAt(5), s.codePointAt(6), s.codePointAt(7));
}
if (length == 9) {
return exports.$stringAllocate1_9(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4), s.codePointAt(5), s.codePointAt(6), s.codePointAt(7), s.codePointAt(8));
}
if (length == 10) {
return exports.$stringAllocate1_10(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4), s.codePointAt(5), s.codePointAt(6), s.codePointAt(7), s.codePointAt(8), s.codePointAt(9));
}
}
const dartString = exports.$stringAllocate1(length);
const write = exports.$stringWrite1;
for (let i = 0; i < length; i++) {
write(dartString, i, s.codePointAt(i));
}
return dartString;
} else {
const dartString = exports.$stringAllocate2(length);
const write = exports.$stringWrite2;
for (let i = 0; i < length; i++) {
write(dartString, i, s.charCodeAt(i));
}
return dartString;
}
}
,
_247: l => new Array(l),
_251: (o, p) => o[p],
_255: o => String(o),
_260: x0 => x0.index,
_262: x0 => x0.length,
_264: (x0,x1) => x0[x1],
_265: (x0,x1) => x0.exec(x1),
_267: x0 => x0.flags,
_268: x0 => x0.multiline,
_269: x0 => x0.ignoreCase,
_270: x0 => x0.unicode,
_271: x0 => x0.dotAll,
_272: (x0,x1) => x0.lastIndex = x1
};
const baseImports = {
dart2wasm: dart2wasm,
Math: Math,
Date: Date,
Object: Object,
Array: Array,
Reflect: Reflect,
};
const jsStringPolyfill = {
"charCodeAt": (s, i) => s.charCodeAt(i),
"compare": (s1, s2) => {
if (s1 < s2) return -1;
if (s1 > s2) return 1;
return 0;
},
"concat": (s1, s2) => s1 + s2,
"equals": (s1, s2) => s1 === s2,
"fromCharCode": (i) => String.fromCharCode(i),
"length": (s) => s?.length||0,
"substring": (s, a, b) => s.substring(a, b),
};
dartInstance = await WebAssembly.instantiate(await modulePromise, {
...baseImports,
...(await importObjectPromise),
"wasm:js-string": jsStringPolyfill,
});
return dartInstance;
};
// Call the main function for the instantiated module
// `moduleInstance` is the instantiated dart2wasm module
// `args` are any arguments that should be passed into the main function.
export const invoke = (moduleInstance, ...args) => {
moduleInstance.exports.$invokeMain(args);
};
export let format;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,10 @@
import fs from "node:fs/promises";
import initAsync from "./dart_fmt.js";
const wasm = new URL("./dart_fmt.wasm", import.meta.url);
export default function __wbg_init(init = fs.readFile(wasm)) {
return initAsync(init);
}
export * from "./dart_fmt.js";

View File

@@ -0,0 +1,8 @@
import initAsync from "./dart_fmt.js";
import wasm from "./dart_fmt.wasm?url";
export default function __wbg_init(input = wasm) {
return initAsync(input);
}
export * from "./dart_fmt.js";

View File

@@ -0,0 +1,132 @@
/**
* Prettier Plugin for Dart formatting using dart_fmt WebAssembly
*
* This plugin provides support for formatting Dart files using the dart_fmt WASM implementation.
* dart_fmt is the official Dart code formatter integrated into the Dart SDK.
*/
import type { Plugin, Parser, Printer } from 'prettier';
// Import the Dart formatter WASM module
import dartInit, { format } from './dart_fmt_vite.js';
const parserName = 'dart';
// Language configuration
const languages = [
{
name: 'Dart',
aliases: ['dart'],
parsers: [parserName],
extensions: ['.dart'],
aceMode: 'dart',
tmScope: 'source.dart',
linguistLanguageId: 103,
vscodeLanguageIds: ['dart']
}
];
// Parser configuration
const dartParser: Parser<string> = {
astFormat: parserName,
parse: (text: string) => text,
locStart: () => 0,
locEnd: (node: string) => node.length,
};
// Lazy initialize Dart WASM module
let initPromise: Promise<void> | null = null;
let isInitialized = false;
function initDart(): Promise<void> {
if (isInitialized) {
return Promise.resolve();
}
if (!initPromise) {
initPromise = (async () => {
try {
await dartInit();
isInitialized = true;
} catch (error) {
console.warn('Failed to initialize Dart WASM module:', error);
initPromise = null;
throw error;
}
})();
}
return initPromise;
}
// Printer configuration
const dartPrinter: Printer<string> = {
// @ts-expect-error -- Support async printer like shell plugin
async print(path, options) {
try {
// Wait for initialization to complete
await initDart();
const text = (path as any).getValue ? (path as any).getValue() : path.node;
const config = getDartConfig(options);
// Format using dart_fmt (synchronous call)
const formatted = format(text, undefined, config);
return formatted.trim();
} catch (error) {
console.warn('Dart formatting failed:', error);
// Return original text if formatting fails
return (path as any).getValue ? (path as any).getValue() : path.node;
}
},
};
// Helper function to create Dart config from Prettier options
function getDartConfig(options: any): any {
const config: any = {};
// Map Prettier options to Dart formatter config
if (options.printWidth !== undefined) {
config.line_width = options.printWidth;
}
if (options.endOfLine !== undefined) {
config.line_ending = options.endOfLine === 'crlf' ? 'crlf' : 'lf';
}
// Dart language version (if specified)
if (options.dartLanguageVersion !== undefined) {
config.language_version = options.dartLanguageVersion;
}
return config;
}
// Plugin options
const options = {
dartLanguageVersion: {
since: '0.0.1',
category: 'Format' as const,
type: 'string' as const,
default: undefined,
description: 'Dart language version (e.g., "3.0", "2.17")'
}
};
// Plugin object
const dartPlugin: Plugin = {
languages,
parsers: {
[parserName]: dartParser,
},
printers: {
[parserName]: dartPrinter,
},
options,
};
export default dartPlugin;
export { languages };
export const parsers = dartPlugin.parsers;
export const printers = dartPlugin.printers;

View File

@@ -0,0 +1,31 @@
import 'dart:convert';
import 'dart:js_interop';
import 'package:dart_fmt/dart_fmt.dart';
class Options {
final int? pageWidth;
final String? lineEnding;
final String? languageVersion;
Options.fromJson(Map<String, dynamic> json)
: pageWidth = json['pageWidth'] as int?,
lineEnding = json['lineEnding'] as String?,
languageVersion = json['languageVersion'] as String?;
}
String formatWrapper(String source, String filename, String options) {
final config = Options.fromJson(jsonDecode(options));
try {
return "o${format(source, filename, pageWidth: config.pageWidth, lineEnding: config.lineEnding, version: config.languageVersion)}";
} catch (e) {
return "x$e";
}
}
@JS('format')
external set formatExport(JSFunction handler);
void main(List<String> arguments) {
formatExport = formatWrapper.toJS;
}

View File

@@ -0,0 +1,5 @@
cd $(dirname $0)/..
dart compile wasm ./lib/binding.dart -o ./build/dart_fmt.wasm

Some files were not shown because too many files have changed in this diff Show More