Compare commits
16 Commits
v1.5.6
...
4c5fff5390
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c5fff5390 | |||
| 34c8f2a185 | |||
| 1480f5b617 | |||
| 03191a219f | |||
| 7a92935dc6 | |||
| 6e49516962 | |||
| b6c325198d | |||
| 532d30aa93 | |||
| aae86d8b4e | |||
| 0b91447b05 | |||
| 4b1fb765b0 | |||
|
|
aa5ce2b038 | ||
| 533f732c53 | |||
| 009274e4ad | |||
| 76f6c30b9d | |||
| 9ec22add55 |
@@ -18,6 +18,14 @@ import * as application$0 from "../../application/models.js";
|
||||
// @ts-ignore: Unused imports
|
||||
import * as $models from "./models.js";
|
||||
|
||||
/**
|
||||
* GetBadge returns the badge label on the application icon.
|
||||
*/
|
||||
export function GetBadge(): Promise<string | null> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1150236961) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* HideAppIcon hides the app icon in the dock/taskbar.
|
||||
*/
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
/**
|
||||
* BackupService 提供基于Git的备份同步功能
|
||||
* @module
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as models$0 from "../models/models.js";
|
||||
|
||||
/**
|
||||
* HandleConfigChange 处理配置变更
|
||||
*/
|
||||
export function HandleConfigChange(config: models$0.GitBackupConfig | null): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(395287784, config) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize 初始化备份服务
|
||||
*/
|
||||
export function Initialize(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1052437974) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reinitialize 重新初始化
|
||||
*/
|
||||
export function Reinitialize(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(301562543) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceShutdown 服务关闭
|
||||
*/
|
||||
export function ServiceShutdown(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(422131801) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2900331732, options) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* StartAutoBackup 启动自动备份
|
||||
*/
|
||||
export function StartAutoBackup(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3035755449) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* StopAutoBackup 停止自动备份
|
||||
*/
|
||||
export function StopAutoBackup(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2641894021) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync 执行完整的同步流程:导出 -> commit -> pull -> 解决冲突 -> push -> 导入
|
||||
*/
|
||||
export function Sync(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3420383211) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
import * as BackupService from "./backupservice.js";
|
||||
import * as ConfigService from "./configservice.js";
|
||||
import * as DatabaseService from "./databaseservice.js";
|
||||
import * as DialogService from "./dialogservice.js";
|
||||
import * as DocumentService from "./documentservice.js";
|
||||
import * as ExtensionService from "./extensionservice.js";
|
||||
import * as HotkeyService from "./hotkeyservice.js";
|
||||
import * as HttpClientService from "./httpclientservice.js";
|
||||
import * as KeyBindingService from "./keybindingservice.js";
|
||||
import * as MigrationService from "./migrationservice.js";
|
||||
import * as SelfUpdateService from "./selfupdateservice.js";
|
||||
import * as StartupService from "./startupservice.js";
|
||||
import * as SystemService from "./systemservice.js";
|
||||
import * as TestService from "./testservice.js";
|
||||
import * as ThemeService from "./themeservice.js";
|
||||
import * as TranslationService from "./translationservice.js";
|
||||
import * as WindowService from "./windowservice.js";
|
||||
export {
|
||||
BackupService,
|
||||
ConfigService,
|
||||
DatabaseService,
|
||||
DialogService,
|
||||
DocumentService,
|
||||
ExtensionService,
|
||||
HotkeyService,
|
||||
HttpClientService,
|
||||
KeyBindingService,
|
||||
MigrationService,
|
||||
SelfUpdateService,
|
||||
StartupService,
|
||||
SystemService,
|
||||
TestService,
|
||||
ThemeService,
|
||||
TranslationService,
|
||||
WindowService
|
||||
};
|
||||
|
||||
export * from "./models.js";
|
||||
@@ -89,13 +89,21 @@ export function UpdateKeyBindingEnabled(id: number, enabled: boolean): Promise<v
|
||||
}
|
||||
|
||||
/**
|
||||
* UpdateKeyBindingKeys 更新快捷键绑定(根据操作系统自动判断更新哪个字段)
|
||||
* UpdateKeyBindingKeys 更新快捷键绑定
|
||||
*/
|
||||
export function UpdateKeyBindingKeys(id: number, key: string): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3432755175, id, key) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* UpdateKeyBindingPreventDefault 更新快捷键 PreventDefault 状态
|
||||
*/
|
||||
export function UpdateKeyBindingPreventDefault(id: number, preventDefault: boolean): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(202386744, id, preventDefault) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
// Private type creation functions
|
||||
const $$createType0 = models$0.KeyBinding.createFrom;
|
||||
const $$createType1 = $Create.Array($$createType0);
|
||||
|
||||
@@ -285,7 +285,7 @@ export class SelfUpdateResult {
|
||||
"error": string;
|
||||
|
||||
/**
|
||||
* 更新源(github/gitea)
|
||||
* 更新源(github)
|
||||
*/
|
||||
"source": string;
|
||||
|
||||
|
||||
4
frontend/components.d.ts
vendored
@@ -11,6 +11,8 @@ export {}
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
AccordionContainer: typeof import('./src/components/accordion/AccordionContainer.vue')['default']
|
||||
AccordionItem: typeof import('./src/components/accordion/AccordionItem.vue')['default']
|
||||
BlockLanguageSelector: typeof import('./src/components/toolbar/BlockLanguageSelector.vue')['default']
|
||||
DocumentSelector: typeof import('./src/components/toolbar/DocumentSelector.vue')['default']
|
||||
LinuxTitleBar: typeof import('./src/components/titlebar/LinuxTitleBar.vue')['default']
|
||||
@@ -22,6 +24,8 @@ declare module 'vue' {
|
||||
TabContainer: typeof import('./src/components/tabs/TabContainer.vue')['default']
|
||||
TabContextMenu: typeof import('./src/components/tabs/TabContextMenu.vue')['default']
|
||||
TabItem: typeof import('./src/components/tabs/TabItem.vue')['default']
|
||||
Toast: typeof import('./src/components/toast/Toast.vue')['default']
|
||||
ToastContainer: typeof import('./src/components/toast/ToastContainer.vue')['default']
|
||||
Toolbar: typeof import('./src/components/toolbar/Toolbar.vue')['default']
|
||||
WindowsTitleBar: typeof import('./src/components/titlebar/WindowsTitleBar.vue')['default']
|
||||
WindowTitleBar: typeof import('./src/components/titlebar/WindowTitleBar.vue')['default']
|
||||
|
||||
3730
frontend/package-lock.json
generated
@@ -22,19 +22,19 @@
|
||||
"app:generate": "cd .. && wails3 generate bindings -ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.20.0",
|
||||
"@codemirror/commands": "^6.10.1",
|
||||
"@codemirror/autocomplete": "^6.20.1",
|
||||
"@codemirror/commands": "^6.10.3",
|
||||
"@codemirror/lang-angular": "^0.1.4",
|
||||
"@codemirror/lang-cpp": "^6.0.3",
|
||||
"@codemirror/lang-css": "^6.3.1",
|
||||
"@codemirror/lang-go": "^6.0.1",
|
||||
"@codemirror/lang-html": "^6.4.11",
|
||||
"@codemirror/lang-java": "^6.0.2",
|
||||
"@codemirror/lang-javascript": "^6.2.4",
|
||||
"@codemirror/lang-javascript": "^6.2.5",
|
||||
"@codemirror/lang-json": "^6.0.2",
|
||||
"@codemirror/lang-less": "^6.0.2",
|
||||
"@codemirror/lang-lezer": "^6.0.2",
|
||||
"@codemirror/lang-liquid": "^6.3.1",
|
||||
"@codemirror/lang-liquid": "^6.3.2",
|
||||
"@codemirror/lang-markdown": "^6.5.0",
|
||||
"@codemirror/lang-php": "^6.0.2",
|
||||
"@codemirror/lang-python": "^6.2.1",
|
||||
@@ -43,66 +43,61 @@
|
||||
"@codemirror/lang-sql": "^6.10.0",
|
||||
"@codemirror/lang-vue": "^0.1.3",
|
||||
"@codemirror/lang-wast": "^6.0.2",
|
||||
"@codemirror/lang-yaml": "^6.1.2",
|
||||
"@codemirror/language": "^6.12.1",
|
||||
"@codemirror/lang-yaml": "^6.1.3",
|
||||
"@codemirror/language": "^6.12.3",
|
||||
"@codemirror/language-data": "^6.5.2",
|
||||
"@codemirror/legacy-modes": "^6.5.2",
|
||||
"@codemirror/lint": "^6.9.2",
|
||||
"@codemirror/search": "^6.5.11",
|
||||
"@codemirror/state": "^6.5.3",
|
||||
"@codemirror/view": "^6.39.6",
|
||||
"@codemirror/lint": "^6.9.5",
|
||||
"@codemirror/search": "^6.6.0",
|
||||
"@codemirror/state": "^6.6.0",
|
||||
"@codemirror/view": "^6.40.0",
|
||||
"@cospaia/prettier-plugin-clojure": "^0.0.2",
|
||||
"@lezer/highlight": "^1.2.3",
|
||||
"@lezer/lr": "^1.4.5",
|
||||
"@lezer/lr": "^1.4.8",
|
||||
"@prettier/plugin-xml": "^3.4.2",
|
||||
"@replit/codemirror-lang-svelte": "^6.0.0",
|
||||
"@toml-tools/lexer": "^1.0.1",
|
||||
"@toml-tools/parser": "^1.0.1",
|
||||
"@types/katex": "^0.16.7",
|
||||
"@zumer/snapdom": "^2.0.1",
|
||||
"@types/katex": "^0.16.8",
|
||||
"@zumer/snapdom": "^2.7.0",
|
||||
"codemirror": "^6.0.2",
|
||||
"codemirror-lang-elixir": "^4.0.0",
|
||||
"colors-named": "^1.0.4",
|
||||
"colors-named-hex": "^1.0.3",
|
||||
"codemirror-lang-elixir": "^4.0.1",
|
||||
"colors-named": "^1.0.5",
|
||||
"colors-named-hex": "^1.0.4",
|
||||
"groovy-beautify": "^0.0.17",
|
||||
"hsl-matcher": "^1.2.4",
|
||||
"java-parser": "^3.0.1",
|
||||
"katex": "^0.16.27",
|
||||
"linguist-languages": "^9.1.11",
|
||||
"marked": "^17.0.1",
|
||||
"mermaid": "^11.12.2",
|
||||
"php-parser": "^3.2.5",
|
||||
"katex": "^0.16.44",
|
||||
"linguist-languages": "^9.3.1",
|
||||
"marked": "^17.0.5",
|
||||
"mermaid": "^11.13.0",
|
||||
"php-parser": "^3.5.0",
|
||||
"pinia": "^3.0.4",
|
||||
"pinia-plugin-persistedstate": "^4.7.1",
|
||||
"prettier": "^3.7.4",
|
||||
"sass": "^1.97.1",
|
||||
"vue": "^3.5.26",
|
||||
"vue-i18n": "^11.2.7",
|
||||
"prettier": "^3.8.1",
|
||||
"sass": "^1.98.0",
|
||||
"vue": "^3.5.31",
|
||||
"vue-i18n": "^11.3.0",
|
||||
"vue-pick-colors": "^1.8.0",
|
||||
"vue-router": "^4.6.4"
|
||||
"vue-router": "^5.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.2",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@lezer/generator": "^1.8.0",
|
||||
"@types/node": "^25.0.3",
|
||||
"@vitejs/plugin-vue": "^6.0.3",
|
||||
"@wailsio/runtime": "^3.0.0-alpha.77",
|
||||
"@types/node": "^25.5.0",
|
||||
"@vitejs/plugin-vue": "^6.0.5",
|
||||
"@wailsio/runtime": "^3.0.0-alpha.79",
|
||||
"cross-env": "^10.1.0",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-plugin-vue": "^10.6.2",
|
||||
"globals": "^16.5.0",
|
||||
"happy-dom": "^20.0.11",
|
||||
"eslint": "^10.1.0",
|
||||
"eslint-plugin-vue": "^10.8.0",
|
||||
"globals": "^17.4.0",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.50.1",
|
||||
"unplugin-vue-components": "^30.0.0",
|
||||
"vite": "npm:rolldown-vite@latest",
|
||||
"vite-plugin-node-polyfills": "^0.24.0",
|
||||
"typescript-eslint": "^8.57.2",
|
||||
"unplugin-vue-components": "^32.0.0",
|
||||
"vite": "^7.3.1",
|
||||
"vite-plugin-node-polyfills": "^0.25.0",
|
||||
"vitepress": "^2.0.0-alpha.12",
|
||||
"vitest": "^4.0.16",
|
||||
"vue-eslint-parser": "^10.2.0",
|
||||
"vue-tsc": "^3.2.1"
|
||||
},
|
||||
"overrides": {
|
||||
"vite": "npm:rolldown-vite@latest"
|
||||
"vitest": "^4.1.2",
|
||||
"vue-eslint-parser": "^10.4.0",
|
||||
"vue-tsc": "^3.2.6"
|
||||
}
|
||||
}
|
||||
|
||||
1
frontend/public/images/blockImage.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1767366893329" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="106998" width="200" height="200"><path d="M344.064 36.571429v95.085714H155.428571a23.771429 23.771429 0 0 0-23.259428 19.017143l-0.512 4.754285-0.073143 509.952L306.176 505.417143a95.085714 95.085714 0 0 1 121.270857-5.997714l4.827429 3.876571 152.137143 130.413714 102.546285-102.4a95.085714 95.085714 0 0 1 119.003429-12.507428l5.266286 3.657143 81.042285 60.781714 0.073143-118.784 0.512-7.021714a47.542857 47.542857 0 0 1 94.061714 0l0.512 7.021714v404.114286c0 62.171429-47.762286 113.225143-108.617142 118.418285l-10.24 0.438857h-713.142858l-10.24-0.438857a118.857143 118.857143 0 0 1-108.105142-107.52L36.571429 868.498286v-713.142857l0.438857-10.24A118.857143 118.857143 0 0 1 155.428571 36.571429h188.708572z m26.331429 538.916571L131.657143 794.404571l0.073143 74.166858c0 11.483429 8.118857 21.065143 19.017143 23.259428l4.754285 0.512h713.142857a23.771429 23.771429 0 0 0 23.259429-19.017143l0.512-4.754285-0.073143-166.473143-138.093714-103.570286-97.353143 97.28 76.288 65.316571a47.542857 47.542857 0 0 1-58.002286 75.190858l-3.876571-2.925715-300.836572-257.901714zM649.069714 60.269714a47.542857 47.542857 0 0 1 3.291429 63.634286l-3.291429 3.657143-61.44 61.44 61.44 61.44a47.542857 47.542857 0 0 1 3.291429 63.634286l-3.291429 3.657142a47.542857 47.542857 0 0 1-63.634285 3.218286l-3.584-3.291428-95.085715-95.085715a47.542857 47.542857 0 0 1-3.291428-63.634285l3.291428-3.584 95.085715-95.085715a47.542857 47.542857 0 0 1 67.291428 0zM855.259429 57.051429l3.584 3.218285 95.085714 95.085715a47.542857 47.542857 0 0 1 3.291428 63.634285l-3.291428 3.657143-95.085714 95.085714a47.542857 47.542857 0 0 1-70.509715-63.634285l3.291429-3.657143 61.44-61.44-61.44-61.44a47.542857 47.542857 0 0 1-3.291429-63.634286l3.291429-3.657143a47.542857 47.542857 0 0 1 63.634286-3.218285zM344.210286 36.571429a47.542857 47.542857 0 0 1 7.021714 94.573714l-7.021714 0.512V36.571429z" p-id="106999" fill="#e0620d"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
1
frontend/public/images/colorSelector.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1767367606621" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15611" width="200" height="200"><path d="M511.966509 0.066982c96.036384 0 185.886507 26.451603 262.724147 72.443261L767.949763 68.670494l-127.948963 221.638835A254.788666 254.788666 0 0 0 511.966509 256.050237V0.066982z" fill="#E70212" p-id="15612"></path><path d="M767.949763 68.670494a509.577332 509.577332 0 0 1 191.304819 194.120635L955.329506 256.050237l-221.638835 127.991627a257.263171 257.263171 0 0 0-93.689871-93.689871L767.949763 68.670494z" fill="#EA6101" p-id="15613"></path><path d="M955.329506 256.050237A509.577332 509.577332 0 0 1 1023.933018 519.798317V512.033491h-255.983255a254.788666 254.788666 0 0 0-34.259092-127.991627l221.638835-127.991627z" fill="#F39801" p-id="15614"></path><path d="M1023.933018 512.033491c0 90.020778-23.251812 174.665907-64.038478 248.175765l-4.565034 7.80749-221.638835-127.948964c21.758577-37.672202 34.259092-81.402675 34.259092-128.034291v-0.042664L1023.933018 512.033491z" fill="#FCC902" p-id="15615"></path><path d="M733.690671 640.025118l221.638835 127.991628a509.66266 509.66266 0 0 1-179.52959 182.900035l-7.850153 4.479707-127.991627-221.638835A257.263171 257.263171 0 0 0 733.690671 640.025118z" fill="#FEF200" p-id="15616"></path><path d="M640.0008 733.757653L767.949763 955.396488A509.66266 509.66266 0 0 1 521.011251 1024H511.966509v-255.983254a254.788666 254.788666 0 0 0 128.034291-34.259093z" fill="#90C320" p-id="15617"></path><path d="M511.966509 768.016746v255.983254c-90.020778 0-174.665907-23.251812-248.175765-64.038477L255.983254 955.396488l127.991628-221.638835c37.672202 21.758577 81.402675 34.259092 128.034291 34.259093z" fill="#019A44" p-id="15618"></path><path d="M383.974882 733.757653l-127.991628 221.638835a509.66266 509.66266 0 0 1-182.900035-179.529589L68.603512 768.016746l221.638835-127.991628A257.263171 257.263171 0 0 0 383.974882 733.757653z" fill="#019E97" p-id="15619"></path><path d="M255.983254 512.033491c0 46.631616 12.457852 90.362089 34.259093 128.034291L68.603512 768.016746A509.66266 509.66266 0 0 1 0 521.078233V512.033491h255.983254z" fill="#0169B8" p-id="15620"></path><path d="M68.603512 256.050237l221.638835 127.991627A254.788666 254.788666 0 0 0 255.983254 512.033491H0c0-90.020778 23.251812-174.665907 64.038477-248.175765L68.603512 256.050237z" fill="#1C2089" p-id="15621"></path><path d="M262.681483 64.745418L255.983254 68.670494l127.991628 221.638835A257.263171 257.263171 0 0 0 290.242347 384.041864L68.603512 256.050237a509.577332 509.577332 0 0 1 194.120635-191.304819z" fill="#621988" p-id="15622"></path><path d="M519.731334 0.066982H511.966509v255.983255a254.788666 254.788666 0 0 0-128.034291 34.259092L255.983254 68.670494A509.577332 509.577332 0 0 1 519.731334 0.066982z" fill="#910783" p-id="15623"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
1
frontend/public/images/contextMenu.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1767366808037" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="103196" width="200" height="200"><path d="M878.921143 96c27.099429 0 49.078857 21.942857 49.078857 49.078857v349.842286c0 27.099429-21.942857 49.078857-49.078857 49.078857h-221.842286c-27.099429 0-49.078857-21.942857-49.078857-49.078857V145.078857c0-27.099429 21.942857-49.078857 49.078857-49.078857z m-14.921143 320h-192v64h192v-64z m0-128h-192v64h192v-64z m0-128h-192v64h192v-64zM384 309.321143A202.678857 202.678857 0 0 1 586.678857 512v213.321143a202.678857 202.678857 0 0 1-202.678857 202.678857H298.678857a202.678857 202.678857 0 0 1-202.678857-202.678857V512a202.678857 202.678857 0 0 1 202.678857-202.678857z m138.642286 298.642286H160v117.394285a138.678857 138.678857 0 0 0 131.547429 138.459429l7.131428 0.182857H384a138.678857 138.678857 0 0 0 138.678857-138.678857l-0.036571-117.357714z m-213.321143-234.642286h-10.642286A138.678857 138.678857 0 0 0 160 512v31.963429h149.321143v-170.642286z" p-id="103197" fill="#8992c8"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
1
frontend/public/images/fold.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1767366707029" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="100833" width="200" height="200"><path d="M533.333 54c16.138 0 29.75 12.016 31.753 28.03l17.828 142.569L896 224.6c17.496 0 31.713 14.042 31.996 31.47l0.004 0.53V939c0 17.673-14.327 32-32 32H469.735a31.94 31.94 0 0 1-3.051-0.102l-0.09-0.009c-10.834-0.868-19.434-6.86-24.45-14.999a31.766 31.766 0 0 1-3.374-7.39 32.348 32.348 0 0 1-1.405-7.246L419.752 800.4H128c-17.496 0-31.713-14.042-31.996-31.47L96 768.4V86c0-17.673 14.327-32 32-32zM864 288.599H590.917l13.33 106.6L704 395.2c17.673 0 32 14.327 32 32 0 17.496-14.042 31.713-31.47 31.996l-0.53 0.004-91.75-0.001 13.331 106.6L704 565.8c17.673 0 32 14.327 32 32 0 17.496-14.042 31.713-31.47 31.996l-0.53 0.004-70.416-0.001 16.548 132.332 0.287 2.298c0.024 0.188 0.046 0.376 0.066 0.565 0.986 9.171-2.002 17.793-7.523 24.232l-0.217 0.25L539.872 907H864v-618.4zM548.127 800.4H484.25l7.985 63.851 55.892-63.851zM505.085 118H160v618.4h287.598c0.302-0.004 0.603-0.004 0.904 0h133.913l-0.001-0.004L569.01 629.21l-3.386-27.076c-0.03-0.225-0.058-0.45-0.084-0.676l-21.256-169.977c-0.03-0.219-0.056-0.438-0.081-0.659L505.085 118zM448 565.8c17.673 0 32 14.327 32 32 0 17.496-14.042 31.713-31.47 31.996l-0.53 0.004H256c-17.673 0-32-14.327-32-32 0-17.496 14.042-31.713 31.47-31.996l0.53-0.004h192z m-21.333-170.6c17.673 0 32 14.327 32 32 0 17.496-14.042 31.713-31.471 31.996l-0.53 0.004H256c-17.673 0-32-14.327-32-32 0-17.496 14.042-31.713 31.47-31.996l0.53-0.004h170.667z m-21.334-170.6c17.673 0 32 14.327 32 32 0 17.496-14.041 31.713-31.47 31.996l-0.53 0.004H256c-17.673 0-32-14.327-32-32 0-17.496 14.042-31.713 31.47-31.996l0.53-0.004h149.333z" p-id="100834" fill="#1aaba8"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
1
frontend/public/images/highlightTrailingWhitespace.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1767367226608" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5585" width="200" height="200"><path d="M416 64H768v64h-64v704h64v64H448v-64h64V512H416a224 224 0 1 1 0-448zM576 832h64V128H576v704zM416 128H512v320H416a160 160 0 0 1 0-320z" fill="#a4579d" p-id="5586"></path></svg>
|
||||
|
After Width: | Height: | Size: 330 B |
1
frontend/public/images/highlightWhitespace.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1767366207284" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="95138" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M753.5 130.4v763.3h70.1v66.4H477v-66.4h70.1V547.1H443.9c-44.2 0-84.8-11.1-121.7-33.2-36.9-22.1-66.4-51.6-88.5-88.5s-33.2-76.8-33.2-119.8 11.1-83.6 33.2-121.7c22.1-38.1 51.6-67.6 88.5-88.5s77.4-31.3 121.7-31.3h379.8v66.4h-70.1z m-206.5 0H443.8c-49.2 0-90.3 17.2-123.5 51.6-33.2 34.4-49.8 75.6-49.8 123.5s16.6 88.5 49.8 121.7c33.2 33.2 74.4 49.8 123.5 49.8H547V130.4z m140.1 0H617v763.3h70.1V130.4z" p-id="95139"></path></svg>
|
||||
|
After Width: | Height: | Size: 758 B |
1
frontend/public/images/httpClient.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1767365935803" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="81379" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M896 64a64 64 0 0 1 64 64v613.952l-121.28-95.68a83.2 83.2 0 0 0-110.848 7.04l-6.016 6.784-5.312 7.616a83.2 83.2 0 0 0-12.032 34.56l-0.064 1.728H595.2A83.2 83.2 0 0 0 512 787.2v44.8H128a64 64 0 0 1-64-64V128a64 64 0 0 1 64-64h768z" fill="#B5E3CC" p-id="81380"></path><path d="M640 256a32 32 0 1 1 0 64h-32v224a32 32 0 0 1-64 0V320h-64v224a32 32 0 0 1-64 0V320H384a32 32 0 0 1 0-64h256z m160 0a96 96 0 0 1 0 192H768v96a32 32 0 0 1-26.24 31.488L736 576a32 32 0 0 1-32-32v-256a32 32 0 0 1 32-32h64zM768 384h32a32 32 0 1 0 0-64H768v64zM288 256a32 32 0 0 1 32 32v256a32 32 0 0 1-64 0V448H192v96a32 32 0 0 1-64 0v-256a32 32 0 0 1 64 0V384h64V288a32 32 0 0 1 32-32zM772.096 699.712a19.2 19.2 0 0 0-4.096 11.904v56.32L595.2 768a19.2 19.2 0 0 0-19.2 19.2v89.6a19.2 19.2 0 0 0 19.2 19.2H768v56.384a19.2 19.2 0 0 0 31.104 15.104l152.576-120.384a19.2 19.2 0 0 0 0-30.208l-152.576-120.32a19.2 19.2 0 0 0-27.008 3.136z" fill="#129250" p-id="81381"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
1
frontend/public/images/hyperlink.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1767365120766" class="icon" viewBox="0 0 1160 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12589" xmlns:xlink="http://www.w3.org/1999/xlink" width="226.5625" height="200"><path d="M398.5408 736.142222l435.768889-384.568889a44.009244 44.009244 0 0 0 0-67.675022 59.483022 59.483022 0 0 0-76.731733 0l-435.768889 384.568889a43.872711 43.872711 0 0 0 0 67.675022 59.164444 59.164444 0 0 0 76.731733 0z m143.7696-66.582755a39.867733 39.867733 0 0 1 9.102222 25.031111 38.866489 38.866489 0 0 1-13.653333 30.128355L308.929422 926.151111a52.8384 52.8384 0 0 1-68.266666 0L111.047111 811.781689a39.139556 39.139556 0 0 1 0-60.302222l228.192711-201.9328a50.335289 50.335289 0 0 1 34.178845-11.969423 53.748622 53.748622 0 0 1 22.755555 4.551112l69.632-60.848356c-57.617067-41.824711-141.7216-38.365867-194.696533 7.964444L42.052267 690.631111c-56.069689 50.3808-56.069689 131.117511 0 181.4528l130.207289 114.323911a158.651733 158.651733 0 0 0 204.8 0L605.297778 785.066667c54.613333-48.196267 57.025422-125.474133 5.779911-176.355556zM1117.980444 151.916089L988.410311 37.546667c-56.797867-50.062222-148.821333-50.062222-205.664711 0L554.552889 238.933333c-52.519822 46.739911-56.388267 120.968533-9.102222 171.804445L614.4 349.889422a41.688178 41.688178 0 0 1-5.142756-20.48 38.866489 38.866489 0 0 1 13.653334-30.128355l228.875378-201.386667a50.335289 50.335289 0 0 1 34.178844-12.515556 52.383289 52.383289 0 0 1 34.178844 12.515556l129.570134 114.323911a39.139556 39.139556 0 0 1 0 60.302222l-228.192711 201.9328a50.335289 50.335289 0 0 1-34.178845 11.969423 57.7536 57.7536 0 0 1-28.353422-7.418312l-68.266667 60.302223c57.389511 45.101511 144.543289 43.099022 199.202134-4.551111l228.192711-201.9328a117.418667 117.418667 0 0 0 0-180.906667z" fill="#1A97F0" p-id="12590"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
1
frontend/public/images/lineNumbers.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1767366749042" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="102155" width="200" height="200"><path d="M426.7008 256c0-23.552 19.0976-42.6496 42.6496-42.6496h384a42.6496 42.6496 0 1 1 0 85.2992h-384A42.6496 42.6496 0 0 1 426.7008 256zM426.7008 512c0-23.552 19.0976-42.6496 42.6496-42.6496h384a42.6496 42.6496 0 1 1 0 85.2992h-384A42.6496 42.6496 0 0 1 426.7008 512zM469.2992 768c0-23.552 19.1488-42.6496 42.7008-42.6496h341.2992a42.6496 42.6496 0 0 1 0 85.2992H512A42.6496 42.6496 0 0 1 469.2992 768zM256 640a42.6496 42.6496 0 0 0-42.6496 42.6496 42.6496 42.6496 0 0 1-85.3504 0 128 128 0 1 1 256 0c0 25.856-11.264 45.6192-22.4256 59.8528-8.0384 10.1888-18.5856 20.48-26.8288 28.5184l-5.888 5.8368a42.4448 42.4448 0 0 1-2.8672 2.56l-37.4784 31.232h52.8384a42.6496 42.6496 0 1 1 0 85.3504H170.6496a42.6496 42.6496 0 0 1-27.2896-75.4688l126.5152-105.472 6.5024-6.3488 0.4096-0.4096 7.2704-7.168c4.608-4.608 7.936-8.192 10.3936-11.3664a28.672 28.672 0 0 0 3.9424-6.0928c0.3072-0.7168 0.256-0.9728 0.256-1.024A42.6496 42.6496 0 0 0 256 640zM272.3328 131.2256a42.6496 42.6496 0 0 1 26.3168 39.424v256a42.6496 42.6496 0 0 1-85.2992 0V273.664l-12.4928 12.4928a42.6496 42.6496 0 1 1-60.3648-60.3136l85.3504-85.3504a42.6496 42.6496 0 0 1 46.4896-9.216z" p-id="102156" fill="#87c38f"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
1
frontend/public/images/markdown.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1767365625477" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="51837" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M98.649225 16.619164h734.636904v990.162784h-734.636904z" fill="#FF8A90" p-id="51838"></path><path d="M881.197231 128.411736h-31.940735V32.589531c0-17.642265-14.29847-31.940735-31.940735-31.940735H114.619592c-17.642265 0-31.940735 14.29847-31.940735 31.940735v958.222049c0 17.642265 14.29847 31.940735 31.940735 31.940735h702.696169c17.642265 0 31.940735-14.29847 31.940735-31.940735V415.878351h31.940735c17.642265 0 31.940735-14.29847 31.940735-31.940735V160.352471c0-17.642265-14.29847-31.940735-31.940735-31.940735z m-63.88147 0h-383.288819c-17.642265 0-31.940735 14.29847-31.940735 31.940735V383.937616c0 17.642265 14.29847 31.940735 31.940735 31.940735h383.288819v574.933229H114.619592v-958.222049h702.696169v95.822205z" fill="#2B3139" p-id="51839"></path><path d="M434.026942 160.352471h447.170289V383.937616h-447.170289z" fill="#FFFFFF" p-id="51840"></path><path d="M544.808889 215.175748h49.969783l16.294765 67.025636h0.311922l16.294765-67.025636h49.957306v113.963544h-33.18842v-73.101861h-0.336875l-19.80076 73.101861h-26.176431l-19.788283-73.101861h-0.336875v73.101861h-33.200897zM698.947889 215.175748h57.618092c37.992007 0 51.392143 28.097865 51.392143 56.819573 0 34.947656-18.490691 57.143971-58.241934 57.143971h-50.768301v-113.963544z" fill="#2B3139" p-id="51841"></path><path d="M726.584111 299.918511h13.712058c21.85944 0 25.065991-17.717126 25.065991-28.409787 0-7.174189-2.233356-27.112194-27.611269-27.112194h-11.154303v55.521981z" fill="#FFFFFF" p-id="51842"></path><path d="M630.512369 785.841895l-94.998733-129.709328h57.318647V544.888976h76.445658v111.243591h57.30617L630.487415 785.841895z" fill="#1EB9B0" p-id="51843"></path><path d="M508.139428 785.754557l-76.445657 0.087338v-120.439029l-57.331124 77.194268-57.343601-77.194268v120.439029H240.573389V544.976313h76.445657l57.343601 80.288528 57.331124-80.288528 76.445657-0.087337z" fill="#1EB9B0" p-id="51844"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
1
frontend/public/images/minimap.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1767367796950" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="18541" width="200" height="200"><path d="M880.489336 512H993.882278v455.107765A56.786824 56.786824 0 0 1 937.185807 1024h-56.681412V512z" fill="#4AC3BB" fill-opacity=".603" p-id="18542"></path><path d="M143.510513 1024a113.182118 113.182118 0 0 1-80.188235-33.325176A113.980235 113.980235 0 0 1 30.117572 910.215529V113.784471C30.117572 83.606588 42.059219 54.663529 63.322278 33.325176A113.182118 113.182118 0 0 1 143.510513 0h510.238118C778.977807 0 880.489336 101.872941 880.489336 227.553882v739.553883A56.786824 56.786824 0 0 0 937.185807 1024H143.510513z" fill="#4AC3BB" p-id="18543"></path><path d="M575.653572 335.329882c64.331294 65.024 66.529882 169.758118 4.954353 237.477647l-4.954353 5.240471-98.063059 122.473412a28.175059 28.175059 0 0 1-21.985882 10.586353 28.175059 28.175059 0 0 1-21.985883-10.586353l-98.078117-122.473412-4.412236-4.638118c-63.503059-68.487529-60.777412-175.841882 6.098824-240.941176a168.478118 168.478118 0 0 1 238.426353 2.861176z m-120.048941 64.150589a56.470588 56.470588 0 0 0-49.016471 28.611764 57.735529 57.735529 0 0 0 0 57.193412 56.470588 56.470588 0 0 0 49.016471 28.611765c31.247059-0.015059 56.576-25.630118 56.576-57.22353 0-31.578353-25.328941-57.193412-56.576-57.193411z" fill="#FFFFFF" fill-opacity=".95" p-id="18544"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
1
frontend/public/images/rainbowBrackets.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1767364585914" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8006" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M960 128c0-35.36-28.704-64.224-63.936-61.728A896.48 896.48 0 0 0 326.4 326.4a896.48 896.48 0 0 0-260.128 569.664C63.776 931.296 92.64 960 128 960c0 0 19.52 0.576 32 0 64-272 84.48-383.68 234.24-533.76C544.32 276.16 752 192 960 160V128z" fill="#F8312F" p-id="8007"></path><path d="M960 256V160a800 800 0 0 0-565.76 234.24A801.344 801.344 0 0 0 160 960h96c32-128 106.24-333.76 238.08-465.92C625.92 362.24 752 304 960 256z" fill="#FFB02E" p-id="8008"></path><path d="M960 256v96c-160 16-284.16 96-398.08 209.92C448 676.16 384 800 352 960H256c0-186.56 74.24-365.76 206.08-497.92A704.32 704.32 0 0 1 960 256z" fill="#FFF478" p-id="8009"></path><path d="M630.08 630.08C534.08 726.08 480 832 448 960h-96c0-161.28 64-315.84 177.92-430.08C643.84 416 798.72 352 960 352v96c-144 32-233.92 86.08-329.92 182.08z" fill="#00D26A" p-id="8010"></path><path d="M960 544v-96c-135.68 0-265.92 54.08-361.92 150.08-96 96-150.08 226.24-150.08 361.92h96c0-110.4 43.84-216.32 121.92-294.08C744 587.84 849.92 544 960 544z" fill="#3F5FFF" p-id="8011"></path><path d="M960 576c0 35.36-28.928 63.36-63.584 70.336a320.384 320.384 0 0 0-250.112 250.08C639.36 931.04 611.392 960 576 960h-32c0-110.4 43.84-216.32 121.92-294.08C744 587.84 849.92 544 960 544v32z" fill="#8D65C5" p-id="8012"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
1
frontend/public/images/search.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1767366992790" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="111373" width="200" height="200"><path d="M581.973333 846.933333a380.8 380.8 0 1 1 380.8-380.8A381.226667 381.226667 0 0 1 581.973333 846.933333z m0-688a307.2 307.2 0 1 0 307.2 307.2 307.413333 307.413333 0 0 0-307.2-307.2z" fill="#FA6302" p-id="111374"></path><path d="M146.56 938.666667a36.906667 36.906667 0 0 1-26.026667-64l192-190.933334a36.906667 36.906667 0 0 1 52.053334 52.266667l-192 192a37.333333 37.333333 0 0 1-26.026667 10.666667z" fill="#43D7B4" p-id="111375"></path><path d="M470.826667 274.773333m-49.066667 0a49.066667 49.066667 0 1 0 98.133333 0 49.066667 49.066667 0 1 0-98.133333 0Z" fill="#43D7B4" p-id="111376"></path><path d="M312.106667 684.8l-23.68 23.466667A388.693333 388.693333 0 0 0 341.333333 760.32l23.466667-23.253333a36.906667 36.906667 0 0 0-52.053333-52.266667z" fill="#425300" p-id="111377"></path></svg>
|
||||
|
After Width: | Height: | Size: 956 B |
1
frontend/public/images/translator.svg
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
@@ -6,7 +6,10 @@ import {useKeybindingStore} from '@/stores/keybindingStore';
|
||||
import {useThemeStore} from '@/stores/themeStore';
|
||||
import {useUpdateStore} from '@/stores/updateStore';
|
||||
import WindowTitleBar from '@/components/titlebar/WindowTitleBar.vue';
|
||||
import ToastContainer from '@/components/toast/ToastContainer.vue';
|
||||
import {useTranslationStore} from "@/stores/translationStore";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import {LanguageType} from "../bindings/voidraft/internal/models";
|
||||
|
||||
const configStore = useConfigStore();
|
||||
const systemStore = useSystemStore();
|
||||
@@ -14,6 +17,7 @@ const keybindingStore = useKeybindingStore();
|
||||
const themeStore = useThemeStore();
|
||||
const updateStore = useUpdateStore();
|
||||
const translationStore = useTranslationStore();
|
||||
const {locale} = useI18n();
|
||||
|
||||
onBeforeMount(async () => {
|
||||
// 并行初始化配置、系统信息和快捷键配置
|
||||
@@ -22,9 +26,8 @@ onBeforeMount(async () => {
|
||||
systemStore.initSystemInfo(),
|
||||
keybindingStore.loadKeyBindings(),
|
||||
]);
|
||||
|
||||
// 初始化语言和主题
|
||||
await configStore.initLanguage();
|
||||
|
||||
locale.value = configStore.config.appearance.language || LanguageType.LangEnUS;
|
||||
await themeStore.initTheme();
|
||||
await translationStore.loadTranslators();
|
||||
|
||||
@@ -39,6 +42,7 @@ onBeforeMount(async () => {
|
||||
<div class="app-content">
|
||||
<router-view/>
|
||||
</div>
|
||||
<ToastContainer/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
1
frontend/src/assets/images/translator.svg
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
@@ -1,20 +1,16 @@
|
||||
import {
|
||||
AppConfig,
|
||||
AuthMethod,
|
||||
SyncTarget,
|
||||
KeyBindingType,
|
||||
LanguageType,
|
||||
SystemThemeType,
|
||||
TabType,
|
||||
UpdateSourceType
|
||||
} from '@/../bindings/voidraft/internal/models/models';
|
||||
import {FONT_OPTIONS} from './fonts';
|
||||
|
||||
export type NumberConfigKey = 'fontSize' | 'tabSize' | 'lineHeight';
|
||||
export type ConfigSection = 'general' | 'editing' | 'appearance' | 'updates' | 'backup';
|
||||
|
||||
// 统一配置键映射(平级展开)
|
||||
export const CONFIG_KEY_MAP = {
|
||||
// general
|
||||
alwaysOnTop: 'general.alwaysOnTop',
|
||||
dataPath: 'general.dataPath',
|
||||
enableSystemTray: 'general.enableSystemTray',
|
||||
@@ -24,7 +20,8 @@ export const CONFIG_KEY_MAP = {
|
||||
enableWindowSnap: 'general.enableWindowSnap',
|
||||
enableLoadingAnimation: 'general.enableLoadingAnimation',
|
||||
enableTabs: 'general.enableTabs',
|
||||
// editing
|
||||
enableMemoryMonitor: 'general.enableMemoryMonitor',
|
||||
|
||||
fontSize: 'editing.fontSize',
|
||||
fontFamily: 'editing.fontFamily',
|
||||
fontWeight: 'editing.fontWeight',
|
||||
@@ -34,33 +31,35 @@ export const CONFIG_KEY_MAP = {
|
||||
tabType: 'editing.tabType',
|
||||
keymapMode: 'editing.keymapMode',
|
||||
autoSaveDelay: 'editing.autoSaveDelay',
|
||||
// appearance
|
||||
|
||||
language: 'appearance.language',
|
||||
systemTheme: 'appearance.systemTheme',
|
||||
currentTheme: 'appearance.currentTheme',
|
||||
// updates
|
||||
version: 'updates.version',
|
||||
|
||||
autoUpdate: 'updates.autoUpdate',
|
||||
primarySource: 'updates.primarySource',
|
||||
backupSource: 'updates.backupSource',
|
||||
backupBeforeUpdate: 'updates.backupBeforeUpdate',
|
||||
updateTimeout: 'updates.updateTimeout',
|
||||
github: 'updates.github',
|
||||
gitea: 'updates.gitea',
|
||||
// backup
|
||||
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',
|
||||
|
||||
sync_target: 'sync.target',
|
||||
git_enabled: 'sync.git.enabled',
|
||||
git_auto_sync: 'sync.git.auto_sync',
|
||||
git_sync_interval: 'sync.git.sync_interval',
|
||||
git_repo_url: 'sync.git.repo_url',
|
||||
git_auth_method: 'sync.git.auth_method',
|
||||
git_username: 'sync.git.username',
|
||||
git_password: 'sync.git.password',
|
||||
git_token: 'sync.git.token',
|
||||
git_ssh_key_path: 'sync.git.ssh_key_path',
|
||||
git_ssh_key_passphrase: 'sync.git.ssh_key_passphrase',
|
||||
localfs_enabled: 'sync.localfs.enabled',
|
||||
localfs_auto_sync: 'sync.localfs.auto_sync',
|
||||
localfs_sync_interval: 'sync.localfs.sync_interval',
|
||||
localfs_root_path: 'sync.localfs.root_path',
|
||||
} as const;
|
||||
|
||||
export type ConfigKey = keyof typeof CONFIG_KEY_MAP;
|
||||
export type NumberConfigKey = 'fontSize' | 'tabSize' | 'lineHeight';
|
||||
|
||||
// 配置限制
|
||||
export const CONFIG_LIMITS = {
|
||||
@@ -88,6 +87,7 @@ export const DEFAULT_CONFIG: AppConfig = {
|
||||
enableWindowSnap: true,
|
||||
enableLoadingAnimation: true,
|
||||
enableTabs: false,
|
||||
enableMemoryMonitor: true,
|
||||
},
|
||||
editing: {
|
||||
fontSize: CONFIG_LIMITS.fontSize.default,
|
||||
@@ -108,34 +108,36 @@ export const DEFAULT_CONFIG: AppConfig = {
|
||||
updates: {
|
||||
version: "1.0.0",
|
||||
autoUpdate: true,
|
||||
primarySource: UpdateSourceType.UpdateSourceGithub,
|
||||
backupSource: UpdateSourceType.UpdateSourceGitea,
|
||||
backupBeforeUpdate: true,
|
||||
updateTimeout: 30,
|
||||
updateTimeout: 120,
|
||||
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,
|
||||
sync: {
|
||||
target: SyncTarget.SyncTargetGit,
|
||||
git: {
|
||||
enabled: false,
|
||||
auto_sync: false,
|
||||
sync_interval: 60,
|
||||
repo_url: '',
|
||||
auth_method: AuthMethod.UserPass,
|
||||
username: '',
|
||||
password: '',
|
||||
token: '',
|
||||
ssh_key_path: '',
|
||||
ssh_key_passphrase: '',
|
||||
},
|
||||
localfs: {
|
||||
enabled: false,
|
||||
auto_sync: false,
|
||||
sync_interval: 60,
|
||||
root_path: '',
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
version: '1.0.0',
|
||||
lastUpdated: new Date().toString(),
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// 编辑器实例管理
|
||||
export const EDITOR_CONFIG = {
|
||||
/** 最多缓存的编辑器实例数量 */
|
||||
MAX_INSTANCES: 5,
|
||||
MAX_INSTANCES: 10,
|
||||
/** 语法树缓存过期时间(毫秒) */
|
||||
SYNTAX_TREE_CACHE_TIMEOUT: 30000,
|
||||
/** 加载状态延迟时间(毫秒) */
|
||||
|
||||
@@ -23,7 +23,7 @@ const languages = [
|
||||
{
|
||||
name: 'C',
|
||||
aliases: ['c'],
|
||||
parsers: ['c'],
|
||||
parsers: [parserName],
|
||||
extensions: ['.c', '.h'],
|
||||
filenames: ['*.c', '*.h'],
|
||||
aceMode: 'c_cpp',
|
||||
@@ -34,7 +34,7 @@ const languages = [
|
||||
{
|
||||
name: 'C++',
|
||||
aliases: ['cpp', 'cxx', 'cc'],
|
||||
parsers: ['cpp'],
|
||||
parsers: [parserName],
|
||||
extensions: ['.cpp', '.cxx', '.cc', '.hpp', '.hxx', '.hh', '.C', '.H'],
|
||||
filenames: ['*.cpp', '*.cxx', '*.cc', '*.hpp', '*.hxx', '*.hh', '*.C', '*.H'],
|
||||
aceMode: 'c_cpp',
|
||||
@@ -45,7 +45,7 @@ const languages = [
|
||||
{
|
||||
name: 'Objective-C',
|
||||
aliases: ['objc', 'objectivec'],
|
||||
parsers: ['objective-c'],
|
||||
parsers: [parserName],
|
||||
extensions: ['.m'],
|
||||
filenames: ['*.m'],
|
||||
aceMode: 'objectivec',
|
||||
@@ -56,7 +56,7 @@ const languages = [
|
||||
{
|
||||
name: 'Objective-C++',
|
||||
aliases: ['objcpp', 'objectivecpp'],
|
||||
parsers: ['objective-cpp'],
|
||||
parsers: [parserName],
|
||||
extensions: ['.mm'],
|
||||
filenames: ['*.mm'],
|
||||
aceMode: 'objectivec',
|
||||
@@ -67,7 +67,7 @@ const languages = [
|
||||
{
|
||||
name: 'C#',
|
||||
aliases: ['csharp', 'cs'],
|
||||
parsers: ['cs'],
|
||||
parsers: [parserName],
|
||||
extensions: ['.cs'],
|
||||
filenames: ['*.cs'],
|
||||
aceMode: 'csharp',
|
||||
@@ -78,7 +78,7 @@ const languages = [
|
||||
{
|
||||
name: 'Java',
|
||||
aliases: ['java'],
|
||||
parsers: ['java'],
|
||||
parsers: [parserName],
|
||||
extensions: ['.java'],
|
||||
filenames: ['*.java'],
|
||||
aceMode: 'java',
|
||||
@@ -89,7 +89,7 @@ const languages = [
|
||||
{
|
||||
name: 'Protocol Buffer',
|
||||
aliases: ['protobuf', 'proto'],
|
||||
parsers: ['proto'],
|
||||
parsers: [parserName],
|
||||
extensions: ['.proto'],
|
||||
filenames: ['*.proto'],
|
||||
aceMode: 'protobuf',
|
||||
@@ -158,17 +158,7 @@ const clangPrinter: Printer<string> = {
|
||||
|
||||
// Helper function to determine clang-format style
|
||||
function getClangStyle(options: any): string {
|
||||
// You can extend this to support more options
|
||||
const style = options.clangStyle || 'LLVM';
|
||||
|
||||
// Support common styles
|
||||
const validStyles = ['LLVM', 'Google', 'Chromium', 'Mozilla', 'WebKit', 'Microsoft', 'GNU'];
|
||||
if (validStyles.includes(style)) {
|
||||
return style;
|
||||
}
|
||||
|
||||
// Default to LLVM style
|
||||
return 'LLVM';
|
||||
return options.clangStyle || 'LLVM';
|
||||
}
|
||||
|
||||
// Plugin options
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import type { IToken } from "java-parser";
|
||||
import { type AstPath } from "prettier";
|
||||
import { type JavaNode, type JavaNonTerminal, type JavaParserOptions } from "./printers/helpers.js";
|
||||
export declare function determineFormatterOffOnRanges(cst: JavaNonTerminal): void;
|
||||
export declare function isFullyBetweenFormatterOffOn(path: AstPath<JavaNode>): boolean;
|
||||
export declare function canAttachComment(node: JavaNode): boolean;
|
||||
export declare function handleLineComment(commentNode: JavaComment, _: string, options: JavaParserOptions): boolean;
|
||||
export declare function handleRemainingComment(commentNode: JavaComment): boolean;
|
||||
export type JavaComment = IToken & {
|
||||
value: string;
|
||||
leading: boolean;
|
||||
trailing: boolean;
|
||||
printed: boolean;
|
||||
enclosingNode?: JavaNonTerminal;
|
||||
precedingNode?: JavaNonTerminal;
|
||||
followingNode?: JavaNonTerminal;
|
||||
};
|
||||
@@ -1,199 +0,0 @@
|
||||
import { util } from "prettier";
|
||||
import parser from "./parser.js";
|
||||
import { isEmptyStatement, isNonTerminal, isTerminal } from "./printers/helpers.js";
|
||||
const formatterOffOnRangesByCst = new WeakMap();
|
||||
export function determineFormatterOffOnRanges(cst) {
|
||||
const { comments } = cst;
|
||||
if (!comments) {
|
||||
return;
|
||||
}
|
||||
const ranges = comments
|
||||
.filter(({ image }) => /^(\/\/\s*@formatter:(off|on)\s*|\/\*\s*@formatter:(off|on)\s*\*\/)$/.test(image))
|
||||
.reduce((ranges, { image, startOffset }) => {
|
||||
const previous = ranges.at(-1);
|
||||
if (image.endsWith("off")) {
|
||||
if ((previous === null || previous === void 0 ? void 0 : previous.on) !== Infinity) {
|
||||
ranges.push({ off: startOffset, on: Infinity });
|
||||
}
|
||||
}
|
||||
else if ((previous === null || previous === void 0 ? void 0 : previous.on) === Infinity) {
|
||||
previous.on = startOffset;
|
||||
}
|
||||
return ranges;
|
||||
}, new Array());
|
||||
formatterOffOnRangesByCst.set(cst, ranges);
|
||||
}
|
||||
export function isFullyBetweenFormatterOffOn(path) {
|
||||
var _a;
|
||||
const { node, root } = path;
|
||||
const start = parser.locStart(node);
|
||||
const end = parser.locEnd(node);
|
||||
return (((_a = formatterOffOnRangesByCst
|
||||
.get(root)) === null || _a === void 0 ? void 0 : _a.some(range => range.off < start && end < range.on)) === true);
|
||||
}
|
||||
export function canAttachComment(node) {
|
||||
var _a, _b, _c;
|
||||
if (isTerminal(node)) {
|
||||
const { name, CATEGORIES } = node.tokenType;
|
||||
return (name === "Identifier" ||
|
||||
(CATEGORIES === null || CATEGORIES === void 0 ? void 0 : CATEGORIES.find(({ name }) => name === "BinaryOperator")) !== undefined);
|
||||
}
|
||||
const { children, name } = node;
|
||||
switch (name) {
|
||||
case "argumentList":
|
||||
case "blockStatements":
|
||||
case "emptyStatement":
|
||||
case "enumBodyDeclarations":
|
||||
return false;
|
||||
case "annotationInterfaceMemberDeclaration":
|
||||
case "classMemberDeclaration":
|
||||
case "interfaceMemberDeclaration":
|
||||
case "methodBody":
|
||||
return !children.Semicolon;
|
||||
case "blockStatement":
|
||||
return !children.statement || !isEmptyStatement(children.statement[0]);
|
||||
case "classBodyDeclaration":
|
||||
return !((_a = children.classMemberDeclaration) === null || _a === void 0 ? void 0 : _a[0].children.Semicolon);
|
||||
case "recordBodyDeclaration":
|
||||
return !((_c = (_b = children.classBodyDeclaration) === null || _b === void 0 ? void 0 : _b[0].children.classMemberDeclaration) === null || _c === void 0 ? void 0 : _c[0].children.Semicolon);
|
||||
case "statement":
|
||||
return !isEmptyStatement(node);
|
||||
case "statementWithoutTrailingSubstatement":
|
||||
return !children.emptyStatement;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
export function handleLineComment(commentNode, _, options) {
|
||||
return [
|
||||
handleBinaryExpressionComments,
|
||||
handleFqnOrRefTypeComments,
|
||||
handleIfStatementComments,
|
||||
handleJumpStatementComments,
|
||||
handleLabeledStatementComments,
|
||||
handleNameComments
|
||||
].some(fn => fn(commentNode, options));
|
||||
}
|
||||
export function handleRemainingComment(commentNode) {
|
||||
return [
|
||||
handleFqnOrRefTypeComments,
|
||||
handleMethodDeclaratorComments,
|
||||
handleNameComments,
|
||||
handleJumpStatementComments
|
||||
].some(fn => fn(commentNode));
|
||||
}
|
||||
function handleBinaryExpressionComments(commentNode, options) {
|
||||
const { enclosingNode, precedingNode, followingNode } = commentNode;
|
||||
if (enclosingNode &&
|
||||
isNonTerminal(enclosingNode) &&
|
||||
enclosingNode.name === "binaryExpression") {
|
||||
if (isBinaryOperator(followingNode)) {
|
||||
if (options.experimentalOperatorPosition === "start") {
|
||||
util.addLeadingComment(followingNode, commentNode);
|
||||
}
|
||||
else {
|
||||
util.addTrailingComment(followingNode, commentNode);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (options.experimentalOperatorPosition === "start" &&
|
||||
isBinaryOperator(precedingNode)) {
|
||||
util.addLeadingComment(precedingNode, commentNode);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function handleFqnOrRefTypeComments(commentNode) {
|
||||
const { enclosingNode, followingNode } = commentNode;
|
||||
if (enclosingNode &&
|
||||
isNonTerminal(enclosingNode) &&
|
||||
enclosingNode.name === "fqnOrRefType" &&
|
||||
followingNode) {
|
||||
util.addLeadingComment(followingNode, commentNode);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function handleIfStatementComments(commentNode) {
|
||||
const { enclosingNode, precedingNode } = commentNode;
|
||||
if (enclosingNode &&
|
||||
isNonTerminal(enclosingNode) &&
|
||||
enclosingNode.name === "ifStatement" &&
|
||||
precedingNode &&
|
||||
isNonTerminal(precedingNode) &&
|
||||
precedingNode.name === "statement") {
|
||||
util.addDanglingComment(enclosingNode, commentNode, undefined);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function handleJumpStatementComments(commentNode) {
|
||||
const { enclosingNode, precedingNode, followingNode } = commentNode;
|
||||
if (enclosingNode &&
|
||||
!precedingNode &&
|
||||
!followingNode &&
|
||||
isNonTerminal(enclosingNode) &&
|
||||
["breakStatement", "continueStatement", "returnStatement"].includes(enclosingNode.name)) {
|
||||
util.addTrailingComment(enclosingNode, commentNode);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function handleLabeledStatementComments(commentNode) {
|
||||
const { enclosingNode, precedingNode } = commentNode;
|
||||
if (enclosingNode &&
|
||||
precedingNode &&
|
||||
isNonTerminal(enclosingNode) &&
|
||||
enclosingNode.name === "labeledStatement" &&
|
||||
isTerminal(precedingNode) &&
|
||||
precedingNode.tokenType.name === "Identifier") {
|
||||
util.addLeadingComment(precedingNode, commentNode);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function handleMethodDeclaratorComments(commentNode) {
|
||||
const { enclosingNode } = commentNode;
|
||||
if (enclosingNode &&
|
||||
isNonTerminal(enclosingNode) &&
|
||||
enclosingNode.name === "methodDeclarator" &&
|
||||
!enclosingNode.children.receiverParameter &&
|
||||
!enclosingNode.children.formalParameterList &&
|
||||
enclosingNode.children.LBrace[0].startOffset < commentNode.startOffset &&
|
||||
commentNode.startOffset < enclosingNode.children.RBrace[0].startOffset) {
|
||||
util.addDanglingComment(enclosingNode, commentNode, undefined);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function handleNameComments(commentNode) {
|
||||
const { enclosingNode, precedingNode } = commentNode;
|
||||
if (enclosingNode &&
|
||||
precedingNode &&
|
||||
isNonTerminal(enclosingNode) &&
|
||||
isTerminal(precedingNode) &&
|
||||
precedingNode.tokenType.name === "Identifier" &&
|
||||
[
|
||||
"ambiguousName",
|
||||
"classOrInterfaceTypeToInstantiate",
|
||||
"expressionName",
|
||||
"moduleDeclaration",
|
||||
"moduleName",
|
||||
"packageDeclaration",
|
||||
"packageName",
|
||||
"packageOrTypeName",
|
||||
"typeName"
|
||||
].includes(enclosingNode.name)) {
|
||||
util.addTrailingComment(precedingNode, commentNode);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function isBinaryOperator(node) {
|
||||
var _a;
|
||||
return (node !== undefined &&
|
||||
(isNonTerminal(node)
|
||||
? node.name === "shiftOperator"
|
||||
: (_a = node.tokenType.CATEGORIES) === null || _a === void 0 ? void 0 : _a.some(({ name }) => name === "BinaryOperator")));
|
||||
}
|
||||
563
frontend/src/common/prettier/plugins/java/index.d.ts
vendored
@@ -1,563 +0,0 @@
|
||||
import type { JavaNode } from "./printers/helpers.js";
|
||||
declare const _default: {
|
||||
languages: {
|
||||
name: string;
|
||||
parsers: "java"[];
|
||||
group: string;
|
||||
tmScope: string;
|
||||
aceMode: string;
|
||||
codemirrorMode: string;
|
||||
codemirrorMimeType: string;
|
||||
extensions: string[];
|
||||
linguistLanguageId: number;
|
||||
vscodeLanguageIds: string[];
|
||||
}[];
|
||||
parsers: {
|
||||
java: {
|
||||
parse(text: string, options: import("./printers/helpers.js").JavaParserOptions): import("./printers/helpers.js").JavaNonTerminal;
|
||||
astFormat: string;
|
||||
hasPragma(text: string): boolean;
|
||||
locStart(node: JavaNode): number;
|
||||
locEnd(node: JavaNode): number;
|
||||
};
|
||||
};
|
||||
printers: {
|
||||
java: {
|
||||
print(path: import("prettier").AstPath<import("java-parser").ArrayInitializerCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").VariableInitializerListCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").BlockCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").BlockStatementsCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").LocalVariableDeclarationStatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").LocalVariableDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").LabeledStatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ExpressionStatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").IfStatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").AssertStatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").SwitchStatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").SwitchBlockCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").SwitchBlockStatementGroupCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").SwitchLabelCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").SwitchRuleCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").WhileStatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").DoStatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").BasicForStatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").StatementExpressionListCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").EnhancedForStatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").BreakStatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ContinueStatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ReturnStatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ThrowStatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").SynchronizedStatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").TryStatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").CatchesCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").CatchClauseCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").CatchFormalParameterCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").CatchTypeCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").FinallyCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").TryWithResourcesStatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ResourceSpecificationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ResourceListCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").YieldStatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ForInitCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ForUpdateCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").StatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").StatementWithoutTrailingSubstatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ForStatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").BlockStatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").CaseConstantCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").CasePatternCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").EmptyStatementCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").StatementExpressionCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").LocalVariableTypeCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ResourceCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").VariableAccessCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ClassDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").NormalClassDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").TypeParametersCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").TypeParameterListCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ClassExtendsCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ClassImplementsCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").InterfaceTypeListCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ClassBodyCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ClassMemberDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").FieldDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").VariableDeclaratorListCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").VariableDeclaratorCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").VariableDeclaratorIdCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").UnannPrimitiveTypeWithOptionalDimsSuffixCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").UnannReferenceTypeCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").MethodDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").MethodHeaderCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").MethodDeclaratorCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ReceiverParameterCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").FormalParameterListCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").VariableParaRegularParameterCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").VariableArityParameterCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ThrowsCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ExceptionTypeListCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").StaticInitializerCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ConstructorDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ConstructorDeclaratorCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ConstructorBodyCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").UnqualifiedExplicitConstructorInvocationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").QualifiedExplicitConstructorInvocationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").EnumDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").EnumBodyCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").EnumConstantListCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").EnumConstantCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").EnumBodyDeclarationsCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").RecordDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").RecordHeaderCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").RecordComponentListCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").RecordComponentCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").VariableArityRecordComponentCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").RecordBodyCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").CompactConstructorDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").UnannTypeCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").VariableInitializerCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").VariableModifierCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").UnannClassTypeCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ClassBodyDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").InstanceInitializerCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ClassModifierCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ClassPermitsCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").FieldModifierCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").MethodModifierCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").MethodBodyCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ConstructorModifierCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").SimpleTypeNameCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ExplicitConstructorInvocationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").EnumConstantModifierCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ExceptionTypeCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").FormalParameterCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ResultCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").RecordBodyDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").RecordComponentModifierCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").UnannClassOrInterfaceTypeCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").UnannInterfaceTypeCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").UnannPrimitiveTypeCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").UnannTypeVariableCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").LambdaExpressionCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").LambdaParametersCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").LambdaParametersWithBracesCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ConciseLambdaParameterListCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").NormalLambdaParameterListCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").RegularLambdaParameterCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ConditionalExpressionCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").BinaryExpressionCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").UnaryExpressionCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").UnaryExpressionNotPlusMinusCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").PrimaryCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").PrimarySuffixCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").FqnOrRefTypeCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").FqnOrRefTypePartFirstCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").FqnOrRefTypePartRestCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").FqnOrRefTypePartCommonCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ParenthesisExpressionCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").PrimitiveCastExpressionCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ReferenceTypeCastExpressionCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").UnqualifiedClassInstanceCreationExpressionCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ClassOrInterfaceTypeToInstantiateCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").MethodInvocationSuffixCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ArgumentListCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ArrayCreationExpressionCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ArrayCreationExpressionWithoutInitializerSuffixCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ArrayCreationWithInitializerSuffixCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").DimExprsCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").DimExprCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ClassLiteralSuffixCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ArrayAccessSuffixCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").MethodReferenceSuffixCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").StringTemplateCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").TextBlockTemplateCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").RecordPatternCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ComponentPatternListCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").GuardCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").TemplateCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").PatternCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ExpressionCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").TypePatternCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").CastExpressionCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").TypeArgumentsOrDiamondCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").DiamondCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ComponentPatternCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").MatchAllPatternCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ConciseLambdaParameterCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").EmbeddedExpressionCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").LambdaBodyCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").LambdaParameterListCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").NormalLambdaParameterCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").LambdaParameterTypeCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").NewExpressionCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").PrimaryPrefixCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").TemplateArgumentCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").InterfaceDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").NormalInterfaceDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").InterfaceExtendsCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").InterfaceBodyCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").InterfaceMemberDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ConstantDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").InterfaceMethodDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").AnnotationInterfaceDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").AnnotationInterfaceBodyCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").AnnotationInterfaceMemberDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").AnnotationInterfaceElementDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").DefaultValueCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").AnnotationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ElementValuePairListCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ElementValuePairCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ElementValueArrayInitializerCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ElementValueListCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ElementValueCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").AnnotationInterfaceElementModifierCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ConstantModifierCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").InterfaceModifierCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").InterfacePermitsCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").InterfaceMethodModifierCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").LiteralCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ShiftOperatorCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").BooleanLiteralCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").FloatingPointLiteralCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").IntegerLiteralCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").MethodNameCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").AmbiguousNameCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").TypeNameCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").TypeIdentifierCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ExpressionNameCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").PackageNameCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ModuleNameCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").PackageOrTypeNameCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").CompilationUnitCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").OrdinaryCompilationUnitCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ModularCompilationUnitCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").PackageDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ImportDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").TypeDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ModuleDeclarationCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").RequiresModuleDirectiveCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ExportsModuleDirectiveCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").OpensModuleDirectiveCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").UsesModuleDirectiveCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ProvidesModuleDirectiveCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ModuleDirectiveCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").RequiresModifierCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").PackageModifierCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").PrimitiveTypeCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ReferenceTypeCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").TypeVariableCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").DimsCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").TypeParameterCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").TypeBoundCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").AdditionalBoundCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").TypeArgumentsCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").TypeArgumentListCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").WildcardCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").WildcardBoundsCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").InterfaceTypeCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ClassTypeCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").NumericTypeCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").ClassOrInterfaceTypeCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").FloatingPointTypeCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").IntegralTypeCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").TypeArgumentCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").TypeParameterModifierCstNode & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}> | import("prettier").AstPath<import("java-parser").IToken & {
|
||||
comments?: import("./comments.js").JavaComment[];
|
||||
}>, options: import("prettier").ParserOptions<JavaNode>, print: (path: import("prettier").AstPath<JavaNode>) => import("prettier").Doc, args: unknown): import("prettier/doc.js").builders.Doc;
|
||||
hasPrettierIgnore(path: import("prettier").AstPath<JavaNode>): boolean;
|
||||
canAttachComment: typeof import("./comments.js").canAttachComment;
|
||||
isBlockComment(node: JavaNode): boolean;
|
||||
printComment(commentPath: import("prettier").AstPath<JavaNode>): string | import("prettier/doc.js").builders.Doc[];
|
||||
getCommentChildNodes(node: JavaNode): any[];
|
||||
handleComments: {
|
||||
ownLine: typeof import("./comments.js").handleLineComment;
|
||||
endOfLine: typeof import("./comments.js").handleLineComment;
|
||||
remaining: typeof import("./comments.js").handleRemainingComment;
|
||||
};
|
||||
};
|
||||
};
|
||||
options: {
|
||||
entrypoint: {
|
||||
type: "choice";
|
||||
category: string;
|
||||
default: string;
|
||||
choices: {
|
||||
value: string;
|
||||
description: string;
|
||||
}[];
|
||||
description: string;
|
||||
};
|
||||
arrowParens: {
|
||||
type: "choice";
|
||||
category: string;
|
||||
default: string;
|
||||
choices: {
|
||||
value: string;
|
||||
description: string;
|
||||
}[];
|
||||
description: string;
|
||||
};
|
||||
trailingComma: {
|
||||
type: "choice";
|
||||
category: string;
|
||||
default: string;
|
||||
choices: {
|
||||
value: string;
|
||||
description: string;
|
||||
}[];
|
||||
description: string;
|
||||
};
|
||||
experimentalOperatorPosition: {
|
||||
type: "choice";
|
||||
category: string;
|
||||
default: string;
|
||||
choices: {
|
||||
value: string;
|
||||
description: string;
|
||||
}[];
|
||||
description: string;
|
||||
};
|
||||
};
|
||||
defaultOptions: {
|
||||
arrowParens: "avoid";
|
||||
};
|
||||
};
|
||||
export default _default;
|
||||
@@ -1,29 +0,0 @@
|
||||
import options from "./options.js";
|
||||
import parser from "./parser.js";
|
||||
import printer from "./printer.js";
|
||||
export default {
|
||||
languages: [
|
||||
{
|
||||
name: "Java",
|
||||
parsers: ["java"],
|
||||
group: "Java",
|
||||
tmScope: "source.java",
|
||||
aceMode: "java",
|
||||
codemirrorMode: "clike",
|
||||
codemirrorMimeType: "text/x-java",
|
||||
extensions: [".java"],
|
||||
linguistLanguageId: 181,
|
||||
vscodeLanguageIds: ["java"]
|
||||
}
|
||||
],
|
||||
parsers: {
|
||||
java: parser
|
||||
},
|
||||
printers: {
|
||||
java: printer
|
||||
},
|
||||
options,
|
||||
defaultOptions: {
|
||||
arrowParens: "avoid"
|
||||
}
|
||||
};
|
||||
@@ -1,43 +0,0 @@
|
||||
declare const _default: {
|
||||
entrypoint: {
|
||||
type: "choice";
|
||||
category: string;
|
||||
default: string;
|
||||
choices: {
|
||||
value: string;
|
||||
description: string;
|
||||
}[];
|
||||
description: string;
|
||||
};
|
||||
arrowParens: {
|
||||
type: "choice";
|
||||
category: string;
|
||||
default: string;
|
||||
choices: {
|
||||
value: string;
|
||||
description: string;
|
||||
}[];
|
||||
description: string;
|
||||
};
|
||||
trailingComma: {
|
||||
type: "choice";
|
||||
category: string;
|
||||
default: string;
|
||||
choices: {
|
||||
value: string;
|
||||
description: string;
|
||||
}[];
|
||||
description: string;
|
||||
};
|
||||
experimentalOperatorPosition: {
|
||||
type: "choice";
|
||||
category: string;
|
||||
default: string;
|
||||
choices: {
|
||||
value: string;
|
||||
description: string;
|
||||
}[];
|
||||
description: string;
|
||||
};
|
||||
};
|
||||
export default _default;
|
||||
@@ -1,284 +0,0 @@
|
||||
export default {
|
||||
entrypoint: {
|
||||
type: "choice",
|
||||
category: "Global",
|
||||
default: "compilationUnit",
|
||||
// sed -nr 's/.*\.RULE\(([^,]+),.*/\1/p' $(ls path/to/java-parser/rules/folder/*)
|
||||
choices: [
|
||||
{ value: "arrayInitializer", description: "" },
|
||||
{ value: "variableInitializerList", description: "" },
|
||||
{ value: "block", description: "" },
|
||||
{ value: "blockStatements", description: "" },
|
||||
{ value: "blockStatement", description: "" },
|
||||
{ value: "localVariableDeclarationStatement", description: "" },
|
||||
{ value: "localVariableDeclaration", description: "" },
|
||||
{ value: "localVariableType", description: "" },
|
||||
{ value: "statement", description: "" },
|
||||
{ value: "statementWithoutTrailingSubstatement", description: "" },
|
||||
{ value: "emptyStatement", description: "" },
|
||||
{ value: "labeledStatement", description: "" },
|
||||
{ value: "expressionStatement", description: "" },
|
||||
{ value: "statementExpression", description: "" },
|
||||
{ value: "ifStatement", description: "" },
|
||||
{ value: "assertStatement", description: "" },
|
||||
{ value: "switchStatement", description: "" },
|
||||
{ value: "switchBlock", description: "" },
|
||||
{ value: "switchBlockStatementGroup", description: "" },
|
||||
{ value: "switchLabel", description: "" },
|
||||
{ value: "switchRule", description: "" },
|
||||
{ value: "caseConstant", description: "" },
|
||||
{ value: "casePattern", description: "" },
|
||||
{ value: "whileStatement", description: "" },
|
||||
{ value: "doStatement", description: "" },
|
||||
{ value: "forStatement", description: "" },
|
||||
{ value: "basicForStatement", description: "" },
|
||||
{ value: "forInit", description: "" },
|
||||
{ value: "forUpdate", description: "" },
|
||||
{ value: "statementExpressionList", description: "" },
|
||||
{ value: "enhancedForStatement", description: "" },
|
||||
{ value: "breakStatement", description: "" },
|
||||
{ value: "continueStatement", description: "" },
|
||||
{ value: "returnStatement", description: "" },
|
||||
{ value: "throwStatement", description: "" },
|
||||
{ value: "synchronizedStatement", description: "" },
|
||||
{ value: "tryStatement", description: "" },
|
||||
{ value: "catches", description: "" },
|
||||
{ value: "catchClause", description: "" },
|
||||
{ value: "catchFormalParameter", description: "" },
|
||||
{ value: "catchType", description: "" },
|
||||
{ value: "finally", description: "" },
|
||||
{ value: "tryWithResourcesStatement", description: "" },
|
||||
{ value: "resourceSpecification", description: "" },
|
||||
{ value: "resourceList", description: "" },
|
||||
{ value: "resource", description: "" },
|
||||
{ value: "yieldStatement", description: "" },
|
||||
{ value: "variableAccess", description: "" },
|
||||
{ value: "classDeclaration", description: "" },
|
||||
{ value: "normalClassDeclaration", description: "" },
|
||||
{ value: "classModifier", description: "" },
|
||||
{ value: "typeParameters", description: "" },
|
||||
{ value: "typeParameterList", description: "" },
|
||||
{ value: "classExtends", description: "" },
|
||||
{ value: "classImplements", description: "" },
|
||||
{ value: "interfaceTypeList", description: "" },
|
||||
{ value: "classPermits", description: "" },
|
||||
{ value: "classBody", description: "" },
|
||||
{ value: "classBodyDeclaration", description: "" },
|
||||
{ value: "classMemberDeclaration", description: "" },
|
||||
{ value: "fieldDeclaration", description: "" },
|
||||
{ value: "fieldModifier", description: "" },
|
||||
{ value: "variableDeclaratorList", description: "" },
|
||||
{ value: "variableDeclarator", description: "" },
|
||||
{ value: "variableDeclaratorId", description: "" },
|
||||
{ value: "variableInitializer", description: "" },
|
||||
{ value: "unannType", description: "" },
|
||||
{ value: "unannPrimitiveTypeWithOptionalDimsSuffix", description: "" },
|
||||
{ value: "unannPrimitiveType", description: "" },
|
||||
{ value: "unannReferenceType", description: "" },
|
||||
{ value: "unannClassOrInterfaceType", description: "" },
|
||||
{ value: "unannClassType", description: "" },
|
||||
{ value: "unannInterfaceType", description: "" },
|
||||
{ value: "unannTypeVariable", description: "" },
|
||||
{ value: "methodDeclaration", description: "" },
|
||||
{ value: "methodModifier", description: "" },
|
||||
{ value: "methodHeader", description: "" },
|
||||
{ value: "result", description: "" },
|
||||
{ value: "methodDeclarator", description: "" },
|
||||
{ value: "receiverParameter", description: "" },
|
||||
{ value: "formalParameterList", description: "" },
|
||||
{ value: "formalParameter", description: "" },
|
||||
{ value: "variableParaRegularParameter", description: "" },
|
||||
{ value: "variableArityParameter", description: "" },
|
||||
{ value: "variableModifier", description: "" },
|
||||
{ value: "throws", description: "" },
|
||||
{ value: "exceptionTypeList", description: "" },
|
||||
{ value: "exceptionType", description: "" },
|
||||
{ value: "methodBody", description: "" },
|
||||
{ value: "instanceInitializer", description: "" },
|
||||
{ value: "staticInitializer", description: "" },
|
||||
{ value: "constructorDeclaration", description: "" },
|
||||
{ value: "constructorModifier", description: "" },
|
||||
{ value: "constructorDeclarator", description: "" },
|
||||
{ value: "simpleTypeName", description: "" },
|
||||
{ value: "constructorBody", description: "" },
|
||||
{ value: "explicitConstructorInvocation", description: "" },
|
||||
{ value: "unqualifiedExplicitConstructorInvocation", description: "" },
|
||||
{ value: "qualifiedExplicitConstructorInvocation", description: "" },
|
||||
{ value: "enumDeclaration", description: "" },
|
||||
{ value: "enumBody", description: "" },
|
||||
{ value: "enumConstantList", description: "" },
|
||||
{ value: "enumConstant", description: "" },
|
||||
{ value: "enumConstantModifier", description: "" },
|
||||
{ value: "enumBodyDeclarations", description: "" },
|
||||
{ value: "recordDeclaration", description: "" },
|
||||
{ value: "recordHeader", description: "" },
|
||||
{ value: "recordComponentList", description: "" },
|
||||
{ value: "recordComponent", description: "" },
|
||||
{ value: "variableArityRecordComponent", description: "" },
|
||||
{ value: "recordComponentModifier", description: "" },
|
||||
{ value: "recordBody", description: "" },
|
||||
{ value: "recordBodyDeclaration", description: "" },
|
||||
{ value: "compactConstructorDeclaration", description: "" },
|
||||
{ value: "isDims", description: "" },
|
||||
{ value: "expression", description: "" },
|
||||
{ value: "lambdaExpression", description: "" },
|
||||
{ value: "lambdaParameters", description: "" },
|
||||
{ value: "lambdaParametersWithBraces", description: "" },
|
||||
{ value: "lambdaParameterList", description: "" },
|
||||
{ value: "conciseLambdaParameterList", description: "" },
|
||||
{ value: "normalLambdaParameterList", description: "" },
|
||||
{ value: "normalLambdaParameter", description: "" },
|
||||
{ value: "regularLambdaParameter", description: "" },
|
||||
{ value: "lambdaParameterType", description: "" },
|
||||
{ value: "conciseLambdaParameter", description: "" },
|
||||
{ value: "lambdaBody", description: "" },
|
||||
{ value: "conditionalExpression", description: "" },
|
||||
{ value: "binaryExpression", description: "" },
|
||||
{ value: "unaryExpression", description: "" },
|
||||
{ value: "unaryExpressionNotPlusMinus", description: "" },
|
||||
{ value: "primary", description: "" },
|
||||
{ value: "primaryPrefix", description: "" },
|
||||
{ value: "primarySuffix", description: "" },
|
||||
{ value: "fqnOrRefType", description: "" },
|
||||
{ value: "fqnOrRefTypePartRest", description: "" },
|
||||
{ value: "fqnOrRefTypePartCommon", description: "" },
|
||||
{ value: "fqnOrRefTypePartFirst", description: "" },
|
||||
{ value: "parenthesisExpression", description: "" },
|
||||
{ value: "castExpression", description: "" },
|
||||
{ value: "primitiveCastExpression", description: "" },
|
||||
{ value: "referenceTypeCastExpression", description: "" },
|
||||
{ value: "newExpression", description: "" },
|
||||
{ value: "unqualifiedClassInstanceCreationExpression", description: "" },
|
||||
{ value: "classOrInterfaceTypeToInstantiate", description: "" },
|
||||
{ value: "typeArgumentsOrDiamond", description: "" },
|
||||
{ value: "diamond", description: "" },
|
||||
{ value: "methodInvocationSuffix", description: "" },
|
||||
{ value: "argumentList", description: "" },
|
||||
{ value: "arrayCreationExpression", description: "" },
|
||||
{
|
||||
value: "arrayCreationExpressionWithoutInitializerSuffix",
|
||||
description: ""
|
||||
},
|
||||
{ value: "arrayCreationWithInitializerSuffix", description: "" },
|
||||
{ value: "dimExprs", description: "" },
|
||||
{ value: "dimExpr", description: "" },
|
||||
{ value: "classLiteralSuffix", description: "" },
|
||||
{ value: "arrayAccessSuffix", description: "" },
|
||||
{ value: "methodReferenceSuffix", description: "" },
|
||||
{ value: "templateArgument", description: "" },
|
||||
{ value: "template", description: "" },
|
||||
{ value: "stringTemplate", description: "" },
|
||||
{ value: "textBlockTemplate", description: "" },
|
||||
{ value: "embeddedExpression", description: "" },
|
||||
{ value: "pattern", description: "" },
|
||||
{ value: "typePattern", description: "" },
|
||||
{ value: "recordPattern", description: "" },
|
||||
{ value: "componentPatternList", description: "" },
|
||||
{ value: "componentPattern", description: "" },
|
||||
{ value: "matchAllPattern", description: "" },
|
||||
{ value: "guard", description: "" },
|
||||
{ value: "isRefTypeInMethodRef", description: "" },
|
||||
{ value: "interfaceDeclaration", description: "" },
|
||||
{ value: "normalInterfaceDeclaration", description: "" },
|
||||
{ value: "interfaceModifier", description: "" },
|
||||
{ value: "interfaceExtends", description: "" },
|
||||
{ value: "interfacePermits", description: "" },
|
||||
{ value: "interfaceBody", description: "" },
|
||||
{ value: "interfaceMemberDeclaration", description: "" },
|
||||
{ value: "constantDeclaration", description: "" },
|
||||
{ value: "constantModifier", description: "" },
|
||||
{ value: "interfaceMethodDeclaration", description: "" },
|
||||
{ value: "interfaceMethodModifier", description: "" },
|
||||
{ value: "annotationInterfaceDeclaration", description: "" },
|
||||
{ value: "annotationInterfaceBody", description: "" },
|
||||
{ value: "annotationInterfaceMemberDeclaration", description: "" },
|
||||
{ value: "annotationInterfaceElementDeclaration", description: "" },
|
||||
{ value: "annotationInterfaceElementModifier", description: "" },
|
||||
{ value: "defaultValue", description: "" },
|
||||
{ value: "annotation", description: "" },
|
||||
{ value: "elementValuePairList", description: "" },
|
||||
{ value: "elementValuePair", description: "" },
|
||||
{ value: "elementValue", description: "" },
|
||||
{ value: "elementValueArrayInitializer", description: "" },
|
||||
{ value: "elementValueList", description: "" },
|
||||
{ value: "literal", description: "" },
|
||||
{ value: "integerLiteral", description: "" },
|
||||
{ value: "floatingPointLiteral", description: "" },
|
||||
{ value: "booleanLiteral", description: "" },
|
||||
{ value: "shiftOperator", description: "" },
|
||||
{ value: "moduleName", description: "" },
|
||||
{ value: "packageName", description: "" },
|
||||
{ value: "typeName", description: "" },
|
||||
{ value: "expressionName", description: "" },
|
||||
{ value: "methodName", description: "" },
|
||||
{ value: "packageOrTypeName", description: "" },
|
||||
{ value: "ambiguousName", description: "" },
|
||||
{ value: "compilationUnit", description: "" },
|
||||
{ value: "ordinaryCompilationUnit", description: "" },
|
||||
{ value: "modularCompilationUnit", description: "" },
|
||||
{ value: "packageDeclaration", description: "" },
|
||||
{ value: "packageModifier", description: "" },
|
||||
{ value: "importDeclaration", description: "" },
|
||||
{ value: "typeDeclaration", description: "" },
|
||||
{ value: "moduleDeclaration", description: "" },
|
||||
{ value: "moduleDirective", description: "" },
|
||||
{ value: "requiresModuleDirective", description: "" },
|
||||
{ value: "exportsModuleDirective", description: "" },
|
||||
{ value: "opensModuleDirective", description: "" },
|
||||
{ value: "usesModuleDirective", description: "" },
|
||||
{ value: "providesModuleDirective", description: "" },
|
||||
{ value: "requiresModifier", description: "" },
|
||||
{ value: "primitiveType", description: "" },
|
||||
{ value: "numericType", description: "" },
|
||||
{ value: "integralType", description: "" },
|
||||
{ value: "floatingPointType", description: "" },
|
||||
{ value: "referenceType", description: "" },
|
||||
{ value: "classOrInterfaceType", description: "" },
|
||||
{ value: "classType", description: "" },
|
||||
{ value: "interfaceType", description: "" },
|
||||
{ value: "typeVariable", description: "" },
|
||||
{ value: "dims", description: "" },
|
||||
{ value: "typeParameter", description: "" },
|
||||
{ value: "typeParameterModifier", description: "" },
|
||||
{ value: "typeBound", description: "" },
|
||||
{ value: "additionalBound", description: "" },
|
||||
{ value: "typeArguments", description: "" },
|
||||
{ value: "typeArgumentList", description: "" },
|
||||
{ value: "typeArgument", description: "" },
|
||||
{ value: "wildcard", description: "" },
|
||||
{ value: "wildcardBounds", description: "" }
|
||||
],
|
||||
description: "Prettify from the entrypoint, allowing to use prettier on snippet."
|
||||
},
|
||||
arrowParens: {
|
||||
type: "choice",
|
||||
category: "Java",
|
||||
default: "always",
|
||||
choices: [
|
||||
{ value: "always", description: "" },
|
||||
{ value: "avoid", description: "" }
|
||||
],
|
||||
description: "Include parentheses around a sole arrow function parameter."
|
||||
},
|
||||
trailingComma: {
|
||||
type: "choice",
|
||||
category: "Java",
|
||||
default: "all",
|
||||
choices: [
|
||||
{ value: "all", description: "" },
|
||||
{ value: "es5", description: "" },
|
||||
{ value: "none", description: "" }
|
||||
],
|
||||
description: "Print trailing commas wherever possible when multi-line."
|
||||
},
|
||||
experimentalOperatorPosition: {
|
||||
type: "choice",
|
||||
category: "Java",
|
||||
default: "end",
|
||||
choices: [
|
||||
{ value: "start", description: "" },
|
||||
{ value: "end", description: "" }
|
||||
],
|
||||
description: "Where to print operators when binary expressions wrap lines."
|
||||
}
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
import { type JavaNode, type JavaNonTerminal, type JavaParserOptions } from "./printers/helpers.js";
|
||||
declare const _default: {
|
||||
parse(text: string, options: JavaParserOptions): JavaNonTerminal;
|
||||
astFormat: string;
|
||||
hasPragma(text: string): boolean;
|
||||
locStart(node: JavaNode): number;
|
||||
locEnd(node: JavaNode): number;
|
||||
};
|
||||
export default _default;
|
||||
@@ -1,24 +0,0 @@
|
||||
import { parse } from "java-parser";
|
||||
import { determineFormatterOffOnRanges } from "./comments.js";
|
||||
import { isTerminal } from "./printers/helpers.js";
|
||||
export default {
|
||||
parse(text, options) {
|
||||
var _a;
|
||||
const cst = parse(text, options.entrypoint);
|
||||
(_a = cst.comments) === null || _a === void 0 ? void 0 : _a.forEach(comment => {
|
||||
comment.value = comment.image;
|
||||
});
|
||||
determineFormatterOffOnRanges(cst);
|
||||
return cst;
|
||||
},
|
||||
astFormat: "java",
|
||||
hasPragma(text) {
|
||||
return /^\/\*\*\n\s+\*\s@(format|prettier)\n\s+\*\//.test(text);
|
||||
},
|
||||
locStart(node) {
|
||||
return isTerminal(node) ? node.startOffset : node.location.startOffset;
|
||||
},
|
||||
locEnd(node) {
|
||||
return (isTerminal(node) ? node.endOffset : node.location.endOffset) + 1;
|
||||
}
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
import type { AstPath } from "prettier";
|
||||
import { canAttachComment, handleLineComment, handleRemainingComment } from "./comments.js";
|
||||
import { type JavaNode } from "./printers/helpers.js";
|
||||
declare const _default: {
|
||||
print(path: DistributedAstPath<JavaNode>, options: import("prettier").ParserOptions<JavaNode>, print: (path: AstPath<JavaNode>) => import("prettier").Doc, args: unknown): import("prettier/doc.js").builders.Doc;
|
||||
hasPrettierIgnore(path: AstPath<JavaNode>): boolean;
|
||||
canAttachComment: typeof canAttachComment;
|
||||
isBlockComment(node: JavaNode): boolean;
|
||||
printComment(commentPath: AstPath<JavaNode>): string | import("prettier/doc.js").builders.Doc[];
|
||||
getCommentChildNodes(node: JavaNode): any[];
|
||||
handleComments: {
|
||||
ownLine: typeof handleLineComment;
|
||||
endOfLine: typeof handleLineComment;
|
||||
remaining: typeof handleRemainingComment;
|
||||
};
|
||||
};
|
||||
export default _default;
|
||||
type DistributedAstPath<T> = T extends any ? AstPath<T> : never;
|
||||
@@ -1,40 +0,0 @@
|
||||
import { canAttachComment, handleLineComment, handleRemainingComment, isFullyBetweenFormatterOffOn } from "./comments.js";
|
||||
import { isNonTerminal, isTerminal, printComment } from "./printers/helpers.js";
|
||||
import { printerForNodeType } from "./printers/index.js";
|
||||
export default {
|
||||
print(path, options, print, args) {
|
||||
return hasTerminal(path)
|
||||
? path.node.image
|
||||
: printerForNodeType(path.node.name)(path, print, options, args);
|
||||
},
|
||||
hasPrettierIgnore(path) {
|
||||
var _a;
|
||||
const { node } = path;
|
||||
return (((_a = node.comments) === null || _a === void 0 ? void 0 : _a.some(({ image }) => /^(\/\/\s*prettier-ignore|\/\*\s*prettier-ignore\s*\*\/)$/.test(image))) === true ||
|
||||
(canAttachComment(node) && isFullyBetweenFormatterOffOn(path)));
|
||||
},
|
||||
canAttachComment,
|
||||
isBlockComment(node) {
|
||||
return isTerminal(node) && node.tokenType.name === "TraditionalComment";
|
||||
},
|
||||
printComment(commentPath) {
|
||||
const { node } = commentPath;
|
||||
if (isNonTerminal(node) || node.tokenType.GROUP !== "comments") {
|
||||
throw new Error(`Not a comment: ${JSON.stringify(node)}`);
|
||||
}
|
||||
return printComment(node);
|
||||
},
|
||||
getCommentChildNodes(node) {
|
||||
return isNonTerminal(node)
|
||||
? Object.values(node.children).flatMap(child => child)
|
||||
: [];
|
||||
},
|
||||
handleComments: {
|
||||
ownLine: handleLineComment,
|
||||
endOfLine: handleLineComment,
|
||||
remaining: handleRemainingComment
|
||||
}
|
||||
};
|
||||
function hasTerminal(path) {
|
||||
return isTerminal(path.node);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
declare const _default: {
|
||||
arrayInitializer(path: import("prettier").AstPath<import("java-parser").ArrayInitializerCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn, options: import("./helpers.js").JavaParserOptions): import("prettier/doc.js").builders.Group | "{}";
|
||||
variableInitializerList(path: import("prettier").AstPath<import("java-parser").VariableInitializerListCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): import("prettier/doc.js").builders.Doc[];
|
||||
};
|
||||
export default _default;
|
||||
@@ -1,9 +0,0 @@
|
||||
import { printArrayInitializer, printList } from "./helpers.js";
|
||||
export default {
|
||||
arrayInitializer(path, print, options) {
|
||||
return printArrayInitializer(path, print, options, "variableInitializerList");
|
||||
},
|
||||
variableInitializerList(path, print) {
|
||||
return printList(path, print, "variableInitializer");
|
||||
}
|
||||
};
|
||||
@@ -1,117 +0,0 @@
|
||||
import { builders } from "prettier/doc";
|
||||
import { printSingle } from "./helpers.js";
|
||||
declare const _default: {
|
||||
block(path: import("prettier").AstPath<import("java-parser").BlockCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[];
|
||||
blockStatements(path: import("prettier").AstPath<import("java-parser").BlockStatementsCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
blockStatement: typeof printSingle;
|
||||
localVariableDeclarationStatement(path: import("prettier").AstPath<import("java-parser").LocalVariableDeclarationStatementCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
localVariableDeclaration(path: import("prettier").AstPath<import("java-parser").LocalVariableDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
localVariableType: typeof printSingle;
|
||||
statement: typeof printSingle;
|
||||
statementWithoutTrailingSubstatement: typeof printSingle;
|
||||
emptyStatement(): string;
|
||||
labeledStatement(path: import("prettier").AstPath<import("java-parser").LabeledStatementCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
expressionStatement(path: import("prettier").AstPath<import("java-parser").ExpressionStatementCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
statementExpression: typeof printSingle;
|
||||
ifStatement(path: import("prettier").AstPath<import("java-parser").IfStatementCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
assertStatement(path: import("prettier").AstPath<import("java-parser").AssertStatementCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
switchStatement(path: import("prettier").AstPath<import("java-parser").SwitchStatementCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
switchBlock(path: import("prettier").AstPath<import("java-parser").SwitchBlockCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[];
|
||||
switchBlockStatementGroup(path: import("prettier").AstPath<import("java-parser").SwitchBlockStatementGroupCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
switchLabel(path: import("prettier").AstPath<import("java-parser").SwitchLabelCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): "default" | builders.Group | builders.Doc[];
|
||||
switchRule(path: import("prettier").AstPath<import("java-parser").SwitchRuleCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
caseConstant: typeof printSingle;
|
||||
casePattern: typeof printSingle;
|
||||
whileStatement(path: import("prettier").AstPath<import("java-parser").WhileStatementCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
doStatement(path: import("prettier").AstPath<import("java-parser").DoStatementCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): (string | builders.Group | builders.Doc[])[];
|
||||
forStatement: typeof printSingle;
|
||||
basicForStatement(path: import("prettier").AstPath<import("java-parser").BasicForStatementCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
forInit: typeof printSingle;
|
||||
forUpdate: typeof printSingle;
|
||||
statementExpressionList(path: import("prettier").AstPath<import("java-parser").StatementExpressionListCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Group;
|
||||
enhancedForStatement(path: import("prettier").AstPath<import("java-parser").EnhancedForStatementCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Group;
|
||||
breakStatement(path: import("prettier").AstPath<import("java-parser").BreakStatementCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[] | "break;";
|
||||
continueStatement(path: import("prettier").AstPath<import("java-parser").ContinueStatementCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[] | "continue;";
|
||||
returnStatement(path: import("prettier").AstPath<import("java-parser").ReturnStatementCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
throwStatement(path: import("prettier").AstPath<import("java-parser").ThrowStatementCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
synchronizedStatement(path: import("prettier").AstPath<import("java-parser").SynchronizedStatementCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
tryStatement(path: import("prettier").AstPath<import("java-parser").TryStatementCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc;
|
||||
catches(path: import("prettier").AstPath<import("java-parser").CatchesCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
catchClause(path: import("prettier").AstPath<import("java-parser").CatchClauseCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
catchFormalParameter(path: import("prettier").AstPath<import("java-parser").CatchFormalParameterCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
catchType(path: import("prettier").AstPath<import("java-parser").CatchTypeCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
finally(path: import("prettier").AstPath<import("java-parser").FinallyCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
tryWithResourcesStatement(path: import("prettier").AstPath<import("java-parser").TryWithResourcesStatementCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
resourceSpecification(path: import("prettier").AstPath<import("java-parser").ResourceSpecificationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Group | "()";
|
||||
resourceList(path: import("prettier").AstPath<import("java-parser").ResourceListCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
resource: typeof printSingle;
|
||||
yieldStatement(path: import("prettier").AstPath<import("java-parser").YieldStatementCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
variableAccess: typeof printSingle;
|
||||
};
|
||||
export default _default;
|
||||
@@ -1,337 +0,0 @@
|
||||
import { builders } from "prettier/doc";
|
||||
import { call, definedKeys, indentInParentheses, isBinaryExpression, isEmptyStatement, lineEndWithComments, lineStartWithComments, map, onlyDefinedKey, printBlock, printDanglingComments, printSingle, printWithModifiers } from "./helpers.js";
|
||||
const { group, hardline, ifBreak, indent, join, line, softline } = builders;
|
||||
export default {
|
||||
block(path, print) {
|
||||
const statements = path.node.children.blockStatements
|
||||
? call(path, print, "blockStatements")
|
||||
: [];
|
||||
return printBlock(path, statements.length ? [statements] : []);
|
||||
},
|
||||
blockStatements(path, print) {
|
||||
return join(hardline, map(path, statementPath => {
|
||||
const { node, previous } = statementPath;
|
||||
const statement = print(statementPath);
|
||||
return previous &&
|
||||
lineStartWithComments(node) > lineEndWithComments(previous) + 1
|
||||
? [hardline, statement]
|
||||
: statement;
|
||||
}, "blockStatement").filter(doc => doc !== ""));
|
||||
},
|
||||
blockStatement: printSingle,
|
||||
localVariableDeclarationStatement(path, print) {
|
||||
return [call(path, print, "localVariableDeclaration"), ";"];
|
||||
},
|
||||
localVariableDeclaration(path, print) {
|
||||
const declaration = join(" ", [
|
||||
call(path, print, "localVariableType"),
|
||||
call(path, print, "variableDeclaratorList")
|
||||
]);
|
||||
return printWithModifiers(path, print, "variableModifier", declaration);
|
||||
},
|
||||
localVariableType: printSingle,
|
||||
statement: printSingle,
|
||||
statementWithoutTrailingSubstatement: printSingle,
|
||||
emptyStatement() {
|
||||
return "";
|
||||
},
|
||||
labeledStatement(path, print) {
|
||||
return [
|
||||
call(path, print, "Identifier"),
|
||||
": ",
|
||||
call(path, print, "statement")
|
||||
];
|
||||
},
|
||||
expressionStatement(path, print) {
|
||||
return [call(path, print, "statementExpression"), ";"];
|
||||
},
|
||||
statementExpression: printSingle,
|
||||
ifStatement(path, print) {
|
||||
var _a;
|
||||
const { children } = path.node;
|
||||
const hasEmptyStatement = isEmptyStatement(children.statement[0]);
|
||||
const statements = map(path, print, "statement");
|
||||
const statement = [
|
||||
"if ",
|
||||
indentInParentheses(call(path, print, "expression")),
|
||||
hasEmptyStatement ? ";" : [" ", statements[0]]
|
||||
];
|
||||
if (children.Else) {
|
||||
const danglingComments = printDanglingComments(path);
|
||||
if (danglingComments.length) {
|
||||
statement.push(hardline, ...danglingComments, hardline);
|
||||
}
|
||||
else {
|
||||
const elseHasBlock = ((_a = children.statement[0].children
|
||||
.statementWithoutTrailingSubstatement) === null || _a === void 0 ? void 0 : _a[0].children.block) !==
|
||||
undefined;
|
||||
statement.push(elseHasBlock ? " " : hardline);
|
||||
}
|
||||
const elseHasEmptyStatement = isEmptyStatement(children.statement[1]);
|
||||
statement.push("else", elseHasEmptyStatement ? ";" : [" ", statements[1]]);
|
||||
}
|
||||
return statement;
|
||||
},
|
||||
assertStatement(path, print) {
|
||||
return ["assert ", ...join([" : "], map(path, print, "expression")), ";"];
|
||||
},
|
||||
switchStatement(path, print) {
|
||||
return join(" ", [
|
||||
"switch",
|
||||
indentInParentheses(call(path, print, "expression")),
|
||||
call(path, print, "switchBlock")
|
||||
]);
|
||||
},
|
||||
switchBlock(path, print) {
|
||||
const { children } = path.node;
|
||||
const caseKeys = definedKeys(children, [
|
||||
"switchBlockStatementGroup",
|
||||
"switchRule"
|
||||
]);
|
||||
const cases = caseKeys.length === 1 ? map(path, print, caseKeys[0]) : [];
|
||||
return printBlock(path, cases);
|
||||
},
|
||||
switchBlockStatementGroup(path, print) {
|
||||
var _a, _b;
|
||||
const { children } = path.node;
|
||||
const switchLabel = call(path, print, "switchLabel");
|
||||
if (!children.blockStatements) {
|
||||
return [switchLabel, ":"];
|
||||
}
|
||||
const blockStatements = call(path, print, "blockStatements");
|
||||
const statements = children.blockStatements[0].children.blockStatement;
|
||||
const onlyStatementIsBlock = statements.length === 1 &&
|
||||
((_b = (_a = statements[0].children.statement) === null || _a === void 0 ? void 0 : _a[0].children.statementWithoutTrailingSubstatement) === null || _b === void 0 ? void 0 : _b[0].children.block) !== undefined;
|
||||
return [
|
||||
switchLabel,
|
||||
":",
|
||||
onlyStatementIsBlock
|
||||
? [" ", blockStatements]
|
||||
: indent([hardline, blockStatements])
|
||||
];
|
||||
},
|
||||
switchLabel(path, print) {
|
||||
var _a, _b;
|
||||
const { children } = path.node;
|
||||
if (!((_b = (_a = children.caseConstant) !== null && _a !== void 0 ? _a : children.casePattern) !== null && _b !== void 0 ? _b : children.Null)) {
|
||||
return "default";
|
||||
}
|
||||
const values = [];
|
||||
if (children.Null) {
|
||||
values.push("null");
|
||||
if (children.Default) {
|
||||
values.push("default");
|
||||
}
|
||||
}
|
||||
else {
|
||||
const valuesKey = onlyDefinedKey(children, [
|
||||
"caseConstant",
|
||||
"casePattern"
|
||||
]);
|
||||
values.push(...map(path, print, valuesKey));
|
||||
}
|
||||
const hasMultipleValues = values.length > 1;
|
||||
const label = hasMultipleValues
|
||||
? ["case", indent([line, ...join([",", line], values)])]
|
||||
: ["case ", values[0]];
|
||||
return children.guard
|
||||
? [
|
||||
group([...label, hasMultipleValues ? line : " "]),
|
||||
call(path, print, "guard")
|
||||
]
|
||||
: group(label);
|
||||
},
|
||||
switchRule(path, print) {
|
||||
const { children } = path.node;
|
||||
const bodyKey = onlyDefinedKey(children, [
|
||||
"block",
|
||||
"expression",
|
||||
"throwStatement"
|
||||
]);
|
||||
const parts = [
|
||||
call(path, print, "switchLabel"),
|
||||
" -> ",
|
||||
call(path, print, bodyKey)
|
||||
];
|
||||
if (children.Semicolon) {
|
||||
parts.push(";");
|
||||
}
|
||||
return parts;
|
||||
},
|
||||
caseConstant: printSingle,
|
||||
casePattern: printSingle,
|
||||
whileStatement(path, print) {
|
||||
const statement = call(path, print, "statement");
|
||||
const hasEmptyStatement = isEmptyStatement(path.node.children.statement[0]);
|
||||
return [
|
||||
"while ",
|
||||
indentInParentheses(call(path, print, "expression")),
|
||||
...[hasEmptyStatement ? ";" : " ", statement]
|
||||
];
|
||||
},
|
||||
doStatement(path, print) {
|
||||
const hasEmptyStatement = isEmptyStatement(path.node.children.statement[0]);
|
||||
return [
|
||||
"do",
|
||||
hasEmptyStatement ? ";" : [" ", call(path, print, "statement")],
|
||||
" while ",
|
||||
indentInParentheses(call(path, print, "expression")),
|
||||
";"
|
||||
];
|
||||
},
|
||||
forStatement: printSingle,
|
||||
basicForStatement(path, print) {
|
||||
const { children } = path.node;
|
||||
const danglingComments = printDanglingComments(path);
|
||||
if (danglingComments.length) {
|
||||
danglingComments.push(hardline);
|
||||
}
|
||||
const expressions = ["forInit", "expression", "forUpdate"].map(expressionKey => expressionKey in children ? call(path, print, expressionKey) : "");
|
||||
const hasEmptyStatement = isEmptyStatement(children.statement[0]);
|
||||
return [
|
||||
...danglingComments,
|
||||
"for ",
|
||||
expressions.some(expression => expression !== "")
|
||||
? indentInParentheses(join([";", line], expressions))
|
||||
: "(;;)",
|
||||
hasEmptyStatement ? ";" : [" ", call(path, print, "statement")]
|
||||
];
|
||||
},
|
||||
forInit: printSingle,
|
||||
forUpdate: printSingle,
|
||||
statementExpressionList(path, print) {
|
||||
return group(map(path, print, "statementExpression").map((expression, index) => index === 0 ? expression : [",", indent([line, expression])]));
|
||||
},
|
||||
enhancedForStatement(path, print) {
|
||||
var _a;
|
||||
const statementNode = path.node.children.statement[0];
|
||||
const forStatement = [
|
||||
printDanglingComments(path),
|
||||
"for ",
|
||||
"(",
|
||||
call(path, print, "localVariableDeclaration"),
|
||||
" : ",
|
||||
call(path, print, "expression"),
|
||||
")"
|
||||
];
|
||||
if (isEmptyStatement(statementNode)) {
|
||||
forStatement.push(";");
|
||||
}
|
||||
else {
|
||||
const hasStatementBlock = ((_a = statementNode.children.statementWithoutTrailingSubstatement) === null || _a === void 0 ? void 0 : _a[0].children.block) !== undefined;
|
||||
const statement = call(path, print, "statement");
|
||||
forStatement.push(hasStatementBlock ? [" ", statement] : indent([line, statement]));
|
||||
}
|
||||
return group(forStatement);
|
||||
},
|
||||
breakStatement(path, print) {
|
||||
return path.node.children.Identifier
|
||||
? ["break ", call(path, print, "Identifier"), ";"]
|
||||
: "break;";
|
||||
},
|
||||
continueStatement(path, print) {
|
||||
return path.node.children.Identifier
|
||||
? ["continue ", call(path, print, "Identifier"), ";"]
|
||||
: "continue;";
|
||||
},
|
||||
returnStatement(path, print) {
|
||||
const { children } = path.node;
|
||||
const statement = ["return"];
|
||||
if (children.expression) {
|
||||
statement.push(" ");
|
||||
const expression = call(path, print, "expression");
|
||||
if (isBinaryExpression(children.expression[0])) {
|
||||
statement.push(group([
|
||||
ifBreak("("),
|
||||
indent([softline, expression]),
|
||||
softline,
|
||||
ifBreak(")")
|
||||
]));
|
||||
}
|
||||
else {
|
||||
statement.push(expression);
|
||||
}
|
||||
}
|
||||
statement.push(";");
|
||||
return statement;
|
||||
},
|
||||
throwStatement(path, print) {
|
||||
return ["throw ", call(path, print, "expression"), ";"];
|
||||
},
|
||||
synchronizedStatement(path, print) {
|
||||
return [
|
||||
"synchronized ",
|
||||
indentInParentheses(call(path, print, "expression")),
|
||||
" ",
|
||||
call(path, print, "block")
|
||||
];
|
||||
},
|
||||
tryStatement(path, print) {
|
||||
const { children } = path.node;
|
||||
if (children.tryWithResourcesStatement) {
|
||||
return call(path, print, "tryWithResourcesStatement");
|
||||
}
|
||||
const blocks = ["try", call(path, print, "block")];
|
||||
if (children.catches) {
|
||||
blocks.push(call(path, print, "catches"));
|
||||
}
|
||||
if (children.finally) {
|
||||
blocks.push(call(path, print, "finally"));
|
||||
}
|
||||
return join(" ", blocks);
|
||||
},
|
||||
catches(path, print) {
|
||||
return join(" ", map(path, print, "catchClause"));
|
||||
},
|
||||
catchClause(path, print) {
|
||||
return [
|
||||
"catch ",
|
||||
indentInParentheses(call(path, print, "catchFormalParameter")),
|
||||
" ",
|
||||
call(path, print, "block")
|
||||
];
|
||||
},
|
||||
catchFormalParameter(path, print) {
|
||||
return join(" ", [
|
||||
...map(path, print, "variableModifier"),
|
||||
call(path, print, "catchType"),
|
||||
call(path, print, "variableDeclaratorId")
|
||||
]);
|
||||
},
|
||||
catchType(path, print) {
|
||||
return join([line, "| "], [call(path, print, "unannClassType"), ...map(path, print, "classType")]);
|
||||
},
|
||||
finally(path, print) {
|
||||
return ["finally ", call(path, print, "block")];
|
||||
},
|
||||
tryWithResourcesStatement(path, print) {
|
||||
const { children } = path.node;
|
||||
const blocks = [
|
||||
"try",
|
||||
call(path, print, "resourceSpecification"),
|
||||
call(path, print, "block")
|
||||
];
|
||||
if (children.catches) {
|
||||
blocks.push(call(path, print, "catches"));
|
||||
}
|
||||
if (children.finally) {
|
||||
blocks.push(call(path, print, "finally"));
|
||||
}
|
||||
return join(" ", blocks);
|
||||
},
|
||||
resourceSpecification(path, print) {
|
||||
const resources = [call(path, print, "resourceList")];
|
||||
if (path.node.children.Semicolon) {
|
||||
resources.push(ifBreak(";"));
|
||||
}
|
||||
return indentInParentheses(resources);
|
||||
},
|
||||
resourceList(path, print) {
|
||||
return join([";", line], map(path, print, "resource"));
|
||||
},
|
||||
resource: printSingle,
|
||||
yieldStatement(path, print) {
|
||||
return ["yield ", call(path, print, "expression"), ";"];
|
||||
},
|
||||
variableAccess: printSingle
|
||||
};
|
||||
@@ -1,157 +0,0 @@
|
||||
import type { ClassBodyCstNode, EnumBodyDeclarationsCstNode } from "java-parser";
|
||||
import type { AstPath } from "prettier";
|
||||
import { builders } from "prettier/doc";
|
||||
import { printClassPermits, printClassType, printSingle, type JavaPrintFn } from "./helpers.js";
|
||||
declare const _default: {
|
||||
classDeclaration(path: AstPath<import("java-parser").ClassDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
normalClassDeclaration(path: AstPath<import("java-parser").NormalClassDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
classModifier: typeof printSingle;
|
||||
typeParameters(path: AstPath<import("java-parser").TypeParametersCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Group;
|
||||
typeParameterList(path: AstPath<import("java-parser").TypeParameterListCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
classExtends(path: AstPath<import("java-parser").ClassExtendsCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
classImplements(path: AstPath<import("java-parser").ClassImplementsCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Group;
|
||||
classPermits: typeof printClassPermits;
|
||||
interfaceTypeList(path: AstPath<import("java-parser").InterfaceTypeListCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Group;
|
||||
classBody(path: AstPath<ClassBodyCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[];
|
||||
classBodyDeclaration: typeof printSingle;
|
||||
classMemberDeclaration(path: AstPath<import("java-parser").ClassMemberDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc;
|
||||
fieldDeclaration(path: AstPath<import("java-parser").FieldDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
fieldModifier: typeof printSingle;
|
||||
variableDeclaratorList(path: AstPath<import("java-parser").VariableDeclaratorListCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Group | builders.Doc[];
|
||||
variableDeclarator(path: AstPath<import("java-parser").VariableDeclaratorCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc;
|
||||
variableDeclaratorId(path: AstPath<import("java-parser").VariableDeclaratorIdCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc;
|
||||
variableInitializer: typeof printSingle;
|
||||
unannType: typeof printSingle;
|
||||
unannPrimitiveTypeWithOptionalDimsSuffix(path: AstPath<import("java-parser").UnannPrimitiveTypeWithOptionalDimsSuffixCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc;
|
||||
unannPrimitiveType: typeof printSingle;
|
||||
unannReferenceType(path: AstPath<import("java-parser").UnannReferenceTypeCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc;
|
||||
unannClassOrInterfaceType: typeof printSingle;
|
||||
unannClassType: typeof printClassType;
|
||||
unannInterfaceType: typeof printSingle;
|
||||
unannTypeVariable: typeof printSingle;
|
||||
methodDeclaration(path: AstPath<import("java-parser").MethodDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
methodModifier: typeof printSingle;
|
||||
methodHeader(path: AstPath<import("java-parser").MethodHeaderCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Group;
|
||||
result: typeof printSingle;
|
||||
methodDeclarator(path: AstPath<import("java-parser").MethodDeclaratorCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
receiverParameter(path: AstPath<import("java-parser").ReceiverParameterCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
formalParameterList(path: AstPath<import("java-parser").FormalParameterListCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
formalParameter: typeof printSingle;
|
||||
variableParaRegularParameter(path: AstPath<import("java-parser").VariableParaRegularParameterCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
variableArityParameter(path: AstPath<import("java-parser").VariableArityParameterCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
variableModifier: typeof printSingle;
|
||||
throws(path: AstPath<import("java-parser").ThrowsCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
exceptionTypeList(path: AstPath<import("java-parser").ExceptionTypeListCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
exceptionType: typeof printSingle;
|
||||
methodBody: typeof printSingle;
|
||||
instanceInitializer: typeof printSingle;
|
||||
staticInitializer(path: AstPath<import("java-parser").StaticInitializerCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
constructorDeclaration(path: AstPath<import("java-parser").ConstructorDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
constructorModifier: typeof printSingle;
|
||||
constructorDeclarator(path: AstPath<import("java-parser").ConstructorDeclaratorCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
simpleTypeName: typeof printSingle;
|
||||
constructorBody(path: AstPath<import("java-parser").ConstructorBodyCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[];
|
||||
explicitConstructorInvocation: typeof printSingle;
|
||||
unqualifiedExplicitConstructorInvocation(path: AstPath<import("java-parser").UnqualifiedExplicitConstructorInvocationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
qualifiedExplicitConstructorInvocation(path: AstPath<import("java-parser").QualifiedExplicitConstructorInvocationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
enumDeclaration(path: AstPath<import("java-parser").EnumDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
enumBody(path: AstPath<import("java-parser").EnumBodyCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn, options: import("./helpers.js").JavaParserOptions): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[];
|
||||
enumConstantList(path: AstPath<import("java-parser").EnumConstantListCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
enumConstant(path: AstPath<import("java-parser").EnumConstantCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
enumConstantModifier: typeof printSingle;
|
||||
enumBodyDeclarations(path: AstPath<EnumBodyDeclarationsCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
recordDeclaration(path: AstPath<import("java-parser").RecordDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
recordHeader(path: AstPath<import("java-parser").RecordHeaderCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Group | "()";
|
||||
recordComponentList(path: AstPath<import("java-parser").RecordComponentListCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
recordComponent(path: AstPath<import("java-parser").RecordComponentCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Group;
|
||||
variableArityRecordComponent(path: AstPath<import("java-parser").VariableArityRecordComponentCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
recordComponentModifier: typeof printSingle;
|
||||
recordBody(path: AstPath<import("java-parser").RecordBodyCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[];
|
||||
recordBodyDeclaration: typeof printSingle;
|
||||
compactConstructorDeclaration(path: AstPath<import("java-parser").CompactConstructorDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
};
|
||||
export default _default;
|
||||
@@ -1,446 +0,0 @@
|
||||
import { builders } from "prettier/doc";
|
||||
import { call, each, hasDeclarationAnnotations, hasLeadingComments, indentInParentheses, isBinaryExpression, lineEndWithComments, lineStartWithComments, map, onlyDefinedKey, printBlock, printClassPermits, printClassType, printDanglingComments, printList, printSingle, printWithModifiers } from "./helpers.js";
|
||||
const { group, hardline, indent, indentIfBreak, join, line, softline } = builders;
|
||||
export default {
|
||||
classDeclaration(path, print) {
|
||||
const declarationKey = onlyDefinedKey(path.node.children, [
|
||||
"enumDeclaration",
|
||||
"normalClassDeclaration",
|
||||
"recordDeclaration"
|
||||
]);
|
||||
const declaration = call(path, print, declarationKey);
|
||||
return printWithModifiers(path, print, "classModifier", declaration, true);
|
||||
},
|
||||
normalClassDeclaration(path, print) {
|
||||
const { classExtends, classImplements, classPermits, typeParameters } = path.node.children;
|
||||
const header = ["class ", call(path, print, "typeIdentifier")];
|
||||
if (typeParameters) {
|
||||
header.push(call(path, print, "typeParameters"));
|
||||
}
|
||||
if (classExtends) {
|
||||
header.push(indent([line, call(path, print, "classExtends")]));
|
||||
}
|
||||
if (classImplements) {
|
||||
header.push(indent([line, call(path, print, "classImplements")]));
|
||||
}
|
||||
if (classPermits) {
|
||||
header.push(indent([line, call(path, print, "classPermits")]));
|
||||
}
|
||||
return [group(header), " ", call(path, print, "classBody")];
|
||||
},
|
||||
classModifier: printSingle,
|
||||
typeParameters(path, print) {
|
||||
return group([
|
||||
"<",
|
||||
indent([softline, call(path, print, "typeParameterList")]),
|
||||
softline,
|
||||
">"
|
||||
]);
|
||||
},
|
||||
typeParameterList(path, print) {
|
||||
return printList(path, print, "typeParameter");
|
||||
},
|
||||
classExtends(path, print) {
|
||||
return ["extends ", call(path, print, "classType")];
|
||||
},
|
||||
classImplements(path, print) {
|
||||
return group([
|
||||
"implements",
|
||||
indent([line, call(path, print, "interfaceTypeList")])
|
||||
]);
|
||||
},
|
||||
classPermits: printClassPermits,
|
||||
interfaceTypeList(path, print) {
|
||||
return group(printList(path, print, "interfaceType"));
|
||||
},
|
||||
classBody(path, print) {
|
||||
return printBlock(path, printClassBodyDeclarations(path, print));
|
||||
},
|
||||
classBodyDeclaration: printSingle,
|
||||
classMemberDeclaration(path, print) {
|
||||
const { children } = path.node;
|
||||
return children.Semicolon
|
||||
? ""
|
||||
: call(path, print, onlyDefinedKey(children));
|
||||
},
|
||||
fieldDeclaration(path, print) {
|
||||
const declaration = [
|
||||
call(path, print, "unannType"),
|
||||
" ",
|
||||
call(path, print, "variableDeclaratorList"),
|
||||
";"
|
||||
];
|
||||
return printWithModifiers(path, print, "fieldModifier", declaration);
|
||||
},
|
||||
fieldModifier: printSingle,
|
||||
variableDeclaratorList(path, print) {
|
||||
var _a;
|
||||
const declarators = map(path, print, "variableDeclarator");
|
||||
return declarators.length > 1 &&
|
||||
path.node.children.variableDeclarator.some(({ children }) => children.Equals)
|
||||
? group(indent(join([",", line], declarators)), {
|
||||
shouldBreak: ((_a = path.getNode(4)) === null || _a === void 0 ? void 0 : _a.name) !== "forInit"
|
||||
})
|
||||
: join(", ", declarators);
|
||||
},
|
||||
variableDeclarator(path, print) {
|
||||
var _a, _b;
|
||||
const { children } = path.node;
|
||||
const variableInitializer = (_a = children.variableInitializer) === null || _a === void 0 ? void 0 : _a[0];
|
||||
const declaratorId = call(path, print, "variableDeclaratorId");
|
||||
if (!variableInitializer) {
|
||||
return declaratorId;
|
||||
}
|
||||
const expression = (_b = variableInitializer.children.expression) === null || _b === void 0 ? void 0 : _b[0];
|
||||
const declarator = [declaratorId, " ", call(path, print, "Equals")];
|
||||
const initializer = call(path, print, "variableInitializer");
|
||||
if (hasLeadingComments(variableInitializer) ||
|
||||
(expression && isBinaryExpression(expression))) {
|
||||
declarator.push(group(indent([line, initializer])));
|
||||
}
|
||||
else {
|
||||
const groupId = Symbol("assignment");
|
||||
declarator.push(group(indent(line), { id: groupId }), indentIfBreak(initializer, { groupId }));
|
||||
}
|
||||
return group(declarator);
|
||||
},
|
||||
variableDeclaratorId(path, print) {
|
||||
const { dims, Underscore } = path.node.children;
|
||||
if (Underscore) {
|
||||
return "_";
|
||||
}
|
||||
const identifier = call(path, print, "Identifier");
|
||||
return dims ? [identifier, call(path, print, "dims")] : identifier;
|
||||
},
|
||||
variableInitializer: printSingle,
|
||||
unannType: printSingle,
|
||||
unannPrimitiveTypeWithOptionalDimsSuffix(path, print) {
|
||||
const type = call(path, print, "unannPrimitiveType");
|
||||
return path.node.children.dims ? [type, call(path, print, "dims")] : type;
|
||||
},
|
||||
unannPrimitiveType: printSingle,
|
||||
unannReferenceType(path, print) {
|
||||
const type = call(path, print, "unannClassOrInterfaceType");
|
||||
return path.node.children.dims ? [type, call(path, print, "dims")] : type;
|
||||
},
|
||||
unannClassOrInterfaceType: printSingle,
|
||||
unannClassType: printClassType,
|
||||
unannInterfaceType: printSingle,
|
||||
unannTypeVariable: printSingle,
|
||||
methodDeclaration(path, print) {
|
||||
const declaration = [
|
||||
call(path, print, "methodHeader"),
|
||||
path.node.children.methodBody[0].children.Semicolon ? "" : " ",
|
||||
call(path, print, "methodBody")
|
||||
];
|
||||
return printWithModifiers(path, print, "methodModifier", declaration);
|
||||
},
|
||||
methodModifier: printSingle,
|
||||
methodHeader(path, print) {
|
||||
const { typeParameters, annotation, throws } = path.node.children;
|
||||
const header = [];
|
||||
if (typeParameters) {
|
||||
header.push(call(path, print, "typeParameters"));
|
||||
}
|
||||
if (annotation) {
|
||||
header.push(join(line, map(path, print, "annotation")));
|
||||
}
|
||||
header.push(call(path, print, "result"), call(path, print, "methodDeclarator"));
|
||||
return throws
|
||||
? group([
|
||||
...join(" ", header),
|
||||
group(indent([line, call(path, print, "throws")]))
|
||||
])
|
||||
: group(join(" ", header));
|
||||
},
|
||||
result: printSingle,
|
||||
methodDeclarator(path, print) {
|
||||
const { dims, formalParameterList, receiverParameter } = path.node.children;
|
||||
const declarator = [call(path, print, "Identifier")];
|
||||
const parameters = [];
|
||||
if (receiverParameter) {
|
||||
parameters.push(call(path, print, "receiverParameter"));
|
||||
}
|
||||
if (formalParameterList) {
|
||||
parameters.push(call(path, print, "formalParameterList"));
|
||||
}
|
||||
const items = parameters.length
|
||||
? join([",", line], parameters)
|
||||
: printDanglingComments(path);
|
||||
declarator.push(items.length ? indentInParentheses(items) : "()");
|
||||
if (dims) {
|
||||
declarator.push(call(path, print, "dims"));
|
||||
}
|
||||
return declarator;
|
||||
},
|
||||
receiverParameter(path, print) {
|
||||
return join(" ", [
|
||||
...map(path, print, "annotation"),
|
||||
call(path, print, "unannType"),
|
||||
path.node.children.Identifier
|
||||
? [call(path, print, "Identifier"), ".this"]
|
||||
: "this"
|
||||
]);
|
||||
},
|
||||
formalParameterList(path, print) {
|
||||
return printList(path, print, "formalParameter");
|
||||
},
|
||||
formalParameter: printSingle,
|
||||
variableParaRegularParameter(path, print) {
|
||||
return join(" ", [
|
||||
...map(path, print, "variableModifier"),
|
||||
call(path, print, "unannType"),
|
||||
call(path, print, "variableDeclaratorId")
|
||||
]);
|
||||
},
|
||||
variableArityParameter(path, print) {
|
||||
const type = join(" ", [
|
||||
...map(path, print, "variableModifier"),
|
||||
call(path, print, "unannType"),
|
||||
...map(path, print, "annotation")
|
||||
]);
|
||||
return [type, "... ", call(path, print, "Identifier")];
|
||||
},
|
||||
variableModifier: printSingle,
|
||||
throws(path, print) {
|
||||
return ["throws ", call(path, print, "exceptionTypeList")];
|
||||
},
|
||||
exceptionTypeList(path, print) {
|
||||
return join(", ", map(path, print, "exceptionType"));
|
||||
},
|
||||
exceptionType: printSingle,
|
||||
methodBody: printSingle,
|
||||
instanceInitializer: printSingle,
|
||||
staticInitializer(path, print) {
|
||||
return ["static ", call(path, print, "block")];
|
||||
},
|
||||
constructorDeclaration(path, print) {
|
||||
const declaration = [call(path, print, "constructorDeclarator")];
|
||||
if (path.node.children.throws) {
|
||||
declaration.push(group(indent([line, call(path, print, "throws")])));
|
||||
}
|
||||
declaration.push(" ", call(path, print, "constructorBody"));
|
||||
return printWithModifiers(path, print, "constructorModifier", declaration, true);
|
||||
},
|
||||
constructorModifier: printSingle,
|
||||
constructorDeclarator(path, print) {
|
||||
const { children } = path.node;
|
||||
const parameters = [];
|
||||
if (children.receiverParameter) {
|
||||
parameters.push(call(path, print, "receiverParameter"));
|
||||
}
|
||||
if (children.formalParameterList) {
|
||||
parameters.push(call(path, print, "formalParameterList"));
|
||||
}
|
||||
const header = [call(path, print, "simpleTypeName")];
|
||||
header.push(parameters.length
|
||||
? indentInParentheses(join([",", line], parameters))
|
||||
: "()");
|
||||
return children.typeParameters
|
||||
? [call(path, print, "typeParameters"), " ", ...header]
|
||||
: header;
|
||||
},
|
||||
simpleTypeName: printSingle,
|
||||
constructorBody(path, print) {
|
||||
const { children } = path.node;
|
||||
const statements = [];
|
||||
if (children.explicitConstructorInvocation) {
|
||||
statements.push(call(path, print, "explicitConstructorInvocation"));
|
||||
}
|
||||
if (children.blockStatements) {
|
||||
statements.push(call(path, print, "blockStatements"));
|
||||
}
|
||||
return printBlock(path, statements);
|
||||
},
|
||||
explicitConstructorInvocation: printSingle,
|
||||
unqualifiedExplicitConstructorInvocation(path, print) {
|
||||
const { children } = path.node;
|
||||
const invocation = [];
|
||||
if (children.typeArguments) {
|
||||
invocation.push(call(path, print, "typeArguments"));
|
||||
}
|
||||
invocation.push(children.Super ? "super" : "this");
|
||||
if (children.argumentList) {
|
||||
invocation.push(group(["(", call(path, print, "argumentList"), ")"]));
|
||||
}
|
||||
else {
|
||||
invocation.push(indentInParentheses(printDanglingComments(path), { shouldBreak: true }));
|
||||
}
|
||||
invocation.push(";");
|
||||
return invocation;
|
||||
},
|
||||
qualifiedExplicitConstructorInvocation(path, print) {
|
||||
const { children } = path.node;
|
||||
const invocation = [call(path, print, "expressionName"), "."];
|
||||
if (children.typeArguments) {
|
||||
invocation.push(call(path, print, "typeArguments"));
|
||||
}
|
||||
invocation.push("super");
|
||||
if (children.argumentList) {
|
||||
invocation.push(group(["(", call(path, print, "argumentList"), ")"]));
|
||||
}
|
||||
else {
|
||||
invocation.push(indentInParentheses(printDanglingComments(path), { shouldBreak: true }));
|
||||
}
|
||||
invocation.push(";");
|
||||
return invocation;
|
||||
},
|
||||
enumDeclaration(path, print) {
|
||||
const header = ["enum", call(path, print, "typeIdentifier")];
|
||||
if (path.node.children.classImplements) {
|
||||
header.push(call(path, print, "classImplements"));
|
||||
}
|
||||
return join(" ", [...header, call(path, print, "enumBody")]);
|
||||
},
|
||||
enumBody(path, print, options) {
|
||||
var _a;
|
||||
const { children } = path.node;
|
||||
const contents = [];
|
||||
const hasNonEmptyDeclaration = ((_a = children.enumBodyDeclarations) !== null && _a !== void 0 ? _a : [])
|
||||
.flatMap(({ children }) => { var _a; return (_a = children.classBodyDeclaration) !== null && _a !== void 0 ? _a : []; })
|
||||
.some(({ children }) => { var _a; return !((_a = children.classMemberDeclaration) === null || _a === void 0 ? void 0 : _a[0].children.Semicolon); });
|
||||
if (children.enumConstantList) {
|
||||
contents.push(call(path, print, "enumConstantList"));
|
||||
if (!hasNonEmptyDeclaration && options.trailingComma !== "none") {
|
||||
contents.push(",");
|
||||
}
|
||||
}
|
||||
if (hasNonEmptyDeclaration) {
|
||||
contents.push(";", hardline, call(path, print, "enumBodyDeclarations"));
|
||||
}
|
||||
return printBlock(path, contents.length ? [contents] : []);
|
||||
},
|
||||
enumConstantList(path, print) {
|
||||
return join([",", hardline], map(path, constantPath => {
|
||||
const constant = print(constantPath);
|
||||
const { node, previous } = constantPath;
|
||||
return !previous ||
|
||||
lineStartWithComments(node) <= lineEndWithComments(previous) + 1
|
||||
? constant
|
||||
: [hardline, constant];
|
||||
}, "enumConstant"));
|
||||
},
|
||||
enumConstant(path, print) {
|
||||
const { argumentList, classBody } = path.node.children;
|
||||
const initializer = [call(path, print, "Identifier")];
|
||||
if (argumentList) {
|
||||
initializer.push(group(["(", call(path, print, "argumentList"), ")"]));
|
||||
}
|
||||
if (classBody) {
|
||||
initializer.push(" ", call(path, print, "classBody"));
|
||||
}
|
||||
return printWithModifiers(path, print, "enumConstantModifier", initializer);
|
||||
},
|
||||
enumConstantModifier: printSingle,
|
||||
enumBodyDeclarations(path, print) {
|
||||
return join(hardline, printClassBodyDeclarations(path, print));
|
||||
},
|
||||
recordDeclaration(path, print) {
|
||||
const { children } = path.node;
|
||||
const header = ["record ", call(path, print, "typeIdentifier")];
|
||||
if (children.typeParameters) {
|
||||
header.push(call(path, print, "typeParameters"));
|
||||
}
|
||||
header.push(call(path, print, "recordHeader"));
|
||||
if (children.classImplements) {
|
||||
header.push(" ", call(path, print, "classImplements"));
|
||||
}
|
||||
return [group(header), " ", call(path, print, "recordBody")];
|
||||
},
|
||||
recordHeader(path, print) {
|
||||
return path.node.children.recordComponentList
|
||||
? indentInParentheses(call(path, print, "recordComponentList"))
|
||||
: indentInParentheses(printDanglingComments(path), { shouldBreak: true });
|
||||
},
|
||||
recordComponentList(path, print) {
|
||||
return join([",", line], map(path, componentPath => {
|
||||
const { node, previous } = componentPath;
|
||||
const blankLine = previous &&
|
||||
lineStartWithComments(node) > lineEndWithComments(previous) + 1;
|
||||
const component = print(componentPath);
|
||||
return blankLine ? [softline, component] : component;
|
||||
}, "recordComponent"));
|
||||
},
|
||||
recordComponent(path, print) {
|
||||
const { children } = path.node;
|
||||
const component = [call(path, print, "unannType")];
|
||||
if (children.Identifier ||
|
||||
children.variableArityRecordComponent[0].children.annotation) {
|
||||
component.push(" ");
|
||||
}
|
||||
const suffixKey = onlyDefinedKey(children, [
|
||||
"Identifier",
|
||||
"variableArityRecordComponent"
|
||||
]);
|
||||
component.push(call(path, print, suffixKey));
|
||||
return group(join(line, [...map(path, print, "recordComponentModifier"), component]));
|
||||
},
|
||||
variableArityRecordComponent(path, print) {
|
||||
return [
|
||||
...join(" ", map(path, print, "annotation")),
|
||||
"... ",
|
||||
call(path, print, "Identifier")
|
||||
];
|
||||
},
|
||||
recordComponentModifier: printSingle,
|
||||
recordBody(path, print) {
|
||||
const declarations = [];
|
||||
let previousRequiresPadding = false;
|
||||
each(path, declarationPath => {
|
||||
var _a, _b, _c, _d;
|
||||
const declaration = print(declarationPath);
|
||||
if (declaration === "") {
|
||||
return;
|
||||
}
|
||||
const { node, previous } = declarationPath;
|
||||
const fieldDeclaration = (_c = (_b = (_a = node.children.classBodyDeclaration) === null || _a === void 0 ? void 0 : _a[0].children.classMemberDeclaration) === null || _b === void 0 ? void 0 : _b[0].children.fieldDeclaration) === null || _c === void 0 ? void 0 : _c[0].children;
|
||||
const currentRequiresPadding = !fieldDeclaration ||
|
||||
hasDeclarationAnnotations((_d = fieldDeclaration.fieldModifier) !== null && _d !== void 0 ? _d : []);
|
||||
const blankLine = declarations.length > 0 &&
|
||||
(previousRequiresPadding ||
|
||||
currentRequiresPadding ||
|
||||
lineStartWithComments(node) > lineEndWithComments(previous) + 1);
|
||||
declarations.push(blankLine ? [hardline, declaration] : declaration);
|
||||
previousRequiresPadding = currentRequiresPadding;
|
||||
}, "recordBodyDeclaration");
|
||||
return printBlock(path, declarations);
|
||||
},
|
||||
recordBodyDeclaration: printSingle,
|
||||
compactConstructorDeclaration(path, print) {
|
||||
const declaration = [
|
||||
call(path, print, "simpleTypeName"),
|
||||
" ",
|
||||
call(path, print, "constructorBody")
|
||||
];
|
||||
return printWithModifiers(path, print, "constructorModifier", declaration, true);
|
||||
}
|
||||
};
|
||||
function printClassBodyDeclarations(path, print) {
|
||||
var _a;
|
||||
if (!path.node.children.classBodyDeclaration) {
|
||||
return [];
|
||||
}
|
||||
const declarations = [];
|
||||
let previousRequiresPadding = path.node.name === "enumBodyDeclarations" ||
|
||||
((_a = path.grandparent) === null || _a === void 0 ? void 0 : _a.name) ===
|
||||
"normalClassDeclaration";
|
||||
each(path, declarationPath => {
|
||||
var _a, _b, _c;
|
||||
const declaration = print(declarationPath);
|
||||
if (declaration === "") {
|
||||
return;
|
||||
}
|
||||
const { node, previous } = declarationPath;
|
||||
const fieldDeclaration = (_b = (_a = node.children.classMemberDeclaration) === null || _a === void 0 ? void 0 : _a[0].children.fieldDeclaration) === null || _b === void 0 ? void 0 : _b[0].children;
|
||||
const currentRequiresPadding = fieldDeclaration
|
||||
? hasDeclarationAnnotations((_c = fieldDeclaration.fieldModifier) !== null && _c !== void 0 ? _c : [])
|
||||
: true;
|
||||
const blankLine = previousRequiresPadding ||
|
||||
(declarations.length > 0 &&
|
||||
(currentRequiresPadding ||
|
||||
lineStartWithComments(node) > lineEndWithComments(previous) + 1));
|
||||
declarations.push(blankLine ? [hardline, declaration] : declaration);
|
||||
previousRequiresPadding = currentRequiresPadding;
|
||||
}, "classBodyDeclaration");
|
||||
return declarations;
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
import type { StringTemplateCstNode, TextBlockTemplateCstNode } from "java-parser";
|
||||
import type { AstPath } from "prettier";
|
||||
import { builders } from "prettier/doc";
|
||||
import type { JavaComment } from "../comments.js";
|
||||
import { printSingle, type JavaPrintFn } from "./helpers.js";
|
||||
declare const _default: {
|
||||
expression: typeof printSingle;
|
||||
lambdaExpression(path: AstPath<import("java-parser").LambdaExpressionCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn, _: import("./helpers.js").JavaParserOptions, args?: unknown): builders.Doc[];
|
||||
lambdaParameters(path: AstPath<import("java-parser").LambdaParametersCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn, options: import("./helpers.js").JavaParserOptions): builders.Doc;
|
||||
lambdaParametersWithBraces(path: AstPath<import("java-parser").LambdaParametersWithBracesCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn, options: import("./helpers.js").JavaParserOptions): builders.Doc;
|
||||
lambdaParameterList: typeof printSingle;
|
||||
conciseLambdaParameterList(path: AstPath<import("java-parser").ConciseLambdaParameterListCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
normalLambdaParameterList(path: AstPath<import("java-parser").NormalLambdaParameterListCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
normalLambdaParameter: typeof printSingle;
|
||||
regularLambdaParameter(path: AstPath<import("java-parser").RegularLambdaParameterCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
lambdaParameterType: typeof printSingle;
|
||||
conciseLambdaParameter: typeof printSingle;
|
||||
lambdaBody: typeof printSingle;
|
||||
conditionalExpression(path: AstPath<import("java-parser").ConditionalExpressionCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc;
|
||||
binaryExpression(path: AstPath<import("java-parser").BinaryExpressionCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn, options: import("./helpers.js").JavaParserOptions): builders.Doc;
|
||||
unaryExpression(path: AstPath<import("java-parser").UnaryExpressionCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
unaryExpressionNotPlusMinus(path: AstPath<import("java-parser").UnaryExpressionNotPlusMinusCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
primary(path: AstPath<import("java-parser").PrimaryCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc;
|
||||
primaryPrefix: typeof printSingle;
|
||||
primarySuffix(path: AstPath<import("java-parser").PrimarySuffixCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc;
|
||||
fqnOrRefType(path: AstPath<import("java-parser").FqnOrRefTypeCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn, _: import("./helpers.js").JavaParserOptions, args: unknown): builders.Doc[];
|
||||
fqnOrRefTypePartFirst(path: AstPath<import("java-parser").FqnOrRefTypePartFirstCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
fqnOrRefTypePartRest(path: AstPath<import("java-parser").FqnOrRefTypePartRestCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
fqnOrRefTypePartCommon(path: AstPath<import("java-parser").FqnOrRefTypePartCommonCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc;
|
||||
parenthesisExpression(path: AstPath<import("java-parser").ParenthesisExpressionCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Group | "()" | (string | builders.Indent)[];
|
||||
castExpression: typeof printSingle;
|
||||
primitiveCastExpression(path: AstPath<import("java-parser").PrimitiveCastExpressionCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
referenceTypeCastExpression(path: AstPath<import("java-parser").ReferenceTypeCastExpressionCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
newExpression: typeof printSingle;
|
||||
unqualifiedClassInstanceCreationExpression(path: AstPath<import("java-parser").UnqualifiedClassInstanceCreationExpressionCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
classOrInterfaceTypeToInstantiate(path: AstPath<import("java-parser").ClassOrInterfaceTypeToInstantiateCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
typeArgumentsOrDiamond: typeof printSingle;
|
||||
diamond(): string;
|
||||
methodInvocationSuffix(path: AstPath<import("java-parser").MethodInvocationSuffixCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Group | "()";
|
||||
argumentList(path: AstPath<import("java-parser").ArgumentListCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Group | (builders.Indent | builders.Softline)[] | (builders.BreakParent | builders.Group)[];
|
||||
arrayCreationExpression(path: AstPath<import("java-parser").ArrayCreationExpressionCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
arrayCreationExpressionWithoutInitializerSuffix(path: AstPath<import("java-parser").ArrayCreationExpressionWithoutInitializerSuffixCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc;
|
||||
arrayCreationWithInitializerSuffix(path: AstPath<import("java-parser").ArrayCreationWithInitializerSuffixCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
dimExprs(path: AstPath<import("java-parser").DimExprsCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
dimExpr(path: AstPath<import("java-parser").DimExprCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
classLiteralSuffix(path: AstPath<import("java-parser").ClassLiteralSuffixCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
arrayAccessSuffix(path: AstPath<import("java-parser").ArrayAccessSuffixCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
methodReferenceSuffix(path: AstPath<import("java-parser").MethodReferenceSuffixCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
templateArgument: typeof printSingle;
|
||||
template: typeof printSingle;
|
||||
stringTemplate(path: AstPath<StringTemplateCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Indent;
|
||||
textBlockTemplate(path: AstPath<TextBlockTemplateCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Indent;
|
||||
embeddedExpression: typeof printSingle;
|
||||
pattern: typeof printSingle;
|
||||
typePattern: typeof printSingle;
|
||||
recordPattern(path: AstPath<import("java-parser").RecordPatternCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
componentPatternList(path: AstPath<import("java-parser").ComponentPatternListCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
componentPattern: typeof printSingle;
|
||||
matchAllPattern: typeof printSingle;
|
||||
guard(path: AstPath<import("java-parser").GuardCstNode & {
|
||||
comments?: JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
};
|
||||
export default _default;
|
||||
@@ -1,598 +0,0 @@
|
||||
import { builders, utils } from "prettier/doc";
|
||||
import { call, definedKeys, each, findBaseIndent, flatMap, hasLeadingComments, indentInParentheses, isBinaryExpression, isNonTerminal, isTerminal, map, onlyDefinedKey, printDanglingComments, printList, printName, printSingle } from "./helpers.js";
|
||||
const { breakParent, conditionalGroup, group, hardline, ifBreak, indent, indentIfBreak, join, line, lineSuffixBoundary, softline } = builders;
|
||||
const { removeLines, willBreak } = utils;
|
||||
export default {
|
||||
expression: printSingle,
|
||||
lambdaExpression(path, print, _, args = {}) {
|
||||
var _a;
|
||||
const hug = (_a = args.hug) !== null && _a !== void 0 ? _a : false;
|
||||
const parameters = call(path, print, "lambdaParameters");
|
||||
const expression = [hug ? removeLines(parameters) : parameters, " ->"];
|
||||
const lambdaExpression = path.node.children.lambdaBody[0].children.expression;
|
||||
const body = call(path, print, "lambdaBody");
|
||||
if (lambdaExpression) {
|
||||
const suffix = indent([line, body]);
|
||||
expression.push(group(hug ? [suffix, softline] : suffix));
|
||||
}
|
||||
else {
|
||||
expression.push(" ", body);
|
||||
}
|
||||
return expression;
|
||||
},
|
||||
lambdaParameters(path, print, options) {
|
||||
const parameters = printSingle(path, print);
|
||||
return !path.node.children.lambdaParametersWithBraces &&
|
||||
options.arrowParens === "always"
|
||||
? ["(", parameters, ")"]
|
||||
: parameters;
|
||||
},
|
||||
lambdaParametersWithBraces(path, print, options) {
|
||||
var _a;
|
||||
const { lambdaParameterList } = path.node.children;
|
||||
if (!lambdaParameterList) {
|
||||
return "()";
|
||||
}
|
||||
const { conciseLambdaParameterList, normalLambdaParameterList } = lambdaParameterList[0].children;
|
||||
const parameterCount = ((_a = conciseLambdaParameterList === null || conciseLambdaParameterList === void 0 ? void 0 : conciseLambdaParameterList[0].children.conciseLambdaParameter) !== null && _a !== void 0 ? _a : normalLambdaParameterList === null || normalLambdaParameterList === void 0 ? void 0 : normalLambdaParameterList[0].children.normalLambdaParameter).length;
|
||||
const parameters = call(path, print, "lambdaParameterList");
|
||||
if (parameterCount > 1) {
|
||||
return indentInParentheses(parameters);
|
||||
}
|
||||
return conciseLambdaParameterList && options.arrowParens === "avoid"
|
||||
? parameters
|
||||
: ["(", parameters, ")"];
|
||||
},
|
||||
lambdaParameterList: printSingle,
|
||||
conciseLambdaParameterList(path, print) {
|
||||
return printList(path, print, "conciseLambdaParameter");
|
||||
},
|
||||
normalLambdaParameterList(path, print) {
|
||||
return printList(path, print, "normalLambdaParameter");
|
||||
},
|
||||
normalLambdaParameter: printSingle,
|
||||
regularLambdaParameter(path, print) {
|
||||
return join(" ", [
|
||||
...map(path, print, "variableModifier"),
|
||||
call(path, print, "lambdaParameterType"),
|
||||
call(path, print, "variableDeclaratorId")
|
||||
]);
|
||||
},
|
||||
lambdaParameterType: printSingle,
|
||||
conciseLambdaParameter: printSingle,
|
||||
lambdaBody: printSingle,
|
||||
conditionalExpression(path, print) {
|
||||
var _a;
|
||||
const binaryExpression = call(path, print, "binaryExpression");
|
||||
if (!path.node.children.QuestionMark) {
|
||||
return binaryExpression;
|
||||
}
|
||||
const expressions = map(path, print, "expression");
|
||||
const contents = indent(join(line, [
|
||||
binaryExpression,
|
||||
["? ", expressions[0]],
|
||||
[": ", expressions[1]]
|
||||
]));
|
||||
const isNestedTernary = ((_a = path.getNode(4)) === null || _a === void 0 ? void 0 : _a.name) ===
|
||||
"conditionalExpression";
|
||||
return isNestedTernary ? contents : group(contents);
|
||||
},
|
||||
binaryExpression(path, print, options) {
|
||||
var _a, _b;
|
||||
const { children } = path.node;
|
||||
const operands = flatMap(path, print, definedKeys(children, [
|
||||
"expression",
|
||||
"pattern",
|
||||
"referenceType",
|
||||
"unaryExpression"
|
||||
]));
|
||||
const operators = flatMap(path, operatorPath => {
|
||||
const { node } = operatorPath;
|
||||
let image;
|
||||
if (isTerminal(node)) {
|
||||
image = node.image;
|
||||
}
|
||||
else if (node.children.Less) {
|
||||
image = "<<";
|
||||
}
|
||||
else {
|
||||
image = node.children.Greater.length === 2 ? ">>" : ">>>";
|
||||
}
|
||||
return { image, doc: print(operatorPath) };
|
||||
}, definedKeys(children, [
|
||||
"AssignmentOperator",
|
||||
"BinaryOperator",
|
||||
"Instanceof",
|
||||
"shiftOperator"
|
||||
]));
|
||||
const hasNonAssignmentOperators = (operators.length > 0 && !children.AssignmentOperator) ||
|
||||
(children.expression !== undefined &&
|
||||
isBinaryExpression(children.expression[0]));
|
||||
const isInList = ((_a = path.getNode(4)) === null || _a === void 0 ? void 0 : _a.name) === "elementValue" ||
|
||||
((_b = path.getNode(6)) === null || _b === void 0 ? void 0 : _b.name) === "argumentList";
|
||||
return binary(operands, operators, {
|
||||
hasNonAssignmentOperators,
|
||||
isInList,
|
||||
isRoot: true,
|
||||
operatorPosition: options.experimentalOperatorPosition
|
||||
});
|
||||
},
|
||||
unaryExpression(path, print) {
|
||||
return [
|
||||
...map(path, print, "UnaryPrefixOperator"),
|
||||
call(path, print, "primary"),
|
||||
...map(path, print, "UnarySuffixOperator")
|
||||
];
|
||||
},
|
||||
unaryExpressionNotPlusMinus(path, print) {
|
||||
const { children } = path.node;
|
||||
const expression = [];
|
||||
if (children.UnaryPrefixOperatorNotPlusMinus) {
|
||||
expression.push(...map(path, print, "UnaryPrefixOperatorNotPlusMinus"));
|
||||
}
|
||||
expression.push(call(path, print, "primary"));
|
||||
if (children.UnarySuffixOperator) {
|
||||
expression.push(...map(path, print, "UnarySuffixOperator"));
|
||||
}
|
||||
return join(" ", expression);
|
||||
},
|
||||
primary(path, print) {
|
||||
var _a, _b;
|
||||
const { children } = path.node;
|
||||
if (!children.primarySuffix) {
|
||||
return call(path, print, "primaryPrefix");
|
||||
}
|
||||
const methodInvocations = children.primarySuffix
|
||||
.filter(({ children }) => children.methodInvocationSuffix)
|
||||
.map(({ children }) => children.methodInvocationSuffix[0].children);
|
||||
const hasLambdaMethodParameter = methodInvocations.some(({ argumentList }) => argumentList === null || argumentList === void 0 ? void 0 : argumentList[0].children.expression.some(({ children }) => children.lambdaExpression));
|
||||
const prefixIsCallExpression = children.primaryPrefix[0].children.newExpression;
|
||||
const callExpressionCount = methodInvocations.length +
|
||||
(prefixIsCallExpression ? 1 : 0) +
|
||||
children.primarySuffix.filter(({ children }) => children.unqualifiedClassInstanceCreationExpression).length;
|
||||
const fqnOrRefType = (_a = children.primaryPrefix[0].children.fqnOrRefType) === null || _a === void 0 ? void 0 : _a[0].children;
|
||||
const prefixIsMethodInvocation = (fqnOrRefType === null || fqnOrRefType === void 0 ? void 0 : fqnOrRefType.fqnOrRefTypePartRest) !== undefined &&
|
||||
((_b = children.primarySuffix) === null || _b === void 0 ? void 0 : _b[0].children.methodInvocationSuffix) !== undefined;
|
||||
const prefixIsStaticMethodInvocation = prefixIsMethodInvocation && isCapitalizedIdentifier(fqnOrRefType);
|
||||
const prefixIsInstanceMethodInvocation = prefixIsMethodInvocation && !prefixIsStaticMethodInvocation;
|
||||
const mustBreakForCallExpressions = methodInvocations.length > 2 && hasLambdaMethodParameter;
|
||||
const separator = mustBreakForCallExpressions ? hardline : softline;
|
||||
const prefix = [
|
||||
call(path, prefixPath => print(prefixPath, {
|
||||
lastSeparator: prefixIsStaticMethodInvocation ||
|
||||
(prefixIsInstanceMethodInvocation && callExpressionCount === 1)
|
||||
? ""
|
||||
: separator
|
||||
}), "primaryPrefix")
|
||||
];
|
||||
const canBreakForCallExpressions = callExpressionCount > 2 ||
|
||||
(callExpressionCount === 2 && prefixIsInstanceMethodInvocation) ||
|
||||
willBreak(prefix);
|
||||
const suffixes = [];
|
||||
each(path, suffixPath => {
|
||||
const { node, previous } = suffixPath;
|
||||
const suffix = print(suffixPath);
|
||||
if (node.children.Dot) {
|
||||
if ((canBreakForCallExpressions &&
|
||||
((!previous && prefixIsCallExpression) ||
|
||||
(previous === null || previous === void 0 ? void 0 : previous.children.methodInvocationSuffix) ||
|
||||
(previous === null || previous === void 0 ? void 0 : previous.children.unqualifiedClassInstanceCreationExpression))) ||
|
||||
(!node.children.templateArgument && willBreak(suffix))) {
|
||||
suffixes.push(separator);
|
||||
}
|
||||
suffixes.push(suffix);
|
||||
}
|
||||
else if (previous) {
|
||||
suffixes.push(suffix);
|
||||
}
|
||||
else {
|
||||
prefix.push(prefixIsInstanceMethodInvocation && callExpressionCount >= 2
|
||||
? indent(suffix)
|
||||
: suffix);
|
||||
}
|
||||
}, "primarySuffix");
|
||||
const hasSuffixComments = children.primarySuffix.some(suffix => hasLeadingComments(suffix));
|
||||
return group(canBreakForCallExpressions || hasSuffixComments
|
||||
? [prefix, indent(suffixes)]
|
||||
: [prefix, ...suffixes]);
|
||||
},
|
||||
primaryPrefix: printSingle,
|
||||
primarySuffix(path, print) {
|
||||
const { children } = path.node;
|
||||
if (!children.Dot) {
|
||||
return printSingle(path, print);
|
||||
}
|
||||
const suffix = ["."];
|
||||
if (children.This) {
|
||||
suffix.push("this");
|
||||
}
|
||||
else if (children.Identifier) {
|
||||
if (children.typeArguments) {
|
||||
suffix.push(call(path, print, "typeArguments"));
|
||||
}
|
||||
suffix.push(call(path, print, "Identifier"));
|
||||
}
|
||||
else {
|
||||
const suffixKey = onlyDefinedKey(children, [
|
||||
"templateArgument",
|
||||
"unqualifiedClassInstanceCreationExpression"
|
||||
]);
|
||||
suffix.push(call(path, print, suffixKey));
|
||||
}
|
||||
return suffix;
|
||||
},
|
||||
fqnOrRefType(path, print, _, args) {
|
||||
var _a;
|
||||
const lastSeparator = (_a = args.lastSeparator) !== null && _a !== void 0 ? _a : "";
|
||||
const fqnOrRefType = [
|
||||
call(path, print, "fqnOrRefTypePartFirst"),
|
||||
...map(path, partPath => {
|
||||
const part = print(partPath);
|
||||
return partPath.isLast
|
||||
? [willBreak(part) ? hardline : lastSeparator, part]
|
||||
: part;
|
||||
}, "fqnOrRefTypePartRest")
|
||||
];
|
||||
fqnOrRefType.push(indent(fqnOrRefType.pop()));
|
||||
return path.node.children.dims
|
||||
? [fqnOrRefType, call(path, print, "dims")]
|
||||
: fqnOrRefType;
|
||||
},
|
||||
fqnOrRefTypePartFirst(path, print) {
|
||||
return join(" ", [
|
||||
...map(path, print, "annotation"),
|
||||
call(path, print, "fqnOrRefTypePartCommon")
|
||||
]);
|
||||
},
|
||||
fqnOrRefTypePartRest(path, print) {
|
||||
const common = call(path, print, "fqnOrRefTypePartCommon");
|
||||
const type = path.node.children.typeArguments
|
||||
? [call(path, print, "typeArguments"), common]
|
||||
: common;
|
||||
return [".", ...join(" ", [...map(path, print, "annotation"), type])];
|
||||
},
|
||||
fqnOrRefTypePartCommon(path, print) {
|
||||
const { children } = path.node;
|
||||
const keywordKey = onlyDefinedKey(children, ["Identifier", "Super"]);
|
||||
const keyword = call(path, print, keywordKey);
|
||||
return children.typeArguments
|
||||
? [keyword, call(path, print, "typeArguments")]
|
||||
: keyword;
|
||||
},
|
||||
parenthesisExpression(path, print) {
|
||||
var _a;
|
||||
const expression = call(path, print, "expression");
|
||||
const ancestorName = (_a = path.getNode(14)) === null || _a === void 0 ? void 0 : _a.name;
|
||||
const binaryExpression = path.getNode(8);
|
||||
return ancestorName &&
|
||||
["guard", "returnStatement"].includes(ancestorName) &&
|
||||
binaryExpression &&
|
||||
binaryExpression.name === "binaryExpression" &&
|
||||
Object.keys(binaryExpression.children).length === 1
|
||||
? indentInParentheses(expression)
|
||||
: ["(", indent(expression), ")"];
|
||||
},
|
||||
castExpression: printSingle,
|
||||
primitiveCastExpression(path, print) {
|
||||
return [
|
||||
"(",
|
||||
call(path, print, "primitiveType"),
|
||||
") ",
|
||||
call(path, print, "unaryExpression")
|
||||
];
|
||||
},
|
||||
referenceTypeCastExpression(path, print) {
|
||||
const { children } = path.node;
|
||||
const type = call(path, print, "referenceType");
|
||||
const cast = children.additionalBound
|
||||
? indentInParentheses(join(line, [type, ...map(path, print, "additionalBound")]))
|
||||
: ["(", type, ")"];
|
||||
const expressionKey = onlyDefinedKey(children, [
|
||||
"lambdaExpression",
|
||||
"unaryExpressionNotPlusMinus"
|
||||
]);
|
||||
return [cast, " ", call(path, print, expressionKey)];
|
||||
},
|
||||
newExpression: printSingle,
|
||||
unqualifiedClassInstanceCreationExpression(path, print) {
|
||||
const { children } = path.node;
|
||||
const expression = ["new "];
|
||||
if (children.typeArguments) {
|
||||
expression.push(call(path, print, "typeArguments"));
|
||||
}
|
||||
expression.push(call(path, print, "classOrInterfaceTypeToInstantiate"), children.argumentList
|
||||
? group(["(", call(path, print, "argumentList"), ")"])
|
||||
: "()");
|
||||
if (children.classBody) {
|
||||
expression.push(" ", call(path, print, "classBody"));
|
||||
}
|
||||
return expression;
|
||||
},
|
||||
classOrInterfaceTypeToInstantiate(path, print) {
|
||||
const { children } = path.node;
|
||||
const type = children.annotation
|
||||
? flatMap(path, childPath => [
|
||||
print(childPath),
|
||||
isNonTerminal(childPath.node) ? " " : "."
|
||||
], ["annotation", "Identifier"])
|
||||
: printName(path, print);
|
||||
if (children.typeArgumentsOrDiamond) {
|
||||
type.push(call(path, print, "typeArgumentsOrDiamond"));
|
||||
}
|
||||
return type;
|
||||
},
|
||||
typeArgumentsOrDiamond: printSingle,
|
||||
diamond() {
|
||||
return "<>";
|
||||
},
|
||||
methodInvocationSuffix(path, print) {
|
||||
return path.node.children.argumentList
|
||||
? group(["(", call(path, print, "argumentList"), ")"])
|
||||
: indentInParentheses(printDanglingComments(path), { shouldBreak: true });
|
||||
},
|
||||
argumentList(path, print) {
|
||||
var _a, _b, _c, _d;
|
||||
const expressions = path.node.children.expression;
|
||||
const lastExpression = expressions.at(-1);
|
||||
const lastExpressionLambdaBodyExpression = (_b = (_a = lastExpression.children.lambdaExpression) === null || _a === void 0 ? void 0 : _a[0].children.lambdaBody[0].children.expression) === null || _b === void 0 ? void 0 : _b[0].children;
|
||||
const lastExpressionLambdaBodyTernaryExpression = (_c = lastExpressionLambdaBodyExpression === null || lastExpressionLambdaBodyExpression === void 0 ? void 0 : lastExpressionLambdaBodyExpression.conditionalExpression) === null || _c === void 0 ? void 0 : _c[0].children;
|
||||
const isHuggable = !lastExpression.comments &&
|
||||
(!lastExpressionLambdaBodyExpression ||
|
||||
(lastExpressionLambdaBodyTernaryExpression === null || lastExpressionLambdaBodyTernaryExpression === void 0 ? void 0 : lastExpressionLambdaBodyTernaryExpression.QuestionMark) !== undefined ||
|
||||
((_d = lastExpressionLambdaBodyTernaryExpression === null || lastExpressionLambdaBodyTernaryExpression === void 0 ? void 0 : lastExpressionLambdaBodyTernaryExpression.binaryExpression) === null || _d === void 0 ? void 0 : _d[0].children.unaryExpression.length) === 1) &&
|
||||
expressions.findIndex(({ children }) => children.lambdaExpression) ===
|
||||
expressions.length - 1;
|
||||
const args = map(path, print, "expression");
|
||||
const allArgsExpandable = [
|
||||
indent([softline, ...join([",", line], args)]),
|
||||
softline
|
||||
];
|
||||
if (!isHuggable || willBreak(args.at(-1)[0])) {
|
||||
return allArgsExpandable;
|
||||
}
|
||||
const headArgs = args.slice(0, -1);
|
||||
const huggedLastArg = path.call(argPath => print(argPath, { hug: true }), "children", "expression", args.length - 1);
|
||||
const lastArgExpanded = join(", ", [
|
||||
...headArgs,
|
||||
group(huggedLastArg, { shouldBreak: true })
|
||||
]);
|
||||
if (willBreak(huggedLastArg)) {
|
||||
return [
|
||||
breakParent,
|
||||
conditionalGroup([lastArgExpanded, allArgsExpandable])
|
||||
];
|
||||
}
|
||||
return conditionalGroup([
|
||||
join(", ", [...headArgs, huggedLastArg]),
|
||||
lastArgExpanded,
|
||||
allArgsExpandable
|
||||
]);
|
||||
},
|
||||
arrayCreationExpression(path, print) {
|
||||
const { children } = path.node;
|
||||
const typeKey = onlyDefinedKey(children, [
|
||||
"classOrInterfaceType",
|
||||
"primitiveType"
|
||||
]);
|
||||
const suffixKey = onlyDefinedKey(children, [
|
||||
"arrayCreationExpressionWithoutInitializerSuffix",
|
||||
"arrayCreationWithInitializerSuffix"
|
||||
]);
|
||||
return ["new ", call(path, print, typeKey), call(path, print, suffixKey)];
|
||||
},
|
||||
arrayCreationExpressionWithoutInitializerSuffix(path, print) {
|
||||
const expressions = call(path, print, "dimExprs");
|
||||
return path.node.children.dims
|
||||
? [expressions, call(path, print, "dims")]
|
||||
: expressions;
|
||||
},
|
||||
arrayCreationWithInitializerSuffix(path, print) {
|
||||
return [
|
||||
call(path, print, "dims"),
|
||||
" ",
|
||||
call(path, print, "arrayInitializer")
|
||||
];
|
||||
},
|
||||
dimExprs(path, print) {
|
||||
return map(path, print, "dimExpr");
|
||||
},
|
||||
dimExpr(path, print) {
|
||||
return join(" ", [
|
||||
...map(path, print, "annotation"),
|
||||
["[", call(path, print, "expression"), "]"]
|
||||
]);
|
||||
},
|
||||
classLiteralSuffix(path, print) {
|
||||
const lSquares = map(path, print, "LSquare");
|
||||
const rSquares = map(path, print, "RSquare");
|
||||
return [
|
||||
...lSquares.flatMap((lSquare, index) => [lSquare, rSquares[index]]),
|
||||
".class"
|
||||
];
|
||||
},
|
||||
arrayAccessSuffix(path, print) {
|
||||
return ["[", call(path, print, "expression"), "]"];
|
||||
},
|
||||
methodReferenceSuffix(path, print) {
|
||||
const { children } = path.node;
|
||||
const reference = ["::"];
|
||||
if (children.typeArguments) {
|
||||
reference.push(call(path, print, "typeArguments"));
|
||||
}
|
||||
reference.push(call(path, print, onlyDefinedKey(children, ["Identifier", "New"])));
|
||||
return reference;
|
||||
},
|
||||
templateArgument: printSingle,
|
||||
template: printSingle,
|
||||
stringTemplate(path, print) {
|
||||
return printTemplate(path, print, "StringTemplateBegin", "StringTemplateMid", "StringTemplateEnd");
|
||||
},
|
||||
textBlockTemplate(path, print) {
|
||||
return printTemplate(path, print, "TextBlockTemplateBegin", "TextBlockTemplateMid", "TextBlockTemplateEnd");
|
||||
},
|
||||
embeddedExpression: printSingle,
|
||||
pattern: printSingle,
|
||||
typePattern: printSingle,
|
||||
recordPattern(path, print) {
|
||||
const patterns = path.node.children.componentPatternList
|
||||
? indentInParentheses(call(path, print, "componentPatternList"))
|
||||
: "()";
|
||||
return [call(path, print, "referenceType"), patterns];
|
||||
},
|
||||
componentPatternList(path, print) {
|
||||
return printList(path, print, "componentPattern");
|
||||
},
|
||||
componentPattern: printSingle,
|
||||
matchAllPattern: printSingle,
|
||||
guard(path, print) {
|
||||
var _a;
|
||||
const expression = call(path, print, "expression");
|
||||
const hasParentheses = ((_a = path.node.children.expression[0].children.conditionalExpression) === null || _a === void 0 ? void 0 : _a[0].children.binaryExpression[0].children.unaryExpression[0].children.primary[0].children.primaryPrefix[0].children.parenthesisExpression) !==
|
||||
undefined;
|
||||
return [
|
||||
"when ",
|
||||
hasParentheses
|
||||
? expression
|
||||
: group([
|
||||
ifBreak("("),
|
||||
indent([softline, expression]),
|
||||
softline,
|
||||
ifBreak(")")
|
||||
])
|
||||
];
|
||||
}
|
||||
};
|
||||
function binary(operands, operators, { hasNonAssignmentOperators = false, isInList = false, isRoot = false, operatorPosition }) {
|
||||
let levelOperator;
|
||||
let levelPrecedence;
|
||||
let level = [];
|
||||
while (operators.length) {
|
||||
const nextOperator = operators[0].image;
|
||||
const nextPrecedence = getOperatorPrecedence(nextOperator);
|
||||
if (levelPrecedence === undefined || nextPrecedence === levelPrecedence) {
|
||||
const { image: operator, doc: operatorDoc } = operators.shift();
|
||||
level.push(operands.shift());
|
||||
if (levelOperator !== undefined &&
|
||||
needsParentheses(levelOperator, operator)) {
|
||||
level = [["(", group(indent(level)), ")"]];
|
||||
}
|
||||
const parts = [" ", operatorDoc, line];
|
||||
if (operatorPosition === "start" && !isAssignmentOperator(operator)) {
|
||||
parts.reverse();
|
||||
}
|
||||
level.push(parts);
|
||||
levelOperator = operator;
|
||||
levelPrecedence = nextPrecedence;
|
||||
}
|
||||
else if (nextPrecedence < levelPrecedence) {
|
||||
if (!isRoot) {
|
||||
break;
|
||||
}
|
||||
level.push(operands.shift());
|
||||
const content = group(indent(level));
|
||||
operands.unshift(levelOperator !== undefined &&
|
||||
needsParentheses(levelOperator, nextOperator)
|
||||
? ["(", content, ")"]
|
||||
: content);
|
||||
level = [];
|
||||
levelOperator = undefined;
|
||||
levelPrecedence = undefined;
|
||||
}
|
||||
else {
|
||||
const content = binary(operands, operators, { operatorPosition });
|
||||
operands.unshift(levelOperator !== undefined &&
|
||||
needsParentheses(nextOperator, levelOperator)
|
||||
? ["(", indent(content), ")"]
|
||||
: content);
|
||||
}
|
||||
}
|
||||
level.push(operands.shift());
|
||||
if (!levelOperator ||
|
||||
(!isInList &&
|
||||
!isAssignmentOperator(levelOperator) &&
|
||||
levelOperator !== "instanceof")) {
|
||||
return group(level);
|
||||
}
|
||||
if (!isRoot || hasNonAssignmentOperators) {
|
||||
return group(indent(level));
|
||||
}
|
||||
const groupId = Symbol("assignment");
|
||||
return group([
|
||||
level[0],
|
||||
group(indent(level[1]), { id: groupId }),
|
||||
indentIfBreak(level[2], { groupId })
|
||||
]);
|
||||
}
|
||||
const precedencesByOperator = new Map([
|
||||
["||"],
|
||||
["&&"],
|
||||
["|"],
|
||||
["^"],
|
||||
["&"],
|
||||
["==", "!="],
|
||||
["<", ">", "<=", ">=", "instanceof"],
|
||||
["<<", ">>", ">>>"],
|
||||
["+", "-"],
|
||||
["*", "/", "%"]
|
||||
].flatMap((operators, index) => operators.map(operator => [operator, index])));
|
||||
function getOperatorPrecedence(operator) {
|
||||
var _a;
|
||||
return (_a = precedencesByOperator.get(operator)) !== null && _a !== void 0 ? _a : -1;
|
||||
}
|
||||
function needsParentheses(operator, parentOperator) {
|
||||
return ((operator === "&&" && parentOperator === "||") ||
|
||||
(["|", "^", "&", "<<", ">>", ">>>"].includes(parentOperator) &&
|
||||
getOperatorPrecedence(operator) >
|
||||
getOperatorPrecedence(parentOperator)) ||
|
||||
[operator, parentOperator].every(o => ["==", "!="].includes(o)) ||
|
||||
[operator, parentOperator].every(o => ["<<", ">>", ">>>"].includes(o)) ||
|
||||
(operator === "*" && parentOperator === "/") ||
|
||||
(operator === "/" && parentOperator === "*") ||
|
||||
(operator === "%" && ["+", "-", "*", "/"].includes(parentOperator)) ||
|
||||
(["*", "/"].includes(operator) && parentOperator === "%"));
|
||||
}
|
||||
const assignmentOperators = new Set([
|
||||
"=",
|
||||
"*=",
|
||||
"/=",
|
||||
"%=",
|
||||
"+=",
|
||||
"-=",
|
||||
"<<=",
|
||||
">>=",
|
||||
">>>=",
|
||||
"&=",
|
||||
"^=",
|
||||
"|="
|
||||
]);
|
||||
function isAssignmentOperator(operator) {
|
||||
return assignmentOperators.has(operator);
|
||||
}
|
||||
function isCapitalizedIdentifier(fqnOrRefType) {
|
||||
var _a, _b, _c;
|
||||
const nextToLastIdentifier = (_c = (_b = [
|
||||
fqnOrRefType.fqnOrRefTypePartFirst[0],
|
||||
...((_a = fqnOrRefType.fqnOrRefTypePartRest) !== null && _a !== void 0 ? _a : [])
|
||||
].at(-2)) === null || _b === void 0 ? void 0 : _b.children.fqnOrRefTypePartCommon[0].children.Identifier) === null || _c === void 0 ? void 0 : _c[0].image;
|
||||
return /^\p{Uppercase_Letter}/u.test(nextToLastIdentifier !== null && nextToLastIdentifier !== void 0 ? nextToLastIdentifier : "");
|
||||
}
|
||||
function printTemplate(path, print, beginKey, midKey, endKey) {
|
||||
const begin = call(path, ({ node }) => node.image, beginKey);
|
||||
const mids = map(path, ({ node }) => node.image, midKey);
|
||||
const end = call(path, ({ node }) => node.image, endKey);
|
||||
const lines = [begin, ...mids, end].join("").split("\n").slice(1);
|
||||
const baseIndent = findBaseIndent(lines);
|
||||
const prefix = "\n" + " ".repeat(baseIndent);
|
||||
const parts = [begin, ...mids, end].map(image => join(hardline, image.split(prefix)));
|
||||
return indent([
|
||||
parts[0],
|
||||
...map(path, (expressionPath, index) => {
|
||||
const expression = group([
|
||||
indent([softline, print(expressionPath), lineSuffixBoundary]),
|
||||
softline
|
||||
]);
|
||||
return index === 0 ? expression : [parts[index], expression];
|
||||
}, "embeddedExpression"),
|
||||
parts.at(-1)
|
||||
]);
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
import type { AnnotationCstNode, ClassPermitsCstNode, ClassTypeCtx, CstElement, CstNode, ExpressionCstNode, InterfacePermitsCstNode, IToken, StatementCstNode } from "java-parser";
|
||||
import type { AstPath, Doc, ParserOptions } from "prettier";
|
||||
import { builders } from "prettier/doc";
|
||||
import type { JavaComment } from "../comments.js";
|
||||
export declare function onlyDefinedKey<T extends Record<string, any>, K extends Key<T> & string>(obj: T, options?: K[]): K;
|
||||
export declare function definedKeys<T extends Record<string, any>, K extends Key<T> & string>(obj: T, options?: K[]): K[];
|
||||
export declare function printWithModifiers<T extends CstNode, P extends IterProperties<T["children"]>>(path: AstPath<T>, print: JavaPrintFn, modifierChild: P, contents: Doc, noTypeAnnotations?: boolean): builders.Doc[];
|
||||
export declare function hasDeclarationAnnotations(modifiers: ModifierNode[]): boolean;
|
||||
export declare function call<T extends CstNode, U, P extends IterProperties<T["children"]>>(path: AstPath<T>, callback: MapCallback<IndexValue<IndexValue<T, "children">, P>, U>, child: P): U;
|
||||
export declare function each<T extends CstNode, P extends IterProperties<T["children"]>>(path: AstPath<T>, callback: MapCallback<IndexValue<IndexValue<T, "children">, P>, void>, child: P): void;
|
||||
export declare function map<T extends CstNode, U, P extends IterProperties<T["children"]>>(path: AstPath<T>, callback: MapCallback<IndexValue<IndexValue<T, "children">, P>, U>, child: P): U[];
|
||||
export declare function flatMap<T extends CstNode, U, P extends IterProperties<T["children"]>>(path: AstPath<T>, callback: MapCallback<IndexValue<IndexValue<T, "children">, P>, U>, children: P[]): U[];
|
||||
export declare function printSingle(path: AstPath<JavaNonTerminal>, print: JavaPrintFn, _?: JavaParserOptions, args?: unknown): builders.Doc;
|
||||
export declare function lineStartWithComments(node: JavaNonTerminal): number;
|
||||
export declare function lineEndWithComments(node: JavaNonTerminal): number;
|
||||
export declare function printDanglingComments(path: AstPath<JavaNonTerminal>): builders.Doc[];
|
||||
export declare function printComment(node: JavaTerminal): string | builders.Doc[];
|
||||
export declare function hasLeadingComments(node: JavaNode): boolean | undefined;
|
||||
export declare function indentInParentheses(contents: Doc, opts?: {
|
||||
shouldBreak?: boolean;
|
||||
}): builders.Group | "()";
|
||||
export declare function printArrayInitializer<T extends JavaNonTerminal, P extends IterProperties<T["children"]>>(path: AstPath<T>, print: JavaPrintFn, options: JavaParserOptions, child: P): builders.Group | "{}";
|
||||
export declare function printBlock(path: AstPath<JavaNonTerminal>, contents: Doc[]): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[];
|
||||
export declare function printName(path: AstPath<JavaNonTerminal & {
|
||||
children: {
|
||||
Identifier: IToken[];
|
||||
};
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
export declare function printList<T extends JavaNonTerminal, P extends IterProperties<T["children"]>>(path: AstPath<T>, print: JavaPrintFn, child: P): builders.Doc[];
|
||||
export declare function printClassPermits(path: AstPath<ClassPermitsCstNode | InterfacePermitsCstNode>, print: JavaPrintFn): builders.Group;
|
||||
export declare function printClassType(path: AstPath<JavaNonTerminal & {
|
||||
children: ClassTypeCtx;
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
export declare function isBinaryExpression(expression: ExpressionCstNode): boolean;
|
||||
export declare function findBaseIndent(lines: string[]): number;
|
||||
export declare function isEmptyStatement(statement: StatementCstNode): boolean;
|
||||
export declare function isNonTerminal(node: CstElement): node is JavaNonTerminal;
|
||||
export declare function isTerminal(node: CstElement): node is IToken;
|
||||
export type JavaNode = CstElement & {
|
||||
comments?: JavaComment[];
|
||||
};
|
||||
export type JavaNonTerminal = Exclude<JavaNode, IToken>;
|
||||
export type JavaTerminal = Exclude<JavaNode, CstNode>;
|
||||
export type JavaNodePrinters = {
|
||||
[T in JavaNonTerminal["name"]]: JavaNodePrinter<T>;
|
||||
};
|
||||
export type JavaNodePrinter<T> = (path: AstPath<Extract<JavaNonTerminal, {
|
||||
name: T;
|
||||
}>>, print: JavaPrintFn, options: JavaParserOptions, args?: unknown) => Doc;
|
||||
export type JavaPrintFn = (path: AstPath<JavaNode>, args?: unknown) => Doc;
|
||||
export type JavaParserOptions = ParserOptions<JavaNode> & {
|
||||
entrypoint?: string;
|
||||
};
|
||||
export type IterProperties<T> = T extends any[] ? IndexProperties<T> : ArrayProperties<T>;
|
||||
type Key<T> = T extends T ? keyof T : never;
|
||||
type ModifierNode = JavaNonTerminal & {
|
||||
children: {
|
||||
annotation?: AnnotationCstNode[];
|
||||
};
|
||||
};
|
||||
type IsTuple<T> = T extends [] ? true : T extends [infer _First, ...infer Remain] ? IsTuple<Remain> : false;
|
||||
type IndexProperties<T extends {
|
||||
length: number;
|
||||
}> = IsTuple<T> extends true ? Exclude<Partial<T>["length"], T["length"]> : number;
|
||||
type ArrayProperties<T> = {
|
||||
[K in keyof T]: NonNullable<T[K]> extends readonly any[] ? K : never;
|
||||
}[keyof T];
|
||||
type ArrayElement<T> = T extends Array<infer E> ? E : never;
|
||||
type MapCallback<T, U> = (path: AstPath<ArrayElement<T>>, index: number, value: any) => U;
|
||||
type IndexValue<T, P> = T extends any[] ? P extends number ? T[P] : never : P extends keyof T ? T[P] : never;
|
||||
export {};
|
||||
@@ -1,239 +0,0 @@
|
||||
import { builders } from "prettier/doc";
|
||||
import parser from "../parser.js";
|
||||
const { group, hardline, ifBreak, indent, join, line, softline } = builders;
|
||||
export function onlyDefinedKey(obj, options) {
|
||||
const keys = definedKeys(obj, options);
|
||||
if (keys.length === 1) {
|
||||
return keys[0];
|
||||
}
|
||||
throw new Error(keys.length > 1
|
||||
? `More than one defined key found: ${keys}`
|
||||
: "No defined keys found");
|
||||
}
|
||||
export function definedKeys(obj, options) {
|
||||
return (options !== null && options !== void 0 ? options : Object.keys(obj)).filter(key => obj[key] !== undefined);
|
||||
}
|
||||
const indexByModifier = [
|
||||
"public",
|
||||
"protected",
|
||||
"private",
|
||||
"abstract",
|
||||
"default",
|
||||
"static",
|
||||
"final",
|
||||
"transient",
|
||||
"volatile",
|
||||
"synchronized",
|
||||
"native",
|
||||
"sealed",
|
||||
"non-sealed",
|
||||
"strictfp"
|
||||
].reduce((map, name, index) => map.set(name, index), new Map());
|
||||
export function printWithModifiers(path, print, modifierChild, contents, noTypeAnnotations = false) {
|
||||
const declarationAnnotations = [];
|
||||
const otherModifiers = [];
|
||||
const typeAnnotations = [];
|
||||
each(path, modifierPath => {
|
||||
const { children } = modifierPath.node;
|
||||
const modifier = print(modifierPath);
|
||||
if (children.annotation) {
|
||||
(otherModifiers.length ? typeAnnotations : declarationAnnotations).push(modifier);
|
||||
}
|
||||
else {
|
||||
otherModifiers.push(modifier);
|
||||
declarationAnnotations.push(...typeAnnotations);
|
||||
typeAnnotations.length = 0;
|
||||
}
|
||||
}, modifierChild);
|
||||
if (noTypeAnnotations) {
|
||||
declarationAnnotations.push(...typeAnnotations);
|
||||
typeAnnotations.length = 0;
|
||||
}
|
||||
otherModifiers.sort((a, b) => indexByModifier.get(a) - indexByModifier.get(b));
|
||||
return join(hardline, [
|
||||
...declarationAnnotations,
|
||||
join(" ", [...otherModifiers, ...typeAnnotations, contents])
|
||||
]);
|
||||
}
|
||||
export function hasDeclarationAnnotations(modifiers) {
|
||||
let hasAnnotation = false;
|
||||
let hasNonAnnotation = false;
|
||||
for (const modifier of modifiers) {
|
||||
if (modifier.children.annotation) {
|
||||
hasAnnotation = true;
|
||||
}
|
||||
else if (hasAnnotation) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
hasNonAnnotation = true;
|
||||
}
|
||||
}
|
||||
return hasAnnotation && !hasNonAnnotation;
|
||||
}
|
||||
export function call(path, callback, child) {
|
||||
return path.map(callback, "children", child)[0];
|
||||
}
|
||||
export function each(path, callback, child) {
|
||||
if (path.node.children[child]) {
|
||||
path.each(callback, "children", child);
|
||||
}
|
||||
}
|
||||
export function map(path, callback, child) {
|
||||
return path.node.children[child] ? path.map(callback, "children", child) : [];
|
||||
}
|
||||
export function flatMap(path, callback, children) {
|
||||
return children
|
||||
.flatMap(child => map(path, callback, child).map((doc, index) => {
|
||||
const node = path.node.children[child][index];
|
||||
return {
|
||||
doc,
|
||||
startOffset: parser.locStart(node)
|
||||
};
|
||||
}))
|
||||
.sort((a, b) => a.startOffset - b.startOffset)
|
||||
.map(({ doc }) => doc);
|
||||
}
|
||||
export function printSingle(path, print, _, args) {
|
||||
return call(path, childPath => print(childPath, args), onlyDefinedKey(path.node.children));
|
||||
}
|
||||
export function lineStartWithComments(node) {
|
||||
const { comments, location } = node;
|
||||
return comments
|
||||
? Math.min(location.startLine, comments[0].startLine)
|
||||
: location.startLine;
|
||||
}
|
||||
export function lineEndWithComments(node) {
|
||||
const { comments, location } = node;
|
||||
return comments
|
||||
? Math.max(location.endLine, comments.at(-1).endLine)
|
||||
: location.endLine;
|
||||
}
|
||||
export function printDanglingComments(path) {
|
||||
if (!path.node.comments) {
|
||||
return [];
|
||||
}
|
||||
const comments = [];
|
||||
path.each(commentPath => {
|
||||
const comment = commentPath.node;
|
||||
if (comment.leading || comment.trailing) {
|
||||
return;
|
||||
}
|
||||
comment.printed = true;
|
||||
comments.push(printComment(comment));
|
||||
}, "comments");
|
||||
return join(hardline, comments);
|
||||
}
|
||||
export function printComment(node) {
|
||||
const { image } = node;
|
||||
const lines = image.split("\n").map(line => line.trim());
|
||||
return lines.length > 1 &&
|
||||
lines[0].startsWith("/*") &&
|
||||
lines.slice(1).every(line => line.startsWith("*")) &&
|
||||
lines.at(-1).endsWith("*/")
|
||||
? join(hardline, lines.map((line, index) => (index === 0 ? line : ` ${line}`)))
|
||||
: image;
|
||||
}
|
||||
export function hasLeadingComments(node) {
|
||||
var _a;
|
||||
return (_a = node.comments) === null || _a === void 0 ? void 0 : _a.some(({ leading }) => leading);
|
||||
}
|
||||
export function indentInParentheses(contents, opts) {
|
||||
return !Array.isArray(contents) || contents.length
|
||||
? group(["(", indent([softline, contents]), softline, ")"], opts)
|
||||
: "()";
|
||||
}
|
||||
export function printArrayInitializer(path, print, options, child) {
|
||||
const list = [];
|
||||
if (child && child in path.node.children) {
|
||||
list.push(call(path, print, child));
|
||||
if (options.trailingComma !== "none") {
|
||||
list.push(ifBreak(","));
|
||||
}
|
||||
}
|
||||
list.push(...printDanglingComments(path));
|
||||
return list.length ? group(["{", indent([line, ...list]), line, "}"]) : "{}";
|
||||
}
|
||||
export function printBlock(path, contents) {
|
||||
if (!contents.length) {
|
||||
const danglingComments = printDanglingComments(path);
|
||||
return danglingComments.length
|
||||
? ["{", indent([hardline, ...danglingComments]), hardline, "}"]
|
||||
: "{}";
|
||||
}
|
||||
return group([
|
||||
"{",
|
||||
indent([hardline, ...join(hardline, contents)]),
|
||||
hardline,
|
||||
"}"
|
||||
]);
|
||||
}
|
||||
export function printName(path, print) {
|
||||
return join(".", map(path, print, "Identifier"));
|
||||
}
|
||||
export function printList(path, print, child) {
|
||||
return join([",", line], map(path, print, child));
|
||||
}
|
||||
export function printClassPermits(path, print) {
|
||||
return group([
|
||||
"permits",
|
||||
indent([line, group(printList(path, print, "typeName"))])
|
||||
]);
|
||||
}
|
||||
export function printClassType(path, print) {
|
||||
const { children } = path.node;
|
||||
return definedKeys(children, ["annotation", "Identifier", "typeArguments"])
|
||||
.flatMap(child => children[child].map((node, index) => ({
|
||||
child,
|
||||
index,
|
||||
startOffset: parser.locStart(node)
|
||||
})))
|
||||
.sort((a, b) => a.startOffset - b.startOffset)
|
||||
.flatMap(({ child, index: childIndex }, index, array) => {
|
||||
const node = children[child][childIndex];
|
||||
const next = array.at(index + 1);
|
||||
const nextNode = next && children[next.child][next.index];
|
||||
const docs = [path.call(print, "children", child, childIndex)];
|
||||
if (nextNode) {
|
||||
if (isNonTerminal(node)) {
|
||||
docs.push(node.name === "annotation" ? " " : ".");
|
||||
}
|
||||
else if (isTerminal(nextNode) || nextNode.name === "annotation") {
|
||||
docs.push(".");
|
||||
}
|
||||
}
|
||||
return docs;
|
||||
});
|
||||
}
|
||||
export function isBinaryExpression(expression) {
|
||||
var _a;
|
||||
const conditionalExpression = (_a = expression.children.conditionalExpression) === null || _a === void 0 ? void 0 : _a[0].children;
|
||||
if (!conditionalExpression) {
|
||||
return false;
|
||||
}
|
||||
const isTernary = conditionalExpression.QuestionMark !== undefined;
|
||||
if (isTernary) {
|
||||
return false;
|
||||
}
|
||||
const hasNonAssignmentOperators = Object.values(conditionalExpression.binaryExpression[0].children).some(child => {
|
||||
var _a;
|
||||
return isTerminal(child[0]) &&
|
||||
!((_a = child[0].tokenType.CATEGORIES) === null || _a === void 0 ? void 0 : _a.some(category => category.name === "AssignmentOperator"));
|
||||
});
|
||||
return hasNonAssignmentOperators;
|
||||
}
|
||||
export function findBaseIndent(lines) {
|
||||
return lines.length
|
||||
? Math.min(...lines.map(line => line.search(/\S/)).filter(indent => indent >= 0))
|
||||
: 0;
|
||||
}
|
||||
export function isEmptyStatement(statement) {
|
||||
var _a;
|
||||
return (((_a = statement.children.statementWithoutTrailingSubstatement) === null || _a === void 0 ? void 0 : _a[0].children.emptyStatement) !== undefined);
|
||||
}
|
||||
export function isNonTerminal(node) {
|
||||
return !isTerminal(node);
|
||||
}
|
||||
export function isTerminal(node) {
|
||||
return "tokenType" in node;
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
import type { JavaNodePrinter, JavaNodePrinters } from "./helpers.js";
|
||||
export declare function printerForNodeType<T extends keyof JavaNodePrinters>(type: T): JavaNodePrinter<T>;
|
||||
@@ -1,13 +0,0 @@
|
||||
import arrays from "./arrays.js";
|
||||
import blocksAndStatements from "./blocks-and-statements.js";
|
||||
import classes from "./classes.js";
|
||||
import expressions from "./expressions.js";
|
||||
import interfaces from "./interfaces.js";
|
||||
import lexicalStructure from "./lexical-structure.js";
|
||||
import names from "./names.js";
|
||||
import packagesAndModules from "./packages-and-modules.js";
|
||||
import typesValuesAndVariables from "./types-values-and-variables.js";
|
||||
const printersByNodeType = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, arrays), blocksAndStatements), classes), expressions), interfaces), lexicalStructure), names), packagesAndModules), typesValuesAndVariables);
|
||||
export function printerForNodeType(type) {
|
||||
return printersByNodeType[type];
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import { builders } from "prettier/doc";
|
||||
import { printClassPermits, printSingle } from "./helpers.js";
|
||||
declare const _default: {
|
||||
interfaceDeclaration(path: import("prettier").AstPath<import("java-parser").InterfaceDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
normalInterfaceDeclaration(path: import("prettier").AstPath<import("java-parser").NormalInterfaceDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
interfaceModifier: typeof printSingle;
|
||||
interfaceExtends(path: import("prettier").AstPath<import("java-parser").InterfaceExtendsCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Group;
|
||||
interfacePermits: typeof printClassPermits;
|
||||
interfaceBody(path: import("prettier").AstPath<import("java-parser").InterfaceBodyCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[];
|
||||
interfaceMemberDeclaration(path: import("prettier").AstPath<import("java-parser").InterfaceMemberDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc;
|
||||
constantDeclaration(path: import("prettier").AstPath<import("java-parser").ConstantDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
constantModifier: typeof printSingle;
|
||||
interfaceMethodDeclaration(path: import("prettier").AstPath<import("java-parser").InterfaceMethodDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
interfaceMethodModifier: typeof printSingle;
|
||||
annotationInterfaceDeclaration(path: import("prettier").AstPath<import("java-parser").AnnotationInterfaceDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
annotationInterfaceBody(path: import("prettier").AstPath<import("java-parser").AnnotationInterfaceBodyCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[];
|
||||
annotationInterfaceMemberDeclaration(path: import("prettier").AstPath<import("java-parser").AnnotationInterfaceMemberDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc;
|
||||
annotationInterfaceElementDeclaration(path: import("prettier").AstPath<import("java-parser").AnnotationInterfaceElementDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
annotationInterfaceElementModifier: typeof printSingle;
|
||||
defaultValue(path: import("prettier").AstPath<import("java-parser").DefaultValueCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
annotation(path: import("prettier").AstPath<import("java-parser").AnnotationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
elementValuePairList(path: import("prettier").AstPath<import("java-parser").ElementValuePairListCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
elementValuePair(path: import("prettier").AstPath<import("java-parser").ElementValuePairCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
elementValue: typeof printSingle;
|
||||
elementValueArrayInitializer(path: import("prettier").AstPath<import("java-parser").ElementValueArrayInitializerCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn, options: import("./helpers.js").JavaParserOptions): builders.Group | "{}";
|
||||
elementValueList(path: import("prettier").AstPath<import("java-parser").ElementValueListCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Group;
|
||||
};
|
||||
export default _default;
|
||||
@@ -1,157 +0,0 @@
|
||||
import { builders } from "prettier/doc";
|
||||
import { call, each, hasDeclarationAnnotations, indentInParentheses, lineEndWithComments, lineStartWithComments, onlyDefinedKey, printArrayInitializer, printBlock, printClassPermits, printList, printSingle, printWithModifiers } from "./helpers.js";
|
||||
const { group, hardline, indent, join, line } = builders;
|
||||
export default {
|
||||
interfaceDeclaration(path, print) {
|
||||
const declarationKey = onlyDefinedKey(path.node.children, [
|
||||
"annotationInterfaceDeclaration",
|
||||
"normalInterfaceDeclaration"
|
||||
]);
|
||||
return printWithModifiers(path, print, "interfaceModifier", call(path, print, declarationKey), true);
|
||||
},
|
||||
normalInterfaceDeclaration(path, print) {
|
||||
const { interfaceExtends, interfacePermits, typeParameters } = path.node.children;
|
||||
const header = ["interface ", call(path, print, "typeIdentifier")];
|
||||
if (typeParameters) {
|
||||
header.push(call(path, print, "typeParameters"));
|
||||
}
|
||||
if (interfaceExtends) {
|
||||
header.push(indent([line, call(path, print, "interfaceExtends")]));
|
||||
}
|
||||
if (interfacePermits) {
|
||||
header.push(indent([line, call(path, print, "interfacePermits")]));
|
||||
}
|
||||
return [group(header), " ", call(path, print, "interfaceBody")];
|
||||
},
|
||||
interfaceModifier: printSingle,
|
||||
interfaceExtends(path, print) {
|
||||
return group([
|
||||
"extends",
|
||||
indent([line, call(path, print, "interfaceTypeList")])
|
||||
]);
|
||||
},
|
||||
interfacePermits: printClassPermits,
|
||||
interfaceBody(path, print) {
|
||||
const declarations = [];
|
||||
let previousRequiresPadding = false;
|
||||
each(path, declarationPath => {
|
||||
var _a, _b, _c, _d;
|
||||
const declaration = print(declarationPath);
|
||||
if (declaration === "") {
|
||||
return;
|
||||
}
|
||||
const { node, previous } = declarationPath;
|
||||
const constantDeclaration = (_a = node.children.constantDeclaration) === null || _a === void 0 ? void 0 : _a[0].children;
|
||||
const methodDeclaration = (_b = node.children.interfaceMethodDeclaration) === null || _b === void 0 ? void 0 : _b[0].children;
|
||||
const currentRequiresPadding = (!constantDeclaration && !methodDeclaration) ||
|
||||
(methodDeclaration === null || methodDeclaration === void 0 ? void 0 : methodDeclaration.methodBody[0].children.block) !== undefined ||
|
||||
hasDeclarationAnnotations((_d = (_c = constantDeclaration === null || constantDeclaration === void 0 ? void 0 : constantDeclaration.constantModifier) !== null && _c !== void 0 ? _c : methodDeclaration === null || methodDeclaration === void 0 ? void 0 : methodDeclaration.interfaceMethodModifier) !== null && _d !== void 0 ? _d : []);
|
||||
const blankLine = declarations.length > 0 &&
|
||||
(previousRequiresPadding ||
|
||||
currentRequiresPadding ||
|
||||
lineStartWithComments(node) > lineEndWithComments(previous) + 1);
|
||||
declarations.push(blankLine ? [hardline, declaration] : declaration);
|
||||
previousRequiresPadding = currentRequiresPadding;
|
||||
}, "interfaceMemberDeclaration");
|
||||
return printBlock(path, declarations);
|
||||
},
|
||||
interfaceMemberDeclaration(path, print) {
|
||||
const { children } = path.node;
|
||||
return children.Semicolon
|
||||
? ""
|
||||
: call(path, print, onlyDefinedKey(children));
|
||||
},
|
||||
constantDeclaration(path, print) {
|
||||
const declaration = [
|
||||
call(path, print, "unannType"),
|
||||
" ",
|
||||
call(path, print, "variableDeclaratorList"),
|
||||
";"
|
||||
];
|
||||
return printWithModifiers(path, print, "constantModifier", declaration);
|
||||
},
|
||||
constantModifier: printSingle,
|
||||
interfaceMethodDeclaration(path, print) {
|
||||
const declaration = [
|
||||
call(path, print, "methodHeader"),
|
||||
path.node.children.methodBody[0].children.Semicolon ? "" : " ",
|
||||
call(path, print, "methodBody")
|
||||
];
|
||||
return printWithModifiers(path, print, "interfaceMethodModifier", declaration);
|
||||
},
|
||||
interfaceMethodModifier: printSingle,
|
||||
annotationInterfaceDeclaration(path, print) {
|
||||
return join(" ", [
|
||||
"@interface",
|
||||
call(path, print, "typeIdentifier"),
|
||||
call(path, print, "annotationInterfaceBody")
|
||||
]);
|
||||
},
|
||||
annotationInterfaceBody(path, print) {
|
||||
const declarations = [];
|
||||
each(path, declarationPath => {
|
||||
const declaration = print(declarationPath);
|
||||
if (declaration === "") {
|
||||
return;
|
||||
}
|
||||
declarations.push(declarationPath.isFirst ? declaration : [hardline, declaration]);
|
||||
}, "annotationInterfaceMemberDeclaration");
|
||||
return printBlock(path, declarations);
|
||||
},
|
||||
annotationInterfaceMemberDeclaration(path, print) {
|
||||
const { children } = path.node;
|
||||
return children.Semicolon
|
||||
? ""
|
||||
: call(path, print, onlyDefinedKey(children));
|
||||
},
|
||||
annotationInterfaceElementDeclaration(path, print) {
|
||||
const { dims, defaultValue } = path.node.children;
|
||||
const declaration = [
|
||||
call(path, print, "unannType"),
|
||||
" ",
|
||||
call(path, print, "Identifier"),
|
||||
"()"
|
||||
];
|
||||
if (dims) {
|
||||
declaration.push(call(path, print, "dims"));
|
||||
}
|
||||
if (defaultValue) {
|
||||
declaration.push(" ", call(path, print, "defaultValue"));
|
||||
}
|
||||
declaration.push(";");
|
||||
return printWithModifiers(path, print, "annotationInterfaceElementModifier", declaration);
|
||||
},
|
||||
annotationInterfaceElementModifier: printSingle,
|
||||
defaultValue(path, print) {
|
||||
return ["default ", call(path, print, "elementValue")];
|
||||
},
|
||||
annotation(path, print) {
|
||||
const { children } = path.node;
|
||||
const annotation = ["@", call(path, print, "typeName")];
|
||||
if (children.elementValue || children.elementValuePairList) {
|
||||
const valuesKey = onlyDefinedKey(children, [
|
||||
"elementValue",
|
||||
"elementValuePairList"
|
||||
]);
|
||||
annotation.push(indentInParentheses(call(path, print, valuesKey)));
|
||||
}
|
||||
return annotation;
|
||||
},
|
||||
elementValuePairList(path, print) {
|
||||
return printList(path, print, "elementValuePair");
|
||||
},
|
||||
elementValuePair(path, print) {
|
||||
return join(" ", [
|
||||
call(path, print, "Identifier"),
|
||||
call(path, print, "Equals"),
|
||||
call(path, print, "elementValue")
|
||||
]);
|
||||
},
|
||||
elementValue: printSingle,
|
||||
elementValueArrayInitializer(path, print, options) {
|
||||
return printArrayInitializer(path, print, options, "elementValueList");
|
||||
},
|
||||
elementValueList(path, print) {
|
||||
return group(printList(path, print, "elementValue"));
|
||||
}
|
||||
};
|
||||
@@ -1,14 +0,0 @@
|
||||
import { builders } from "prettier/doc";
|
||||
import { printSingle } from "./helpers.js";
|
||||
declare const _default: {
|
||||
literal(path: import("prettier").AstPath<import("java-parser").LiteralCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc;
|
||||
integerLiteral: typeof printSingle;
|
||||
floatingPointLiteral: typeof printSingle;
|
||||
booleanLiteral: typeof printSingle;
|
||||
shiftOperator(path: import("prettier").AstPath<import("java-parser").ShiftOperatorCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
};
|
||||
export default _default;
|
||||
@@ -1,29 +0,0 @@
|
||||
import { builders } from "prettier/doc";
|
||||
import { findBaseIndent, map, onlyDefinedKey, printSingle } from "./helpers.js";
|
||||
const { hardline, indent, join } = builders;
|
||||
export default {
|
||||
literal(path, print) {
|
||||
const { TextBlock } = path.node.children;
|
||||
if (!TextBlock) {
|
||||
return printSingle(path, print);
|
||||
}
|
||||
const [open, ...lines] = TextBlock[0].image.split("\n");
|
||||
const baseIndent = findBaseIndent(lines);
|
||||
const textBlock = join(hardline, [
|
||||
open,
|
||||
...lines.map(line => line.slice(baseIndent))
|
||||
]);
|
||||
const ancestor = path.getNode(14);
|
||||
return (ancestor === null || ancestor === void 0 ? void 0 : ancestor.name) === "variableInitializer" ||
|
||||
((ancestor === null || ancestor === void 0 ? void 0 : ancestor.name) === "binaryExpression" &&
|
||||
ancestor.children.AssignmentOperator)
|
||||
? indent(textBlock)
|
||||
: textBlock;
|
||||
},
|
||||
integerLiteral: printSingle,
|
||||
floatingPointLiteral: printSingle,
|
||||
booleanLiteral: printSingle,
|
||||
shiftOperator(path, print) {
|
||||
return map(path, print, onlyDefinedKey(path.node.children));
|
||||
}
|
||||
};
|
||||
@@ -1,12 +0,0 @@
|
||||
import { printName, printSingle } from "./helpers.js";
|
||||
declare const _default: {
|
||||
typeIdentifier: typeof printSingle;
|
||||
moduleName: typeof printName;
|
||||
packageName: typeof printName;
|
||||
typeName: typeof printName;
|
||||
expressionName: typeof printName;
|
||||
methodName: typeof printSingle;
|
||||
packageOrTypeName: typeof printName;
|
||||
ambiguousName: typeof printName;
|
||||
};
|
||||
export default _default;
|
||||
@@ -1,11 +0,0 @@
|
||||
import { printName, printSingle } from "./helpers.js";
|
||||
export default {
|
||||
typeIdentifier: printSingle,
|
||||
moduleName: printName,
|
||||
packageName: printName,
|
||||
typeName: printName,
|
||||
expressionName: printName,
|
||||
methodName: printSingle,
|
||||
packageOrTypeName: printName,
|
||||
ambiguousName: printName
|
||||
};
|
||||
@@ -1,46 +0,0 @@
|
||||
import type { ExportsModuleDirectiveCstNode, ImportDeclarationCstNode, OpensModuleDirectiveCstNode } from "java-parser";
|
||||
import type { AstPath } from "prettier";
|
||||
import { builders } from "prettier/doc";
|
||||
import { printSingle, type JavaPrintFn } from "./helpers.js";
|
||||
declare const _default: {
|
||||
compilationUnit(path: AstPath<import("java-parser").CompilationUnitCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
ordinaryCompilationUnit(path: AstPath<import("java-parser").OrdinaryCompilationUnitCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
modularCompilationUnit(path: AstPath<import("java-parser").ModularCompilationUnitCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
packageDeclaration(path: AstPath<import("java-parser").PackageDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
packageModifier: typeof printSingle;
|
||||
importDeclaration(path: AstPath<ImportDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc;
|
||||
typeDeclaration(path: AstPath<import("java-parser").TypeDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc;
|
||||
moduleDeclaration(path: AstPath<import("java-parser").ModuleDeclarationCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
moduleDirective: typeof printSingle;
|
||||
requiresModuleDirective(path: AstPath<import("java-parser").RequiresModuleDirectiveCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
exportsModuleDirective(path: AstPath<ExportsModuleDirectiveCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
opensModuleDirective(path: AstPath<OpensModuleDirectiveCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
usesModuleDirective(path: AstPath<import("java-parser").UsesModuleDirectiveCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
providesModuleDirective(path: AstPath<import("java-parser").ProvidesModuleDirectiveCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: JavaPrintFn): builders.Doc[];
|
||||
requiresModifier: typeof printSingle;
|
||||
};
|
||||
export default _default;
|
||||
@@ -1,169 +0,0 @@
|
||||
import { builders } from "prettier/doc";
|
||||
import { call, lineEndWithComments, lineStartWithComments, map, printBlock, printDanglingComments, printName, printSingle } from "./helpers.js";
|
||||
const { group, hardline, indent, join, line } = builders;
|
||||
export default {
|
||||
compilationUnit(path, print) {
|
||||
return [...printDanglingComments(path), printSingle(path, print), hardline];
|
||||
},
|
||||
ordinaryCompilationUnit(path, print) {
|
||||
const { children } = path.node;
|
||||
const declarations = [];
|
||||
if (children.packageDeclaration) {
|
||||
declarations.push(call(path, print, "packageDeclaration"));
|
||||
}
|
||||
if (children.importDeclaration) {
|
||||
const staticCount = sortImports(children.importDeclaration);
|
||||
const importDeclarations = map(path, print, "importDeclaration").filter(doc => doc !== "");
|
||||
const staticDeclarations = importDeclarations.slice(0, staticCount);
|
||||
const nonStaticDeclarations = importDeclarations.slice(staticCount);
|
||||
declarations.push(...[staticDeclarations, nonStaticDeclarations]
|
||||
.filter(({ length }) => length)
|
||||
.map(declarations => join(hardline, declarations)));
|
||||
}
|
||||
if (children.typeDeclaration) {
|
||||
declarations.push(...map(path, print, "typeDeclaration").filter(declaration => declaration !== ""));
|
||||
}
|
||||
return join([hardline, hardline], declarations);
|
||||
},
|
||||
modularCompilationUnit(path, print) {
|
||||
const { children } = path.node;
|
||||
const declarations = [];
|
||||
if (children.importDeclaration) {
|
||||
const staticCount = sortImports(children.importDeclaration);
|
||||
const importDeclarations = map(path, print, "importDeclaration").filter(doc => doc !== "");
|
||||
const staticDeclarations = importDeclarations.slice(0, staticCount);
|
||||
const nonStaticDeclarations = importDeclarations.slice(staticCount);
|
||||
declarations.push(...[staticDeclarations, nonStaticDeclarations]
|
||||
.filter(({ length }) => length)
|
||||
.map(declarations => join(hardline, declarations)));
|
||||
}
|
||||
declarations.push(call(path, print, "moduleDeclaration"));
|
||||
return join([hardline, hardline], declarations);
|
||||
},
|
||||
packageDeclaration(path, print) {
|
||||
return join(hardline, [
|
||||
...map(path, print, "packageModifier"),
|
||||
["package ", printName(path, print), ";"]
|
||||
]);
|
||||
},
|
||||
packageModifier: printSingle,
|
||||
importDeclaration(path, print) {
|
||||
const { children } = path.node;
|
||||
if (children.emptyStatement) {
|
||||
return call(path, print, "emptyStatement");
|
||||
}
|
||||
const declaration = ["import "];
|
||||
if (children.Static) {
|
||||
declaration.push("static ");
|
||||
}
|
||||
declaration.push(call(path, print, "packageOrTypeName"));
|
||||
if (children.Star) {
|
||||
declaration.push(".*");
|
||||
}
|
||||
declaration.push(";");
|
||||
return declaration;
|
||||
},
|
||||
typeDeclaration(path, print) {
|
||||
return path.node.children.Semicolon ? "" : printSingle(path, print);
|
||||
},
|
||||
moduleDeclaration(path, print) {
|
||||
const { annotation, Open } = path.node.children;
|
||||
const prefix = [];
|
||||
if (annotation) {
|
||||
prefix.push(...map(path, print, "annotation"));
|
||||
}
|
||||
if (Open) {
|
||||
prefix.push("open");
|
||||
}
|
||||
const declarations = map(path, declarationPath => {
|
||||
const declaration = print(declarationPath);
|
||||
const { node, previous } = declarationPath;
|
||||
return !previous ||
|
||||
lineStartWithComments(node) <= lineEndWithComments(previous) + 1
|
||||
? declaration
|
||||
: [hardline, declaration];
|
||||
}, "moduleDirective");
|
||||
return join(" ", [
|
||||
...prefix,
|
||||
"module",
|
||||
printName(path, print),
|
||||
printBlock(path, declarations)
|
||||
]);
|
||||
},
|
||||
moduleDirective: printSingle,
|
||||
requiresModuleDirective(path, print) {
|
||||
return join(" ", [
|
||||
"requires",
|
||||
...map(path, print, "requiresModifier"),
|
||||
[call(path, print, "moduleName"), ";"]
|
||||
]);
|
||||
},
|
||||
exportsModuleDirective(path, print) {
|
||||
return printToModuleNamesDirective(path, print, "exports");
|
||||
},
|
||||
opensModuleDirective(path, print) {
|
||||
return printToModuleNamesDirective(path, print, "opens");
|
||||
},
|
||||
usesModuleDirective(path, print) {
|
||||
return ["uses ", call(path, print, "typeName"), ";"];
|
||||
},
|
||||
providesModuleDirective(path, print) {
|
||||
const [firstTypeName, ...restTypeNames] = map(path, print, "typeName");
|
||||
return [
|
||||
"provides ",
|
||||
firstTypeName,
|
||||
group(indent([
|
||||
line,
|
||||
group(indent(["with", line, ...join([",", line], restTypeNames)]))
|
||||
])),
|
||||
";"
|
||||
];
|
||||
},
|
||||
requiresModifier: printSingle
|
||||
};
|
||||
function sortImports(importDeclarations) {
|
||||
importDeclarations.sort(({ children: a }, { children: b }) => {
|
||||
if (a.Static && !b.Static) {
|
||||
return -1;
|
||||
}
|
||||
else if (b.Static && !a.Static) {
|
||||
return 1;
|
||||
}
|
||||
if (!b.packageOrTypeName) {
|
||||
if (a.packageOrTypeName) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else if (!a.packageOrTypeName) {
|
||||
return 1;
|
||||
}
|
||||
return compareFqn(a.packageOrTypeName[0], b.packageOrTypeName[0]);
|
||||
});
|
||||
return importDeclarations.reduce((staticCount, importDeclaration) => importDeclaration.children.Static ? staticCount + 1 : staticCount, 0);
|
||||
}
|
||||
function compareFqn(a, b) {
|
||||
const identifiersA = a.children.Identifier;
|
||||
const identifiersB = b.children.Identifier;
|
||||
const minParts = Math.min(identifiersA.length, identifiersB.length);
|
||||
for (let i = 0; i < minParts; i++) {
|
||||
const imageA = identifiersA[i].image;
|
||||
const imageB = identifiersB[i].image;
|
||||
if (imageA < imageB) {
|
||||
return -1;
|
||||
}
|
||||
else if (imageA > imageB) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return identifiersA.length - identifiersB.length;
|
||||
}
|
||||
function printToModuleNamesDirective(path, print, prefix) {
|
||||
const directive = [prefix, " ", call(path, print, "packageName")];
|
||||
if (path.node.children.moduleName) {
|
||||
const moduleNames = join([",", line], map(path, print, "moduleName"));
|
||||
directive.push(group(indent([line, group(indent(["to", line, ...moduleNames]))])));
|
||||
}
|
||||
directive.push(";");
|
||||
return directive;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import { builders } from "prettier/doc";
|
||||
import { printClassType, printSingle } from "./helpers.js";
|
||||
declare const _default: {
|
||||
primitiveType(path: import("prettier").AstPath<import("java-parser").PrimitiveTypeCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
numericType: typeof printSingle;
|
||||
integralType: typeof printSingle;
|
||||
floatingPointType: typeof printSingle;
|
||||
referenceType(path: import("prettier").AstPath<import("java-parser").ReferenceTypeCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
classOrInterfaceType: typeof printSingle;
|
||||
classType: typeof printClassType;
|
||||
interfaceType: typeof printSingle;
|
||||
typeVariable(path: import("prettier").AstPath<import("java-parser").TypeVariableCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
dims(path: import("prettier").AstPath<import("java-parser").DimsCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
typeParameter(path: import("prettier").AstPath<import("java-parser").TypeParameterCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
typeParameterModifier: typeof printSingle;
|
||||
typeBound(path: import("prettier").AstPath<import("java-parser").TypeBoundCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
additionalBound(path: import("prettier").AstPath<import("java-parser").AdditionalBoundCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
typeArguments(path: import("prettier").AstPath<import("java-parser").TypeArgumentsCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Group;
|
||||
typeArgumentList(path: import("prettier").AstPath<import("java-parser").TypeArgumentListCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
typeArgument: typeof printSingle;
|
||||
wildcard(path: import("prettier").AstPath<import("java-parser").WildcardCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
wildcardBounds(path: import("prettier").AstPath<import("java-parser").WildcardBoundsCstNode & {
|
||||
comments?: import("../comments.js").JavaComment[];
|
||||
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
|
||||
};
|
||||
export default _default;
|
||||
@@ -1,90 +0,0 @@
|
||||
import { builders } from "prettier/doc";
|
||||
import { call, definedKeys, flatMap, isNonTerminal, map, onlyDefinedKey, printClassType, printList, printSingle } from "./helpers.js";
|
||||
const { group, indent, join, line, softline } = builders;
|
||||
export default {
|
||||
primitiveType(path, print) {
|
||||
const { children } = path.node;
|
||||
const typeKey = onlyDefinedKey(children, ["Boolean", "numericType"]);
|
||||
return join(" ", [
|
||||
...map(path, print, "annotation"),
|
||||
call(path, print, typeKey)
|
||||
]);
|
||||
},
|
||||
numericType: printSingle,
|
||||
integralType: printSingle,
|
||||
floatingPointType: printSingle,
|
||||
referenceType(path, print) {
|
||||
const { children } = path.node;
|
||||
const typeKey = onlyDefinedKey(children, [
|
||||
"primitiveType",
|
||||
"classOrInterfaceType"
|
||||
]);
|
||||
const type = call(path, print, typeKey);
|
||||
return join(" ", [
|
||||
...map(path, print, "annotation"),
|
||||
children.dims ? [type, call(path, print, "dims")] : type
|
||||
]);
|
||||
},
|
||||
classOrInterfaceType: printSingle,
|
||||
classType: printClassType,
|
||||
interfaceType: printSingle,
|
||||
typeVariable(path, print) {
|
||||
return join(" ", [
|
||||
...map(path, print, "annotation"),
|
||||
call(path, print, "Identifier")
|
||||
]);
|
||||
},
|
||||
dims(path, print) {
|
||||
return flatMap(path, childPath => {
|
||||
const child = print(childPath);
|
||||
return isNonTerminal(childPath.node) ? [child, " "] : child;
|
||||
}, definedKeys(path.node.children, ["annotation", "LSquare", "RSquare"]));
|
||||
},
|
||||
typeParameter(path, print) {
|
||||
const parameter = [
|
||||
...map(path, print, "typeParameterModifier"),
|
||||
call(path, print, "typeIdentifier")
|
||||
];
|
||||
if (path.node.children.typeBound) {
|
||||
parameter.push(call(path, print, "typeBound"));
|
||||
}
|
||||
return join(" ", parameter);
|
||||
},
|
||||
typeParameterModifier: printSingle,
|
||||
typeBound(path, print) {
|
||||
const bound = ["extends ", call(path, print, "classOrInterfaceType")];
|
||||
if (path.node.children.additionalBound) {
|
||||
bound.push(group(indent([line, ...join(line, map(path, print, "additionalBound"))])));
|
||||
}
|
||||
return bound;
|
||||
},
|
||||
additionalBound(path, print) {
|
||||
return ["& ", call(path, print, "interfaceType")];
|
||||
},
|
||||
typeArguments(path, print) {
|
||||
return group([
|
||||
"<",
|
||||
indent([softline, call(path, print, "typeArgumentList")]),
|
||||
softline,
|
||||
">"
|
||||
]);
|
||||
},
|
||||
typeArgumentList(path, print) {
|
||||
return printList(path, print, "typeArgument");
|
||||
},
|
||||
typeArgument: printSingle,
|
||||
wildcard(path, print) {
|
||||
const wildcard = [...map(path, print, "annotation"), "?"];
|
||||
if (path.node.children.wildcardBounds) {
|
||||
wildcard.push(call(path, print, "wildcardBounds"));
|
||||
}
|
||||
return join(" ", wildcard);
|
||||
},
|
||||
wildcardBounds(path, print) {
|
||||
return [
|
||||
path.node.children.Extends ? "extends" : "super",
|
||||
" ",
|
||||
call(path, print, "referenceType")
|
||||
];
|
||||
}
|
||||
};
|
||||
@@ -35,7 +35,6 @@ class TomlBeautifierVisitor extends BaseTomlCstVisitor {
|
||||
// Helper methods
|
||||
public mapVisit: (elements: TomlCstNode[] | undefined) => (Doc | string)[];
|
||||
public visitSingle: (ctx: TomlContext) => Doc | string;
|
||||
public visit: (ctx: TomlCstNode, inParam?: any) => Doc | string;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -57,26 +56,38 @@ class TomlBeautifierVisitor extends BaseTomlCstVisitor {
|
||||
const singleElement = getSingle(ctx);
|
||||
return this.visit(singleElement);
|
||||
};
|
||||
}
|
||||
|
||||
// Store reference to inherited visit method and override it
|
||||
const originalVisit = Object.getPrototypeOf(this).visit?.bind(this);
|
||||
this.visit = (ctx: TomlCstNode, inParam?: any): Doc | string => {
|
||||
if (!ctx) {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Override visit method to handle TOML CST nodes
|
||||
* Accepts both single node and array of nodes as per base class signature
|
||||
*/
|
||||
visit(cstNode: any, param?: any): any {
|
||||
// Handle array of nodes
|
||||
if (Array.isArray(cstNode)) {
|
||||
return cstNode.map(node => this.visit(node, param));
|
||||
}
|
||||
|
||||
const ctx = cstNode;
|
||||
if (!ctx) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// 确保节点有name属性才调用基类方法
|
||||
if (ctx.name) {
|
||||
// Try to use the inherited visit method first
|
||||
const originalVisit = super.visit;
|
||||
if (originalVisit) {
|
||||
try {
|
||||
return originalVisit(ctx, inParam);
|
||||
return originalVisit.call(this, ctx, param);
|
||||
} catch (error) {
|
||||
console.warn('Original visit method failed:', error);
|
||||
// Fallback to manual dispatch
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Fallback: manually dispatch based on node name/type
|
||||
const methodName = ctx.name;
|
||||
if (methodName && typeof (this as any)[methodName] === 'function') {
|
||||
if (typeof (this as any)[methodName] === 'function') {
|
||||
const visitMethod = (this as any)[methodName];
|
||||
try {
|
||||
if (ctx.children) {
|
||||
@@ -88,16 +99,16 @@ class TomlBeautifierVisitor extends BaseTomlCstVisitor {
|
||||
console.warn(`Visit method ${methodName} failed:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
// Final fallback: return image if available
|
||||
return ctx.image || '';
|
||||
};
|
||||
}
|
||||
|
||||
// Final fallback: return image if available
|
||||
return ctx.image || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit the root TOML document
|
||||
*/
|
||||
toml(ctx: TomlDocument): Doc {
|
||||
toml(ctx: any): Doc {
|
||||
// Handle empty toml document
|
||||
if (!ctx.expression) {
|
||||
return [line];
|
||||
@@ -164,7 +175,7 @@ class TomlBeautifierVisitor extends BaseTomlCstVisitor {
|
||||
/**
|
||||
* Visit an expression (keyval, table, or comment)
|
||||
*/
|
||||
expression(ctx: TomlExpression): Doc | string {
|
||||
expression(ctx: any): Doc | string {
|
||||
if (ctx.keyval) {
|
||||
let keyValDoc = this.visit(ctx.keyval[0]);
|
||||
if (ctx.Comment) {
|
||||
@@ -189,7 +200,7 @@ class TomlBeautifierVisitor extends BaseTomlCstVisitor {
|
||||
/**
|
||||
* Visit a key-value pair
|
||||
*/
|
||||
keyval(ctx: TomlKeyVal): Doc {
|
||||
keyval(ctx: any): Doc {
|
||||
const keyDoc = this.visit(ctx.key[0]);
|
||||
const valueDoc = this.visit(ctx.val[0]);
|
||||
return [keyDoc, ' = ', valueDoc];
|
||||
|
||||
@@ -1,265 +0,0 @@
|
||||
/**
|
||||
* 操作信息接口
|
||||
*/
|
||||
interface OperationInfo {
|
||||
controller: AbortController;
|
||||
createdAt: number;
|
||||
timeout?: number;
|
||||
timeoutId?: NodeJS.Timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步操作管理器
|
||||
* 用于管理异步操作的竞态条件,确保只有最新的操作有效
|
||||
* 支持操作超时和自动清理机制
|
||||
*
|
||||
* @template T 操作上下文的类型
|
||||
*/
|
||||
export class AsyncManager<T = any> {
|
||||
private operationSequence = 0;
|
||||
private pendingOperations = new Map<number, OperationInfo>();
|
||||
private currentContext: T | null = null;
|
||||
private defaultTimeout: number;
|
||||
|
||||
/**
|
||||
* 创建异步操作管理器
|
||||
*
|
||||
* @param defaultTimeout 默认超时时间(毫秒),0表示不设置超时
|
||||
*/
|
||||
constructor(defaultTimeout: number = 0) {
|
||||
this.defaultTimeout = defaultTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成新的操作ID
|
||||
*
|
||||
* @returns 新的操作ID
|
||||
*/
|
||||
getNextOperationId(): number {
|
||||
return ++this.operationSequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始新的操作
|
||||
*
|
||||
* @param context 操作上下文
|
||||
* @param options 操作选项
|
||||
* @returns 操作ID和AbortController
|
||||
*/
|
||||
startOperation(
|
||||
context: T,
|
||||
options?: {
|
||||
excludeId?: number;
|
||||
timeout?: number;
|
||||
}
|
||||
): { operationId: number; abortController: AbortController } {
|
||||
const operationId = this.getNextOperationId();
|
||||
const abortController = new AbortController();
|
||||
const timeout = options?.timeout ?? this.defaultTimeout;
|
||||
|
||||
// 取消之前的操作
|
||||
this.cancelPreviousOperations(options?.excludeId);
|
||||
|
||||
// 创建操作信息
|
||||
const operationInfo: OperationInfo = {
|
||||
controller: abortController,
|
||||
createdAt: Date.now(),
|
||||
timeout: timeout > 0 ? timeout : undefined
|
||||
};
|
||||
|
||||
// 设置超时处理
|
||||
if (timeout > 0) {
|
||||
operationInfo.timeoutId = setTimeout(() => {
|
||||
this.cancelOperation(operationId, 'timeout');
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
// 设置当前上下文和操作
|
||||
this.currentContext = context;
|
||||
this.pendingOperations.set(operationId, operationInfo);
|
||||
|
||||
return { operationId, abortController };
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查操作是否仍然有效
|
||||
*
|
||||
* @param operationId 操作ID
|
||||
* @param context 操作上下文
|
||||
* @returns 操作是否有效
|
||||
*/
|
||||
isOperationValid(operationId: number, context?: T): boolean {
|
||||
const operationInfo = this.pendingOperations.get(operationId);
|
||||
const contextValid = context === undefined || this.currentContext === context;
|
||||
|
||||
return (
|
||||
operationInfo !== undefined &&
|
||||
!operationInfo.controller.signal.aborted &&
|
||||
contextValid
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成操作
|
||||
*
|
||||
* @param operationId 操作ID
|
||||
*/
|
||||
completeOperation(operationId: number): void {
|
||||
const operationInfo = this.pendingOperations.get(operationId);
|
||||
if (operationInfo) {
|
||||
// 清理超时定时器
|
||||
if (operationInfo.timeoutId) {
|
||||
clearTimeout(operationInfo.timeoutId);
|
||||
}
|
||||
this.pendingOperations.delete(operationId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消指定操作
|
||||
*
|
||||
* @param operationId 操作ID
|
||||
* @param reason 取消原因
|
||||
*/
|
||||
cancelOperation(operationId: number, reason?: string): void {
|
||||
const operationInfo = this.pendingOperations.get(operationId);
|
||||
if (operationInfo) {
|
||||
// 清理超时定时器
|
||||
if (operationInfo.timeoutId) {
|
||||
clearTimeout(operationInfo.timeoutId);
|
||||
}
|
||||
// 取消操作
|
||||
operationInfo.controller.abort(reason);
|
||||
this.pendingOperations.delete(operationId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消之前的操作(修复并发bug)
|
||||
*
|
||||
* @param excludeId 要排除的操作ID(不取消该操作)
|
||||
*/
|
||||
cancelPreviousOperations(excludeId?: number): void {
|
||||
// 创建要取消的操作ID数组,避免在遍历时修改Map
|
||||
const operationIdsToCancel: number[] = [];
|
||||
|
||||
for (const [operationId] of this.pendingOperations) {
|
||||
if (excludeId === undefined || operationId !== excludeId) {
|
||||
operationIdsToCancel.push(operationId);
|
||||
}
|
||||
}
|
||||
|
||||
// 批量取消操作
|
||||
for (const operationId of operationIdsToCancel) {
|
||||
this.cancelOperation(operationId, 'superseded');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消所有操作
|
||||
*/
|
||||
cancelAllOperations(): void {
|
||||
// 创建要取消的操作ID数组,避免在遍历时修改Map
|
||||
const operationIdsToCancel = Array.from(this.pendingOperations.keys());
|
||||
|
||||
// 批量取消操作
|
||||
for (const operationId of operationIdsToCancel) {
|
||||
this.cancelOperation(operationId, 'cancelled');
|
||||
}
|
||||
this.currentContext = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理过期操作(手动清理超时操作)
|
||||
*
|
||||
* @param maxAge 最大存活时间(毫秒)
|
||||
* @returns 清理的操作数量
|
||||
*/
|
||||
cleanupExpiredOperations(maxAge: number): number {
|
||||
const now = Date.now();
|
||||
const expiredOperationIds: number[] = [];
|
||||
|
||||
for (const [operationId, operationInfo] of this.pendingOperations) {
|
||||
if (now - operationInfo.createdAt > maxAge) {
|
||||
expiredOperationIds.push(operationId);
|
||||
}
|
||||
}
|
||||
|
||||
// 批量取消过期操作
|
||||
for (const operationId of expiredOperationIds) {
|
||||
this.cancelOperation(operationId, 'expired');
|
||||
}
|
||||
|
||||
return expiredOperationIds.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取操作统计信息
|
||||
*
|
||||
* @returns 操作统计信息
|
||||
*/
|
||||
getOperationStats(): {
|
||||
total: number;
|
||||
withTimeout: number;
|
||||
averageAge: number;
|
||||
oldestAge: number;
|
||||
} {
|
||||
const now = Date.now();
|
||||
let withTimeout = 0;
|
||||
let totalAge = 0;
|
||||
let oldestAge = 0;
|
||||
|
||||
for (const operationInfo of this.pendingOperations.values()) {
|
||||
const age = now - operationInfo.createdAt;
|
||||
totalAge += age;
|
||||
oldestAge = Math.max(oldestAge, age);
|
||||
|
||||
if (operationInfo.timeout) {
|
||||
withTimeout++;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
total: this.pendingOperations.size,
|
||||
withTimeout,
|
||||
averageAge: this.pendingOperations.size > 0 ? totalAge / this.pendingOperations.size : 0,
|
||||
oldestAge
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前上下文
|
||||
*
|
||||
* @returns 当前上下文
|
||||
*/
|
||||
getCurrentContext(): T | null {
|
||||
return this.currentContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前上下文
|
||||
*
|
||||
* @param context 新的上下文
|
||||
*/
|
||||
setCurrentContext(context: T | null): void {
|
||||
this.currentContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待处理操作数量
|
||||
*
|
||||
* @returns 待处理操作数量
|
||||
*/
|
||||
get pendingCount(): number {
|
||||
return this.pendingOperations.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否有待处理的操作
|
||||
*
|
||||
* @returns 是否有待处理的操作
|
||||
*/
|
||||
hasPendingOperations(): boolean {
|
||||
return this.pendingOperations.size > 0;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,13 @@
|
||||
import { LanguageType } from '@/../bindings/voidraft/internal/models/models';
|
||||
import type { SupportedLocaleType } from '@/common/constant/locales';
|
||||
|
||||
/**
|
||||
* 配置工具类
|
||||
*/
|
||||
export class ConfigUtils {
|
||||
/**
|
||||
* 将后端语言类型转换为前端语言代码
|
||||
*/
|
||||
static backendLanguageToFrontend(language: LanguageType): SupportedLocaleType {
|
||||
return language === LanguageType.LangZhCN ? 'zh-CN' : 'en-US';
|
||||
}
|
||||
|
||||
/**
|
||||
* 将前端语言代码转换为后端语言类型
|
||||
*/
|
||||
static frontendLanguageToBackend(locale: SupportedLocaleType): LanguageType {
|
||||
return locale === 'zh-CN' ? LanguageType.LangZhCN : LanguageType.LangEnUS;
|
||||
}
|
||||
/**
|
||||
* 验证数值是否在指定范围内
|
||||
*/
|
||||
static clamp(value: number, min: number, max: number): number {
|
||||
return Math.max(min, Math.min(max, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证数值是否在指定范围内
|
||||
*/
|
||||
static clamp(value: number, min: number, max: number): number {
|
||||
return Math.max(min, Math.min(max, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证配置值是否有效
|
||||
*/
|
||||
static isValidConfigValue<T>(value: T, validValues: readonly T[]): boolean {
|
||||
return validValues.includes(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置的默认值
|
||||
*/
|
||||
static getDefaultValue<T>(key: string, defaults: Record<string, { default: T }>): T {
|
||||
return defaults[key]?.default;
|
||||
}
|
||||
}
|
||||
@@ -1,329 +0,0 @@
|
||||
/**
|
||||
* DOM Diff 算法单元测试
|
||||
*/
|
||||
import { describe, test, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { morphNode, morphHTML, morphWithKeys } from './domDiff';
|
||||
|
||||
describe('DOM Diff Algorithm', () => {
|
||||
let container: HTMLElement;
|
||||
|
||||
beforeEach(() => {
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
document.body.removeChild(container);
|
||||
});
|
||||
|
||||
describe('morphNode - 基础功能', () => {
|
||||
test('应该更新文本节点内容', () => {
|
||||
const fromNode = document.createTextNode('Hello');
|
||||
const toNode = document.createTextNode('World');
|
||||
container.appendChild(fromNode);
|
||||
|
||||
morphNode(fromNode, toNode);
|
||||
|
||||
expect(fromNode.nodeValue).toBe('World');
|
||||
});
|
||||
|
||||
test('应该保持相同的文本节点不变', () => {
|
||||
const fromNode = document.createTextNode('Hello');
|
||||
const toNode = document.createTextNode('Hello');
|
||||
container.appendChild(fromNode);
|
||||
|
||||
const originalNode = fromNode;
|
||||
morphNode(fromNode, toNode);
|
||||
|
||||
expect(fromNode).toBe(originalNode);
|
||||
expect(fromNode.nodeValue).toBe('Hello');
|
||||
});
|
||||
|
||||
test('应该替换不同类型的节点', () => {
|
||||
const fromNode = document.createElement('span');
|
||||
fromNode.textContent = 'Hello';
|
||||
const toNode = document.createElement('div');
|
||||
toNode.textContent = 'World';
|
||||
container.appendChild(fromNode);
|
||||
|
||||
morphNode(fromNode, toNode);
|
||||
|
||||
expect(container.firstChild?.nodeName).toBe('DIV');
|
||||
expect(container.firstChild?.textContent).toBe('World');
|
||||
});
|
||||
});
|
||||
|
||||
describe('morphNode - 属性更新', () => {
|
||||
test('应该添加新属性', () => {
|
||||
const fromEl = document.createElement('div');
|
||||
const toEl = document.createElement('div');
|
||||
toEl.setAttribute('class', 'test');
|
||||
toEl.setAttribute('id', 'myid');
|
||||
container.appendChild(fromEl);
|
||||
|
||||
morphNode(fromEl, toEl);
|
||||
|
||||
expect(fromEl.getAttribute('class')).toBe('test');
|
||||
expect(fromEl.getAttribute('id')).toBe('myid');
|
||||
});
|
||||
|
||||
test('应该更新已存在的属性', () => {
|
||||
const fromEl = document.createElement('div');
|
||||
fromEl.setAttribute('class', 'old');
|
||||
const toEl = document.createElement('div');
|
||||
toEl.setAttribute('class', 'new');
|
||||
container.appendChild(fromEl);
|
||||
|
||||
morphNode(fromEl, toEl);
|
||||
|
||||
expect(fromEl.getAttribute('class')).toBe('new');
|
||||
});
|
||||
|
||||
test('应该删除不存在的属性', () => {
|
||||
const fromEl = document.createElement('div');
|
||||
fromEl.setAttribute('class', 'test');
|
||||
fromEl.setAttribute('id', 'myid');
|
||||
const toEl = document.createElement('div');
|
||||
toEl.setAttribute('class', 'test');
|
||||
container.appendChild(fromEl);
|
||||
|
||||
morphNode(fromEl, toEl);
|
||||
|
||||
expect(fromEl.getAttribute('class')).toBe('test');
|
||||
expect(fromEl.hasAttribute('id')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('morphNode - 子节点更新', () => {
|
||||
test('应该添加新子节点', () => {
|
||||
const fromEl = document.createElement('ul');
|
||||
fromEl.innerHTML = '<li>1</li><li>2</li>';
|
||||
const toEl = document.createElement('ul');
|
||||
toEl.innerHTML = '<li>1</li><li>2</li><li>3</li>';
|
||||
container.appendChild(fromEl);
|
||||
|
||||
morphNode(fromEl, toEl);
|
||||
|
||||
expect(fromEl.children.length).toBe(3);
|
||||
expect(fromEl.children[2].textContent).toBe('3');
|
||||
});
|
||||
|
||||
test('应该删除多余的子节点', () => {
|
||||
const fromEl = document.createElement('ul');
|
||||
fromEl.innerHTML = '<li>1</li><li>2</li><li>3</li>';
|
||||
const toEl = document.createElement('ul');
|
||||
toEl.innerHTML = '<li>1</li><li>2</li>';
|
||||
container.appendChild(fromEl);
|
||||
|
||||
morphNode(fromEl, toEl);
|
||||
|
||||
expect(fromEl.children.length).toBe(2);
|
||||
expect(fromEl.textContent).toBe('12');
|
||||
});
|
||||
|
||||
test('应该更新子节点内容', () => {
|
||||
const fromEl = document.createElement('div');
|
||||
fromEl.innerHTML = '<p>Old</p>';
|
||||
const toEl = document.createElement('div');
|
||||
toEl.innerHTML = '<p>New</p>';
|
||||
container.appendChild(fromEl);
|
||||
|
||||
const originalP = fromEl.querySelector('p');
|
||||
morphNode(fromEl, toEl);
|
||||
|
||||
// 应该保持同一个 p 元素,只更新内容
|
||||
expect(fromEl.querySelector('p')).toBe(originalP);
|
||||
expect(fromEl.querySelector('p')?.textContent).toBe('New');
|
||||
});
|
||||
});
|
||||
|
||||
describe('morphHTML - HTML 字符串更新', () => {
|
||||
test('应该从 HTML 字符串更新元素', () => {
|
||||
const element = document.createElement('div');
|
||||
element.innerHTML = '<p>Old</p>';
|
||||
container.appendChild(element);
|
||||
|
||||
morphHTML(element, '<p>New</p>');
|
||||
|
||||
expect(element.innerHTML).toBe('<p>New</p>');
|
||||
});
|
||||
|
||||
test('应该处理复杂的 HTML 结构', () => {
|
||||
const element = document.createElement('div');
|
||||
element.innerHTML = '<h1>Title</h1><p>Paragraph</p>';
|
||||
container.appendChild(element);
|
||||
|
||||
morphHTML(element, '<h1>New Title</h1><p>New Paragraph</p><span>Extra</span>');
|
||||
|
||||
expect(element.children.length).toBe(3);
|
||||
expect(element.querySelector('h1')?.textContent).toBe('New Title');
|
||||
expect(element.querySelector('p')?.textContent).toBe('New Paragraph');
|
||||
expect(element.querySelector('span')?.textContent).toBe('Extra');
|
||||
});
|
||||
});
|
||||
|
||||
describe('morphWithKeys - 基于 key 的智能 diff', () => {
|
||||
test('应该保持相同 key 的节点', () => {
|
||||
const fromEl = document.createElement('ul');
|
||||
fromEl.innerHTML = `
|
||||
<li data-key="a">A</li>
|
||||
<li data-key="b">B</li>
|
||||
<li data-key="c">C</li>
|
||||
`;
|
||||
const toEl = document.createElement('ul');
|
||||
toEl.innerHTML = `
|
||||
<li data-key="a">A Updated</li>
|
||||
<li data-key="b">B</li>
|
||||
<li data-key="c">C</li>
|
||||
`;
|
||||
container.appendChild(fromEl);
|
||||
|
||||
const originalA = fromEl.querySelector('[data-key="a"]');
|
||||
morphWithKeys(fromEl, toEl);
|
||||
|
||||
expect(fromEl.querySelector('[data-key="a"]')).toBe(originalA);
|
||||
expect(originalA?.textContent).toBe('A Updated');
|
||||
});
|
||||
|
||||
test('应该重新排序节点', () => {
|
||||
const fromEl = document.createElement('ul');
|
||||
fromEl.innerHTML = `
|
||||
<li data-key="a">A</li>
|
||||
<li data-key="b">B</li>
|
||||
<li data-key="c">C</li>
|
||||
`;
|
||||
const toEl = document.createElement('ul');
|
||||
toEl.innerHTML = `
|
||||
<li data-key="c">C</li>
|
||||
<li data-key="a">A</li>
|
||||
<li data-key="b">B</li>
|
||||
`;
|
||||
container.appendChild(fromEl);
|
||||
|
||||
morphWithKeys(fromEl, toEl);
|
||||
|
||||
const keys = Array.from(fromEl.children).map(child => child.getAttribute('data-key'));
|
||||
expect(keys).toEqual(['c', 'a', 'b']);
|
||||
});
|
||||
|
||||
test('应该添加新的 key 节点', () => {
|
||||
const fromEl = document.createElement('ul');
|
||||
fromEl.innerHTML = `
|
||||
<li data-key="a">A</li>
|
||||
<li data-key="b">B</li>
|
||||
`;
|
||||
const toEl = document.createElement('ul');
|
||||
toEl.innerHTML = `
|
||||
<li data-key="a">A</li>
|
||||
<li data-key="b">B</li>
|
||||
<li data-key="c">C</li>
|
||||
`;
|
||||
container.appendChild(fromEl);
|
||||
|
||||
morphWithKeys(fromEl, toEl);
|
||||
|
||||
expect(fromEl.children.length).toBe(3);
|
||||
expect(fromEl.querySelector('[data-key="c"]')?.textContent).toBe('C');
|
||||
});
|
||||
|
||||
test('应该删除不存在的 key 节点', () => {
|
||||
const fromEl = document.createElement('ul');
|
||||
fromEl.innerHTML = `
|
||||
<li data-key="a">A</li>
|
||||
<li data-key="b">B</li>
|
||||
<li data-key="c">C</li>
|
||||
`;
|
||||
const toEl = document.createElement('ul');
|
||||
toEl.innerHTML = `
|
||||
<li data-key="a">A</li>
|
||||
<li data-key="c">C</li>
|
||||
`;
|
||||
container.appendChild(fromEl);
|
||||
|
||||
morphWithKeys(fromEl, toEl);
|
||||
|
||||
expect(fromEl.children.length).toBe(2);
|
||||
expect(fromEl.querySelector('[data-key="b"]')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('性能测试', () => {
|
||||
test('应该高效处理大量节点', () => {
|
||||
const fromEl = document.createElement('ul');
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
const li = document.createElement('li');
|
||||
li.textContent = `Item ${i}`;
|
||||
fromEl.appendChild(li);
|
||||
}
|
||||
|
||||
const toEl = document.createElement('ul');
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
const li = document.createElement('li');
|
||||
li.textContent = `Updated Item ${i}`;
|
||||
toEl.appendChild(li);
|
||||
}
|
||||
|
||||
container.appendChild(fromEl);
|
||||
|
||||
const startTime = performance.now();
|
||||
morphNode(fromEl, toEl);
|
||||
const endTime = performance.now();
|
||||
|
||||
expect(endTime - startTime).toBeLessThan(100); // 应该在 100ms 内完成
|
||||
expect(fromEl.children.length).toBe(1000);
|
||||
expect(fromEl.children[0].textContent).toBe('Updated Item 0');
|
||||
});
|
||||
});
|
||||
|
||||
describe('边界情况', () => {
|
||||
test('应该处理空节点', () => {
|
||||
const fromEl = document.createElement('div');
|
||||
const toEl = document.createElement('div');
|
||||
container.appendChild(fromEl);
|
||||
|
||||
expect(() => morphNode(fromEl, toEl)).not.toThrow();
|
||||
});
|
||||
|
||||
test('应该处理只有文本的节点', () => {
|
||||
const fromEl = document.createElement('div');
|
||||
fromEl.textContent = 'Hello';
|
||||
const toEl = document.createElement('div');
|
||||
toEl.textContent = 'World';
|
||||
container.appendChild(fromEl);
|
||||
|
||||
morphNode(fromEl, toEl);
|
||||
|
||||
expect(fromEl.textContent).toBe('World');
|
||||
});
|
||||
|
||||
test('应该处理嵌套的复杂结构', () => {
|
||||
const fromEl = document.createElement('div');
|
||||
fromEl.innerHTML = `
|
||||
<div class="outer">
|
||||
<div class="inner">
|
||||
<span>Text</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const toEl = document.createElement('div');
|
||||
toEl.innerHTML = `
|
||||
<div class="outer modified">
|
||||
<div class="inner">
|
||||
<span>Updated Text</span>
|
||||
<strong>New</strong>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
container.appendChild(fromEl);
|
||||
|
||||
morphNode(fromEl, toEl);
|
||||
|
||||
expect(fromEl.querySelector('.outer')?.classList.contains('modified')).toBe(true);
|
||||
expect(fromEl.querySelector('span')?.textContent).toBe('Updated Text');
|
||||
expect(fromEl.querySelector('strong')?.textContent).toBe('New');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
/**
|
||||
* 轻量级 DOM Diff 算法实现
|
||||
* 基于 morphdom 思路,只更新变化的节点,保持未变化的节点不动
|
||||
*/
|
||||
|
||||
/**
|
||||
* 比较并更新两个 DOM 节点
|
||||
* @param fromNode 原节点
|
||||
* @param toNode 目标节点
|
||||
*/
|
||||
export function morphNode(fromNode: Node, toNode: Node): void {
|
||||
// 节点类型不同,直接替换
|
||||
if (fromNode.nodeType !== toNode.nodeType || fromNode.nodeName !== toNode.nodeName) {
|
||||
fromNode.parentNode?.replaceChild(toNode.cloneNode(true), fromNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// 文本节点:比较内容
|
||||
if (fromNode.nodeType === Node.TEXT_NODE) {
|
||||
if (fromNode.nodeValue !== toNode.nodeValue) {
|
||||
fromNode.nodeValue = toNode.nodeValue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 元素节点:更新属性和子节点
|
||||
if (fromNode.nodeType === Node.ELEMENT_NODE) {
|
||||
const fromEl = fromNode as Element;
|
||||
const toEl = toNode as Element;
|
||||
|
||||
// 更新属性
|
||||
morphAttributes(fromEl, toEl);
|
||||
|
||||
// 更新子节点
|
||||
morphChildren(fromEl, toEl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新元素属性
|
||||
*/
|
||||
function morphAttributes(fromEl: Element, toEl: Element): void {
|
||||
// 移除旧属性
|
||||
const fromAttrs = fromEl.attributes;
|
||||
for (let i = fromAttrs.length - 1; i >= 0; i--) {
|
||||
const attr = fromAttrs[i];
|
||||
if (!toEl.hasAttribute(attr.name)) {
|
||||
fromEl.removeAttribute(attr.name);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加/更新新属性
|
||||
const toAttrs = toEl.attributes;
|
||||
for (let i = 0; i < toAttrs.length; i++) {
|
||||
const attr = toAttrs[i];
|
||||
const fromValue = fromEl.getAttribute(attr.name);
|
||||
if (fromValue !== attr.value) {
|
||||
fromEl.setAttribute(attr.name, attr.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新子节点(核心 diff 算法)
|
||||
*/
|
||||
function morphChildren(fromEl: Element, toEl: Element): void {
|
||||
const fromChildren = Array.from(fromEl.childNodes);
|
||||
const toChildren = Array.from(toEl.childNodes);
|
||||
|
||||
const fromLen = fromChildren.length;
|
||||
const toLen = toChildren.length;
|
||||
const minLen = Math.min(fromLen, toLen);
|
||||
|
||||
// 1. 更新公共部分
|
||||
for (let i = 0; i < minLen; i++) {
|
||||
morphNode(fromChildren[i], toChildren[i]);
|
||||
}
|
||||
|
||||
// 2. 移除多余的旧节点
|
||||
if (fromLen > toLen) {
|
||||
for (let i = fromLen - 1; i >= toLen; i--) {
|
||||
fromEl.removeChild(fromChildren[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 添加新节点
|
||||
if (toLen > fromLen) {
|
||||
for (let i = fromLen; i < toLen; i++) {
|
||||
fromEl.appendChild(toChildren[i].cloneNode(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 优化版:使用 key 进行更智能的 diff(可选)
|
||||
* 适用于有 data-key 属性的元素
|
||||
*/
|
||||
export function morphWithKeys(fromEl: Element, toEl: Element): void {
|
||||
const toChildren = Array.from(toEl.children) as Element[];
|
||||
|
||||
// 构建 from 的 key 映射
|
||||
const fromKeyMap = new Map<string, Element>();
|
||||
Array.from(fromEl.children).forEach((child) => {
|
||||
const key = child.getAttribute('data-key');
|
||||
if (key) {
|
||||
fromKeyMap.set(key, child);
|
||||
}
|
||||
});
|
||||
|
||||
const processedKeys = new Set<string>();
|
||||
|
||||
// 按照 toChildren 的顺序处理
|
||||
toChildren.forEach((toChild, toIndex) => {
|
||||
const key = toChild.getAttribute('data-key');
|
||||
if (!key) return;
|
||||
|
||||
processedKeys.add(key);
|
||||
const fromChild = fromKeyMap.get(key);
|
||||
|
||||
if (fromChild) {
|
||||
// 找到对应节点,更新内容
|
||||
morphNode(fromChild, toChild);
|
||||
|
||||
// 确保节点在正确的位置
|
||||
const currentNode = fromEl.children[toIndex];
|
||||
if (currentNode !== fromChild) {
|
||||
// 将 fromChild 移动到正确位置
|
||||
fromEl.insertBefore(fromChild, currentNode);
|
||||
}
|
||||
} else {
|
||||
// 新节点,插入到正确位置
|
||||
const currentNode = fromEl.children[toIndex];
|
||||
fromEl.insertBefore(toChild.cloneNode(true), currentNode || null);
|
||||
}
|
||||
});
|
||||
|
||||
// 删除不再存在的节点(从后往前删除,避免索引问题)
|
||||
const childrenToRemove: Element[] = [];
|
||||
fromKeyMap.forEach((child, key) => {
|
||||
if (!processedKeys.has(key)) {
|
||||
childrenToRemove.push(child);
|
||||
}
|
||||
});
|
||||
childrenToRemove.forEach(child => {
|
||||
fromEl.removeChild(child);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 高级 API:直接从 HTML 字符串更新元素
|
||||
*/
|
||||
export function morphHTML(element: Element, htmlString: string): void {
|
||||
const tempContainer = document.createElement('div');
|
||||
tempContainer.innerHTML = htmlString;
|
||||
|
||||
// 更新元素的子节点列表
|
||||
morphChildren(element, tempContainer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新(使用 DocumentFragment)
|
||||
*/
|
||||
export function batchMorph(element: Element, htmlString: string): void {
|
||||
const tempContainer = document.createElement('div');
|
||||
tempContainer.innerHTML = htmlString;
|
||||
|
||||
const fragment = document.createDocumentFragment();
|
||||
Array.from(tempContainer.childNodes).forEach(node => {
|
||||
fragment.appendChild(node);
|
||||
});
|
||||
|
||||
// 清空原内容
|
||||
while (element.firstChild) {
|
||||
element.removeChild(element.firstChild);
|
||||
}
|
||||
|
||||
// 批量插入
|
||||
element.appendChild(fragment);
|
||||
}
|
||||
|
||||
105
frontend/src/components/accordion/AccordionContainer.vue
Normal file
@@ -0,0 +1,105 @@
|
||||
<script setup lang="ts">
|
||||
import { provide, ref } from 'vue';
|
||||
|
||||
interface Props {
|
||||
/**
|
||||
* 是否允许多个面板同时展开
|
||||
* @default false - 单选模式(手风琴效果)
|
||||
*/
|
||||
multiple?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
multiple: false,
|
||||
});
|
||||
|
||||
// 当前展开的项(单选模式)或展开项列表(多选模式)
|
||||
const expandedItems = ref<Set<string | number>>(new Set());
|
||||
|
||||
/**
|
||||
* 切换展开状态
|
||||
*/
|
||||
const toggleItem = (id: string | number) => {
|
||||
if (props.multiple) {
|
||||
// 多选模式:切换单个项
|
||||
if (expandedItems.value.has(id)) {
|
||||
expandedItems.value.delete(id);
|
||||
} else {
|
||||
expandedItems.value.add(id);
|
||||
}
|
||||
} else {
|
||||
// 单选模式:只能展开一个
|
||||
if (expandedItems.value.has(id)) {
|
||||
expandedItems.value.clear();
|
||||
} else {
|
||||
expandedItems.value.clear();
|
||||
expandedItems.value.add(id);
|
||||
}
|
||||
}
|
||||
// 触发响应式更新
|
||||
expandedItems.value = new Set(expandedItems.value);
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查项是否展开
|
||||
*/
|
||||
const isExpanded = (id: string | number): boolean => {
|
||||
return expandedItems.value.has(id);
|
||||
};
|
||||
|
||||
/**
|
||||
* 展开指定项
|
||||
*/
|
||||
const expand = (id: string | number) => {
|
||||
if (!props.multiple) {
|
||||
expandedItems.value.clear();
|
||||
}
|
||||
expandedItems.value.add(id);
|
||||
expandedItems.value = new Set(expandedItems.value);
|
||||
};
|
||||
|
||||
/**
|
||||
* 收起指定项
|
||||
*/
|
||||
const collapse = (id: string | number) => {
|
||||
expandedItems.value.delete(id);
|
||||
expandedItems.value = new Set(expandedItems.value);
|
||||
};
|
||||
|
||||
/**
|
||||
* 收起所有项
|
||||
*/
|
||||
const collapseAll = () => {
|
||||
expandedItems.value.clear();
|
||||
expandedItems.value = new Set(expandedItems.value);
|
||||
};
|
||||
|
||||
// 通过 provide 向子组件提供状态和方法
|
||||
provide('accordion', {
|
||||
toggleItem,
|
||||
isExpanded,
|
||||
expand,
|
||||
collapse,
|
||||
});
|
||||
|
||||
// 暴露方法供父组件使用
|
||||
defineExpose({
|
||||
expand,
|
||||
collapse,
|
||||
collapseAll,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="accordion-container">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.accordion-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
|
||||
187
frontend/src/components/accordion/AccordionItem.vue
Normal file
@@ -0,0 +1,187 @@
|
||||
<script setup lang="ts">
|
||||
import { inject, computed, ref } from 'vue';
|
||||
|
||||
interface Props {
|
||||
/**
|
||||
* 唯一标识符
|
||||
*/
|
||||
id: string | number;
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
title?: string;
|
||||
/**
|
||||
* 是否禁用
|
||||
*/
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const accordion = inject<{
|
||||
toggleItem: (id: string | number) => void;
|
||||
isExpanded: (id: string | number) => boolean;
|
||||
}>('accordion');
|
||||
|
||||
if (!accordion) {
|
||||
throw new Error('AccordionItem must be used within AccordionContainer');
|
||||
}
|
||||
|
||||
const isExpanded = computed(() => accordion.isExpanded(props.id));
|
||||
|
||||
const toggle = () => {
|
||||
if (!props.disabled) {
|
||||
accordion.toggleItem(props.id);
|
||||
}
|
||||
};
|
||||
|
||||
// 内容容器的引用,用于计算高度
|
||||
const contentRef = ref<HTMLElement>();
|
||||
const contentHeight = computed(() => {
|
||||
if (!contentRef.value) return '0px';
|
||||
return isExpanded.value ? `${contentRef.value.scrollHeight}px` : '0px';
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="accordion-item"
|
||||
:class="{
|
||||
'is-expanded': isExpanded,
|
||||
'is-disabled': disabled
|
||||
}"
|
||||
>
|
||||
<!-- 标题栏 -->
|
||||
<div
|
||||
class="accordion-header"
|
||||
@click="toggle"
|
||||
:aria-expanded="isExpanded"
|
||||
:aria-disabled="disabled"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@keydown.enter="toggle"
|
||||
@keydown.space.prevent="toggle"
|
||||
>
|
||||
<div class="accordion-title">
|
||||
<slot name="title">
|
||||
{{ title }}
|
||||
</slot>
|
||||
</div>
|
||||
<div class="accordion-icon">
|
||||
<svg
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M3 4.5L6 7.5L9 4.5"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<div
|
||||
class="accordion-content-wrapper"
|
||||
:style="{ height: contentHeight }"
|
||||
>
|
||||
<div
|
||||
ref="contentRef"
|
||||
class="accordion-content"
|
||||
>
|
||||
<div class="accordion-content-inner">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.accordion-item {
|
||||
border-bottom: 1px solid var(--settings-border);
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&.is-expanded {
|
||||
background-color: var(--settings-hover);
|
||||
}
|
||||
|
||||
&.is-disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
|
||||
.accordion-header {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.accordion-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
&:hover:not([aria-disabled="true"]) {
|
||||
background-color: var(--settings-hover);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid #4a9eff;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
}
|
||||
|
||||
.accordion-title {
|
||||
flex: 1;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--settings-text);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.accordion-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: var(--text-muted);
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
.is-expanded & {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.accordion-content-wrapper {
|
||||
overflow: hidden;
|
||||
transition: height 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.accordion-content {
|
||||
// 用于测量实际内容高度
|
||||
}
|
||||
|
||||
.accordion-content-inner {
|
||||
padding: 0 16px 12px 16px;
|
||||
color: var(--settings-text);
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
|
||||
3
frontend/src/components/accordion/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { default as AccordionContainer } from './AccordionContainer.vue';
|
||||
export { default as AccordionItem } from './AccordionItem.vue';
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
v-for="tab in tabStore.tabs"
|
||||
:key="tab.documentId"
|
||||
:tab="tab"
|
||||
:isActive="tab.documentId === tabStore.currentDocumentId"
|
||||
:isActive="tab.documentId === documentStore.currentDocumentId"
|
||||
:canClose="tabStore.canCloseTab"
|
||||
@click="switchToTab"
|
||||
@close="closeTab"
|
||||
@@ -35,8 +35,14 @@ import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue';
|
||||
import TabItem from './TabItem.vue';
|
||||
import TabContextMenu from './TabContextMenu.vue';
|
||||
import { useTabStore } from '@/stores/tabStore';
|
||||
import { useDocumentStore } from '@/stores/documentStore';
|
||||
import { useEditorStore } from '@/stores/editorStore';
|
||||
import { useEditorStateStore } from '@/stores/editorStateStore';
|
||||
|
||||
const tabStore = useTabStore();
|
||||
const documentStore = useDocumentStore();
|
||||
const editorStore = useEditorStore();
|
||||
const editorStateStore = useEditorStateStore();
|
||||
|
||||
// DOM 引用
|
||||
const tabBarRef = ref<HTMLElement>();
|
||||
@@ -50,8 +56,36 @@ const contextMenuTargetId = ref<number | null>(null);
|
||||
|
||||
|
||||
// 标签页操作
|
||||
const switchToTab = (documentId: number) => {
|
||||
tabStore.switchToTabAndDocument(documentId);
|
||||
const switchToTab = async (documentId: number) => {
|
||||
|
||||
// 保存旧文档的光标位置
|
||||
const oldDocId = documentStore.currentDocumentId;
|
||||
if (oldDocId) {
|
||||
const cursorPos = editorStore.getCurrentCursorPosition();
|
||||
editorStateStore.saveCursorPosition(oldDocId, cursorPos);
|
||||
}
|
||||
|
||||
// 如果旧文档有未保存修改,保存它
|
||||
if (oldDocId && editorStore.hasUnsavedChanges(oldDocId)) {
|
||||
try {
|
||||
const content = editorStore.getCurrentContent();
|
||||
await documentStore.saveDocument(oldDocId, content);
|
||||
editorStore.syncAfterSave(oldDocId);
|
||||
} catch (error) {
|
||||
console.error('save document error:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 切换文档
|
||||
await tabStore.switchToTabAndDocument(documentId);
|
||||
|
||||
// 切换到新编辑器
|
||||
await editorStore.switchToEditor(documentId);
|
||||
|
||||
// 更新标签页
|
||||
if (documentStore.currentDocument && tabStore.isTabsEnabled) {
|
||||
tabStore.addOrActivateTab(documentStore.currentDocument);
|
||||
}
|
||||
};
|
||||
|
||||
const closeTab = (documentId: number) => {
|
||||
@@ -150,7 +184,7 @@ onUnmounted(() => {
|
||||
});
|
||||
|
||||
// 监听当前活跃标签页的变化
|
||||
watch(() => tabStore.currentDocumentId, () => {
|
||||
watch(() => documentStore.currentDocumentId, () => {
|
||||
scrollToActiveTab();
|
||||
});
|
||||
|
||||
|
||||
292
frontend/src/components/toast/Toast.vue
Normal file
@@ -0,0 +1,292 @@
|
||||
<template>
|
||||
<div
|
||||
:class="['toast-item', `toast-${type}`]"
|
||||
@mouseenter="pauseTimer"
|
||||
@mouseleave="resumeTimer"
|
||||
>
|
||||
<!-- 图标 -->
|
||||
<div class="toast-icon">
|
||||
<svg v-if="type === 'success'" width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<path d="M10 0C4.48 0 0 4.48 0 10s4.48 10 10 10 10-4.48 10-10S15.52 0 10 0zm-2 15l-5-5 1.41-1.41L8 12.17l7.59-7.59L17 6l-9 9z" fill="currentColor"/>
|
||||
</svg>
|
||||
<svg v-else-if="type === 'error'" width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<path d="M10 0C4.48 0 0 4.48 0 10s4.48 10 10 10 10-4.48 10-10S15.52 0 10 0zm1 15H9v-2h2v2zm0-4H9V5h2v6z" fill="currentColor"/>
|
||||
</svg>
|
||||
<svg v-else-if="type === 'warning'" width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<path d="M1 19h18L10 1 1 19zm10-3H9v-2h2v2zm0-4H9v-4h2v4z" fill="currentColor"/>
|
||||
</svg>
|
||||
<svg v-else-if="type === 'info'" width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<path d="M10 0C4.48 0 0 4.48 0 10s4.48 10 10 10 10-4.48 10-10S15.52 0 10 0zm1 15H9V9h2v6zm0-8H9V5h2v2z" fill="currentColor"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- 内容 -->
|
||||
<div class="toast-content">
|
||||
<div v-if="title" class="toast-title">{{ title }}</div>
|
||||
<div class="toast-message">{{ message }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 关闭按钮 -->
|
||||
<button
|
||||
v-if="closable"
|
||||
class="toast-close"
|
||||
@click="close"
|
||||
aria-label="Close"
|
||||
>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M12 4L4 12M4 4L12 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
import type { Toast } from './types';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
toast: Toast;
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
close: [id: string];
|
||||
}>();
|
||||
|
||||
const timer = ref<number | null>(null);
|
||||
const remainingTime = ref(props.toast.duration);
|
||||
const pausedAt = ref<number | null>(null);
|
||||
|
||||
const { id, message, title, type, duration, closable } = props.toast;
|
||||
|
||||
const close = () => {
|
||||
emit('close', id);
|
||||
};
|
||||
|
||||
const startTimer = () => {
|
||||
if (duration > 0) {
|
||||
timer.value = window.setTimeout(() => {
|
||||
close();
|
||||
}, remainingTime.value);
|
||||
}
|
||||
};
|
||||
|
||||
const clearTimer = () => {
|
||||
if (timer.value) {
|
||||
clearTimeout(timer.value);
|
||||
timer.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
const pauseTimer = () => {
|
||||
if (timer.value && duration > 0) {
|
||||
clearTimer();
|
||||
pausedAt.value = Date.now();
|
||||
}
|
||||
};
|
||||
|
||||
const resumeTimer = () => {
|
||||
if (pausedAt.value && duration > 0) {
|
||||
const elapsed = Date.now() - pausedAt.value;
|
||||
remainingTime.value = Math.max(0, remainingTime.value - elapsed);
|
||||
pausedAt.value = null;
|
||||
startTimer();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
startTimer();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
clearTimer();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.toast-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
min-width: 300px;
|
||||
max-width: 420px;
|
||||
padding: 16px 18px;
|
||||
margin-bottom: 12px;
|
||||
|
||||
transform-origin: center center;
|
||||
|
||||
// 毛玻璃效果
|
||||
// 亮色主题
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
backdrop-filter: blur(20px) saturate(180%);
|
||||
-webkit-backdrop-filter: blur(20px) saturate(180%);
|
||||
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
|
||||
box-shadow:
|
||||
0 4px 12px rgba(0, 0, 0, 0.08),
|
||||
0 1px 3px rgba(0, 0, 0, 0.06),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
||||
|
||||
cursor: default;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow:
|
||||
0 6px 16px rgba(0, 0, 0, 0.1),
|
||||
0 2px 6px rgba(0, 0, 0, 0.08),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
// 深色主题适配 - 使用应用的 data-theme 属性
|
||||
:root[data-theme="dark"] .toast-item,
|
||||
:root[data-theme="auto"] .toast-item {
|
||||
background: rgba(45, 45, 45, 0.9);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow:
|
||||
0 4px 12px rgba(0, 0, 0, 0.3),
|
||||
0 1px 3px rgba(0, 0, 0, 0.2),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.05);
|
||||
|
||||
&:hover {
|
||||
box-shadow:
|
||||
0 6px 16px rgba(0, 0, 0, 0.4),
|
||||
0 2px 6px rgba(0, 0, 0, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
}
|
||||
|
||||
// 跟随系统主题时的浅色偏好
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root[data-theme="auto"] .toast-item {
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
box-shadow:
|
||||
0 4px 12px rgba(0, 0, 0, 0.08),
|
||||
0 1px 3px rgba(0, 0, 0, 0.06),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
||||
|
||||
&:hover {
|
||||
box-shadow:
|
||||
0 6px 16px rgba(0, 0, 0, 0.1),
|
||||
0 2px 6px rgba(0, 0, 0, 0.08),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toast-icon {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-top: 2px;
|
||||
|
||||
svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1));
|
||||
}
|
||||
}
|
||||
|
||||
// 有标题时,图标与标题对齐(不需要 margin-top)
|
||||
.toast-item:has(.toast-title) .toast-icon {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.toast-success .toast-icon {
|
||||
color: #16a34a;
|
||||
}
|
||||
|
||||
.toast-error .toast-icon {
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
.toast-warning .toast-icon {
|
||||
color: #f59e0b;
|
||||
}
|
||||
|
||||
.toast-info .toast-icon {
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.toast-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.toast-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--settings-text);
|
||||
margin-bottom: 4px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.toast-message {
|
||||
font-size: 12px;
|
||||
color: var(--settings-text-secondary);
|
||||
line-height: 1.5;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.toast-close {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
margin-top: 0px;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
color: var(--settings-text-secondary);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
color: var(--settings-text);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: rotate(90deg) scale(0.9);
|
||||
}
|
||||
}
|
||||
|
||||
:root[data-theme="dark"] .toast-close,
|
||||
:root[data-theme="auto"] .toast-close {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root[data-theme="auto"] .toast-close {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
168
frontend/src/components/toast/ToastContainer.vue
Normal file
@@ -0,0 +1,168 @@
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<TransitionGroup
|
||||
v-for="position in positions"
|
||||
:key="position"
|
||||
:class="['toast-container', `toast-container-${position}`]"
|
||||
name="toast-list"
|
||||
tag="div"
|
||||
>
|
||||
<ToastItem
|
||||
v-for="toast in getToastsByPosition(position)"
|
||||
:key="toast.id"
|
||||
:toast="toast"
|
||||
@close="removeToast"
|
||||
/>
|
||||
</TransitionGroup>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ToastItem from './Toast.vue';
|
||||
import { useToastStore } from './toastStore';
|
||||
import type { ToastPosition } from './types';
|
||||
|
||||
const toastStore = useToastStore();
|
||||
|
||||
const positions: ToastPosition[] = [
|
||||
'top-left',
|
||||
'top-center',
|
||||
'top-right',
|
||||
'bottom-left',
|
||||
'bottom-center',
|
||||
'bottom-right',
|
||||
];
|
||||
|
||||
const getToastsByPosition = (position: ToastPosition) => {
|
||||
return toastStore.toasts.filter(toast => toast.position === position);
|
||||
};
|
||||
|
||||
const removeToast = (id: string) => {
|
||||
toastStore.remove(id);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.toast-container {
|
||||
position: fixed;
|
||||
z-index: 9999;
|
||||
pointer-events: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
> * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// 顶部位置 - 增加间距避免覆盖标题栏
|
||||
.toast-container-top-left {
|
||||
top: 35px;
|
||||
left: 20px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.toast-container-top-center {
|
||||
top: 35px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.toast-container-top-right {
|
||||
top: 35px;
|
||||
right: 20px;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
// 底部位置
|
||||
.toast-container-bottom-left {
|
||||
bottom: 20px;
|
||||
left: 20px;
|
||||
align-items: flex-start;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.toast-container-bottom-center {
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
align-items: center;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.toast-container-bottom-right {
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
align-items: flex-end;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
// TransitionGroup 列表动画 - 从哪来回哪去,收缩滑出
|
||||
.toast-list-move {
|
||||
transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.toast-list-enter-active {
|
||||
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
|
||||
.toast-list-leave-active {
|
||||
transition: all 0.3s cubic-bezier(0.6, 0, 0.8, 0.4);
|
||||
position: absolute !important;
|
||||
}
|
||||
|
||||
// 右侧位置:从右滑入,收缩向右滑出
|
||||
.toast-container-top-right,
|
||||
.toast-container-bottom-right {
|
||||
.toast-list-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX(100%) scale(0.8);
|
||||
}
|
||||
|
||||
.toast-list-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(100%) scale(0.8);
|
||||
}
|
||||
}
|
||||
|
||||
// 左侧位置:从左滑入,收缩向左滑出
|
||||
.toast-container-top-left,
|
||||
.toast-container-bottom-left {
|
||||
.toast-list-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX(-100%) scale(0.8);
|
||||
}
|
||||
|
||||
.toast-list-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(-100%) scale(0.8);
|
||||
}
|
||||
}
|
||||
|
||||
// 居中位置:从上/下滑入,收缩向上/下滑出
|
||||
.toast-container-top-center {
|
||||
.toast-list-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateY(-100%) scale(0.8);
|
||||
}
|
||||
|
||||
.toast-list-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-100%) scale(0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.toast-container-bottom-center {
|
||||
.toast-list-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateY(100%) scale(0.8);
|
||||
}
|
||||
|
||||
.toast-list-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(100%) scale(0.8);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
80
frontend/src/components/toast/index.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { useToastStore } from './toastStore';
|
||||
import type { ToastOptions } from './types';
|
||||
|
||||
class ToastService {
|
||||
private getStore() {
|
||||
return useToastStore();
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示一个通知
|
||||
*/
|
||||
show(options: ToastOptions): string {
|
||||
return this.getStore().add(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示成功通知
|
||||
*/
|
||||
success(message: string, title?: string, options?: Partial<ToastOptions>): string {
|
||||
return this.show({
|
||||
message,
|
||||
title,
|
||||
type: 'success',
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示错误通知
|
||||
*/
|
||||
error(message: string, title?: string, options?: Partial<ToastOptions>): string {
|
||||
return this.show({
|
||||
message,
|
||||
title,
|
||||
type: 'error',
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示警告通知
|
||||
*/
|
||||
warning(message: string, title?: string, options?: Partial<ToastOptions>): string {
|
||||
return this.show({
|
||||
message,
|
||||
title,
|
||||
type: 'warning',
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示信息通知
|
||||
*/
|
||||
info(message: string, title?: string, options?: Partial<ToastOptions>): string {
|
||||
return this.show({
|
||||
message,
|
||||
title,
|
||||
type: 'info',
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭指定的通知
|
||||
*/
|
||||
close(id: string): void {
|
||||
this.getStore().remove(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有通知
|
||||
*/
|
||||
clear(): void {
|
||||
this.getStore().clear();
|
||||
}
|
||||
}
|
||||
export const toast = new ToastService();
|
||||
export default toast;
|
||||
|
||||
55
frontend/src/components/toast/toastStore.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import type { Toast, ToastOptions } from './types';
|
||||
|
||||
export const useToastStore = defineStore('toast', () => {
|
||||
const toasts = ref<Toast[]>([]);
|
||||
let idCounter = 0;
|
||||
|
||||
/**
|
||||
* 添加一个 Toast
|
||||
*/
|
||||
const add = (options: ToastOptions): string => {
|
||||
const id = `toast-${Date.now()}-${idCounter++}`;
|
||||
|
||||
const toast: Toast = {
|
||||
id,
|
||||
message: options.message,
|
||||
type: options.type || 'info',
|
||||
title: options.title,
|
||||
duration: options.duration ?? 4000,
|
||||
position: options.position || 'top-right',
|
||||
closable: options.closable ?? true,
|
||||
createdAt: Date.now(),
|
||||
};
|
||||
|
||||
toasts.value.push(toast);
|
||||
|
||||
return id;
|
||||
};
|
||||
|
||||
/**
|
||||
* 移除指定 Toast
|
||||
*/
|
||||
const remove = (id: string) => {
|
||||
const index = toasts.value.findIndex(t => t.id === id);
|
||||
if (index > -1) {
|
||||
toasts.value.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 清空所有 Toast
|
||||
*/
|
||||
const clear = () => {
|
||||
toasts.value = [];
|
||||
};
|
||||
|
||||
return {
|
||||
toasts,
|
||||
add,
|
||||
remove,
|
||||
clear,
|
||||
};
|
||||
});
|
||||
|
||||
52
frontend/src/components/toast/types.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Toast 通知类型定义
|
||||
*/
|
||||
|
||||
export type ToastType = 'success' | 'error' | 'warning' | 'info';
|
||||
|
||||
export type ToastPosition =
|
||||
| 'top-right'
|
||||
| 'top-left'
|
||||
| 'bottom-right'
|
||||
| 'bottom-left'
|
||||
| 'top-center'
|
||||
| 'bottom-center';
|
||||
|
||||
export interface ToastOptions {
|
||||
/**
|
||||
* Toast 消息内容
|
||||
*/
|
||||
message: string;
|
||||
|
||||
/**
|
||||
* Toast 类型
|
||||
*/
|
||||
type?: ToastType;
|
||||
|
||||
/**
|
||||
* 标题(可选)
|
||||
*/
|
||||
title?: string;
|
||||
|
||||
/**
|
||||
* 持续时间(毫秒),0 表示不自动关闭
|
||||
*/
|
||||
duration?: number;
|
||||
|
||||
/**
|
||||
* 显示位置
|
||||
*/
|
||||
position?: ToastPosition;
|
||||
|
||||
/**
|
||||
* 是否可关闭
|
||||
*/
|
||||
closable?: boolean;
|
||||
}
|
||||
|
||||
export interface Toast extends Required<Omit<ToastOptions, 'title'>> {
|
||||
id: string;
|
||||
title?: string;
|
||||
createdAt: number;
|
||||
}
|
||||
|
||||
@@ -51,13 +51,13 @@ let editorScope: ReturnType<typeof effectScope> | null = null;
|
||||
|
||||
// 更新当前块语言信息
|
||||
const updateCurrentBlockLanguage = () => {
|
||||
if (!editorStore.editorView) {
|
||||
if (!editorStore.currentEditor) {
|
||||
currentBlockLanguage.value = { name: 'text', auto: false };
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const state = editorStore.editorView.state;
|
||||
const state = editorStore.currentEditor.state;
|
||||
const activeBlock = getActiveNoteBlock(state as any);
|
||||
if (activeBlock) {
|
||||
const newLanguage = {
|
||||
@@ -128,7 +128,7 @@ const setupEventListeners = (view: any) => {
|
||||
|
||||
// 监听编辑器状态变化
|
||||
watch(
|
||||
() => editorStore.editorView,
|
||||
() => editorStore.currentEditor,
|
||||
(newView) => {
|
||||
if (newView) {
|
||||
setupEventListeners(newView);
|
||||
@@ -175,15 +175,15 @@ const closeLanguageMenu = () => {
|
||||
|
||||
// 选择语言 - 优化性能
|
||||
const selectLanguage = (languageId: SupportedLanguage) => {
|
||||
if (!editorStore.editorView) {
|
||||
if (!editorStore.currentEditor) {
|
||||
closeLanguageMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const view = editorStore.editorView;
|
||||
const view = editorStore.currentEditor;
|
||||
const state = view.state;
|
||||
const dispatch = view.dispatch;
|
||||
const dispatch = view.dispatch.bind(view);
|
||||
|
||||
const [targetLanguage, autoDetect] = languageId === 'auto'
|
||||
? ['text', true]
|
||||
@@ -294,9 +294,11 @@ const scrollToCurrentLanguage = () => {
|
||||
<span class="arrow" :class="{ 'open': showLanguageMenu }">▲</span>
|
||||
</button>
|
||||
|
||||
<div class="language-menu" v-if="showLanguageMenu">
|
||||
<!-- 搜索框 -->
|
||||
<div class="search-container">
|
||||
<!-- 菜单 -->
|
||||
<Transition name="slide-up">
|
||||
<div class="language-menu" v-if="showLanguageMenu">
|
||||
<!-- 搜索框 -->
|
||||
<div class="search-container">
|
||||
<input
|
||||
ref="searchInputRef"
|
||||
v-model="searchQuery"
|
||||
@@ -330,11 +332,23 @@ const scrollToCurrentLanguage = () => {
|
||||
{{ t('toolbar.noLanguageFound') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.slide-up-enter-active,
|
||||
.slide-up-leave-active {
|
||||
transition: opacity 0.15s ease, transform 0.15s ease;
|
||||
}
|
||||
|
||||
.slide-up-enter-from,
|
||||
.slide-up-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(8px);
|
||||
}
|
||||
|
||||
.block-language-selector {
|
||||
position: relative;
|
||||
|
||||
@@ -386,15 +400,17 @@ const scrollToCurrentLanguage = () => {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 3px;
|
||||
margin-bottom: 4px;
|
||||
width: 220px;
|
||||
max-height: 280px;
|
||||
width: 280px;
|
||||
max-height: 400px;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.search-container {
|
||||
position: relative;
|
||||
padding: 8px;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
|
||||
.search-input {
|
||||
@@ -403,11 +419,11 @@ const scrollToCurrentLanguage = () => {
|
||||
background-color: var(--bg-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 2px;
|
||||
padding: 5px 8px 5px 26px;
|
||||
font-size: 11px;
|
||||
padding: 6px 10px 6px 30px;
|
||||
font-size: 12px;
|
||||
color: var(--text-primary);
|
||||
outline: none;
|
||||
line-height: 1.2;
|
||||
line-height: 1.4;
|
||||
|
||||
&:focus {
|
||||
border-color: var(--text-muted);
|
||||
@@ -420,7 +436,7 @@ const scrollToCurrentLanguage = () => {
|
||||
|
||||
.search-icon {
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
left: 16px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: var(--text-muted);
|
||||
@@ -429,20 +445,21 @@ const scrollToCurrentLanguage = () => {
|
||||
}
|
||||
|
||||
.language-list {
|
||||
max-height: 200px;
|
||||
max-height: 320px;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
|
||||
.language-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 6px 8px;
|
||||
padding: 8px 10px;
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
font-size: 12px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--border-color);
|
||||
opacity: 0.8;
|
||||
background-color: var(--bg-hover);
|
||||
}
|
||||
|
||||
&.active {
|
||||
@@ -460,17 +477,17 @@ const scrollToCurrentLanguage = () => {
|
||||
}
|
||||
|
||||
.language-alias {
|
||||
font-size: 10px;
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.no-results {
|
||||
padding: 12px 8px;
|
||||
padding: 14px 10px;
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
font-size: 11px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -478,7 +495,7 @@ const scrollToCurrentLanguage = () => {
|
||||
|
||||
/* 自定义滚动条 */
|
||||
.language-list::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.language-list::-webkit-scrollbar-track {
|
||||
@@ -487,10 +504,10 @@ const scrollToCurrentLanguage = () => {
|
||||
|
||||
.language-list::-webkit-scrollbar-thumb {
|
||||
background-color: var(--border-color);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--text-muted);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
import {computed, nextTick, reactive, ref, watch} from 'vue';
|
||||
import {useDocumentStore} from '@/stores/documentStore';
|
||||
import {useTabStore} from '@/stores/tabStore';
|
||||
import {useEditorStore} from '@/stores/editorStore';
|
||||
import {useEditorStateStore} from '@/stores/editorStateStore';
|
||||
import {useWindowStore} from '@/stores/windowStore';
|
||||
import {useI18n} from 'vue-i18n';
|
||||
import {useConfirm} from '@/composables';
|
||||
import {validateDocumentTitle} from '@/common/utils/validation';
|
||||
import {formatDateTime, truncateString} from '@/common/utils/formatter';
|
||||
import type {Document} from '@/../bindings/voidraft/internal/models/ent/models';
|
||||
import {Document} from '@/../bindings/voidraft/internal/models/ent/models';
|
||||
import toast from '@/components/toast';
|
||||
|
||||
// 类型定义
|
||||
interface DocumentItem extends Document {
|
||||
@@ -16,6 +19,8 @@ interface DocumentItem extends Document {
|
||||
|
||||
const documentStore = useDocumentStore();
|
||||
const tabStore = useTabStore();
|
||||
const editorStore = useEditorStore();
|
||||
const editorStateStore = useEditorStateStore();
|
||||
const windowStore = useWindowStore();
|
||||
const {t} = useI18n();
|
||||
|
||||
@@ -27,6 +32,7 @@ const editInputRef = ref<HTMLInputElement>();
|
||||
const state = reactive({
|
||||
isLoaded: false,
|
||||
searchQuery: '',
|
||||
documentList: [] as Document[], // 缓存文档列表
|
||||
editing: {
|
||||
id: null as number | null,
|
||||
title: ''
|
||||
@@ -44,7 +50,7 @@ const currentDocName = computed(() => {
|
||||
});
|
||||
|
||||
const filteredItems = computed<DocumentItem[]>(() => {
|
||||
const docs = documentStore.documentList;
|
||||
const docs = state.documentList;
|
||||
const query = state.searchQuery.trim();
|
||||
|
||||
if (!query) return docs;
|
||||
@@ -67,7 +73,7 @@ const filteredItems = computed<DocumentItem[]>(() => {
|
||||
|
||||
// 核心操作
|
||||
const openMenu = async () => {
|
||||
await documentStore.getDocumentMetaList();
|
||||
state.documentList = await documentStore.getDocumentList();
|
||||
documentStore.openDocumentSelector();
|
||||
state.isLoaded = true;
|
||||
await nextTick();
|
||||
@@ -88,10 +94,10 @@ const closeMenu = () => {
|
||||
resetDeleteConfirm();
|
||||
};
|
||||
|
||||
const selectDoc = async (doc: Document) => {
|
||||
const selectDoc = async (doc: DocumentItem) => {
|
||||
if (doc.id === undefined) return;
|
||||
|
||||
// 如果选择的就是当前文档,直接关闭菜单
|
||||
// 如果选择的就是当前文档,直接关闭菜单
|
||||
if (documentStore.currentDocument?.id === doc.id) {
|
||||
closeMenu();
|
||||
return;
|
||||
@@ -99,17 +105,40 @@ const selectDoc = async (doc: Document) => {
|
||||
|
||||
const hasOpen = await windowStore.isDocumentWindowOpen(doc.id);
|
||||
if (hasOpen) {
|
||||
documentStore.setError(doc.id, t('toolbar.alreadyOpenInNewWindow'));
|
||||
toast.warning(t('toolbar.alreadyOpenInNewWindow'));
|
||||
return;
|
||||
}
|
||||
|
||||
const success = await documentStore.openDocument(doc.id);
|
||||
if (success) {
|
||||
if (tabStore.isTabsEnabled) {
|
||||
tabStore.addOrActivateTab(doc);
|
||||
}
|
||||
closeMenu();
|
||||
|
||||
// 保存旧文档的光标位置
|
||||
const oldDocId = documentStore.currentDocumentId;
|
||||
if (oldDocId) {
|
||||
const cursorPos = editorStore.getCurrentCursorPosition();
|
||||
editorStateStore.saveCursorPosition(oldDocId, cursorPos);
|
||||
}
|
||||
|
||||
// 如果旧文档有未保存修改,保存它
|
||||
if (oldDocId && editorStore.hasUnsavedChanges(oldDocId)) {
|
||||
|
||||
const content = editorStore.getCurrentContent();
|
||||
await documentStore.saveDocument(oldDocId, content);
|
||||
editorStore.syncAfterSave(oldDocId);
|
||||
|
||||
}
|
||||
|
||||
// 打开新文档
|
||||
const success = await documentStore.openDocument(doc.id);
|
||||
if (!success) return;
|
||||
|
||||
// 切换到新编辑器
|
||||
await editorStore.switchToEditor(doc.id);
|
||||
|
||||
// 更新标签页
|
||||
if (documentStore.currentDocument && tabStore.isTabsEnabled) {
|
||||
tabStore.addOrActivateTab(documentStore.currentDocument);
|
||||
}
|
||||
|
||||
closeMenu();
|
||||
};
|
||||
|
||||
const createDoc = async (title: string) => {
|
||||
@@ -119,7 +148,9 @@ const createDoc = async (title: string) => {
|
||||
|
||||
try {
|
||||
const newDoc = await documentStore.createNewDocument(trimmedTitle);
|
||||
if (newDoc) await selectDoc(newDoc);
|
||||
if (newDoc && newDoc.id) {
|
||||
await selectDoc(newDoc);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to create document:', error);
|
||||
}
|
||||
@@ -142,7 +173,7 @@ const handleSearchEnter = () => {
|
||||
};
|
||||
|
||||
// 编辑操作
|
||||
const renameDoc = (doc: Document, event: Event) => {
|
||||
const renameDoc = (doc: DocumentItem, event: Event) => {
|
||||
event.stopPropagation();
|
||||
state.editing.id = doc.id ?? null;
|
||||
state.editing.title = doc.title || '';
|
||||
@@ -165,8 +196,8 @@ const saveEdit = async () => {
|
||||
if (error) return;
|
||||
|
||||
try {
|
||||
await documentStore.updateDocumentMetadata(state.editing.id, trimmedTitle);
|
||||
await documentStore.getDocumentMetaList();
|
||||
await documentStore.updateDocumentTitle(state.editing.id, trimmedTitle);
|
||||
state.documentList = await documentStore.getDocumentList();
|
||||
|
||||
// 如果tabs功能开启且该文档有标签页,更新标签页标题
|
||||
if (tabStore.isTabsEnabled && tabStore.hasTab(state.editing.id)) {
|
||||
@@ -186,17 +217,21 @@ const cancelEdit = () => {
|
||||
};
|
||||
|
||||
// 其他操作
|
||||
const openInNewWindow = async (doc: Document, event: Event) => {
|
||||
const openInNewWindow = async (doc: DocumentItem, event: Event) => {
|
||||
event.stopPropagation();
|
||||
if (doc.id === undefined) return;
|
||||
try {
|
||||
// 在打开新窗口前,如果启用了标签且该文档有标签,先关闭标签
|
||||
if (tabStore.isTabsEnabled && tabStore.hasTab(doc.id)) {
|
||||
await tabStore.closeTab(doc.id);
|
||||
}
|
||||
await documentStore.openDocumentInNewWindow(doc.id);
|
||||
} catch (error) {
|
||||
console.error('Failed to open document in new window:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (doc: Document, event: Event) => {
|
||||
const handleDelete = async (doc: DocumentItem, event: Event) => {
|
||||
event.stopPropagation();
|
||||
if (doc.id === undefined) return;
|
||||
|
||||
@@ -204,17 +239,17 @@ const handleDelete = async (doc: Document, event: Event) => {
|
||||
// 确认删除前检查文档是否在其他窗口打开
|
||||
const hasOpen = await windowStore.isDocumentWindowOpen(doc.id);
|
||||
if (hasOpen) {
|
||||
documentStore.setError(doc.id, t('toolbar.alreadyOpenInNewWindow'));
|
||||
toast.warning(t('toolbar.alreadyOpenInNewWindow'));
|
||||
resetDeleteConfirm();
|
||||
return;
|
||||
}
|
||||
|
||||
const deleteSuccess = await documentStore.deleteDocument(doc.id);
|
||||
if (deleteSuccess) {
|
||||
await documentStore.getDocumentMetaList();
|
||||
// 如果删除的是当前文档,切换到第一个文档
|
||||
if (documentStore.currentDocument?.id === doc.id && documentStore.documentList.length > 0) {
|
||||
const firstDoc = documentStore.documentList[0];
|
||||
state.documentList = await documentStore.getDocumentList();
|
||||
// 如果删除的是当前文档,切换到第一个文档
|
||||
if (documentStore.currentDocument?.id === doc.id && state.documentList.length > 0) {
|
||||
const firstDoc = state.documentList[0];
|
||||
if (firstDoc) await selectDoc(firstDoc);
|
||||
}
|
||||
}
|
||||
@@ -307,11 +342,7 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
||||
<!-- 普通显示 -->
|
||||
<div v-if="state.editing.id !== item.id" class="doc-info">
|
||||
<div class="doc-title">{{ item.title }}</div>
|
||||
<!-- 根据状态显示错误信息或时间 -->
|
||||
<div v-if="documentStore.selectorError?.docId === item.id" class="doc-error">
|
||||
{{ documentStore.selectorError?.message }}
|
||||
</div>
|
||||
<div v-else class="doc-date">{{ formatDateTime(item.updated_at) }}</div>
|
||||
<div class="doc-date">{{ formatDateTime(item.updated_at) }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 编辑状态 -->
|
||||
@@ -353,7 +384,7 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
v-if="documentStore.documentList.length > 1 && item.id !== 1"
|
||||
v-if="state.documentList.length > 1"
|
||||
class="action-btn delete-btn"
|
||||
:class="{ 'delete-confirm': isDeleting(item.id!) }"
|
||||
@click="handleDelete(item, $event)"
|
||||
@@ -444,7 +475,7 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 3px;
|
||||
margin-bottom: 4px;
|
||||
width: 300px;
|
||||
width: 340px;
|
||||
max-height: calc(100vh - 40px);
|
||||
z-index: 1000;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||
@@ -454,7 +485,7 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
||||
|
||||
.input-box {
|
||||
position: relative;
|
||||
padding: 8px;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
|
||||
.main-input {
|
||||
@@ -463,8 +494,8 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
||||
background-color: var(--bg-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 2px;
|
||||
padding: 5px 8px 5px 26px;
|
||||
font-size: 11px;
|
||||
padding: 6px 10px 6px 30px;
|
||||
font-size: 12px;
|
||||
color: var(--text-primary);
|
||||
outline: none;
|
||||
|
||||
@@ -479,7 +510,7 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
||||
|
||||
.input-icon {
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
left: 16px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: var(--text-muted);
|
||||
@@ -500,7 +531,7 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
||||
background-color: var(--bg-hover);
|
||||
}
|
||||
|
||||
&.active {
|
||||
&.active {
|
||||
background-color: var(--selection-bg);
|
||||
|
||||
.doc-item-content .doc-info {
|
||||
@@ -508,7 +539,7 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
||||
color: var(--selection-text);
|
||||
}
|
||||
|
||||
.doc-date, .doc-error {
|
||||
.doc-date {
|
||||
color: var(--selection-text);
|
||||
opacity: 0.7;
|
||||
}
|
||||
@@ -520,8 +551,8 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 8px;
|
||||
font-size: 11px;
|
||||
padding: 10px 10px;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
|
||||
svg {
|
||||
@@ -535,15 +566,15 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 8px;
|
||||
padding: 10px 10px;
|
||||
|
||||
.doc-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
.doc-title {
|
||||
font-size: 12px;
|
||||
margin-bottom: 2px;
|
||||
font-size: 13px;
|
||||
margin-bottom: 3px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@@ -551,17 +582,10 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
||||
}
|
||||
|
||||
.doc-date {
|
||||
font-size: 10px;
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.doc-error {
|
||||
font-size: 10px;
|
||||
color: var(--text-danger);
|
||||
font-weight: 500;
|
||||
animation: fadeInOut 3s forwards;
|
||||
}
|
||||
}
|
||||
|
||||
.doc-edit {
|
||||
@@ -573,8 +597,8 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
||||
background-color: var(--bg-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 2px;
|
||||
padding: 4px 6px;
|
||||
font-size: 11px;
|
||||
padding: 5px 8px;
|
||||
font-size: 12px;
|
||||
color: var(--text-primary);
|
||||
outline: none;
|
||||
|
||||
@@ -586,7 +610,7 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
||||
|
||||
.doc-actions {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
gap: 8px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
|
||||
@@ -595,7 +619,7 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
padding: 5px;
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -616,7 +640,7 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
||||
color: white;
|
||||
|
||||
.confirm-text {
|
||||
font-size: 9px;
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
@@ -631,27 +655,12 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
|
||||
}
|
||||
|
||||
.empty {
|
||||
padding: 16px 8px;
|
||||
padding: 18px 10px;
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInOut {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
10% {
|
||||
opacity: 1;
|
||||
}
|
||||
90% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,6 +3,7 @@ import {useI18n} from 'vue-i18n';
|
||||
import {computed, onMounted, onUnmounted, ref, watch, shallowRef, readonly, toRefs, effectScope, onScopeDispose} from 'vue';
|
||||
import {useConfigStore} from '@/stores/configStore';
|
||||
import {useEditorStore} from '@/stores/editorStore';
|
||||
import {useEditorStateStore} from '@/stores/editorStateStore';
|
||||
import {useUpdateStore} from '@/stores/updateStore';
|
||||
import {useWindowStore} from '@/stores/windowStore';
|
||||
import {useSystemStore} from '@/stores/systemStore';
|
||||
@@ -15,6 +16,7 @@ import {formatBlockContent} from '@/views/editor/extensions/codeblock/formatCode
|
||||
import {createDebounce} from '@/common/utils/debounce';
|
||||
|
||||
const editorStore = useEditorStore();
|
||||
const editorStateStore = useEditorStateStore();
|
||||
const configStore = useConfigStore();
|
||||
const updateStore = useUpdateStore();
|
||||
const windowStore = useWindowStore();
|
||||
@@ -25,7 +27,6 @@ const router = useRouter();
|
||||
const canFormatCurrentBlock = ref(false);
|
||||
const isLoaded = shallowRef(false);
|
||||
|
||||
const { documentStats } = toRefs(editorStore);
|
||||
const { config } = toRefs(configStore);
|
||||
|
||||
// 窗口置顶状态
|
||||
@@ -57,14 +58,14 @@ const goToSettings = () => {
|
||||
|
||||
// 执行格式化
|
||||
const formatCurrentBlock = () => {
|
||||
if (!canFormatCurrentBlock.value || !editorStore.editorView) return;
|
||||
formatBlockContent(editorStore.editorView);
|
||||
if (!canFormatCurrentBlock.value || !editorStore.currentEditor) return;
|
||||
formatBlockContent(editorStore.currentEditor);
|
||||
};
|
||||
|
||||
|
||||
// 统一更新按钮状态
|
||||
const updateButtonStates = () => {
|
||||
const view: any = editorStore.editorView;
|
||||
const view: any = editorStore.currentEditor;
|
||||
if (!view) {
|
||||
canFormatCurrentBlock.value = false;
|
||||
return;
|
||||
@@ -125,7 +126,7 @@ const setupEditorListeners = (view: any) => {
|
||||
|
||||
// 监听编辑器视图变化
|
||||
watch(
|
||||
() => editorStore.editorView,
|
||||
() => editorStore.currentEditor,
|
||||
(newView) => {
|
||||
// 在 scope 中管理副作用
|
||||
editorScope.run(() => {
|
||||
@@ -191,11 +192,13 @@ const updateButtonTitle = computed(() => {
|
||||
});
|
||||
|
||||
// 统计数据的计算属性
|
||||
const statsData = computed(() => ({
|
||||
lines: documentStats.value.lines,
|
||||
characters: documentStats.value.characters,
|
||||
selectedCharacters: documentStats.value.selectedCharacters
|
||||
}));
|
||||
const statsData = computed(() => {
|
||||
const docId = editorStore.currentEditorId;
|
||||
if (!docId) {
|
||||
return { lines: 0, characters: 0, selectedCharacters: 0 };
|
||||
}
|
||||
return editorStateStore.getDocumentStats(docId);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
export default {
|
||||
locale: 'en-US',
|
||||
common: {
|
||||
ok: 'OK',
|
||||
cancel: 'Cancel',
|
||||
edit: 'Edit',
|
||||
delete: 'Delete',
|
||||
confirm: 'Confirm',
|
||||
save: 'Save',
|
||||
reset: 'Reset'
|
||||
},
|
||||
titlebar: {
|
||||
minimize: 'Minimize',
|
||||
maximize: 'Maximize',
|
||||
@@ -56,6 +65,19 @@ export default {
|
||||
},
|
||||
resetToDefault: 'Reset to Default',
|
||||
confirmReset: 'Confirm Reset?',
|
||||
noKeybinding: 'Not Set',
|
||||
waitingForKey: 'Waiting...',
|
||||
clickToSet: 'Click to set keybinding',
|
||||
editKeybinding: 'Edit keybinding',
|
||||
config: {
|
||||
enabled: 'Enabled',
|
||||
preventDefault: 'Prevent Default',
|
||||
keybinding: 'Keybinding'
|
||||
},
|
||||
keyPlaceholder: 'Enter key, press Enter to add',
|
||||
invalidFormat: 'Invalid format',
|
||||
conflict: 'Conflict: {command}',
|
||||
maxKeysReached: 'Maximum 4 keys allowed',
|
||||
commands: {
|
||||
showSearch: 'Show search panel',
|
||||
hideSearch: 'Hide search panel',
|
||||
@@ -160,7 +182,7 @@ export default {
|
||||
general: 'General',
|
||||
editing: 'Editor',
|
||||
appearance: 'Appearance',
|
||||
backupPage: 'Backup',
|
||||
syncPage: 'Sync',
|
||||
keyBindings: 'Key Bindings',
|
||||
updates: 'Updates',
|
||||
reset: 'Reset',
|
||||
@@ -178,6 +200,7 @@ export default {
|
||||
enableWindowSnap: 'Enable Window Snapping',
|
||||
enableLoadingAnimation: 'Enable Loading Animation',
|
||||
enableTabs: 'Enable Tabs',
|
||||
enableMemoryMonitor: 'Enable Memory Monitor',
|
||||
startup: 'Startup Settings',
|
||||
startAtLogin: 'Start at Login',
|
||||
dataStorage: 'Data Storage',
|
||||
@@ -223,6 +246,7 @@ export default {
|
||||
categoryEditing: 'Editing Enhancement',
|
||||
categoryUI: 'UI Enhancement',
|
||||
categoryTools: 'Tools',
|
||||
enabled: 'Enabled',
|
||||
configuration: 'Configuration',
|
||||
resetToDefault: 'Reset to Default Configuration',
|
||||
},
|
||||
@@ -233,11 +257,16 @@ export default {
|
||||
restartNow: 'Restart Now',
|
||||
hotkeyPreview: 'Preview:',
|
||||
none: 'None',
|
||||
backup: {
|
||||
basicSettings: 'Basic Settings',
|
||||
enableBackup: 'Enable Git Backup',
|
||||
autoBackup: 'Auto Backup',
|
||||
backupInterval: 'Backup Interval',
|
||||
sync: {
|
||||
basicSettings: 'Basic Settings',
|
||||
enableSync: 'Enable Sync',
|
||||
targetType: 'Sync Type',
|
||||
targetTypes: {
|
||||
git: 'Git',
|
||||
localfs: 'Local File System'
|
||||
},
|
||||
autoSync: 'Auto Sync',
|
||||
syncInterval: 'Sync Interval',
|
||||
intervals: {
|
||||
'5min': '5 minutes',
|
||||
'10min': '10 minutes',
|
||||
@@ -246,8 +275,11 @@ export default {
|
||||
'1hour': '1 hour'
|
||||
},
|
||||
repositoryConfig: 'Repository Configuration',
|
||||
repoUrl: 'Repository URL',
|
||||
repoUrlPlaceholder: 'Enter Git repository URL',
|
||||
storageConfig: 'Storage Configuration',
|
||||
repoUrl: 'Repository URL',
|
||||
repoUrlPlaceholder: 'Enter Git repository URL',
|
||||
localfsRootPath: 'Local Storage Directory',
|
||||
localfsRootPathPlaceholder: 'Select local sync directory',
|
||||
authConfig: 'Authentication Configuration',
|
||||
authMethod: 'Authentication Method',
|
||||
authMethods: {
|
||||
@@ -265,9 +297,11 @@ export default {
|
||||
sshKeyPathPlaceholder: 'Select SSH key file',
|
||||
sshKeyPassphrase: 'SSH Key Passphrase',
|
||||
sshKeyPassphrasePlaceholder: 'Enter SSH key passphrase',
|
||||
backupOperations: 'Backup Operations',
|
||||
syncToRemote: 'Sync to Remote',
|
||||
syncOperations: 'Sync Operations',
|
||||
syncToRemote: 'Sync to Remote',
|
||||
syncToTarget: 'Sync to Target',
|
||||
syncing: 'Syncing...',
|
||||
syncSuccess: 'Sync completed',
|
||||
actions: {
|
||||
sync: 'Sync',
|
||||
}
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
export default {
|
||||
locale: 'zh-CN',
|
||||
common: {
|
||||
ok: '确定',
|
||||
cancel: '取消',
|
||||
edit: '编辑',
|
||||
delete: '删除',
|
||||
confirm: '确认',
|
||||
save: '保存',
|
||||
reset: '重置'
|
||||
},
|
||||
titlebar: {
|
||||
minimize: '最小化',
|
||||
maximize: '最大化',
|
||||
@@ -56,6 +65,19 @@ export default {
|
||||
},
|
||||
resetToDefault: '重置为默认',
|
||||
confirmReset: '确认重置?',
|
||||
noKeybinding: '未设置',
|
||||
waitingForKey: '等待输入...',
|
||||
clickToSet: '点击设置快捷键',
|
||||
editKeybinding: '编辑快捷键',
|
||||
config: {
|
||||
enabled: '启用',
|
||||
preventDefault: '阻止默认',
|
||||
keybinding: '快捷键'
|
||||
},
|
||||
keyPlaceholder: '输入键名, 回车添加',
|
||||
invalidFormat: '格式错误',
|
||||
conflict: '冲突: {command}',
|
||||
maxKeysReached: '最多只能添加4个键',
|
||||
commands: {
|
||||
showSearch: '显示搜索面板',
|
||||
hideSearch: '隐藏搜索面板',
|
||||
@@ -160,7 +182,7 @@ export default {
|
||||
general: '常规',
|
||||
editing: '编辑器',
|
||||
appearance: '外观',
|
||||
backupPage: '备份',
|
||||
syncPage: '同步',
|
||||
extensions: '扩展',
|
||||
keyBindings: '快捷键',
|
||||
updates: '更新',
|
||||
@@ -179,6 +201,7 @@ export default {
|
||||
enableWindowSnap: '启用窗口吸附',
|
||||
enableLoadingAnimation: '启用加载动画',
|
||||
enableTabs: '启用标签页',
|
||||
enableMemoryMonitor: '启用内存监视器',
|
||||
startup: '启动设置',
|
||||
startAtLogin: '开机自启动',
|
||||
dataStorage: '数据存储',
|
||||
@@ -226,6 +249,7 @@ export default {
|
||||
categoryEditing: '编辑增强',
|
||||
categoryUI: '界面增强',
|
||||
categoryTools: '工具扩展',
|
||||
enabled: '启用',
|
||||
configuration: '配置',
|
||||
resetToDefault: '重置为默认配置',
|
||||
},
|
||||
@@ -235,11 +259,16 @@ export default {
|
||||
colorValue: '颜色值',
|
||||
hotkeyPreview: '预览:',
|
||||
none: '无',
|
||||
backup: {
|
||||
sync: {
|
||||
basicSettings: '基本设置',
|
||||
enableBackup: '启用备份',
|
||||
autoBackup: '自动备份',
|
||||
backupInterval: '备份间隔',
|
||||
enableSync: '启用同步',
|
||||
targetType: '同步方式',
|
||||
targetTypes: {
|
||||
git: 'Git',
|
||||
localfs: '本地文件系统'
|
||||
},
|
||||
autoSync: '自动同步',
|
||||
syncInterval: '同步间隔',
|
||||
intervals: {
|
||||
'5min': '5分钟',
|
||||
'10min': '10分钟',
|
||||
@@ -248,8 +277,11 @@ export default {
|
||||
'1hour': '1小时'
|
||||
},
|
||||
repositoryConfig: '仓库配置',
|
||||
storageConfig: '存储配置',
|
||||
repoUrl: '仓库地址',
|
||||
repoUrlPlaceholder: '请输入Git仓库地址',
|
||||
localfsRootPath: '本地存储目录',
|
||||
localfsRootPathPlaceholder: '请选择本地同步目录',
|
||||
authConfig: '认证配置',
|
||||
authMethod: '认证方式',
|
||||
authMethods: {
|
||||
@@ -267,9 +299,11 @@ export default {
|
||||
sshKeyPathPlaceholder: '请选择SSH密钥文件',
|
||||
sshKeyPassphrase: 'SSH密钥密码',
|
||||
sshKeyPassphrasePlaceholder: '请输入SSH密钥密码',
|
||||
backupOperations: '备份操作',
|
||||
syncOperations: '同步操作',
|
||||
syncToRemote: '同步到远程',
|
||||
syncToTarget: '同步到目标',
|
||||
syncing: '同步中...',
|
||||
syncSuccess: '同步成功',
|
||||
actions: {
|
||||
sync: '同步',
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import AppearancePage from '@/views/settings/pages/AppearancePage.vue';
|
||||
import KeyBindingsPage from '@/views/settings/pages/KeyBindingsPage.vue';
|
||||
import UpdatesPage from '@/views/settings/pages/UpdatesPage.vue';
|
||||
import ExtensionsPage from '@/views/settings/pages/ExtensionsPage.vue';
|
||||
import BackupPage from '@/views/settings/pages/BackupPage.vue';
|
||||
import SyncPage from '@/views/settings/pages/SyncPage.vue';
|
||||
// 测试页面
|
||||
import TestPage from '@/views/settings/pages/TestPage.vue';
|
||||
|
||||
@@ -44,9 +44,9 @@ const settingsChildren: RouteRecordRaw[] = [
|
||||
component: UpdatesPage
|
||||
},
|
||||
{
|
||||
path: 'backup',
|
||||
name: 'SettingsBackup',
|
||||
component: BackupPage
|
||||
path: 'sync',
|
||||
name: 'SettingsSync',
|
||||
component: SyncPage
|
||||
}
|
||||
];
|
||||
|
||||
@@ -79,4 +79,4 @@ const router = createRouter({
|
||||
routes: routes
|
||||
});
|
||||
|
||||
export default router;
|
||||
export default router;
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import { BackupService } from '@/../bindings/voidraft/internal/services';
|
||||
|
||||
export const useBackupStore = defineStore('backup', () => {
|
||||
const isSyncing = ref(false);
|
||||
const error = ref<string | null>(null);
|
||||
|
||||
const sync = async (): Promise<void> => {
|
||||
if (isSyncing.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
isSyncing.value = true;
|
||||
error.value = null;
|
||||
|
||||
try {
|
||||
await BackupService.Sync();
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : String(e);
|
||||
} finally {
|
||||
isSyncing.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
isSyncing,
|
||||
error,
|
||||
sync
|
||||
};
|
||||
});
|
||||
@@ -4,293 +4,232 @@ import {ConfigService, StartupService} from '@/../bindings/voidraft/internal/ser
|
||||
import {
|
||||
AppConfig,
|
||||
AuthMethod,
|
||||
EditingConfig,
|
||||
SyncTarget,
|
||||
LanguageType,
|
||||
SystemThemeType,
|
||||
TabType
|
||||
} from '@/../bindings/voidraft/internal/models/models';
|
||||
import {useI18n} from 'vue-i18n';
|
||||
import {ConfigUtils} from '@/common/utils/configUtils';
|
||||
import {FONT_OPTIONS} from '@/common/constant/fonts';
|
||||
import {SUPPORTED_LOCALES} from '@/common/constant/locales';
|
||||
import {
|
||||
CONFIG_KEY_MAP,
|
||||
CONFIG_LIMITS,
|
||||
ConfigKey,
|
||||
ConfigSection,
|
||||
DEFAULT_CONFIG,
|
||||
NumberConfigKey
|
||||
} from '@/common/constant/config';
|
||||
import {CONFIG_KEY_MAP, CONFIG_LIMITS, ConfigKey, DEFAULT_CONFIG, NumberConfigKey} from '@/common/constant/config';
|
||||
import * as runtime from '@wailsio/runtime';
|
||||
|
||||
export const useConfigStore = defineStore('config', () => {
|
||||
const {locale} = useI18n();
|
||||
|
||||
// 响应式状态
|
||||
const state = reactive({
|
||||
config: {...DEFAULT_CONFIG} as AppConfig,
|
||||
config: structuredClone(DEFAULT_CONFIG) as AppConfig,
|
||||
isLoading: false,
|
||||
configLoaded: false
|
||||
});
|
||||
|
||||
// Font options (no longer localized)
|
||||
const fontOptions = computed(() => FONT_OPTIONS);
|
||||
|
||||
// 计算属性
|
||||
const createLimitComputed = (key: NumberConfigKey) => computed(() => CONFIG_LIMITS[key]);
|
||||
const limits = Object.fromEntries(
|
||||
(['fontSize', 'tabSize', 'lineHeight'] as const).map(key => [key, createLimitComputed(key)])
|
||||
) as Record<NumberConfigKey, ReturnType<typeof createLimitComputed>>;
|
||||
const applyConfig = (appConfig?: AppConfig | null): void => {
|
||||
const nextConfig = structuredClone(DEFAULT_CONFIG) as AppConfig;
|
||||
|
||||
// 统一配置更新方法
|
||||
const updateConfig = async <K extends ConfigKey>(key: K, value: any): Promise<void> => {
|
||||
if (appConfig?.general) Object.assign(nextConfig.general, appConfig.general);
|
||||
if (appConfig?.editing) Object.assign(nextConfig.editing, appConfig.editing);
|
||||
if (appConfig?.appearance) Object.assign(nextConfig.appearance, appConfig.appearance);
|
||||
if (appConfig?.updates) Object.assign(nextConfig.updates, appConfig.updates);
|
||||
if (appConfig?.sync) {
|
||||
if (appConfig.sync.target) {
|
||||
nextConfig.sync.target = appConfig.sync.target;
|
||||
}
|
||||
if (appConfig.sync.git) {
|
||||
Object.assign(nextConfig.sync.git, appConfig.sync.git);
|
||||
}
|
||||
if (appConfig.sync.localfs) {
|
||||
Object.assign(nextConfig.sync.localfs, appConfig.sync.localfs);
|
||||
}
|
||||
}
|
||||
if (appConfig?.metadata) Object.assign(nextConfig.metadata, appConfig.metadata);
|
||||
|
||||
state.config = nextConfig;
|
||||
};
|
||||
|
||||
const ensureConfigLoaded = async (): Promise<void> => {
|
||||
if (!state.configLoaded && !state.isLoading) {
|
||||
await initConfig();
|
||||
}
|
||||
};
|
||||
|
||||
const backendKey = CONFIG_KEY_MAP[key];
|
||||
if (!backendKey) {
|
||||
throw new Error(`No backend key mapping found for ${String(key)}`);
|
||||
const setValueByPath = (target: Record<string, any>, path: string, value: unknown): void => {
|
||||
const segments = path.split('.');
|
||||
const lastIndex = segments.length - 1;
|
||||
|
||||
let current: Record<string, any> = target;
|
||||
for (let index = 0; index < lastIndex; index++) {
|
||||
current = current[segments[index]];
|
||||
}
|
||||
|
||||
// 从 backendKey 提取 section(例如 'general.alwaysOnTop' -> 'general')
|
||||
const section = backendKey.split('.')[0] as ConfigSection;
|
||||
|
||||
await ConfigService.Set(backendKey, value);
|
||||
(state.config[section] as any)[key] = value;
|
||||
current[segments[lastIndex]] = value;
|
||||
};
|
||||
|
||||
// 只更新本地状态,不保存到后端
|
||||
const updateConfigLocal = <K extends ConfigKey>(key: K, value: any): void => {
|
||||
const backendKey = CONFIG_KEY_MAP[key];
|
||||
const section = backendKey.split('.')[0] as ConfigSection;
|
||||
(state.config[section] as any)[key] = value;
|
||||
const getValueByPath = (target: Record<string, any>, path: string): unknown => {
|
||||
return path.split('.').reduce<unknown>((current, segment) => (current as Record<string, any>)[segment], target);
|
||||
};
|
||||
|
||||
const updateConfig = async <K extends ConfigKey>(key: K, value: unknown): Promise<void> => {
|
||||
await ensureConfigLoaded();
|
||||
const path = CONFIG_KEY_MAP[key];
|
||||
await ConfigService.Set(path, value);
|
||||
setValueByPath(state.config as Record<string, any>, path, value);
|
||||
};
|
||||
|
||||
const updateConfigLocal = <K extends ConfigKey>(key: K, value: unknown): void => {
|
||||
setValueByPath(state.config as Record<string, any>, CONFIG_KEY_MAP[key], value);
|
||||
};
|
||||
|
||||
// 保存指定配置到后端
|
||||
const saveConfig = async <K extends ConfigKey>(key: K): Promise<void> => {
|
||||
const backendKey = CONFIG_KEY_MAP[key];
|
||||
const section = backendKey.split('.')[0] as ConfigSection;
|
||||
await ConfigService.Set(backendKey, (state.config[section] as any)[key]);
|
||||
const path = CONFIG_KEY_MAP[key];
|
||||
await ConfigService.Set(path, getValueByPath(state.config as Record<string, any>, path));
|
||||
};
|
||||
|
||||
// 加载配置
|
||||
const activeSyncKey = <G extends ConfigKey, L extends ConfigKey>(gitKey: G, localFSKey: L): G | L => (
|
||||
state.config.sync.target === SyncTarget.SyncTargetGit ? gitKey : localFSKey
|
||||
);
|
||||
|
||||
const initConfig = async (): Promise<void> => {
|
||||
if (state.isLoading) return;
|
||||
|
||||
state.isLoading = true;
|
||||
try {
|
||||
const appConfig = await ConfigService.GetConfig();
|
||||
|
||||
if (appConfig) {
|
||||
// 合并配置
|
||||
if (appConfig.general) Object.assign(state.config.general, appConfig.general);
|
||||
if (appConfig.editing) Object.assign(state.config.editing, appConfig.editing);
|
||||
if (appConfig.appearance) Object.assign(state.config.appearance, appConfig.appearance);
|
||||
if (appConfig.updates) Object.assign(state.config.updates, appConfig.updates);
|
||||
if (appConfig.backup) Object.assign(state.config.backup, appConfig.backup);
|
||||
if (appConfig.metadata) Object.assign(state.config.metadata, appConfig.metadata);
|
||||
}
|
||||
|
||||
applyConfig(await ConfigService.GetConfig());
|
||||
state.configLoaded = true;
|
||||
|
||||
} finally {
|
||||
state.isLoading = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 通用数值调整器工厂
|
||||
const createAdjuster = <T extends NumberConfigKey>(key: T) => {
|
||||
const limit = CONFIG_LIMITS[key];
|
||||
const clamp = (value: number) => ConfigUtils.clamp(value, limit.min, limit.max);
|
||||
|
||||
return {
|
||||
increase: async () => await updateConfig(key, clamp(state.config.editing[key] + 1)),
|
||||
decrease: async () => await updateConfig(key, clamp(state.config.editing[key] - 1)),
|
||||
set: async (value: number) => await updateConfig(key, clamp(value)),
|
||||
reset: async () => await updateConfig(key, limit.default),
|
||||
increaseLocal: () => updateConfigLocal(key, clamp(state.config.editing[key] + 1)),
|
||||
decreaseLocal: () => updateConfigLocal(key, clamp(state.config.editing[key] - 1))
|
||||
};
|
||||
};
|
||||
|
||||
const createEditingToggler = <T extends keyof EditingConfig>(key: T) =>
|
||||
async () => await updateConfig(key as ConfigKey, !state.config.editing[key] as EditingConfig[T]);
|
||||
|
||||
// 枚举值切换器
|
||||
const createEnumToggler = <T extends TabType>(key: 'tabType', values: readonly T[]) =>
|
||||
async () => {
|
||||
const currentIndex = values.indexOf(state.config.editing[key] as T);
|
||||
const nextIndex = (currentIndex + 1) % values.length;
|
||||
return await updateConfig(key, values[nextIndex]);
|
||||
};
|
||||
|
||||
// 重置配置
|
||||
const resetConfig = async (): Promise<void> => {
|
||||
if (state.isLoading) return;
|
||||
|
||||
state.isLoading = true;
|
||||
try {
|
||||
|
||||
await ConfigService.ResetConfig();
|
||||
const appConfig = await ConfigService.GetConfig();
|
||||
if (appConfig) {
|
||||
state.config = JSON.parse(JSON.stringify(appConfig)) as AppConfig;
|
||||
}
|
||||
applyConfig(await ConfigService.GetConfig());
|
||||
state.configLoaded = true;
|
||||
} finally {
|
||||
state.isLoading = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 语言设置方法
|
||||
const setLanguage = async (language: LanguageType): Promise<void> => {
|
||||
await updateConfig('language', language);
|
||||
const frontendLocale = ConfigUtils.backendLanguageToFrontend(language);
|
||||
locale.value = frontendLocale as any;
|
||||
const clampValue = (value: number, key: NumberConfigKey): number => {
|
||||
const limit = CONFIG_LIMITS[key];
|
||||
return ConfigUtils.clamp(value, limit.min, limit.max);
|
||||
};
|
||||
|
||||
// 系统主题设置方法
|
||||
const setSystemTheme = async (systemTheme: SystemThemeType): Promise<void> => {
|
||||
await updateConfig('systemTheme', systemTheme);
|
||||
};
|
||||
const fontConfig = computed(() => ({
|
||||
fontSize: state.config.editing.fontSize,
|
||||
fontFamily: state.config.editing.fontFamily,
|
||||
lineHeight: state.config.editing.lineHeight,
|
||||
fontWeight: state.config.editing.fontWeight
|
||||
}));
|
||||
|
||||
// 当前主题设置方法
|
||||
const setCurrentTheme = async (themeName: string): Promise<void> => {
|
||||
await updateConfig('currentTheme', themeName);
|
||||
};
|
||||
|
||||
|
||||
// 初始化语言设置
|
||||
const initLanguage = async (): Promise<void> => {
|
||||
try {
|
||||
// 如果配置未加载,先加载配置
|
||||
if (!state.configLoaded) {
|
||||
await initConfig();
|
||||
}
|
||||
|
||||
// 同步前端语言设置
|
||||
const frontendLocale = ConfigUtils.backendLanguageToFrontend(state.config.appearance.language);
|
||||
locale.value = frontendLocale as any;
|
||||
} catch (_error) {
|
||||
const browserLang = SUPPORTED_LOCALES[0].code;
|
||||
locale.value = browserLang as any;
|
||||
}
|
||||
};
|
||||
|
||||
// 创建数值调整器实例
|
||||
const adjusters = {
|
||||
fontSize: createAdjuster('fontSize'),
|
||||
tabSize: createAdjuster('tabSize'),
|
||||
lineHeight: createAdjuster('lineHeight')
|
||||
};
|
||||
|
||||
// 创建切换器实例
|
||||
const togglers = {
|
||||
tabIndent: createEditingToggler('enableTabIndent'),
|
||||
alwaysOnTop: async () => {
|
||||
await updateConfig('alwaysOnTop', !state.config.general.alwaysOnTop);
|
||||
await runtime.Window.SetAlwaysOnTop(state.config.general.alwaysOnTop);
|
||||
},
|
||||
tabType: createEnumToggler('tabType', CONFIG_LIMITS.tabType.values)
|
||||
};
|
||||
const tabConfig = computed(() => ({
|
||||
tabSize: state.config.editing.tabSize,
|
||||
enableTabIndent: state.config.editing.enableTabIndent,
|
||||
tabType: state.config.editing.tabType
|
||||
}));
|
||||
|
||||
return {
|
||||
// 状态
|
||||
config: computed(() => state.config),
|
||||
configLoaded: computed(() => state.configLoaded),
|
||||
isLoading: computed(() => state.isLoading),
|
||||
fontOptions,
|
||||
fontConfig,
|
||||
tabConfig,
|
||||
|
||||
// 限制常量
|
||||
...limits,
|
||||
|
||||
// 核心方法
|
||||
initConfig,
|
||||
resetConfig,
|
||||
|
||||
// 语言相关方法
|
||||
setLanguage,
|
||||
initLanguage,
|
||||
setLanguage: async (value: LanguageType) => {
|
||||
await updateConfig('language', value);
|
||||
locale.value = value as any;
|
||||
},
|
||||
|
||||
// 主题相关方法
|
||||
setSystemTheme,
|
||||
setCurrentTheme,
|
||||
setSystemTheme: (value: SystemThemeType) => updateConfig('systemTheme', value),
|
||||
setCurrentTheme: (value: string) => updateConfig('currentTheme', value),
|
||||
|
||||
// 字体大小操作
|
||||
...adjusters.fontSize,
|
||||
increaseFontSize: adjusters.fontSize.increase,
|
||||
decreaseFontSize: adjusters.fontSize.decrease,
|
||||
resetFontSize: adjusters.fontSize.reset,
|
||||
setFontSize: adjusters.fontSize.set,
|
||||
// 字体大小操作
|
||||
increaseFontSizeLocal: adjusters.fontSize.increaseLocal,
|
||||
decreaseFontSizeLocal: adjusters.fontSize.decreaseLocal,
|
||||
saveFontSize: () => saveConfig('fontSize'),
|
||||
setFontSize: async (value: number) => {
|
||||
await updateConfig('fontSize', clampValue(value, 'fontSize'));
|
||||
},
|
||||
increaseFontSize: async () => {
|
||||
await updateConfig('fontSize', clampValue(state.config.editing.fontSize + 1, 'fontSize'));
|
||||
},
|
||||
decreaseFontSize: async () => {
|
||||
await updateConfig('fontSize', clampValue(state.config.editing.fontSize - 1, 'fontSize'));
|
||||
},
|
||||
resetFontSize: async () => {
|
||||
await updateConfig('fontSize', CONFIG_LIMITS.fontSize.default);
|
||||
},
|
||||
increaseFontSizeLocal: () => {
|
||||
updateConfigLocal('fontSize', clampValue(state.config.editing.fontSize + 1, 'fontSize'));
|
||||
},
|
||||
decreaseFontSizeLocal: () => {
|
||||
updateConfigLocal('fontSize', clampValue(state.config.editing.fontSize - 1, 'fontSize'));
|
||||
},
|
||||
saveFontSize: async () => {
|
||||
await saveConfig('fontSize');
|
||||
},
|
||||
|
||||
// Tab操作
|
||||
toggleTabIndent: togglers.tabIndent,
|
||||
setEnableTabIndent: (value: boolean) => updateConfig('enableTabIndent', value),
|
||||
...adjusters.tabSize,
|
||||
increaseTabSize: adjusters.tabSize.increase,
|
||||
decreaseTabSize: adjusters.tabSize.decrease,
|
||||
setTabSize: adjusters.tabSize.set,
|
||||
toggleTabType: togglers.tabType,
|
||||
|
||||
// 行高操作
|
||||
setLineHeight: adjusters.lineHeight.set,
|
||||
|
||||
// 窗口操作
|
||||
toggleAlwaysOnTop: togglers.alwaysOnTop,
|
||||
setAlwaysOnTop: (value: boolean) => updateConfig('alwaysOnTop', value),
|
||||
|
||||
// 字体操作
|
||||
setFontFamily: (value: string) => updateConfig('fontFamily', value),
|
||||
setFontWeight: (value: string) => updateConfig('fontWeight', value),
|
||||
setLineHeight: async (value: number) => {
|
||||
await updateConfig('lineHeight', clampValue(value, 'lineHeight'));
|
||||
},
|
||||
|
||||
// 路径操作
|
||||
setEnableTabIndent: (value: boolean) => updateConfig('enableTabIndent', value),
|
||||
setTabSize: async (value: number) => {
|
||||
await updateConfig('tabSize', clampValue(value, 'tabSize'));
|
||||
},
|
||||
increaseTabSize: async () => {
|
||||
await updateConfig('tabSize', clampValue(state.config.editing.tabSize + 1, 'tabSize'));
|
||||
},
|
||||
decreaseTabSize: async () => {
|
||||
await updateConfig('tabSize', clampValue(state.config.editing.tabSize - 1, 'tabSize'));
|
||||
},
|
||||
toggleTabType: async () => {
|
||||
const values = CONFIG_LIMITS.tabType.values;
|
||||
const currentIndex = values.indexOf(state.config.editing.tabType as typeof values[number]);
|
||||
await updateConfig('tabType', values[(currentIndex + 1) % values.length]);
|
||||
},
|
||||
|
||||
toggleAlwaysOnTop: async () => {
|
||||
await updateConfig('alwaysOnTop', !state.config.general.alwaysOnTop);
|
||||
await runtime.Window.SetAlwaysOnTop(state.config.general.alwaysOnTop);
|
||||
},
|
||||
setAlwaysOnTop: (value: boolean) => updateConfig('alwaysOnTop', value),
|
||||
setDataPath: (value: string) => updateConfigLocal('dataPath', value),
|
||||
|
||||
// 保存配置相关方法
|
||||
setAutoSaveDelay: (value: number) => updateConfig('autoSaveDelay', value),
|
||||
|
||||
// 热键配置相关方法
|
||||
setEnableGlobalHotkey: (value: boolean) => updateConfig('enableGlobalHotkey', value),
|
||||
setGlobalHotkey: (hotkey: any) => updateConfig('globalHotkey', hotkey),
|
||||
|
||||
// 系统托盘配置相关方法
|
||||
setEnableSystemTray: (value: boolean) => updateConfig('enableSystemTray', value),
|
||||
|
||||
// 开机启动配置相关方法
|
||||
setStartAtLogin: async (value: boolean) => {
|
||||
await updateConfig('startAtLogin', value);
|
||||
await StartupService.SetEnabled(value);
|
||||
},
|
||||
|
||||
// 窗口吸附配置相关方法
|
||||
setEnableWindowSnap: (value: boolean) => updateConfig('enableWindowSnap', value),
|
||||
|
||||
// 加载动画配置相关方法
|
||||
setEnableLoadingAnimation: (value: boolean) => updateConfig('enableLoadingAnimation', value),
|
||||
|
||||
// 标签页配置相关方法
|
||||
setEnableTabs: (value: boolean) => updateConfig('enableTabs', value),
|
||||
|
||||
// 快捷键模式配置相关方法
|
||||
setEnableMemoryMonitor: (value: boolean) => updateConfig('enableMemoryMonitor', value),
|
||||
setKeymapMode: (value: any) => updateConfig('keymapMode', value),
|
||||
|
||||
// 更新配置相关方法
|
||||
setAutoUpdate: (value: boolean) => updateConfig('autoUpdate', value),
|
||||
|
||||
// 备份配置相关方法
|
||||
setEnableBackup: (value: boolean) => updateConfig('enabled', value),
|
||||
setAutoBackup: (value: boolean) => updateConfig('auto_backup', value),
|
||||
setRepoUrl: (value: string) => updateConfig('repo_url', value),
|
||||
setAuthMethod: (value: AuthMethod) => updateConfig('auth_method', value),
|
||||
setUsername: (value: string) => updateConfig('username', value),
|
||||
setPassword: (value: string) => updateConfig('password', value),
|
||||
setToken: (value: string) => updateConfig('token', value),
|
||||
setSshKeyPath: (value: string) => updateConfig('ssh_key_path', value),
|
||||
setSshKeyPassphrase: (value: string) => updateConfig('ssh_key_passphrase', value),
|
||||
setBackupInterval: (value: number) => updateConfig('backup_interval', value),
|
||||
setSyncTarget: (value: SyncTarget) => updateConfig('sync_target', value),
|
||||
setEnableSync: (value: boolean) => updateConfig(activeSyncKey('git_enabled', 'localfs_enabled'), value),
|
||||
setAutoSync: (value: boolean) => updateConfig(activeSyncKey('git_auto_sync', 'localfs_auto_sync'), value),
|
||||
setSyncInterval: (value: number) => updateConfig(
|
||||
activeSyncKey('git_sync_interval', 'localfs_sync_interval'),
|
||||
Math.max(1, value)
|
||||
),
|
||||
setRepoUrl: (value: string) => updateConfig('git_repo_url', value),
|
||||
setAuthMethod: (value: AuthMethod) => updateConfig('git_auth_method', value),
|
||||
setUsername: (value: string) => updateConfig('git_username', value),
|
||||
setPassword: (value: string) => updateConfig('git_password', value),
|
||||
setToken: (value: string) => updateConfig('git_token', value),
|
||||
setSshKeyPath: (value: string) => updateConfig('git_ssh_key_path', value),
|
||||
setSshKeyPassphrase: (value: string) => updateConfig('git_ssh_key_passphrase', value),
|
||||
setLocalFSRootPath: (value: string) => updateConfig('localfs_root_path', value),
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,79 +1,70 @@
|
||||
import {defineStore} from 'pinia';
|
||||
import {computed, ref} from 'vue';
|
||||
import {ref} from 'vue';
|
||||
import {DocumentService} from '@/../bindings/voidraft/internal/services';
|
||||
import {OpenDocumentWindow} from '@/../bindings/voidraft/internal/services/windowservice';
|
||||
import {Document} from '@/../bindings/voidraft/internal/models/ent/models';
|
||||
import {useTabStore} from "@/stores/tabStore";
|
||||
import type {EditorViewState} from '@/stores/editorStore';
|
||||
import type {TimerManager} from '@/common/utils/timerUtils';
|
||||
import {createTimerManager} from '@/common/utils/timerUtils';
|
||||
|
||||
export const useDocumentStore = defineStore('document', () => {
|
||||
|
||||
// === 核心状态 ===
|
||||
const documents = ref<Record<number, Document>>({});
|
||||
const currentDocumentId = ref<number | null>(null);
|
||||
const currentDocument = ref<Document | null>(null);
|
||||
|
||||
// === 编辑器状态持久化 ===
|
||||
const documentStates = ref<Record<number, EditorViewState>>({});
|
||||
|
||||
// 自动保存定时器
|
||||
const autoSaveTimers = ref<Map<number, TimerManager>>(new Map());
|
||||
|
||||
// === UI状态 ===
|
||||
const showDocumentSelector = ref(false);
|
||||
const selectorError = ref<{ docId: number; message: string } | null>(null);
|
||||
const isLoading = ref(false);
|
||||
|
||||
// === 计算属性 ===
|
||||
const documentList = computed(() =>
|
||||
Object.values(documents.value).sort((a, b) => {
|
||||
const timeA = a.updated_at ? new Date(a.updated_at).getTime() : 0;
|
||||
const timeB = b.updated_at ? new Date(b.updated_at).getTime() : 0;
|
||||
return timeB - timeA;
|
||||
})
|
||||
);
|
||||
|
||||
const setDocuments = (docs: Document[]) => {
|
||||
documents.value = {};
|
||||
docs.forEach(doc => {
|
||||
if (doc.id !== undefined) {
|
||||
documents.value[doc.id] = doc;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// === 错误处理 ===
|
||||
const setError = (docId: number, message: string) => {
|
||||
selectorError.value = {docId, message};
|
||||
// 3秒后自动清除错误状态
|
||||
setTimeout(() => {
|
||||
if (selectorError.value?.docId === docId) {
|
||||
selectorError.value = null;
|
||||
}
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
const clearError = () => {
|
||||
selectorError.value = null;
|
||||
};
|
||||
|
||||
// === UI控制方法 ===
|
||||
const openDocumentSelector = () => {
|
||||
showDocumentSelector.value = true;
|
||||
clearError();
|
||||
};
|
||||
|
||||
const closeDocumentSelector = () => {
|
||||
showDocumentSelector.value = false;
|
||||
clearError();
|
||||
};
|
||||
|
||||
|
||||
// 获取文档列表
|
||||
const getDocumentList = async (): Promise<Document[]> => {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
const docs = await DocumentService.ListAllDocumentsMeta();
|
||||
return docs?.filter((doc): doc is Document => doc !== null) || [];
|
||||
} catch (_error) {
|
||||
return [];
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取单个文档
|
||||
const getDocument = async (docId: number): Promise<Document | null> => {
|
||||
try {
|
||||
return await DocumentService.GetDocumentByID(docId);
|
||||
} catch (error) {
|
||||
console.error('Failed to get document:', error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// 保存文档内容
|
||||
const saveDocument = async (docId: number, content: string): Promise<Document | null> => {
|
||||
try {
|
||||
await DocumentService.UpdateDocumentContent(docId, content);
|
||||
return await DocumentService.GetDocumentByID(docId);
|
||||
} catch (error) {
|
||||
console.error('Failed to save document:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// 在新窗口中打开文档
|
||||
const openDocumentInNewWindow = async (docId: number): Promise<boolean> => {
|
||||
try {
|
||||
const tabStore = useTabStore();
|
||||
if (tabStore.isTabsEnabled && tabStore.hasTab(docId)) {
|
||||
tabStore.closeTab(docId);
|
||||
}
|
||||
await OpenDocumentWindow(docId);
|
||||
return true;
|
||||
} catch (error) {
|
||||
@@ -86,36 +77,16 @@ export const useDocumentStore = defineStore('document', () => {
|
||||
const createNewDocument = async (title: string): Promise<Document | null> => {
|
||||
try {
|
||||
const doc = await DocumentService.CreateDocument(title);
|
||||
if (doc && doc.id !== undefined) {
|
||||
documents.value[doc.id] = doc;
|
||||
return doc;
|
||||
}
|
||||
return null;
|
||||
return doc || null;
|
||||
} catch (error) {
|
||||
console.error('Failed to create document:', error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取文档列表
|
||||
const getDocumentMetaList = async () => {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
const docs = await DocumentService.ListAllDocumentsMeta();
|
||||
if (docs) {
|
||||
setDocuments(docs.filter((doc): doc is Document => doc !== null));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to update documents:', error);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 打开文档
|
||||
const openDocument = async (docId: number): Promise<boolean> => {
|
||||
try {
|
||||
// 获取完整文档数据
|
||||
const doc = await DocumentService.GetDocumentByID(docId);
|
||||
if (!doc) {
|
||||
throw new Error(`Document ${docId} not found`);
|
||||
@@ -123,7 +94,6 @@ export const useDocumentStore = defineStore('document', () => {
|
||||
|
||||
currentDocumentId.value = docId;
|
||||
currentDocument.value = doc;
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Failed to open document:', error);
|
||||
@@ -131,30 +101,20 @@ export const useDocumentStore = defineStore('document', () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 更新文档元数据
|
||||
const updateDocumentMetadata = async (docId: number, title: string): Promise<boolean> => {
|
||||
// 更新文档标题
|
||||
const updateDocumentTitle = async (docId: number, title: string): Promise<boolean> => {
|
||||
try {
|
||||
await DocumentService.UpdateDocumentTitle(docId, title);
|
||||
|
||||
// 更新本地状态
|
||||
const doc = documents.value[docId];
|
||||
if (doc) {
|
||||
doc.title = title;
|
||||
doc.updated_at = new Date().toISOString();
|
||||
}
|
||||
|
||||
// 更新当前文档状态
|
||||
if (currentDocument.value?.id === docId) {
|
||||
currentDocument.value.title = title;
|
||||
currentDocument.value.updated_at = new Date().toISOString();
|
||||
}
|
||||
|
||||
// 同步更新标签页标题
|
||||
const tabStore = useTabStore();
|
||||
tabStore.updateTabTitle(docId, title);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Failed to update document metadata:', error);
|
||||
console.error('Failed to update document title:', error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -164,20 +124,18 @@ export const useDocumentStore = defineStore('document', () => {
|
||||
try {
|
||||
await DocumentService.DeleteDocument(docId);
|
||||
|
||||
// 更新本地状态
|
||||
delete documents.value[docId];
|
||||
|
||||
// 同步清理标签页
|
||||
const tabStore = useTabStore();
|
||||
if (tabStore.hasTab(docId)) {
|
||||
tabStore.closeTab(docId);
|
||||
// 清理定时器
|
||||
const timer = autoSaveTimers.value.get(docId);
|
||||
if (timer) {
|
||||
timer.clear();
|
||||
autoSaveTimers.value.delete(docId);
|
||||
}
|
||||
|
||||
// 如果删除的是当前文档,切换到第一个可用文档
|
||||
if (currentDocumentId.value === docId) {
|
||||
const availableDocs = Object.values(documents.value);
|
||||
if (availableDocs.length > 0 && availableDocs[0].id !== undefined) {
|
||||
await openDocument(availableDocs[0].id);
|
||||
const docs = await getDocumentList();
|
||||
if (docs.length > 0 && docs[0].id !== undefined) {
|
||||
await openDocument(docs[0].id);
|
||||
} else {
|
||||
currentDocumentId.value = null;
|
||||
currentDocument.value = null;
|
||||
@@ -190,23 +148,46 @@ export const useDocumentStore = defineStore('document', () => {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// 调度自动保存
|
||||
const scheduleAutoSave = (docId: number, saveCallback: () => Promise<void>, delay: number = 2000) => {
|
||||
let timer = autoSaveTimers.value.get(docId);
|
||||
if (!timer) {
|
||||
timer = createTimerManager();
|
||||
autoSaveTimers.value.set(docId, timer);
|
||||
}
|
||||
|
||||
timer.set(async () => {
|
||||
try {
|
||||
await saveCallback();
|
||||
} catch (error) {
|
||||
console.error(`auto save for document ${docId} failed:`, error);
|
||||
}
|
||||
}, delay);
|
||||
};
|
||||
|
||||
// 取消自动保存
|
||||
const cancelAutoSave = (docId: number) => {
|
||||
const timer = autoSaveTimers.value.get(docId);
|
||||
if (timer) {
|
||||
timer.clear();
|
||||
}
|
||||
};
|
||||
|
||||
// === 初始化 ===
|
||||
const initialize = async (urlDocumentId?: number): Promise<void> => {
|
||||
// 初始化文档
|
||||
const initDocument = async (urlDocumentId?: number): Promise<void> => {
|
||||
try {
|
||||
await getDocumentMetaList();
|
||||
const docs = await getDocumentList();
|
||||
|
||||
// 优先使用URL参数中的文档ID
|
||||
if (urlDocumentId && documents.value[urlDocumentId]) {
|
||||
if (urlDocumentId) {
|
||||
await openDocument(urlDocumentId);
|
||||
} else if (currentDocumentId.value && documents.value[currentDocumentId.value]) {
|
||||
// 如果URL中没有指定文档ID,则使用持久化的文档ID
|
||||
} else if (currentDocumentId.value) {
|
||||
// 使用持久化的文档ID
|
||||
await openDocument(currentDocumentId.value);
|
||||
} else {
|
||||
// 否则打开第一个文档
|
||||
if (documentList.value[0].id) {
|
||||
await openDocument(documentList.value[0].id);
|
||||
}
|
||||
} else if (docs.length > 0 && docs[0].id !== undefined) {
|
||||
// 打开第一个文档
|
||||
await openDocument(docs[0].id);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize document store:', error);
|
||||
@@ -215,32 +196,35 @@ export const useDocumentStore = defineStore('document', () => {
|
||||
|
||||
return {
|
||||
// 状态
|
||||
documents,
|
||||
documentList,
|
||||
currentDocumentId,
|
||||
currentDocument,
|
||||
documentStates,
|
||||
showDocumentSelector,
|
||||
selectorError,
|
||||
isLoading,
|
||||
|
||||
// 方法
|
||||
getDocumentMetaList,
|
||||
getDocumentList,
|
||||
getDocument,
|
||||
saveDocument,
|
||||
createNewDocument,
|
||||
updateDocumentTitle,
|
||||
deleteDocument,
|
||||
openDocument,
|
||||
openDocumentInNewWindow,
|
||||
createNewDocument,
|
||||
updateDocumentMetadata,
|
||||
deleteDocument,
|
||||
|
||||
// 自动保存
|
||||
scheduleAutoSave,
|
||||
cancelAutoSave,
|
||||
|
||||
// UI 控制
|
||||
openDocumentSelector,
|
||||
closeDocumentSelector,
|
||||
setError,
|
||||
clearError,
|
||||
initialize,
|
||||
|
||||
// 初始化
|
||||
initDocument,
|
||||
};
|
||||
}, {
|
||||
persist: {
|
||||
key: 'voidraft-document',
|
||||
storage: localStorage,
|
||||
pick: ['currentDocumentId', 'documents', 'documentStates']
|
||||
pick: ['currentDocumentId']
|
||||
}
|
||||
});
|
||||
98
frontend/src/stores/editorStateStore.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import {defineStore} from 'pinia';
|
||||
import {ref} from 'vue';
|
||||
|
||||
export interface DocumentStats {
|
||||
lines: number;
|
||||
characters: number;
|
||||
selectedCharacters: number;
|
||||
}
|
||||
|
||||
export interface FoldRange {
|
||||
// 字符偏移(备用)
|
||||
from: number;
|
||||
to: number;
|
||||
|
||||
// 行号
|
||||
fromLine: number;
|
||||
toLine: number;
|
||||
}
|
||||
|
||||
export const useEditorStateStore = defineStore('editorState', () => {
|
||||
// 光标位置存储 Record<docId, cursorPosition>
|
||||
const cursorPositions = ref<Record<number, number>>({});
|
||||
|
||||
// 文档统计数据存储 Record<docId, DocumentStats>
|
||||
const documentStats = ref<Record<number, DocumentStats>>({});
|
||||
|
||||
// 折叠状态存储 Record<docId, FoldRange[]>
|
||||
const foldStates = ref<Record<number, FoldRange[]>>({});
|
||||
|
||||
// 保存光标位置
|
||||
const saveCursorPosition = (docId: number, position: number) => {
|
||||
cursorPositions.value[docId] = position;
|
||||
};
|
||||
|
||||
// 获取光标位置
|
||||
const getCursorPosition = (docId: number): number | undefined => {
|
||||
return cursorPositions.value[docId];
|
||||
};
|
||||
|
||||
// 保存文档统计数据
|
||||
const saveDocumentStats = (docId: number, stats: DocumentStats) => {
|
||||
documentStats.value[docId] = stats;
|
||||
};
|
||||
|
||||
// 获取文档统计数据
|
||||
const getDocumentStats = (docId: number): DocumentStats => {
|
||||
return documentStats.value[docId] || {
|
||||
lines: 0,
|
||||
characters: 0,
|
||||
selectedCharacters: 0
|
||||
};
|
||||
};
|
||||
|
||||
// 保存折叠状态
|
||||
const saveFoldState = (docId: number, foldRanges: FoldRange[]) => {
|
||||
foldStates.value[docId] = foldRanges;
|
||||
};
|
||||
|
||||
// 获取折叠状态
|
||||
const getFoldState = (docId: number): FoldRange[] => {
|
||||
return foldStates.value[docId] || [];
|
||||
};
|
||||
|
||||
// 清除文档状态
|
||||
const clearDocumentState = (docId: number) => {
|
||||
delete cursorPositions.value[docId];
|
||||
delete documentStats.value[docId];
|
||||
delete foldStates.value[docId];
|
||||
};
|
||||
|
||||
// 清除所有状态
|
||||
const clearAllStates = () => {
|
||||
cursorPositions.value = {};
|
||||
documentStats.value = {};
|
||||
foldStates.value = {};
|
||||
};
|
||||
|
||||
return {
|
||||
cursorPositions,
|
||||
documentStats,
|
||||
foldStates,
|
||||
saveCursorPosition,
|
||||
getCursorPosition,
|
||||
saveDocumentStats,
|
||||
getDocumentStats,
|
||||
saveFoldState,
|
||||
getFoldState,
|
||||
clearDocumentState,
|
||||
clearAllStates
|
||||
};
|
||||
}, {
|
||||
persist: {
|
||||
key: 'voidraft-editor-state',
|
||||
storage: localStorage,
|
||||
pick: ['cursorPositions', 'foldStates']
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import {defineStore} from 'pinia';
|
||||
import {computed, nextTick, ref, watch} from 'vue';
|
||||
import {computed, readonly, ref} from 'vue';
|
||||
import {EditorView} from '@codemirror/view';
|
||||
import {EditorState, Extension} from '@codemirror/state';
|
||||
import {Document} from '@/../bindings/voidraft/internal/models/ent/models';
|
||||
import {useConfigStore} from './configStore';
|
||||
import {useDocumentStore} from './documentStore';
|
||||
import {DocumentService, ExtensionService} from '@/../bindings/voidraft/internal/services';
|
||||
import {ensureSyntaxTree} from "@codemirror/language";
|
||||
import {createBasicSetup} from '@/views/editor/basic/basicSetup';
|
||||
import {createThemeExtension, updateEditorTheme} from '@/views/editor/basic/themeExtension';
|
||||
import {getTabExtensions, updateTabConfig} from '@/views/editor/basic/tabExtension';
|
||||
@@ -14,141 +13,69 @@ import {createStatsUpdateExtension} from '@/views/editor/basic/statsExtension';
|
||||
import {createContentChangePlugin} from '@/views/editor/basic/contentChangeExtension';
|
||||
import {createWheelZoomExtension} from '@/views/editor/basic/wheelZoomExtension';
|
||||
import {createCursorPositionExtension, scrollToCursor} from '@/views/editor/basic/cursorPositionExtension';
|
||||
import {createFoldStateExtension, restoreFoldState} from '@/views/editor/basic/foldStateExtension';
|
||||
import {createDynamicKeymapExtension, updateKeymapExtension} from '@/views/editor/keymap';
|
||||
import {
|
||||
createDynamicExtensions,
|
||||
getExtensionManager,
|
||||
removeExtensionManagerView,
|
||||
setExtensionManagerView
|
||||
} from '@/views/editor/manager';
|
||||
import {useExtensionStore} from './extensionStore';
|
||||
import createCodeBlockExtension from "@/views/editor/extensions/codeblock";
|
||||
import {LruCache} from '@/common/utils/lruCache';
|
||||
import {AsyncManager} from '@/common/utils/asyncManager';
|
||||
import {generateContentHash} from "@/common/utils/hashUtils";
|
||||
import {createTimerManager, type TimerManager} from '@/common/utils/timerUtils';
|
||||
import {EDITOR_CONFIG} from '@/common/constant/editor';
|
||||
import {createDebounce} from '@/common/utils/debounce';
|
||||
import {useKeybindingStore} from "@/stores/keybindingStore";
|
||||
|
||||
export interface DocumentStats {
|
||||
lines: number;
|
||||
characters: number;
|
||||
selectedCharacters: number;
|
||||
}
|
||||
|
||||
// 修复:只保存光标位置,恢复时自动滚动到光标处
|
||||
export interface EditorViewState {
|
||||
cursorPos: number;
|
||||
}
|
||||
import {useEditorStateStore, type DocumentStats} from './editorStateStore';
|
||||
|
||||
// 编辑器实例
|
||||
interface EditorInstance {
|
||||
view: EditorView;
|
||||
documentId: number;
|
||||
content: string;
|
||||
contentTimestamp: string; // 文档时间戳
|
||||
contentLength: number; // 内容长度
|
||||
isDirty: boolean;
|
||||
lastModified: Date;
|
||||
autoSaveTimer: TimerManager;
|
||||
syntaxTreeCache: {
|
||||
lastDocLength: number;
|
||||
lastContentHash: string;
|
||||
lastParsed: Date;
|
||||
} | null;
|
||||
// 修复:使用统一的类型,可选但不是 undefined | {...}
|
||||
editorState?: EditorViewState;
|
||||
lastModified: number;
|
||||
}
|
||||
|
||||
export const useEditorStore = defineStore('editor', () => {
|
||||
// === 依赖store ===
|
||||
const configStore = useConfigStore();
|
||||
const documentStore = useDocumentStore();
|
||||
const extensionStore = useExtensionStore();
|
||||
const editorStateStore = useEditorStateStore();
|
||||
|
||||
// === 核心状态 ===
|
||||
const editorCache = new LruCache<number, EditorInstance>(EDITOR_CONFIG.MAX_INSTANCES);
|
||||
const containerElement = ref<HTMLElement | null>(null);
|
||||
|
||||
const currentEditor = ref<EditorView | null>(null);
|
||||
const documentStats = ref<DocumentStats>({
|
||||
lines: 0,
|
||||
characters: 0,
|
||||
selectedCharacters: 0
|
||||
});
|
||||
|
||||
// 编辑器加载状态
|
||||
const currentEditorId = ref<number | null>(null);
|
||||
const isLoading = ref(false);
|
||||
// 修复:使用操作计数器精确管理加载状态
|
||||
const loadingOperations = ref(0);
|
||||
|
||||
// 异步操作管理器
|
||||
const operationManager = new AsyncManager<number>();
|
||||
|
||||
// 自动保存设置 - 从配置动态获取
|
||||
const getAutoSaveDelay = () => configStore.config.editing.autoSaveDelay;
|
||||
|
||||
// 创建防抖的语法树缓存清理函数
|
||||
const debouncedClearSyntaxCache = createDebounce((instance) => {
|
||||
if (instance) {
|
||||
instance.syntaxTreeCache = null;
|
||||
}
|
||||
}, { delay: 500 }); // 500ms 内的多次输入只清理一次
|
||||
|
||||
|
||||
// 缓存化的语法树确保方法
|
||||
const ensureSyntaxTreeCached = (view: EditorView, documentId: number): void => {
|
||||
const instance = editorCache.get(documentId);
|
||||
if (!instance) return;
|
||||
// 验证缓存是否有效
|
||||
const isCacheValid = (cached: EditorInstance, doc: Document): boolean => {
|
||||
return cached.contentTimestamp === doc.updated_at
|
||||
&& cached.contentLength === (doc.content || '').length;
|
||||
};
|
||||
|
||||
const docLength = view.state.doc.length;
|
||||
const content = view.state.doc.toString();
|
||||
const contentHash = generateContentHash(content);
|
||||
const now = new Date();
|
||||
|
||||
// 检查是否需要重新构建语法树
|
||||
const cache = instance.syntaxTreeCache;
|
||||
const shouldRebuild = !cache ||
|
||||
cache.lastDocLength !== docLength ||
|
||||
cache.lastContentHash !== contentHash ||
|
||||
(now.getTime() - cache.lastParsed.getTime()) > EDITOR_CONFIG.SYNTAX_TREE_CACHE_TIMEOUT;
|
||||
|
||||
if (shouldRebuild) {
|
||||
try {
|
||||
ensureSyntaxTree(view.state, docLength, 5000);
|
||||
|
||||
// 更新缓存
|
||||
instance.syntaxTreeCache = {
|
||||
lastDocLength: docLength,
|
||||
lastContentHash: contentHash,
|
||||
lastParsed: now
|
||||
};
|
||||
} catch (error) {
|
||||
console.warn('Failed to ensure syntax tree:', error);
|
||||
}
|
||||
}
|
||||
// 检查内容是否真的不同
|
||||
const hasContentChanged = (cached: EditorInstance, doc: Document): boolean => {
|
||||
const currentContent = cached.view.state.doc.toString();
|
||||
return currentContent !== (doc.content || '');
|
||||
};
|
||||
|
||||
// 创建编辑器实例
|
||||
const createEditorInstance = async (
|
||||
content: string,
|
||||
operationId: number,
|
||||
documentId: number
|
||||
): Promise<EditorView> => {
|
||||
docId: number,
|
||||
doc: Document
|
||||
): Promise<EditorInstance> => {
|
||||
if (!containerElement.value) {
|
||||
throw new Error('Editor container not set');
|
||||
}
|
||||
|
||||
// 检查操作是否仍然有效
|
||||
if (!operationManager.isOperationValid(operationId, documentId)) {
|
||||
throw new Error('Operation cancelled');
|
||||
}
|
||||
const content = doc.content || '';
|
||||
|
||||
// 获取基本扩展
|
||||
// 基本扩展
|
||||
const basicExtensions = createBasicSetup();
|
||||
|
||||
// 获取主题扩展
|
||||
// 主题扩展
|
||||
const themeExtension = createThemeExtension();
|
||||
|
||||
// Tab相关扩展
|
||||
// Tab 扩展
|
||||
const tabExtensions = getTabExtensions(
|
||||
configStore.config.editing.tabSize,
|
||||
configStore.config.editing.enableTabIndent,
|
||||
@@ -163,18 +90,31 @@ export const useEditorStore = defineStore('editor', () => {
|
||||
fontWeight: configStore.config.editing.fontWeight
|
||||
});
|
||||
|
||||
// 滚轮缩放扩展
|
||||
const wheelZoomExtension = createWheelZoomExtension({
|
||||
increaseFontSize: () => configStore.increaseFontSizeLocal(),
|
||||
decreaseFontSize: () => configStore.decreaseFontSizeLocal(),
|
||||
increaseFontSize: () => {
|
||||
configStore.increaseFontSizeLocal();
|
||||
applyFontSettings();
|
||||
},
|
||||
decreaseFontSize: () => {
|
||||
configStore.decreaseFontSizeLocal();
|
||||
applyFontSettings();
|
||||
},
|
||||
onSave: () => configStore.saveFontSize(),
|
||||
saveDelay: 1000
|
||||
});
|
||||
|
||||
// 统计扩展
|
||||
const statsExtension = createStatsUpdateExtension(updateDocumentStats);
|
||||
const statsExtension = createStatsUpdateExtension((stats: DocumentStats) => {
|
||||
if (currentEditorId.value) {
|
||||
editorStateStore.saveDocumentStats(currentEditorId.value, stats);
|
||||
}
|
||||
});
|
||||
|
||||
// 内容变化扩展
|
||||
const contentChangeExtension = createContentChangePlugin();
|
||||
const contentChangeExtension = createContentChangePlugin(() => {
|
||||
handleContentChange(docId);
|
||||
});
|
||||
|
||||
// 代码块扩展
|
||||
const codeBlockExtension = createCodeBlockExtension({
|
||||
@@ -183,29 +123,17 @@ export const useEditorStore = defineStore('editor', () => {
|
||||
});
|
||||
|
||||
// 光标位置持久化扩展
|
||||
const cursorPositionExtension = createCursorPositionExtension(documentId);
|
||||
const cursorPositionExtension = createCursorPositionExtension(docId);
|
||||
|
||||
// 再次检查操作有效性
|
||||
if (!operationManager.isOperationValid(operationId, documentId)) {
|
||||
throw new Error('Operation cancelled');
|
||||
}
|
||||
// 折叠状态持久化扩展
|
||||
const foldStateExtension = createFoldStateExtension(docId);
|
||||
|
||||
// 快捷键扩展
|
||||
const keymapExtension = await createDynamicKeymapExtension();
|
||||
|
||||
// 检查操作有效性
|
||||
if (!operationManager.isOperationValid(operationId, documentId)) {
|
||||
throw new Error('Operation cancelled');
|
||||
}
|
||||
|
||||
// 动态扩展,传递文档ID以便扩展管理器可以预初始化
|
||||
// 动态扩展
|
||||
const dynamicExtensions = await createDynamicExtensions();
|
||||
|
||||
// 最终检查操作有效性
|
||||
if (!operationManager.isOperationValid(operationId, documentId)) {
|
||||
throw new Error('Operation cancelled');
|
||||
}
|
||||
|
||||
// 组合所有扩展
|
||||
const extensions: Extension[] = [
|
||||
keymapExtension,
|
||||
@@ -218,327 +146,303 @@ export const useEditorStore = defineStore('editor', () => {
|
||||
contentChangeExtension,
|
||||
codeBlockExtension,
|
||||
cursorPositionExtension,
|
||||
foldStateExtension,
|
||||
...dynamicExtensions,
|
||||
];
|
||||
|
||||
// 获取保存的光标位置
|
||||
const savedState = documentStore.documentStates[documentId];
|
||||
const savedCursorPos = editorStateStore.getCursorPosition(docId);
|
||||
const docLength = content.length;
|
||||
const initialCursorPos = savedState?.cursorPos !== undefined
|
||||
? Math.min(savedState.cursorPos, docLength)
|
||||
const initialCursorPos = savedCursorPos !== undefined
|
||||
? Math.min(savedCursorPos, docLength)
|
||||
: docLength;
|
||||
|
||||
|
||||
// 创建编辑器状态,设置初始光标位置
|
||||
// 创建编辑器状态
|
||||
const state = EditorState.create({
|
||||
doc: content,
|
||||
extensions,
|
||||
selection: { anchor: initialCursorPos, head: initialCursorPos }
|
||||
selection: {anchor: initialCursorPos, head: initialCursorPos}
|
||||
});
|
||||
|
||||
return new EditorView({
|
||||
state
|
||||
});
|
||||
};
|
||||
const view = new EditorView({state});
|
||||
|
||||
// 添加编辑器到缓存
|
||||
const addEditorToCache = (documentId: number, view: EditorView, content: string) => {
|
||||
const instance: EditorInstance = {
|
||||
return {
|
||||
view,
|
||||
documentId,
|
||||
content,
|
||||
documentId: docId,
|
||||
contentTimestamp: doc.updated_at || '',
|
||||
contentLength: content.length,
|
||||
isDirty: false,
|
||||
lastModified: new Date(),
|
||||
autoSaveTimer: createTimerManager(),
|
||||
syntaxTreeCache: null,
|
||||
editorState: documentStore.documentStates[documentId]
|
||||
lastModified: Date.now()
|
||||
};
|
||||
|
||||
// 使用LRU缓存的onEvict回调处理被驱逐的实例
|
||||
editorCache.set(documentId, instance, (_evictedKey, evictedInstance) => {
|
||||
// 清除自动保存定时器
|
||||
evictedInstance.autoSaveTimer.clear();
|
||||
// 移除DOM元素
|
||||
if (evictedInstance.view.dom.parentElement) {
|
||||
evictedInstance.view.dom.remove();
|
||||
}
|
||||
evictedInstance.view.destroy();
|
||||
});
|
||||
|
||||
// 初始化语法树缓存
|
||||
ensureSyntaxTreeCached(view, documentId);
|
||||
};
|
||||
|
||||
// 获取或创建编辑器
|
||||
const getOrCreateEditor = async (
|
||||
documentId: number,
|
||||
content: string,
|
||||
operationId: number
|
||||
): Promise<EditorView> => {
|
||||
// 检查缓存
|
||||
const cached = editorCache.get(documentId);
|
||||
if (cached) {
|
||||
return cached.view;
|
||||
// 更新编辑器内容
|
||||
const updateEditorContent = (instance: EditorInstance, doc: Document) => {
|
||||
const currentContent = instance.view.state.doc.toString();
|
||||
const newContent = doc.content || '';
|
||||
|
||||
// 如果内容相同,只更新元数据
|
||||
if (currentContent === newContent) {
|
||||
instance.contentTimestamp = doc.updated_at || '';
|
||||
instance.contentLength = newContent.length;
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查操作是否仍然有效
|
||||
if (!operationManager.isOperationValid(operationId, documentId)) {
|
||||
throw new Error('Operation cancelled');
|
||||
}
|
||||
// 保存当前光标位置
|
||||
const currentCursorPos = instance.view.state.selection.main.head;
|
||||
|
||||
// 创建新的编辑器实例
|
||||
const view = await createEditorInstance(content, operationId, documentId);
|
||||
|
||||
// 完善取消操作时的清理逻辑
|
||||
if (!operationManager.isOperationValid(operationId, documentId)) {
|
||||
// 如果操作已取消,彻底清理创建的实例
|
||||
try {
|
||||
// 移除 DOM 元素(如果已添加到文档)
|
||||
if (view.dom && view.dom.parentElement) {
|
||||
view.dom.remove();
|
||||
}
|
||||
// 销毁编辑器视图
|
||||
view.destroy();
|
||||
} catch (error) {
|
||||
console.error('Error cleaning up cancelled editor:', error);
|
||||
// 更新内容
|
||||
instance.view.dispatch({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: instance.view.state.doc.length,
|
||||
insert: newContent
|
||||
}
|
||||
throw new Error('Operation cancelled');
|
||||
});
|
||||
|
||||
// 智能恢复光标位置
|
||||
const newContentLength = newContent.length;
|
||||
const safeCursorPos = Math.min(currentCursorPos, newContentLength);
|
||||
|
||||
if (safeCursorPos > 0 && safeCursorPos < newContentLength) {
|
||||
instance.view.dispatch({
|
||||
selection: {anchor: safeCursorPos, head: safeCursorPos}
|
||||
});
|
||||
}
|
||||
|
||||
addEditorToCache(documentId, view, content);
|
||||
|
||||
return view;
|
||||
// 同步元数据
|
||||
instance.contentTimestamp = doc.updated_at || '';
|
||||
instance.contentLength = newContent.length;
|
||||
instance.isDirty = false;
|
||||
};
|
||||
|
||||
// 显示编辑器
|
||||
const showEditor = (documentId: number) => {
|
||||
const instance = editorCache.get(documentId);
|
||||
if (!instance || !containerElement.value) return;
|
||||
const showEditor = (instance: EditorInstance) => {
|
||||
if (!containerElement.value) return;
|
||||
|
||||
try {
|
||||
// 移除当前编辑器DOM
|
||||
if (currentEditor.value && currentEditor.value.dom && currentEditor.value.dom.parentElement) {
|
||||
currentEditor.value.dom.remove();
|
||||
// 移除当前编辑器 DOM
|
||||
const currentEditor = editorCache.get(currentEditorId.value || 0);
|
||||
if (currentEditor && currentEditor.view.dom && currentEditor.view.dom.parentElement) {
|
||||
currentEditor.view.dom.remove();
|
||||
}
|
||||
|
||||
// 将目标编辑器DOM添加到容器
|
||||
// 添加目标编辑器 DOM
|
||||
containerElement.value.appendChild(instance.view.dom);
|
||||
currentEditor.value = instance.view;
|
||||
currentEditorId.value = instance.documentId;
|
||||
|
||||
// 设置扩展管理器视图
|
||||
setExtensionManagerView(instance.view, documentId);
|
||||
setExtensionManagerView(instance.view, instance.documentId);
|
||||
|
||||
//使用 nextTick + requestAnimationFrame 确保 DOM 完全渲染
|
||||
nextTick(() => {
|
||||
requestAnimationFrame(() => {
|
||||
// 滚动到当前光标位置
|
||||
scrollToCursor(instance.view);
|
||||
|
||||
// 聚焦编辑器
|
||||
instance.view.focus();
|
||||
|
||||
// 使用缓存的语法树确保方法
|
||||
ensureSyntaxTreeCached(instance.view, documentId);
|
||||
});
|
||||
// 使用 requestAnimationFrame 确保 DOM 渲染
|
||||
requestAnimationFrame(() => {
|
||||
scrollToCursor(instance.view);
|
||||
instance.view.focus();
|
||||
|
||||
// 恢复折叠状态
|
||||
const savedFoldState = editorStateStore.getFoldState(instance.documentId);
|
||||
if (savedFoldState.length > 0) {
|
||||
restoreFoldState(instance.view, savedFoldState);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error showing editor:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 保存编辑器内容
|
||||
const saveEditorContent = async (documentId: number): Promise<boolean> => {
|
||||
const instance = editorCache.get(documentId);
|
||||
if (!instance || !instance.isDirty) return true;
|
||||
|
||||
try {
|
||||
const content = instance.view.state.doc.toString();
|
||||
const lastModified = instance.lastModified;
|
||||
|
||||
await DocumentService.UpdateDocumentContent(documentId, content);
|
||||
|
||||
// 检查在保存期间内容是否又被修改了
|
||||
if (instance.lastModified === lastModified) {
|
||||
instance.content = content;
|
||||
instance.isDirty = false;
|
||||
instance.lastModified = new Date();
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Failed to save editor content:', error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// 内容变化处理
|
||||
const onContentChange = () => {
|
||||
const documentId = documentStore.currentDocumentId;
|
||||
if (!documentId) return;
|
||||
const instance = editorCache.get(documentId);
|
||||
const handleContentChange = (docId: number) => {
|
||||
const instance = editorCache.get(docId);
|
||||
if (!instance) return;
|
||||
|
||||
// 立即设置脏标记和修改时间(切换文档时需要判断)
|
||||
// 标记为脏数据
|
||||
instance.isDirty = true;
|
||||
instance.lastModified = new Date();
|
||||
|
||||
// 优使用防抖清理语法树缓存
|
||||
debouncedClearSyntaxCache.debouncedFn(instance);
|
||||
instance.lastModified = Date.now();
|
||||
|
||||
// 设置自动保存定时器(已经是防抖效果:每次重置定时器)
|
||||
instance.autoSaveTimer.set(() => {
|
||||
saveEditorContent(documentId);
|
||||
}, getAutoSaveDelay());
|
||||
// 调度自动保存
|
||||
const autoSaveDelay = configStore.config.editing.autoSaveDelay;
|
||||
documentStore.scheduleAutoSave(
|
||||
docId,
|
||||
async () => {
|
||||
const content = instance.view.state.doc.toString();
|
||||
const savedDoc = await documentStore.saveDocument(docId, content);
|
||||
|
||||
// 同步版本信息
|
||||
if (savedDoc) {
|
||||
instance.contentTimestamp = savedDoc.updated_at || '';
|
||||
instance.contentLength = (savedDoc.content || '').length;
|
||||
instance.isDirty = false;
|
||||
}
|
||||
},
|
||||
autoSaveDelay
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// 设置编辑器容器
|
||||
const setEditorContainer = (container: HTMLElement | null) => {
|
||||
containerElement.value = container;
|
||||
|
||||
// 如果设置容器时已有当前文档,立即加载编辑器
|
||||
if (container && documentStore.currentDocument && documentStore.currentDocument.id !== undefined) {
|
||||
loadEditor(documentStore.currentDocument.id, documentStore.currentDocument.content || '');
|
||||
}
|
||||
};
|
||||
|
||||
// 加载编辑器
|
||||
const loadEditor = async (documentId: number, content: string) => {
|
||||
// 修复:使用计数器精确管理加载状态
|
||||
loadingOperations.value++;
|
||||
// 切换到指定编辑器
|
||||
const switchToEditor = async (docId: number) => {
|
||||
isLoading.value = true;
|
||||
|
||||
// 开始新的操作
|
||||
const { operationId } = operationManager.startOperation(documentId);
|
||||
|
||||
try {
|
||||
// 验证参数
|
||||
if (!documentId) {
|
||||
throw new Error('Invalid parameters for loadEditor');
|
||||
// 直接从后端获取文档
|
||||
const doc = await documentStore.getDocument(docId);
|
||||
if (!doc) {
|
||||
throw new Error(`Failed to load document ${docId}`);
|
||||
}
|
||||
|
||||
// 保存当前编辑器内容
|
||||
if (currentEditor.value) {
|
||||
const currentDocId = documentStore.currentDocumentId;
|
||||
if (currentDocId && currentDocId !== documentId) {
|
||||
await saveEditorContent(currentDocId);
|
||||
|
||||
// 检查操作是否仍然有效
|
||||
if (!operationManager.isOperationValid(operationId, documentId)) {
|
||||
return;
|
||||
const cached = editorCache.get(docId);
|
||||
|
||||
if (cached) {
|
||||
// 场景1:缓存有效
|
||||
if (isCacheValid(cached, doc)) {
|
||||
showEditor(cached);
|
||||
return;
|
||||
}
|
||||
|
||||
// 场景2:有未保存修改
|
||||
if (cached.isDirty) {
|
||||
// 检查内容是否真的不同
|
||||
if (!hasContentChanged(cached, doc)) {
|
||||
// 内容实际相同,只是元数据变了,同步元数据
|
||||
cached.contentTimestamp = doc.updated_at || '';
|
||||
cached.contentLength = (doc.content || '').length;
|
||||
cached.isDirty = false;
|
||||
}
|
||||
// 内容不同,保留用户编辑
|
||||
showEditor(cached);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取或创建编辑器
|
||||
const view = await getOrCreateEditor(documentId, content, operationId);
|
||||
|
||||
// 检查操作是否仍然有效
|
||||
if (!operationManager.isOperationValid(operationId, documentId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新内容(如果需要)
|
||||
const instance = editorCache.get(documentId);
|
||||
if (instance && instance.content !== content) {
|
||||
// 确保编辑器视图有效
|
||||
if (view && view.state && view.dispatch) {
|
||||
view.dispatch({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: view.state.doc.length,
|
||||
insert: content
|
||||
}
|
||||
});
|
||||
instance.content = content;
|
||||
instance.isDirty = false;
|
||||
// 清理语法树缓存,因为内容已更新
|
||||
instance.syntaxTreeCache = null;
|
||||
// 修复:内容变了,清空光标位置,避免越界
|
||||
instance.editorState = undefined;
|
||||
delete documentStore.documentStates[documentId];
|
||||
}
|
||||
}
|
||||
|
||||
// 最终检查操作有效性
|
||||
if (!operationManager.isOperationValid(operationId, documentId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示编辑器
|
||||
showEditor(documentId);
|
||||
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message === 'Operation cancelled') {
|
||||
console.log(`Editor loading cancelled for document ${documentId}`);
|
||||
// 场景3:缓存失效且无脏数据,更新内容
|
||||
updateEditorContent(cached, doc);
|
||||
showEditor(cached);
|
||||
} else {
|
||||
console.error('Failed to load editor:', error);
|
||||
// 场景4:创建新编辑器
|
||||
const editor = await createEditorInstance(docId, doc);
|
||||
|
||||
// 添加到缓存
|
||||
editorCache.set(docId, editor, (_evictedKey, evictedInstance) => {
|
||||
// 保存光标位置
|
||||
const cursorPos = evictedInstance.view.state.selection.main.head;
|
||||
editorStateStore.saveCursorPosition(evictedInstance.documentId, cursorPos);
|
||||
|
||||
// 从扩展管理器移除
|
||||
removeExtensionManagerView(evictedInstance.documentId);
|
||||
|
||||
// 移除 DOM
|
||||
if (evictedInstance.view.dom.parentElement) {
|
||||
evictedInstance.view.dom.remove();
|
||||
}
|
||||
|
||||
// 销毁编辑器
|
||||
evictedInstance.view.destroy();
|
||||
});
|
||||
|
||||
showEditor(editor);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to switch editor:', error);
|
||||
} finally {
|
||||
// 完成操作
|
||||
operationManager.completeOperation(operationId);
|
||||
|
||||
// 修复:使用计数器精确管理加载状态,避免快速切换时状态不准确
|
||||
loadingOperations.value--;
|
||||
// 延迟一段时间后再取消加载状态,但要确保所有操作都完成了
|
||||
setTimeout(() => {
|
||||
if (loadingOperations.value <= 0) {
|
||||
loadingOperations.value = 0;
|
||||
isLoading.value = false;
|
||||
}
|
||||
isLoading.value = false;
|
||||
}, EDITOR_CONFIG.LOADING_DELAY);
|
||||
}
|
||||
};
|
||||
|
||||
// 移除编辑器
|
||||
const removeEditor = async (documentId: number) => {
|
||||
const instance = editorCache.get(documentId);
|
||||
if (instance) {
|
||||
try {
|
||||
// 如果正在加载这个文档,取消操作
|
||||
if (operationManager.getCurrentContext() === documentId) {
|
||||
operationManager.cancelAllOperations();
|
||||
}
|
||||
// 获取当前内容
|
||||
const getCurrentContent = (): string => {
|
||||
if (!currentEditorId.value) return '';
|
||||
const instance = editorCache.get(currentEditorId.value);
|
||||
return instance ? instance.view.state.doc.toString() : '';
|
||||
};
|
||||
|
||||
// 修复:移除前先保存内容(如果有未保存的修改)
|
||||
if (instance.isDirty) {
|
||||
await saveEditorContent(documentId);
|
||||
}
|
||||
// 获取当前光标位置
|
||||
const getCurrentCursorPosition = (): number => {
|
||||
if (!currentEditorId.value) return 0;
|
||||
const instance = editorCache.get(currentEditorId.value);
|
||||
return instance ? instance.view.state.selection.main.head : 0;
|
||||
};
|
||||
|
||||
// 清除自动保存定时器
|
||||
instance.autoSaveTimer.clear();
|
||||
// 检查是否有未保存修改
|
||||
const hasUnsavedChanges = (docId: number): boolean => {
|
||||
const instance = editorCache.get(docId);
|
||||
return instance?.isDirty || false;
|
||||
};
|
||||
|
||||
// 从扩展管理器中移除视图
|
||||
removeExtensionManagerView(documentId);
|
||||
// 同步保存后的版本信息
|
||||
const syncAfterSave = async (docId: number) => {
|
||||
const instance = editorCache.get(docId);
|
||||
if (!instance) return;
|
||||
|
||||
// 移除DOM元素
|
||||
if (instance.view && instance.view.dom && instance.view.dom.parentElement) {
|
||||
instance.view.dom.remove();
|
||||
}
|
||||
|
||||
// 销毁编辑器
|
||||
if (instance.view && instance.view.destroy) {
|
||||
instance.view.destroy();
|
||||
}
|
||||
|
||||
// 清理引用
|
||||
if (currentEditor.value === instance.view) {
|
||||
currentEditor.value = null;
|
||||
}
|
||||
|
||||
// 从缓存中删除
|
||||
editorCache.delete(documentId);
|
||||
} catch (error) {
|
||||
console.error('Error removing editor:', error);
|
||||
}
|
||||
const doc = await documentStore.getDocument(docId);
|
||||
if (doc) {
|
||||
instance.contentTimestamp = doc.updated_at || '';
|
||||
instance.contentLength = (doc.content || '').length;
|
||||
instance.isDirty = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 更新文档统计
|
||||
const updateDocumentStats = (stats: DocumentStats) => {
|
||||
documentStats.value = stats;
|
||||
// 销毁编辑器
|
||||
const destroyEditor = async (docId: number) => {
|
||||
const instance = editorCache.get(docId);
|
||||
if (!instance) return;
|
||||
|
||||
try {
|
||||
// 保存光标位置
|
||||
const cursorPos = instance.view.state.selection.main.head;
|
||||
editorStateStore.saveCursorPosition(docId, cursorPos);
|
||||
|
||||
// 从扩展管理器移除
|
||||
removeExtensionManagerView(docId);
|
||||
|
||||
// 移除 DOM
|
||||
if (instance.view.dom && instance.view.dom.parentElement) {
|
||||
instance.view.dom.remove();
|
||||
}
|
||||
|
||||
// 销毁编辑器
|
||||
instance.view.destroy();
|
||||
|
||||
// 从缓存删除
|
||||
editorCache.delete(docId);
|
||||
|
||||
// 清空当前编辑器引用
|
||||
if (currentEditorId.value === docId) {
|
||||
currentEditorId.value = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error destroying editor:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 清空所有编辑器
|
||||
const destroyAllEditors = () => {
|
||||
editorCache.clear((_documentId, instance) => {
|
||||
// 保存光标位置
|
||||
const cursorPos = instance.view.state.selection.main.head;
|
||||
editorStateStore.saveCursorPosition(instance.documentId, cursorPos);
|
||||
|
||||
// 从扩展管理器移除
|
||||
removeExtensionManagerView(instance.documentId);
|
||||
|
||||
// 移除 DOM
|
||||
if (instance.view.dom.parentElement) {
|
||||
instance.view.dom.remove();
|
||||
}
|
||||
|
||||
// 销毁编辑器
|
||||
instance.view.destroy();
|
||||
});
|
||||
|
||||
currentEditorId.value = null;
|
||||
};
|
||||
|
||||
// 设置编辑器容器
|
||||
const setEditorContainer = (container: HTMLElement | null) => {
|
||||
containerElement.value = container;
|
||||
};
|
||||
|
||||
|
||||
// 应用字体设置
|
||||
const applyFontSettings = () => {
|
||||
editorCache.values().forEach(instance => {
|
||||
@@ -558,8 +462,7 @@ export const useEditorStore = defineStore('editor', () => {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// 应用Tab设置
|
||||
// 应用 Tab 设置
|
||||
const applyTabSettings = () => {
|
||||
editorCache.values().forEach(instance => {
|
||||
updateTabConfig(
|
||||
@@ -573,7 +476,6 @@ export const useEditorStore = defineStore('editor', () => {
|
||||
|
||||
// 应用快捷键设置
|
||||
const applyKeymapSettings = async () => {
|
||||
// 确保所有编辑器实例的快捷键都更新
|
||||
await Promise.all(
|
||||
editorCache.values().map(instance =>
|
||||
updateKeymapExtension(instance.view)
|
||||
@@ -581,104 +483,36 @@ export const useEditorStore = defineStore('editor', () => {
|
||||
);
|
||||
};
|
||||
|
||||
// 清空所有编辑器
|
||||
const clearAllEditors = () => {
|
||||
// 取消所有挂起的操作
|
||||
operationManager.cancelAllOperations();
|
||||
|
||||
editorCache.clear((_documentId, instance) => {
|
||||
// 清除自动保存定时器
|
||||
instance.autoSaveTimer.clear();
|
||||
// 从扩展管理器移除
|
||||
removeExtensionManagerView(instance.documentId);
|
||||
// 移除DOM元素
|
||||
if (instance.view.dom.parentElement) {
|
||||
instance.view.dom.remove();
|
||||
}
|
||||
// 销毁编辑器
|
||||
instance.view.destroy();
|
||||
});
|
||||
|
||||
currentEditor.value = null;
|
||||
};
|
||||
|
||||
// 更新扩展
|
||||
const updateExtension = async (id: number, enabled: boolean, config?: any) => {
|
||||
// 更新启用状态
|
||||
await ExtensionService.UpdateExtensionEnabled(id, enabled);
|
||||
|
||||
// 如果需要更新配置
|
||||
if (config !== undefined) {
|
||||
await ExtensionService.UpdateExtensionConfig(id, config);
|
||||
}
|
||||
|
||||
// 重新加载扩展配置
|
||||
await extensionStore.loadExtensions();
|
||||
|
||||
// 获取更新后的扩展名称
|
||||
const extension = extensionStore.extensions.find(ext => ext.id === id);
|
||||
if (!extension) return;
|
||||
|
||||
// 更新前端编辑器扩展 - 应用于所有实例
|
||||
const manager = getExtensionManager();
|
||||
if (manager) {
|
||||
// 直接更新前端扩展至所有视图
|
||||
manager.updateExtension(extension.name, enabled, config);
|
||||
}
|
||||
|
||||
await useKeybindingStore().loadKeyBindings();
|
||||
await applyKeymapSettings();
|
||||
};
|
||||
|
||||
// 监听文档切换
|
||||
watch(() => documentStore.currentDocument, async (newDoc, oldDoc) => {
|
||||
if (newDoc && newDoc.id !== undefined && containerElement.value) {
|
||||
// 等待 DOM 更新完成,再加载新文档的编辑器
|
||||
await nextTick();
|
||||
loadEditor(newDoc.id, newDoc.content || '');
|
||||
}
|
||||
const hasContainer = computed(() => containerElement.value !== null);
|
||||
const currentEditor = computed(() => {
|
||||
if (!currentEditorId.value) return null;
|
||||
const instance = editorCache.get(currentEditorId.value);
|
||||
return instance ? instance.view : null;
|
||||
});
|
||||
|
||||
// 创建字体配置的计算属性
|
||||
const fontConfig = computed(() => ({
|
||||
fontSize: configStore.config.editing.fontSize,
|
||||
fontFamily: configStore.config.editing.fontFamily,
|
||||
lineHeight: configStore.config.editing.lineHeight,
|
||||
fontWeight: configStore.config.editing.fontWeight
|
||||
}));
|
||||
// 创建Tab配置的计算属性
|
||||
const tabConfig = computed(() => ({
|
||||
tabSize: configStore.config.editing.tabSize,
|
||||
enableTabIndent: configStore.config.editing.enableTabIndent,
|
||||
tabType: configStore.config.editing.tabType
|
||||
}));
|
||||
// 监听字体配置变化
|
||||
watch(fontConfig, applyFontSettings, { deep: true });
|
||||
// 监听Tab配置变化
|
||||
watch(tabConfig, applyTabSettings, { deep: true });
|
||||
|
||||
return {
|
||||
// 状态
|
||||
currentEditorId: readonly(currentEditorId),
|
||||
currentEditor,
|
||||
documentStats,
|
||||
isLoading,
|
||||
isLoading: readonly(isLoading),
|
||||
hasContainer,
|
||||
|
||||
// 方法
|
||||
// 编辑器管理
|
||||
setEditorContainer,
|
||||
loadEditor,
|
||||
removeEditor,
|
||||
clearAllEditors,
|
||||
onContentChange,
|
||||
switchToEditor,
|
||||
destroyEditor,
|
||||
destroyAllEditors,
|
||||
|
||||
// 配置更新方法
|
||||
// 查询方法
|
||||
getCurrentContent,
|
||||
getCurrentCursorPosition,
|
||||
hasUnsavedChanges,
|
||||
syncAfterSave,
|
||||
|
||||
// 配置应用
|
||||
applyFontSettings,
|
||||
applyThemeSettings,
|
||||
applyTabSettings,
|
||||
applyKeymapSettings,
|
||||
|
||||
// 扩展管理方法
|
||||
updateExtension,
|
||||
|
||||
editorView: currentEditor,
|
||||
};
|
||||
});
|
||||
|
||||
25
frontend/src/stores/syncStore.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import { SyncService } from '@/../bindings/voidraft/internal/services';
|
||||
|
||||
export const useSyncStore = defineStore('sync', () => {
|
||||
const isSyncing = ref(false);
|
||||
|
||||
const sync = async (): Promise<void> => {
|
||||
if (isSyncing.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
isSyncing.value = true;
|
||||
try {
|
||||
await SyncService.Sync();
|
||||
} finally {
|
||||
isSyncing.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
isSyncing,
|
||||
sync
|
||||
};
|
||||
});
|
||||
@@ -18,12 +18,9 @@ export const useTabStore = defineStore('tab', () => {
|
||||
const tabsMap = ref<Record<number, Tab>>({});
|
||||
const tabOrder = ref<number[]>([]); // 维护标签页顺序
|
||||
const draggedTabId = ref<number | null>(null);
|
||||
|
||||
// === 计算属性 ===
|
||||
|
||||
|
||||
const isTabsEnabled = computed(() => configStore.config.general.enableTabs);
|
||||
const canCloseTab = computed(() => tabOrder.value.length > 1);
|
||||
const currentDocumentId = computed(() => documentStore.currentDocumentId);
|
||||
|
||||
// 按顺序返回标签页数组(用于UI渲染)
|
||||
const tabs = computed(() => {
|
||||
@@ -75,7 +72,7 @@ export const useTabStore = defineStore('tab', () => {
|
||||
/**
|
||||
* 关闭标签页
|
||||
*/
|
||||
const closeTab = (documentId: number) => {
|
||||
const closeTab = async (documentId: number) => {
|
||||
if (!hasTab(documentId)) return;
|
||||
|
||||
const tabIndex = tabOrder.value.indexOf(documentId);
|
||||
@@ -95,7 +92,7 @@ export const useTabStore = defineStore('tab', () => {
|
||||
|
||||
if (nextIndex >= 0 && tabOrder.value[nextIndex]) {
|
||||
const nextDocumentId = tabOrder.value[nextIndex];
|
||||
switchToTabAndDocument(nextDocumentId);
|
||||
await switchToTabAndDocument(nextDocumentId);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -120,15 +117,15 @@ export const useTabStore = defineStore('tab', () => {
|
||||
/**
|
||||
* 切换到指定标签页并打开对应文档
|
||||
*/
|
||||
const switchToTabAndDocument = (documentId: number) => {
|
||||
const switchToTabAndDocument = async (documentId: number) => {
|
||||
if (!hasTab(documentId)) return;
|
||||
|
||||
|
||||
// 如果点击的是当前已激活的文档,不需要重复请求
|
||||
if (documentStore.currentDocumentId === documentId) {
|
||||
return;
|
||||
}
|
||||
|
||||
documentStore.openDocument(documentId);
|
||||
await documentStore.openDocument(documentId);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -154,8 +151,9 @@ export const useTabStore = defineStore('tab', () => {
|
||||
/**
|
||||
* 验证并清理无效的标签页
|
||||
*/
|
||||
const validateTabs = () => {
|
||||
const validDocIds = Object.keys(documentStore.documents).map(Number);
|
||||
const validateTabs = async () => {
|
||||
const docs = await documentStore.getDocumentList();
|
||||
const validDocIds = docs.map(doc => doc.id).filter((id): id is number => id !== undefined);
|
||||
|
||||
// 找出无效的标签页(文档已被删除)
|
||||
const invalidTabIds = tabOrder.value.filter(docId => !validDocIds.includes(docId));
|
||||
@@ -172,9 +170,9 @@ export const useTabStore = defineStore('tab', () => {
|
||||
/**
|
||||
* 初始化标签页(当前文档)
|
||||
*/
|
||||
const initializeTab = () => {
|
||||
// 先验证并清理无效的标签页(处理持久化的脏数据)
|
||||
validateTabs();
|
||||
const initTab = async () => {
|
||||
// 先验证并清理无效的标签页
|
||||
await validateTabs();
|
||||
|
||||
if (isTabsEnabled.value) {
|
||||
const currentDoc = documentStore.currentDocument;
|
||||
@@ -189,7 +187,7 @@ export const useTabStore = defineStore('tab', () => {
|
||||
/**
|
||||
* 关闭其他标签页(除了指定的标签页)
|
||||
*/
|
||||
const closeOtherTabs = (keepDocumentId: number) => {
|
||||
const closeOtherTabs = async (keepDocumentId: number) => {
|
||||
if (!hasTab(keepDocumentId)) return;
|
||||
|
||||
// 获取所有其他标签页的ID
|
||||
@@ -200,14 +198,14 @@ export const useTabStore = defineStore('tab', () => {
|
||||
|
||||
// 如果当前打开的文档在被关闭的标签中,需要切换到保留的文档
|
||||
if (otherTabIds.includes(documentStore.currentDocumentId!)) {
|
||||
switchToTabAndDocument(keepDocumentId);
|
||||
await switchToTabAndDocument(keepDocumentId);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭指定标签页右侧的所有标签页
|
||||
*/
|
||||
const closeTabsToRight = (documentId: number) => {
|
||||
const closeTabsToRight = async (documentId: number) => {
|
||||
const index = getTabIndex(documentId);
|
||||
if (index === -1) return;
|
||||
|
||||
@@ -219,14 +217,14 @@ export const useTabStore = defineStore('tab', () => {
|
||||
|
||||
// 如果当前打开的文档在被关闭的右侧标签中,需要切换到指定的文档
|
||||
if (rightTabIds.includes(documentStore.currentDocumentId!)) {
|
||||
switchToTabAndDocument(documentId);
|
||||
await switchToTabAndDocument(documentId);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭指定标签页左侧的所有标签页
|
||||
*/
|
||||
const closeTabsToLeft = (documentId: number) => {
|
||||
const closeTabsToLeft = async (documentId: number) => {
|
||||
const index = getTabIndex(documentId);
|
||||
if (index <= 0) return;
|
||||
|
||||
@@ -238,7 +236,7 @@ export const useTabStore = defineStore('tab', () => {
|
||||
|
||||
// 如果当前打开的文档在被关闭的左侧标签中,需要切换到指定的文档
|
||||
if (leftTabIds.includes(documentStore.currentDocumentId!)) {
|
||||
switchToTabAndDocument(documentId);
|
||||
await switchToTabAndDocument(documentId);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -262,7 +260,6 @@ export const useTabStore = defineStore('tab', () => {
|
||||
// 计算属性
|
||||
isTabsEnabled,
|
||||
canCloseTab,
|
||||
currentDocumentId,
|
||||
|
||||
// 方法
|
||||
addOrActivateTab,
|
||||
@@ -273,7 +270,7 @@ export const useTabStore = defineStore('tab', () => {
|
||||
switchToTabAndDocument,
|
||||
moveTab,
|
||||
getTabIndex,
|
||||
initializeTab,
|
||||
initTab,
|
||||
clearAllTabs,
|
||||
updateTabTitle,
|
||||
validateTabs,
|
||||
|
||||
@@ -4,12 +4,12 @@ import {SystemThemeType} from '@/../bindings/voidraft/internal/models/models';
|
||||
import {Type as ThemeType} from '@/../bindings/voidraft/internal/models/ent/theme/models';
|
||||
import {ThemeService} from '@/../bindings/voidraft/internal/services';
|
||||
import {useConfigStore} from './configStore';
|
||||
import {useEditorStore} from './editorStore';
|
||||
import type {ThemeColors} from '@/views/editor/theme/types';
|
||||
import {cloneThemeColors, FALLBACK_THEME_NAME, themePresetList, themePresetMap} from '@/views/editor/theme/presets';
|
||||
import {useEditorStore} from "@/stores/editorStore";
|
||||
|
||||
// 类型定义
|
||||
type ThemeOption = {name: string; type: ThemeType};
|
||||
type ThemeOption = { name: string; type: ThemeType };
|
||||
|
||||
// 解析主题名称,确保返回有效的主题
|
||||
const resolveThemeName = (name?: string): string =>
|
||||
@@ -62,15 +62,11 @@ export const useThemeStore = defineStore('theme', () => {
|
||||
// 从服务器获取主题颜色
|
||||
const fetchThemeColors = async (themeName: string): Promise<ThemeColors> => {
|
||||
const safeName = resolveThemeName(themeName);
|
||||
try {
|
||||
const theme = await ThemeService.GetThemeByName(safeName);
|
||||
if (theme?.colors) {
|
||||
const colors = cloneThemeColors(theme.colors as ThemeColors);
|
||||
colors.themeName = safeName;
|
||||
return colors;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load theme override:', error);
|
||||
const theme = await ThemeService.GetThemeByName(safeName);
|
||||
if (theme?.colors) {
|
||||
const colors = cloneThemeColors(theme.colors as ThemeColors);
|
||||
colors.themeName = safeName;
|
||||
return colors;
|
||||
}
|
||||
return getPresetColors(safeName);
|
||||
};
|
||||
@@ -80,21 +76,36 @@ export const useThemeStore = defineStore('theme', () => {
|
||||
const targetName = resolveThemeName(
|
||||
themeName || configStore.config?.appearance?.currentTheme
|
||||
);
|
||||
currentColors.value = getPresetColors(targetName);
|
||||
currentColors.value = await fetchThemeColors(targetName);
|
||||
|
||||
};
|
||||
|
||||
// 获取可用的主题颜色
|
||||
const getEffectiveColors = (): ThemeColors => {
|
||||
const targetName = resolveThemeName(
|
||||
currentColors.value?.themeName || configStore.config?.appearance?.currentTheme
|
||||
);
|
||||
return currentColors.value ?? getPresetColors(targetName);
|
||||
};
|
||||
|
||||
// 同步应用到 DOM 与编辑器
|
||||
const applyAllThemes = () => {
|
||||
applyThemeToDOM(currentTheme.value);
|
||||
const editorStore = useEditorStore();
|
||||
editorStore.applyThemeSettings();
|
||||
};
|
||||
|
||||
// 初始化主题
|
||||
const initTheme = async () => {
|
||||
applyThemeToDOM(currentTheme.value);
|
||||
await loadThemeColors();
|
||||
refreshEditorTheme();
|
||||
applyAllThemes();
|
||||
};
|
||||
|
||||
// 设置系统主题
|
||||
const setTheme = async (theme: SystemThemeType) => {
|
||||
await configStore.setSystemTheme(theme);
|
||||
applyThemeToDOM(theme);
|
||||
refreshEditorTheme();
|
||||
applyAllThemes();
|
||||
};
|
||||
|
||||
// 切换到指定主题
|
||||
@@ -106,7 +117,7 @@ export const useThemeStore = defineStore('theme', () => {
|
||||
|
||||
await loadThemeColors(themeName);
|
||||
await configStore.setCurrentTheme(themeName);
|
||||
refreshEditorTheme();
|
||||
applyAllThemes();
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -128,7 +139,7 @@ export const useThemeStore = defineStore('theme', () => {
|
||||
await ThemeService.UpdateTheme(themeName, currentColors.value);
|
||||
|
||||
await loadThemeColors(themeName);
|
||||
refreshEditorTheme();
|
||||
applyAllThemes();
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -142,16 +153,10 @@ export const useThemeStore = defineStore('theme', () => {
|
||||
await ThemeService.ResetTheme(themeName);
|
||||
|
||||
await loadThemeColors(themeName);
|
||||
refreshEditorTheme();
|
||||
applyAllThemes();
|
||||
return true;
|
||||
};
|
||||
|
||||
// 刷新编辑器主题
|
||||
const refreshEditorTheme = () => {
|
||||
applyThemeToDOM(currentTheme.value);
|
||||
const editorStore = useEditorStore();
|
||||
editorStore?.applyThemeSettings();
|
||||
};
|
||||
|
||||
return {
|
||||
availableThemes,
|
||||
@@ -164,7 +169,8 @@ export const useThemeStore = defineStore('theme', () => {
|
||||
updateCurrentColors,
|
||||
saveCurrentTheme,
|
||||
resetCurrentTheme,
|
||||
refreshEditorTheme,
|
||||
applyThemeToDOM,
|
||||
applyAllThemes,
|
||||
getEffectiveColors,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -28,11 +28,16 @@ onMounted(async () => {
|
||||
|
||||
const urlDocumentId = windowStore.currentDocumentId ? parseInt(windowStore.currentDocumentId) : undefined;
|
||||
|
||||
await documentStore.initialize(urlDocumentId);
|
||||
await documentStore.initDocument(urlDocumentId);
|
||||
|
||||
editorStore.setEditorContainer(editorElement.value);
|
||||
|
||||
await tabStore.initializeTab();
|
||||
const currentDocId = documentStore.currentDocumentId;
|
||||
if (currentDocId) {
|
||||
await editorStore.switchToEditor(currentDocId);
|
||||
}
|
||||
|
||||
await tabStore.initTab();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import {EditorView, ViewPlugin, ViewUpdate} from '@codemirror/view';
|
||||
import type {Text} from '@codemirror/state';
|
||||
import {useEditorStore} from '@/stores/editorStore';
|
||||
|
||||
/**
|
||||
* 内容变化监听扩展
|
||||
* 通过回调函数解耦,不直接依赖 Store
|
||||
*/
|
||||
export function createContentChangePlugin() {
|
||||
export function createContentChangePlugin(onContentChange: () => void) {
|
||||
return ViewPlugin.fromClass(
|
||||
class ContentChangePlugin {
|
||||
private readonly editorStore = useEditorStore();
|
||||
private lastDoc: Text;
|
||||
private rafId: number | null = null;
|
||||
private pendingNotification = false;
|
||||
@@ -40,7 +40,7 @@ export function createContentChangePlugin() {
|
||||
this.rafId = requestAnimationFrame(() => {
|
||||
this.pendingNotification = false;
|
||||
this.rafId = null;
|
||||
this.editorStore.onContentChange();
|
||||
onContentChange(); // 调用注入的回调
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import {EditorView, ViewPlugin, ViewUpdate} from '@codemirror/view';
|
||||
import {useDocumentStore} from '@/stores/documentStore';
|
||||
import {useEditorStateStore} from '@/stores/editorStateStore';
|
||||
import {createDebounce} from '@/common/utils/debounce';
|
||||
|
||||
/**
|
||||
* 光标位置持久化扩展
|
||||
* 实时监听光标位置变化并持久化到 documentStore
|
||||
* 实时监听光标位置变化并持久化到 editorStateStore
|
||||
*/
|
||||
export function createCursorPositionExtension(documentId: number) {
|
||||
return ViewPlugin.fromClass(
|
||||
class CursorPositionPlugin {
|
||||
private readonly documentStore = useDocumentStore();
|
||||
private readonly editorStateStore = useEditorStateStore();
|
||||
private readonly debouncedSave;
|
||||
|
||||
constructor(private view: EditorView) {
|
||||
@@ -42,11 +42,7 @@ export function createCursorPositionExtension(documentId: number) {
|
||||
|
||||
private saveCursorPosition() {
|
||||
const cursorPos = this.view.state.selection.main.head;
|
||||
if (!this.documentStore.documentStates[documentId]) {
|
||||
this.documentStore.documentStates[documentId] = {cursorPos};
|
||||
} else {
|
||||
this.documentStore.documentStates[documentId].cursorPos = cursorPos;
|
||||
}
|
||||
this.editorStateStore.saveCursorPosition(documentId, cursorPos);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
113
frontend/src/views/editor/basic/foldStateExtension.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import {EditorView, ViewPlugin, ViewUpdate} from '@codemirror/view';
|
||||
import {foldedRanges, foldEffect, unfoldEffect} from '@codemirror/language';
|
||||
import {StateEffect} from '@codemirror/state';
|
||||
import {useEditorStateStore, type FoldRange} from '@/stores/editorStateStore';
|
||||
import {createDebounce} from '@/common/utils/debounce';
|
||||
|
||||
/**
|
||||
* 折叠状态持久化扩展
|
||||
*/
|
||||
export function createFoldStateExtension(documentId: number) {
|
||||
return ViewPlugin.fromClass(
|
||||
class FoldStatePlugin {
|
||||
private readonly editorStateStore = useEditorStateStore();
|
||||
private readonly debouncedSave;
|
||||
|
||||
constructor(private view: EditorView) {
|
||||
const {debouncedFn, flush} = createDebounce(
|
||||
() => this.saveFoldState(),
|
||||
{delay: 500}
|
||||
);
|
||||
this.debouncedSave = {fn: debouncedFn, flush};
|
||||
}
|
||||
|
||||
update(update: ViewUpdate) {
|
||||
// 检查是否有折叠/展开操作
|
||||
const hasFoldChange = update.transactions.some(tr =>
|
||||
tr.effects.some(effect =>
|
||||
effect.is(foldEffect) || effect.is(unfoldEffect)
|
||||
)
|
||||
);
|
||||
|
||||
if (hasFoldChange) {
|
||||
this.debouncedSave.fn();
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
// 销毁时立即执行待保存的操作
|
||||
this.debouncedSave.flush();
|
||||
// 再保存一次确保最新状态
|
||||
this.saveFoldState();
|
||||
}
|
||||
|
||||
private saveFoldState() {
|
||||
const foldRanges: FoldRange[] = [];
|
||||
const foldCursor = foldedRanges(this.view.state).iter();
|
||||
const doc = this.view.state.doc;
|
||||
|
||||
// 遍历所有折叠区间
|
||||
while (foldCursor.value !== null) {
|
||||
const from = foldCursor.from;
|
||||
const to = foldCursor.to;
|
||||
|
||||
// 同时记录字符偏移和行号
|
||||
const fromLine = doc.lineAt(from).number;
|
||||
const toLine = doc.lineAt(to).number;
|
||||
|
||||
foldRanges.push({
|
||||
from,
|
||||
to,
|
||||
fromLine,
|
||||
toLine
|
||||
});
|
||||
|
||||
foldCursor.next();
|
||||
}
|
||||
|
||||
this.editorStateStore.saveFoldState(documentId, foldRanges);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复折叠状态(基于行号,更稳定)
|
||||
* @param view 编辑器视图
|
||||
* @param foldRanges 要恢复的折叠区间
|
||||
*/
|
||||
export function restoreFoldState(view: EditorView, foldRanges: FoldRange[]) {
|
||||
if (foldRanges.length === 0) return;
|
||||
|
||||
const doc = view.state.doc;
|
||||
const effects: StateEffect<any>[] = [];
|
||||
|
||||
for (const range of foldRanges) {
|
||||
try {
|
||||
// 优先使用行号恢复
|
||||
if (range.fromLine && range.toLine) {
|
||||
// 确保行号在有效范围内
|
||||
if (range.fromLine >= 1 && range.toLine <= doc.lines && range.fromLine <= range.toLine) {
|
||||
const fromPos = doc.line(range.fromLine).from;
|
||||
const toPos = doc.line(range.toLine).to;
|
||||
|
||||
effects.push(foldEffect.of({from: fromPos, to: toPos}));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 使用字符偏移
|
||||
if (range.from >= 0 && range.to <= doc.length && range.from < range.to) {
|
||||
effects.push(foldEffect.of({from: range.from, to: range.to}));
|
||||
}
|
||||
} catch (error) {
|
||||
// 忽略无效的折叠区间
|
||||
console.warn('Failed to restore fold range:', range, error);
|
||||
}
|
||||
}
|
||||
|
||||
if (effects.length > 0) {
|
||||
view.dispatch({effects});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {Extension} from '@codemirror/state';
|
||||
import {EditorView} from '@codemirror/view';
|
||||
import {DocumentStats} from '@/stores/editorStore';
|
||||
import {DocumentStats} from '@/stores/editorStateStore';
|
||||
import {getActiveNoteBlock} from '@/views/editor/extensions/codeblock/state';
|
||||
|
||||
// 更新编辑器文档统计信息
|
||||
|
||||
@@ -9,15 +9,11 @@ export const themeCompartment = new Compartment();
|
||||
/**
|
||||
* 根据主题类型获取主题扩展
|
||||
*/
|
||||
const getThemeExtension = (): Extension | null => {
|
||||
const getThemeExtension = (): Extension => {
|
||||
const themeStore = useThemeStore();
|
||||
|
||||
// 直接获取当前主题颜色配置
|
||||
const colors = themeStore.currentColors;
|
||||
|
||||
if (!colors) {
|
||||
return null;
|
||||
}
|
||||
// 获取有效主题颜色
|
||||
const colors = themeStore.getEffectiveColors();
|
||||
|
||||
// 使用颜色配置创建主题
|
||||
return createThemeByColors(colors);
|
||||
@@ -28,12 +24,6 @@ const getThemeExtension = (): Extension | null => {
|
||||
*/
|
||||
export const createThemeExtension = (): Extension => {
|
||||
const extension = getThemeExtension();
|
||||
|
||||
// 如果主题未加载,返回空扩展
|
||||
if (!extension) {
|
||||
return themeCompartment.of([]);
|
||||
}
|
||||
|
||||
return themeCompartment.of(extension);
|
||||
};
|
||||
|
||||
@@ -48,11 +38,6 @@ export const updateEditorTheme = (view: EditorView): void => {
|
||||
try {
|
||||
const extension = getThemeExtension();
|
||||
|
||||
// 如果主题未加载,不更新
|
||||
if (!extension) {
|
||||
return;
|
||||
}
|
||||
|
||||
view.dispatch({
|
||||
effects: themeCompartment.reconfigure(extension)
|
||||
});
|
||||
@@ -60,4 +45,3 @@ export const updateEditorTheme = (view: EditorView): void => {
|
||||
console.error('Failed to update editor theme:', error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { EditorView } from "@codemirror/view";
|
||||
import { Command } from "@codemirror/view";
|
||||
import { LANGUAGES } from "./lang-parser/languages";
|
||||
import { USER_EVENTS, codeBlockEvent, CONTENT_EDIT } from "./annotation";
|
||||
import * as runtime from "@wailsio/runtime";
|
||||
|
||||
/**
|
||||
* 构建块分隔符正则表达式
|
||||
@@ -62,29 +63,37 @@ function copiedRange(state: EditorState, forCut: boolean = false) {
|
||||
*/
|
||||
export const codeBlockCopyCut = EditorView.domEventHandlers({
|
||||
copy(event, view) {
|
||||
event.preventDefault();
|
||||
|
||||
let { text } = copiedRange(view.state);
|
||||
// 将块分隔符替换为双换行符
|
||||
text = text.replaceAll(blockSeparatorRegex, "\n\n");
|
||||
|
||||
const data = event.clipboardData;
|
||||
if (data) {
|
||||
event.preventDefault();
|
||||
data.clearData();
|
||||
data.setData("text/plain", text);
|
||||
}
|
||||
// 优先使用 Wails 原生剪贴板 API
|
||||
runtime.Clipboard.SetText(text).catch(() => {
|
||||
// 降级方案:使用浏览器剪贴板
|
||||
const data = event.clipboardData;
|
||||
if (data) {
|
||||
data.clearData();
|
||||
data.setData("text/plain", text);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
cut(event, view) {
|
||||
event.preventDefault();
|
||||
|
||||
let { text, ranges } = copiedRange(view.state, true);
|
||||
// 将块分隔符替换为双换行符
|
||||
text = text.replaceAll(blockSeparatorRegex, "\n\n");
|
||||
|
||||
const data = event.clipboardData;
|
||||
if (data) {
|
||||
event.preventDefault();
|
||||
data.clearData();
|
||||
data.setData("text/plain", text);
|
||||
}
|
||||
// 优先使用 Wails 原生剪贴板 API
|
||||
runtime.Clipboard.SetText(text).catch(() => {
|
||||
// 降级方案:使用浏览器剪贴板
|
||||
const data = event.clipboardData;
|
||||
if (data) {
|
||||
data.clearData();
|
||||
data.setData("text/plain", text);
|
||||
}
|
||||
});
|
||||
|
||||
if (!view.state.readOnly) {
|
||||
view.dispatch({
|
||||
@@ -94,20 +103,52 @@ export const codeBlockCopyCut = EditorView.domEventHandlers({
|
||||
annotations: [codeBlockEvent.of(CONTENT_EDIT)],
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
paste(event, view) {
|
||||
if (view.state.readOnly) {
|
||||
return false;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
// 使用 Wails 原生剪贴板 API
|
||||
runtime.Clipboard.Text()
|
||||
.then(text => {
|
||||
if (text) {
|
||||
doPaste(view, text);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('[Clipboard] Failed to read from system clipboard:', error);
|
||||
|
||||
const data = event.clipboardData;
|
||||
if (data) {
|
||||
const text = data.getData("text/plain");
|
||||
if (text) {
|
||||
doPaste(view, text);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 复制和剪切的通用函数
|
||||
* 复制和剪切的通用函数 - 使用 Wails 原生剪贴板 API
|
||||
*/
|
||||
const copyCut = (view: EditorView, cut: boolean): boolean => {
|
||||
let { text, ranges } = copiedRange(view.state, cut);
|
||||
// 将块分隔符替换为双换行符
|
||||
text = text.replaceAll(blockSeparatorRegex, "\n\n");
|
||||
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(text);
|
||||
}
|
||||
runtime.Clipboard.SetText(text).catch(err => {
|
||||
console.error('[Clipboard] Failed to write to system clipboard:', err);
|
||||
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(text);
|
||||
}
|
||||
});
|
||||
|
||||
if (cut && !view.state.readOnly) {
|
||||
view.dispatch({
|
||||
@@ -166,20 +207,32 @@ export const cutCommand: Command = (view) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* 粘贴命令
|
||||
* 粘贴命令 - 使用 Wails 原生剪贴板 API
|
||||
*/
|
||||
export const pasteCommand: Command = (view) => {
|
||||
if (navigator.clipboard && navigator.clipboard.readText) {
|
||||
navigator.clipboard.readText()
|
||||
.then(text => {
|
||||
// 使用 Wails 原生剪贴板 API,正确处理系统编码
|
||||
runtime.Clipboard.Text()
|
||||
.then(text => {
|
||||
if (text) {
|
||||
doPaste(view, text);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Failed to read from clipboard:', err);
|
||||
});
|
||||
} else {
|
||||
console.warn('The clipboard API is not available, please use your browser\'s native paste feature');
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('[Clipboard] Failed to read from system clipboard:', err);
|
||||
|
||||
if (navigator.clipboard && navigator.clipboard.readText) {
|
||||
navigator.clipboard.readText()
|
||||
.then(text => {
|
||||
if (text) {
|
||||
doPaste(view, text);
|
||||
}
|
||||
})
|
||||
.catch(fallbackErr => {
|
||||
console.error('[Clipboard] Fallback also failed:', fallbackErr);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
@@ -39,6 +39,9 @@ export interface CodeBlockOptions {
|
||||
|
||||
/** 新建块时的默认语言 */
|
||||
defaultLanguage?: SupportedLanguage;
|
||||
|
||||
/** 分隔符高度(像素) */
|
||||
separatorHeight?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -68,13 +68,20 @@ function createDetectionMap(): Map<string, SupportedLanguage> {
|
||||
LANGUAGES.forEach(lang => {
|
||||
if (lang.detectIds) {
|
||||
lang.detectIds.forEach(detectId => {
|
||||
map.set(detectId, lang.token);
|
||||
// 保留首个映射,避免重复 detectId 覆盖更基础的语言,例如 js -> ts。
|
||||
if (!map.has(detectId)) {
|
||||
map.set(detectId, lang.token);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
function createWorkerUrl(): URL {
|
||||
return new URL(`${import.meta.env.BASE_URL}langdetect-worker.js`, window.location.href);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测ID到语言token的映射表
|
||||
*/
|
||||
@@ -131,7 +138,7 @@ class LanguageDetectionWorker {
|
||||
*/
|
||||
private initWorker(): void {
|
||||
try {
|
||||
this.worker = new Worker('/langdetect-worker.js');
|
||||
this.worker = new Worker(createWorkerUrl());
|
||||
this.worker.onmessage = (event) => {
|
||||
const response: WorkerResponse = event.data;
|
||||
const request = this.pendingRequests.get(response.idx);
|
||||
@@ -316,4 +323,4 @@ export async function detectLanguages(contents: string[]): Promise<LanguageDetec
|
||||
} finally {
|
||||
worker.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,6 @@ import yamlPrettierPlugin from "prettier/plugins/yaml";
|
||||
import goPrettierPlugin from "@/common/prettier/plugins/go";
|
||||
import sqlPrettierPlugin from "@/common/prettier/plugins/sql";
|
||||
import phpPrettierPlugin from "@/common/prettier/plugins/php";
|
||||
import javaPrettierPlugin from "@/common/prettier/plugins/java";
|
||||
import xmlPrettierPlugin from "@prettier/plugin-xml";
|
||||
import shellPrettierPlugin from "@/common/prettier/plugins/shell";
|
||||
import dockerfilePrettierPlugin from "@/common/prettier/plugins/docker";
|
||||
@@ -134,8 +133,12 @@ export const LANGUAGES: LanguageInfo[] = [
|
||||
plugins: [markdownPrettierPlugin]
|
||||
}),
|
||||
new LanguageInfo("java", "Java", javaLanguage.parser, ["java"], {
|
||||
parser: "java",
|
||||
plugins: [javaPrettierPlugin]
|
||||
parser: "clang-format",
|
||||
plugins: [clangPrettierPlugin],
|
||||
options: {
|
||||
filename: "Main.java",
|
||||
clangStyle: "Google"
|
||||
}
|
||||
}),
|
||||
new LanguageInfo("php", "PHP", phpLanguage.configure({top: "Program"}).parser, ["php"], {
|
||||
parser: "php",
|
||||
@@ -268,4 +271,4 @@ export function getLanguage(token: SupportedLanguage): LanguageInfo | undefined
|
||||
*/
|
||||
export function getAllSupportedLanguages(): SupportedLanguage[] {
|
||||
return ['auto', ...LANGUAGES.map(lang => lang.token)];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,10 +46,10 @@ function collectBlocksFromTree(tree: Tree, state: EditorState): Block[] | null {
|
||||
if (child.type.id === BlockDelimiter) {
|
||||
delimiter = { from: child.from, to: child.to };
|
||||
const delimiterText = doc.sliceString(child.from, child.to);
|
||||
const match = delimiterText.match(/∞∞∞([a-zA-Z0-9_-]+)(-a)?\n/);
|
||||
if (match) {
|
||||
language = match[1] || DEFAULT_LANGUAGE;
|
||||
auto = match[2] === '-a';
|
||||
const delimiterInfo = parseDelimiter(delimiterText);
|
||||
if (delimiterInfo) {
|
||||
language = delimiterInfo.language;
|
||||
auto = delimiterInfo.auto;
|
||||
} else {
|
||||
child.node.firstChild?.cursor().iterate(langChild => {
|
||||
if (langChild.type.id === BlockLanguage) {
|
||||
|
||||
@@ -87,7 +87,7 @@ export interface EditorOptions {
|
||||
|
||||
|
||||
// 分隔符格式常量
|
||||
export const DELIMITER_REGEX = /^\n∞∞∞([a-zA-Z0-9_-]+)(-a)?\n/gm;
|
||||
export const DELIMITER_REGEX = /^\n∞∞∞([a-zA-Z0-9_]+)(-a)?\n/gm;
|
||||
export const DELIMITER_PREFIX = '\n∞∞∞';
|
||||
export const DELIMITER_SUFFIX = '\n';
|
||||
export const AUTO_DETECT_SUFFIX = '-a';
|
||||
|
||||