Compare commits
42 Commits
v1.0.0
...
f72010bd69
| Author | SHA1 | Date | |
|---|---|---|---|
| f72010bd69 | |||
| cd027097f8 | |||
| 9cbbf729c0 | |||
|
|
c26c11e253 | ||
| 338ac358db | |||
| a83c7139c9 | |||
| 42c7d11c09 | |||
| 5ca5aa64c7 | |||
| eda7ef771e | |||
| 593c4d7783 | |||
| d24a522b32 | |||
| 41afb834ae | |||
| b745329e26 | |||
| 1fb4f64cb3 | |||
| 1f8e8981ce | |||
| a257d30dba | |||
| 97ee3b0667 | |||
| 8e2bafba5f | |||
| 6149bc133d | |||
| 5f22ee3b1f | |||
| fa72ff8061 | |||
| 65f24860e6 | |||
|
|
4881233211 | ||
| bc01fdf362 | |||
| 709998ff9c | |||
| 6adeadeed4 | |||
| 7b70a39b23 | |||
| 873a3c0e60 | |||
| 5b88efcfbe | |||
| f37c659c89 | |||
| 9fff7bcfca | |||
| b4b0ad9bba | |||
| 6d8fdf62f1 | |||
| 9f53d7421d | |||
| 80c8ecb4cf | |||
| d10059a82d | |||
| 737f83cd5f | |||
| a720a4cfb8 | |||
| b5510d605c | |||
| 4d62da912a | |||
| b52e067d50 | |||
| 8dce06c30e |
26
README.md
26
README.md
@@ -1,10 +1,10 @@
|
||||
# <img src="./frontend/public/appicon.png" alt="VoidRaft Logo" width="32" height="32" style="vertical-align: middle;"> VoidRaft
|
||||
# <img src="./frontend/public/appicon.png" alt="voidraft Logo" width="32" height="32" style="vertical-align: middle;"> voidraft
|
||||
|
||||
[中文](README_ZH.md) | **English**
|
||||
|
||||
> *An elegant text snippet recording tool designed for developers.*
|
||||
|
||||
VoidRaft is a modern developer-focused text editor that allows you to record, organize, and manage various text snippets anytime, anywhere. Whether it's temporary code snippets, API responses, meeting notes, or daily to-do lists, VoidRaft provides a smooth and elegant editing experience.
|
||||
voidraft is a modern developer-focused text editor that allows you to record, organize, and manage various text snippets anytime, anywhere. Whether it's temporary code snippets, API responses, meeting notes, or daily to-do lists, voidraft provides a smooth and elegant editing experience.
|
||||
|
||||
## Core Features
|
||||
|
||||
@@ -14,6 +14,8 @@ VoidRaft is a modern developer-focused text editor that allows you to record, or
|
||||
- Smart language detection - Automatically recognizes code block language types
|
||||
- Code formatting - Built-in Prettier support for one-click code beautification
|
||||
- Block editing mode - Split content into independent code blocks, each with different language settings
|
||||
- Multi-window support - edit multiple documents at the same time
|
||||
- Support for custom themes - Custom editor themes
|
||||
|
||||
### Modern Interface
|
||||
|
||||
@@ -53,6 +55,7 @@ cd voidraft
|
||||
# Install frontend dependencies
|
||||
cd frontend
|
||||
npm install
|
||||
npm run build
|
||||
cd ..
|
||||
|
||||
# Start development server
|
||||
@@ -84,7 +87,7 @@ After building, the executable will be generated in the `bin` directory.
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
Voidraft/
|
||||
voidraft/
|
||||
├── frontend/ # Vue 3 frontend application
|
||||
│ ├── src/
|
||||
│ │ ├── views/editor/ # Editor core views
|
||||
@@ -116,24 +119,21 @@ Voidraft/
|
||||
| Linux | Planned | Future versions |
|
||||
|
||||
### Planned Features
|
||||
- [ ] Custom themes - Customize editor themes
|
||||
- [ ] Multi-window support - Support editing multiple documents simultaneously
|
||||
- ✅ Custom themes - Customize editor themes
|
||||
- ✅ Multi-window support - Support editing multiple documents simultaneously
|
||||
- ✅ Data synchronization - Cloud backup for documents
|
||||
- [ ] Enhanced clipboard - Monitor and manage clipboard history
|
||||
- Automatic text content saving
|
||||
- Image content support
|
||||
- History management
|
||||
- [ ] Data synchronization - Cloud backup for configurations and documents
|
||||
- [ ] Extension system - Support for custom plugins
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
> Standing on the shoulders of giants, paying tribute to the open source spirit
|
||||
|
||||
The birth of VoidRaft is inseparable from the following excellent open source projects:
|
||||
The birth of voidraft is inseparable from the following excellent open source projects:
|
||||
|
||||
### Special Thanks
|
||||
|
||||
- **[Heynote](https://github.com/heyman/heynote/)** - VoidRaft is a feature-enhanced version based on Heynote's concept
|
||||
- **[Heynote](https://github.com/heyman/heynote/)** - voidraft is a feature-enhanced version based on Heynote's concept
|
||||
- Inherits Heynote's elegant block editing philosophy
|
||||
- Adds more practical features on the original foundation
|
||||
- Rebuilt with modern technology stack
|
||||
@@ -157,7 +157,7 @@ This project is open source under the [MIT License](LICENSE).
|
||||
Welcome to Fork, Star, and contribute code.
|
||||
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://github.com/yourusername/Voidraft)
|
||||
[](https://github.com/yourusername/Voidraft)
|
||||
[](https://github.com/yourusername/voidraft)
|
||||
[](https://github.com/yourusername/voidraft)
|
||||
|
||||
*Made with ❤️ by landaiqing*
|
||||
26
README_ZH.md
26
README_ZH.md
@@ -1,10 +1,10 @@
|
||||
# <img src="./frontend/public/appicon.png" alt="Voidraft Logo" width="32" height="32" style="vertical-align: middle;"> Voidraft
|
||||
# <img src="./frontend/public/appicon.png" alt="voidraft Logo" width="32" height="32" style="vertical-align: middle;"> voidraft
|
||||
|
||||
**中文** | [English](README.md)
|
||||
|
||||
> *一个专为开发者打造的优雅文本片段记录工具。*
|
||||
|
||||
Voidraft 是一个现代化的开发者专用文本编辑器,让你能够随时随地记录、整理和管理各种文本片段。无论是临时的代码片段、API 响应、会议笔记,还是日常的待办事项,Voidraft 都能为你提供流畅而优雅的编辑体验。
|
||||
voidraft 是一个现代化的开发者专用文本编辑器,让你能够随时随地记录、整理和管理各种文本片段。无论是临时的代码片段、API 响应、会议笔记,还是日常的待办事项,voidraft 都能为你提供流畅而优雅的编辑体验。
|
||||
|
||||
## 核心特性
|
||||
|
||||
@@ -14,6 +14,8 @@ Voidraft 是一个现代化的开发者专用文本编辑器,让你能够随
|
||||
- 智能语言检测 - 自动识别代码块语言类型
|
||||
- 代码格式化 - 内置 Prettier 支持,一键美化代码
|
||||
- 块状编辑模式 - 将内容分割为独立的代码块,每个块可设置不同语言
|
||||
- 支持多窗口 - 同时编辑多个文档
|
||||
- 支持自定义主题 - 自定义编辑器主题
|
||||
|
||||
### 现代化界面
|
||||
|
||||
@@ -54,6 +56,7 @@ cd voidraft
|
||||
# 安装前端依赖
|
||||
cd frontend
|
||||
npm install
|
||||
npm run build
|
||||
cd ..
|
||||
|
||||
# 启动开发服务器
|
||||
@@ -85,7 +88,7 @@ wails3 package
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
Voidraft/
|
||||
voidraft/
|
||||
├── frontend/ # Vue 3 前端应用
|
||||
│ ├── src/
|
||||
│ │ ├── views/editor/ # 编辑器核心视图
|
||||
@@ -117,13 +120,10 @@ Voidraft/
|
||||
| Linux | 计划中 | 后续版本 |
|
||||
|
||||
### 计划添加的功能
|
||||
- [ ] 自定义主题 - 自定义编辑器主题
|
||||
- [ ] 多窗口支持 - 支持同时编辑多个文档
|
||||
- ✅ 自定义主题 - 自定义编辑器主题
|
||||
- ✅ 多窗口支持 - 支持同时编辑多个文档
|
||||
- ✅ 数据同步 - 文档云端备份
|
||||
- [ ] 剪切板增强 - 监听和管理剪切板历史
|
||||
- 文本内容自动保存
|
||||
- 图片内容支持
|
||||
- 历史记录管理
|
||||
- [ ] 数据同步 - 配置和文档云端备份
|
||||
- [ ] 扩展系统 - 支持自定义插件
|
||||
|
||||
|
||||
@@ -131,11 +131,11 @@ Voidraft/
|
||||
|
||||
> 站在巨人的肩膀上,致敬开源精神
|
||||
|
||||
Voidraft 的诞生离不开以下优秀的开源项目:
|
||||
voidraft 的诞生离不开以下优秀的开源项目:
|
||||
|
||||
### 特别感谢
|
||||
|
||||
- **[Heynote](https://github.com/heyman/heynote/)** - Voidraft 是基于 Heynote 概念的功能增强版本
|
||||
- **[Heynote](https://github.com/heyman/heynote/)** - voidraft 是基于 Heynote 概念的功能增强版本
|
||||
- 继承了 Heynote 优雅的块状编辑理念
|
||||
- 在原有基础上增加了更多实用功能
|
||||
- 采用现代化技术栈重新构建
|
||||
@@ -159,7 +159,7 @@ Voidraft 的诞生离不开以下优秀的开源项目:
|
||||
欢迎 Fork、Star 和贡献代码。
|
||||
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://github.com/yourusername/Voidraft)
|
||||
[](https://github.com/yourusername/Voidraft)
|
||||
[](https://github.com/yourusername/voidraft)
|
||||
[](https://github.com/yourusername/voidraft)
|
||||
|
||||
*Made with ❤️ by landaiqing*
|
||||
|
||||
12
Taskfile.yml
12
Taskfile.yml
@@ -12,13 +12,25 @@ vars:
|
||||
VITE_PORT: '{{.WAILS_VITE_PORT | default 9245}}'
|
||||
|
||||
tasks:
|
||||
version:
|
||||
summary: Generate version information
|
||||
cmds:
|
||||
- '{{if eq OS "windows"}}cmd /c ".\scripts\version.bat"{{else}}bash ./scripts/version.sh{{end}}'
|
||||
sources:
|
||||
- scripts/version.bat
|
||||
- scripts/version.sh
|
||||
generates:
|
||||
- version.txt
|
||||
|
||||
build:
|
||||
summary: Builds the application
|
||||
deps: [version]
|
||||
cmds:
|
||||
- task: "{{OS}}:build"
|
||||
|
||||
package:
|
||||
summary: Packages a production build of the application
|
||||
deps: [version]
|
||||
cmds:
|
||||
- task: "{{OS}}:package"
|
||||
|
||||
|
||||
@@ -5,12 +5,12 @@ version: '3'
|
||||
|
||||
# This information is used to generate the build assets.
|
||||
info:
|
||||
companyName: "Voidraft" # The name of the company
|
||||
productName: "Voidraft" # The name of the application
|
||||
companyName: "voidraft" # The name of the company
|
||||
productName: "voidraft" # The name of the application
|
||||
productIdentifier: "landaiqing" # The unique product identifier
|
||||
description: "Voidraft" # The application description
|
||||
copyright: "© 2025 Voidraft. All rights reserved." # Copyright text
|
||||
comments: "Voidraft" # Comments
|
||||
description: "voidraft" # The application description
|
||||
copyright: "© 2025 voidraft. All rights reserved." # Copyright text
|
||||
comments: "voidraft" # Comments
|
||||
version: "0.0.1.0" # The application version
|
||||
|
||||
# Dev mode configuration
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Voidraft</string>
|
||||
<string>voidraft</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Voidraft</string>
|
||||
<string>voidraft</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>landaiqing</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.0.1.0</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>Voidraft</string>
|
||||
<string>voidraft</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.0.1.0</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
@@ -22,7 +22,7 @@
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<string>true</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>© 2025 Voidraft. All rights reserved.</string>
|
||||
<string>© 2025 voidraft. All rights reserved.</string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsLocalNetworking</key>
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Voidraft</string>
|
||||
<string>voidraft</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Voidraft</string>
|
||||
<string>voidraft</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>landaiqing</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.0.1.0</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>Voidraft</string>
|
||||
<string>voidraft</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.0.1.0</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
@@ -22,6 +22,6 @@
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<string>true</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>© 2025 Voidraft. All rights reserved.</string>
|
||||
<string>© 2025 voidraft. All rights reserved.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -11,9 +11,12 @@ tasks:
|
||||
- task: common:build:frontend
|
||||
- task: common:generate:icons
|
||||
cmds:
|
||||
- go build {{.BUILD_FLAGS}} -o {{.OUTPUT}}
|
||||
- go build {{.BUILD_FLAGS}} -ldflags="{{.LDFLAGS}} {{.VERSION_FLAGS}}" -o {{.OUTPUT}}
|
||||
vars:
|
||||
BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s"{{else}}-buildvcs=false -gcflags=all="-l"{{end}}'
|
||||
BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false{{else}}-buildvcs=false -gcflags=all="-l"{{end}}'
|
||||
LDFLAGS: '{{if eq .PRODUCTION "true"}}-w -s{{else}}{{end}}'
|
||||
VERSION_FLAGS:
|
||||
sh: 'grep "VERSION=" version.txt | cut -d"=" -f2 | xargs -I {} echo "-X voidraft/internal/version.Version={}"'
|
||||
DEFAULT_OUTPUT: '{{.BIN_DIR}}/{{.APP_NAME}}'
|
||||
OUTPUT: '{{ .OUTPUT | default .DEFAULT_OUTPUT }}'
|
||||
env:
|
||||
|
||||
@@ -11,9 +11,12 @@ tasks:
|
||||
- task: common:build:frontend
|
||||
- task: common:generate:icons
|
||||
cmds:
|
||||
- go build {{.BUILD_FLAGS}} -o {{.BIN_DIR}}/{{.APP_NAME}}
|
||||
- go build {{.BUILD_FLAGS}} -ldflags="{{.LDFLAGS}} {{.VERSION_FLAGS}}" -o {{.BIN_DIR}}/{{.APP_NAME}}
|
||||
vars:
|
||||
BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s"{{else}}-buildvcs=false -gcflags=all="-l"{{end}}'
|
||||
BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false{{else}}-buildvcs=false -gcflags=all="-l"{{end}}'
|
||||
LDFLAGS: '{{if eq .PRODUCTION "true"}}-w -s{{else}}{{end}}'
|
||||
VERSION_FLAGS:
|
||||
sh: 'grep "VERSION=" version.txt | cut -d"=" -f2 | xargs -I {} echo "-X voidraft/internal/version.Version={}"'
|
||||
env:
|
||||
GOOS: linux
|
||||
CGO_ENABLED: 1
|
||||
|
||||
@@ -3,26 +3,26 @@
|
||||
#
|
||||
# The lines below are called `modelines`. See `:help modeline`
|
||||
|
||||
name: "Voidraft"
|
||||
name: "voidraft"
|
||||
arch: ${GOARCH}
|
||||
platform: "linux"
|
||||
version: "0.0.1.0"
|
||||
section: "default"
|
||||
priority: "extra"
|
||||
maintainer: ${GIT_COMMITTER_NAME} <${GIT_COMMITTER_EMAIL}>
|
||||
description: "Voidraft"
|
||||
vendor: "Voidraft"
|
||||
homepage: "https://wails.io"
|
||||
description: "voidraft"
|
||||
vendor: "voidraft"
|
||||
homepage: "https://voidraft.landaiqing.cn"
|
||||
license: "MIT"
|
||||
release: "1"
|
||||
|
||||
contents:
|
||||
- src: "./bin/Voidraft"
|
||||
dst: "/usr/local/bin/Voidraft"
|
||||
- src: "./bin/voidraft"
|
||||
dst: "/usr/local/bin/voidraft"
|
||||
- src: "./build/appicon.png"
|
||||
dst: "/usr/share/icons/hicolor/128x128/apps/Voidraft.png"
|
||||
- src: "./build/linux/Voidraft.desktop"
|
||||
dst: "/usr/share/applications/Voidraft.desktop"
|
||||
dst: "/usr/share/icons/hicolor/128x128/apps/voidraft.png"
|
||||
- src: "./build/linux/voidraft.desktop"
|
||||
dst: "/usr/share/applications/voidraft.desktop"
|
||||
|
||||
depends:
|
||||
- gtk3
|
||||
|
||||
@@ -14,13 +14,16 @@ tasks:
|
||||
- task: common:generate:icons
|
||||
cmds:
|
||||
- task: generate:syso
|
||||
- go build {{.BUILD_FLAGS}} -o {{.BIN_DIR}}/{{.APP_NAME}}.exe
|
||||
- go build {{.BUILD_FLAGS}} -ldflags="{{.LDFLAGS}} {{.VERSION_FLAGS}}" -o {{.BIN_DIR}}/{{.APP_NAME}}.exe
|
||||
- cmd: powershell Remove-item *.syso
|
||||
platforms: [windows]
|
||||
- cmd: rm -f *.syso
|
||||
platforms: [linux, darwin]
|
||||
vars:
|
||||
BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s -H windowsgui"{{else}}-buildvcs=false -gcflags=all="-l"{{end}}'
|
||||
BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false{{else}}-buildvcs=false -gcflags=all="-l"{{end}}'
|
||||
LDFLAGS: '{{if eq .PRODUCTION "true"}}-w -s -H windowsgui{{else}}{{end}}'
|
||||
VERSION_FLAGS:
|
||||
sh: 'powershell -Command "(Get-Content version.txt) -replace ''VERSION='', ''-X voidraft/internal/version.Version=''"'
|
||||
env:
|
||||
GOOS: windows
|
||||
CGO_ENABLED: 1
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
"info": {
|
||||
"0000": {
|
||||
"ProductVersion": "0.0.1.0",
|
||||
"CompanyName": "Voidraft",
|
||||
"FileDescription": "Voidraft",
|
||||
"LegalCopyright": "© 2025 Voidraft. All rights reserved.",
|
||||
"ProductName": "Voidraft",
|
||||
"Comments": "Voidraft"
|
||||
"CompanyName": "voidraft",
|
||||
"FileDescription": "voidraft",
|
||||
"LegalCopyright": "© 2025 voidraft. All rights reserved.",
|
||||
"ProductName": "voidraft",
|
||||
"Comments": "voidraft"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,19 +5,19 @@
|
||||
!include "FileFunc.nsh"
|
||||
|
||||
!ifndef INFO_PROJECTNAME
|
||||
!define INFO_PROJECTNAME "Voidraft"
|
||||
!define INFO_PROJECTNAME "voidraft"
|
||||
!endif
|
||||
!ifndef INFO_COMPANYNAME
|
||||
!define INFO_COMPANYNAME "Voidraft"
|
||||
!define INFO_COMPANYNAME "voidraft"
|
||||
!endif
|
||||
!ifndef INFO_PRODUCTNAME
|
||||
!define INFO_PRODUCTNAME "Voidraft"
|
||||
!define INFO_PRODUCTNAME "voidraft"
|
||||
!endif
|
||||
!ifndef INFO_PRODUCTVERSION
|
||||
!define INFO_PRODUCTVERSION "0.0.1.0"
|
||||
!endif
|
||||
!ifndef INFO_COPYRIGHT
|
||||
!define INFO_COPYRIGHT "© 2025 Voidraft. All rights reserved."
|
||||
!define INFO_COPYRIGHT "© 2025 voidraft. All rights reserved."
|
||||
!endif
|
||||
!ifndef PRODUCT_EXECUTABLE
|
||||
!define PRODUCT_EXECUTABLE "${INFO_PROJECTNAME}.exe"
|
||||
|
||||
1
docs/CNAME
Normal file
1
docs/CNAME
Normal file
@@ -0,0 +1 @@
|
||||
voidraft.landaiqing.cn
|
||||
75
docs/changelog.html
Normal file
75
docs/changelog.html
Normal file
@@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>voidraft - Changelog</title>
|
||||
<link rel="stylesheet" href="css/styles.css">
|
||||
<link rel="stylesheet" href="css/changelog.css">
|
||||
<link rel="icon" href="img/favicon.ico">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Space+Mono&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
</head>
|
||||
<body class="theme-dark">
|
||||
<div class="container">
|
||||
<!-- 主卡片 -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h1 class="card-title" data-en="voidraft Changelog" data-zh="voidraft 更新日志">voidraft Changelog</h1>
|
||||
<div class="card-controls">
|
||||
<button id="theme-toggle" class="btn btn-secondary" title="切换主题">
|
||||
<i class="fas fa-sun"></i> <span data-en="Theme" data-zh="主题">Theme</span>
|
||||
</button>
|
||||
<button id="lang-toggle" class="btn btn-secondary">
|
||||
<i class="fas fa-language"></i> 中/EN
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<!-- 导航区域 -->
|
||||
<div class="nav-links">
|
||||
<a href="index.html" class="btn btn-secondary">
|
||||
<i class="fas fa-home"></i> <span data-en="Home" data-zh="首页">Home</span>
|
||||
</a>
|
||||
<a href="https://github.com/landaiqing/voidraft" class="btn btn-secondary">
|
||||
<i class="fab fa-github"></i> <span data-en="Source Code" data-zh="源代码">Source Code</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- 加载中提示 -->
|
||||
<div id="loading" class="loading-container">
|
||||
<div class="loading-spinner"></div>
|
||||
<p data-en="Loading releases..." data-zh="正在加载版本信息...">Loading releases...</p>
|
||||
</div>
|
||||
|
||||
<!-- 更新日志内容 -->
|
||||
<div id="changelog" class="changelog-container">
|
||||
<!-- 通过JavaScript动态填充内容 -->
|
||||
</div>
|
||||
|
||||
<!-- 错误信息 -->
|
||||
<div id="error-message" class="error-container" style="display: none;">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<p data-en="Failed to load release information. Please try again later."
|
||||
data-zh="加载版本信息失败,请稍后再试。">Failed to load release information. Please try again later.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 页脚 -->
|
||||
<footer class="footer">
|
||||
<p class="footer-text" data-en="© 2025 voidraft - An elegant text snippet recording tool designed for developers" data-zh="© 2025 voidraft - 专为开发者打造的优雅文本片段记录工具">© 2023-2024 voidraft - An elegant text snippet recording tool designed for developers</p>
|
||||
<div class="footer-links">
|
||||
<a href="https://github.com/landaiqing/voidraft" target="_blank" class="footer-link">GitHub</a>
|
||||
<a href="https://github.com/landaiqing/voidraft/issues" target="_blank" class="footer-link" data-en="Issues" data-zh="问题反馈">Issues</a>
|
||||
<a href="https://github.com/landaiqing/voidraft/releases" target="_blank" class="footer-link" data-en="Releases" data-zh="版本发布">Releases</a>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="js/script.js"></script>
|
||||
<script src="js/changelog.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
347
docs/css/changelog.css
Normal file
347
docs/css/changelog.css
Normal file
@@ -0,0 +1,347 @@
|
||||
/* 更新日志页面样式 */
|
||||
.nav-links {
|
||||
margin-bottom: 30px;
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
border: 4px solid rgba(0, 0, 0, 0.1);
|
||||
border-left-color: var(--primary-color);
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 0 auto 20px;
|
||||
}
|
||||
|
||||
.theme-dark .loading-spinner {
|
||||
border-color: rgba(255, 255, 255, 0.1);
|
||||
border-left-color: var(--primary-color);
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.error-container {
|
||||
text-align: center;
|
||||
color: var(--error-color);
|
||||
padding: 20px;
|
||||
border: 2px dashed var(--error-color);
|
||||
margin: 20px 0;
|
||||
border-radius: 4px;
|
||||
background-color: rgba(var(--card-bg-rgb), 0.7);
|
||||
}
|
||||
|
||||
.error-container i {
|
||||
font-size: 24px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* 更新日志容器 */
|
||||
.changelog-container {
|
||||
display: none;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.release {
|
||||
margin-bottom: 40px;
|
||||
border-left: 4px solid var(--primary-color);
|
||||
padding-left: 20px;
|
||||
background-color: rgba(var(--card-bg-rgb), 0.5);
|
||||
padding: 15px 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.release-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.release-version {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.release-date {
|
||||
color: var(--text-color);
|
||||
opacity: 0.7;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.release-badge {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
margin-left: 10px;
|
||||
background-color: var(--primary-color);
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.release-badge.pre-release {
|
||||
background-color: var(--warning-color);
|
||||
}
|
||||
|
||||
.release-description {
|
||||
margin-bottom: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.release-assets {
|
||||
background-color: rgba(var(--light-bg-rgb), 0.7);
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.release-assets-title {
|
||||
font-size: 16px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.asset-list {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.asset-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid rgba(128, 128, 128, 0.2);
|
||||
}
|
||||
|
||||
.asset-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.asset-icon {
|
||||
margin-right: 10px;
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
.asset-name {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.asset-size {
|
||||
font-size: 12px;
|
||||
color: var(--text-color);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* 资源下载按钮 */
|
||||
.download-btn {
|
||||
margin-left: 10px;
|
||||
padding: 3px 10px;
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s ease;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.download-btn:hover {
|
||||
background-color: var(--secondary-color);
|
||||
}
|
||||
|
||||
.markdown-content {
|
||||
line-height: 1.8;
|
||||
overflow-wrap: break-word;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.markdown-content h1,
|
||||
.markdown-content h2,
|
||||
.markdown-content h3 {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.markdown-content ul,
|
||||
.markdown-content ol {
|
||||
padding-left: 20px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.markdown-content li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.markdown-content li:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.markdown-content hr {
|
||||
border: none;
|
||||
border-top: 2px dashed var(--border-color);
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.markdown-content br {
|
||||
display: block;
|
||||
content: "";
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.markdown-content code {
|
||||
font-family: 'IBM Plex Mono', monospace;
|
||||
background-color: rgba(128, 128, 128, 0.1);
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.markdown-content pre {
|
||||
background-color: rgba(128, 128, 128, 0.1);
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
overflow-x: auto;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.markdown-content pre code {
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.markdown-content a {
|
||||
color: var(--primary-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.markdown-content a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.data-source {
|
||||
padding: 10px 15px;
|
||||
margin-bottom: 20px;
|
||||
background-color: rgba(var(--light-bg-rgb), 0.7);
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
text-align: right;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.data-source a {
|
||||
color: var(--primary-color);
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.data-source a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Markdown内容样式增强 */
|
||||
.markdown-content blockquote {
|
||||
border-left: 4px solid var(--primary-color);
|
||||
padding: 10px 15px;
|
||||
margin: 15px 0;
|
||||
background-color: rgba(var(--light-bg-rgb), 0.5);
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
.markdown-content ul,
|
||||
.markdown-content ol {
|
||||
padding-left: 20px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
/* 移动设备响应式优化 */
|
||||
@media (max-width: 768px) {
|
||||
.release-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.release-assets {
|
||||
padding: 12px 8px;
|
||||
}
|
||||
|
||||
.asset-item {
|
||||
flex-wrap: wrap;
|
||||
padding: 12px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.asset-name {
|
||||
width: 100%;
|
||||
margin-bottom: 8px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.asset-size {
|
||||
margin-left: 25px;
|
||||
}
|
||||
|
||||
.download-btn {
|
||||
margin-left: 10px;
|
||||
padding: 5px 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.release {
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
.asset-item {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.asset-icon {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.asset-size {
|
||||
margin-left: 0;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.download-btn {
|
||||
margin-left: 0;
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.markdown-content pre {
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 确保日志页面页脚样式一致 */
|
||||
.footer {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
margin: 0 0 15px 0;
|
||||
}
|
||||
45
docs/css/ibm-plex-mono-font.css
Normal file
45
docs/css/ibm-plex-mono-font.css
Normal file
@@ -0,0 +1,45 @@
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(../font/ibm-plex-mono/-F63fjptAgt5VM-kVkqdyU8n1iIq129k.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(../font/ibm-plex-mono/-F63fjptAgt5VM-kVkqdyU8n1isq129k.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(../font/ibm-plex-mono/-F63fjptAgt5VM-kVkqdyU8n1iAq129k.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(../font/ibm-plex-mono/-F63fjptAgt5VM-kVkqdyU8n1iEq129k.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'IBM Plex Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(../font/ibm-plex-mono/-F63fjptAgt5VM-kVkqdyU8n1i8q1w.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
27
docs/css/space-mono-font.css
Normal file
27
docs/css/space-mono-font.css
Normal file
@@ -0,0 +1,27 @@
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Space Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(../font/space-mono/i7dPIFZifjKcF5UAWdDRYE58RWq7.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Space Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(../font/space-mono/i7dPIFZifjKcF5UAWdDRYE98RWq7.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Space Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(../font/space-mono/i7dPIFZifjKcF5UAWdDRYEF8RQ.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
717
docs/css/styles.css
Normal file
717
docs/css/styles.css
Normal file
@@ -0,0 +1,717 @@
|
||||
@import url('./space-mono-font.css');
|
||||
@import url('./ibm-plex-mono-font.css');
|
||||
|
||||
/* 浅色主题 */
|
||||
:root {
|
||||
--bg-color: #fefefe;
|
||||
--text-color: #000000;
|
||||
--primary-color: #F08080;
|
||||
--primary-color-rgb: 240, 128, 128;
|
||||
--secondary-color: #ff006e;
|
||||
--accent-color: #073B4C;
|
||||
--card-bg: #ffffff;
|
||||
--card-bg-rgb: 255, 255, 255;
|
||||
--border-color: #000000;
|
||||
--light-bg: #f0f0f0;
|
||||
--light-bg-rgb: 240, 240, 240;
|
||||
--shadow-color: rgba(240, 128, 128, 0.5);
|
||||
--success-color: #27c93f;
|
||||
--warning-color: #FFD166;
|
||||
--error-color: #ff006e;
|
||||
--info-color: #118ab2;
|
||||
--code-bg: #ffffff;
|
||||
--code-bg-rgb: 255, 255, 255;
|
||||
--preview-header-bg: #f0f0f0;
|
||||
--preview-header-bg-rgb: 240, 240, 240;
|
||||
--grid-color-1: rgba(0, 0, 0, 0.08);
|
||||
--grid-color-2: rgba(0, 0, 0, 0.05);
|
||||
--header-title-color: #000000;
|
||||
}
|
||||
|
||||
/* 暗色主题变量 */
|
||||
.theme-dark {
|
||||
--bg-color: #121212;
|
||||
--text-color: #ffffff;
|
||||
--primary-color: #F08080;
|
||||
--primary-color-rgb: 240, 128, 128;
|
||||
--secondary-color: #ff006e;
|
||||
--accent-color: #118ab2;
|
||||
--card-bg: #1e1e1e;
|
||||
--card-bg-rgb: 30, 30, 30;
|
||||
--border-color: #ffffff;
|
||||
--light-bg: #2a2a2a;
|
||||
--light-bg-rgb: 42, 42, 42;
|
||||
--shadow-color: rgba(240, 128, 128, 0.5);
|
||||
--success-color: #27c93f;
|
||||
--warning-color: #FFD166;
|
||||
--error-color: #ff006e;
|
||||
--info-color: #118ab2;
|
||||
--code-bg: #1e1e1e;
|
||||
--code-bg-rgb: 30, 30, 30;
|
||||
--preview-header-bg: #252526;
|
||||
--preview-header-bg-rgb: 37, 37, 38;
|
||||
--grid-color-1: rgba(255, 255, 255, 0.08);
|
||||
--grid-color-2: rgba(255, 255, 255, 0.05);
|
||||
--header-title-color: #000000;
|
||||
}
|
||||
|
||||
/* 主题切换和语言切换的过渡效果 */
|
||||
.theme-transition,
|
||||
.theme-transition *,
|
||||
.lang-transition,
|
||||
.lang-transition * {
|
||||
transition: all 0.3s ease !important;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@keyframes gridMove {
|
||||
0% {
|
||||
background-position: 0px 0px, 0px 0px, 0px 0px, 0px 0px;
|
||||
}
|
||||
100% {
|
||||
background-position: 80px 80px, 80px 80px, 20px 20px, 20px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-color);
|
||||
background-image:
|
||||
linear-gradient(var(--grid-color-1) 1px, transparent 1px),
|
||||
linear-gradient(90deg, var(--grid-color-1) 1px, transparent 1px),
|
||||
linear-gradient(var(--grid-color-2) 0.5px, transparent 0.5px),
|
||||
linear-gradient(90deg, var(--grid-color-2) 0.5px, transparent 0.5px);
|
||||
background-size: 80px 80px, 80px 80px, 20px 20px, 20px 20px;
|
||||
background-position: center;
|
||||
animation: gridMove 40s linear infinite;
|
||||
font-family: 'Space Mono', monospace;
|
||||
color: var(--text-color);
|
||||
line-height: 1.6;
|
||||
padding: 20px;
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* 卡片容器 */
|
||||
.card {
|
||||
background-color: var(--card-bg);
|
||||
background-image:
|
||||
linear-gradient(var(--grid-color-1) 1px, transparent 1px),
|
||||
linear-gradient(90deg, var(--grid-color-1) 1px, transparent 1px),
|
||||
linear-gradient(var(--grid-color-2) 0.5px, transparent 0.5px),
|
||||
linear-gradient(90deg, var(--grid-color-2) 0.5px, transparent 0.5px);
|
||||
background-size: 80px 80px, 80px 80px, 20px 20px, 20px 20px;
|
||||
background-position: center;
|
||||
border: 4px solid var(--border-color);
|
||||
box-shadow: 12px 12px 0 var(--shadow-color);
|
||||
margin-bottom: 40px;
|
||||
overflow: hidden;
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 16px 16px 0 var(--shadow-color);
|
||||
}
|
||||
|
||||
/* 卡片头部 */
|
||||
.card-header {
|
||||
background-color: rgba(var(--primary-color-rgb), 0.9);
|
||||
border-bottom: 4px solid var(--border-color);
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
color: var(--header-title-color);
|
||||
}
|
||||
|
||||
.card-controls {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 10px 20px;
|
||||
background: var(--secondary-color);
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
border: 3px solid var(--border-color);
|
||||
box-shadow: 4px 4px 0 var(--shadow-color);
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
font-family: 'Space Mono', monospace;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: var(--card-bg);
|
||||
color: var(--primary-color);
|
||||
border: 3px solid var(--primary-color);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: var(--light-bg);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: var(--card-bg);
|
||||
color: var(--primary-color);
|
||||
border: 3px solid var(--primary-color);
|
||||
}
|
||||
|
||||
/* 卡片内容 */
|
||||
.card-content {
|
||||
padding: 30px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background-color: rgba(var(--card-bg-rgb), 0.5);
|
||||
}
|
||||
|
||||
/* Logo区域 */
|
||||
.logo-container {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.logo-frame {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
background: var(--card-bg);
|
||||
border: 4px solid var(--border-color);
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.logo-image {
|
||||
width: 130px;
|
||||
height: 130px;
|
||||
object-fit: contain;
|
||||
border: 2px solid var(--border-color);
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tagline {
|
||||
font-size: 16px;
|
||||
margin: 10px 0 0;
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
/* 介绍区域 */
|
||||
.intro-box {
|
||||
border: 2px dashed var(--border-color);
|
||||
padding: 20px;
|
||||
background-color: rgba(var(--light-bg-rgb), 0.7);
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.intro-text {
|
||||
font-size: 16px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* 按钮组 */
|
||||
.button-group {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
/* 特性网格 */
|
||||
.features-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 30px;
|
||||
margin: 40px 0;
|
||||
}
|
||||
|
||||
/* 特性卡片 */
|
||||
.feature-card {
|
||||
background-color: rgba(var(--card-bg-rgb), 0.8);
|
||||
border: 3px solid var(--border-color);
|
||||
box-shadow: 5px 5px 0 var(--shadow-color);
|
||||
padding: 20px;
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.feature-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 7px 7px 0 var(--shadow-color);
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
font-size: 24px;
|
||||
margin-bottom: 15px;
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
|
||||
.feature-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.feature-desc {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 预览区域 */
|
||||
.preview-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.preview-container {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* 预览窗口 */
|
||||
.preview-window {
|
||||
border: 3px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
margin: 10px;
|
||||
flex: 1;
|
||||
min-width: 300px;
|
||||
background-color: rgba(var(--card-bg-rgb), 0.7);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 5px 5px 0 var(--shadow-color);
|
||||
}
|
||||
|
||||
/* 预览头部 */
|
||||
.preview-header {
|
||||
background-color: rgba(var(--preview-header-bg-rgb), 0.9);
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 2px solid var(--border-color);
|
||||
}
|
||||
|
||||
.preview-controls {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.preview-btn {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
border: 0.5px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.preview-btn:nth-child(1) {
|
||||
background-color: #ff5f56;
|
||||
}
|
||||
|
||||
.preview-btn:nth-child(2) {
|
||||
background-color: #ffbd2e;
|
||||
}
|
||||
|
||||
.preview-btn:nth-child(3) {
|
||||
background-color: #27c93f;
|
||||
}
|
||||
|
||||
.preview-title {
|
||||
font-size: 13px;
|
||||
opacity: 0.8;
|
||||
color: var(--text-color);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* 预览内容 */
|
||||
.preview-content {
|
||||
padding: 15px;
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
background-color: rgba(var(--code-bg-rgb), 0.5);
|
||||
}
|
||||
|
||||
/* 代码块容器 */
|
||||
.code-block-wrapper {
|
||||
background-color: rgba(var(--code-bg-rgb), 0.8);
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* 块头部 */
|
||||
.block-header {
|
||||
background-color: rgba(var(--light-bg-rgb), 0.8);
|
||||
padding: 8px 12px;
|
||||
border-bottom: 2px solid var(--border-color);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.block-language {
|
||||
color: rgba(128, 128, 128, 0.8);
|
||||
font-family: 'IBM Plex Mono', monospace;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.block-language::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 5px;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23888'%3E%3Cpath d='M9.7,16.7L5.3,12.3C4.9,11.9 4.9,11.1 5.3,10.7C5.7,10.3 6.3,10.3 6.7,10.7L10.5,14.5L17.3,7.7C17.7,7.3 18.3,7.3 18.7,7.7C19.1,8.1 19.1,8.7 18.7,9.1L11.3,16.7C10.9,17.1 10.1,17.1 9.7,16.7Z'/%3E%3C/svg%3E");
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.code-block {
|
||||
font-family: 'IBM Plex Mono', monospace;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
white-space: pre;
|
||||
tab-size: 4;
|
||||
-moz-tab-size: 4;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.theme-dark .code-block-wrapper {
|
||||
border-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
.theme-dark .block-header {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
border-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
.theme-dark .block-language {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.theme-dark .block-language::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23aaa'%3E%3Cpath d='M9.7,16.7L5.3,12.3C4.9,11.9 4.9,11.1 5.3,10.7C5.7,10.3 6.3,10.3 6.7,10.7L10.5,14.5L17.3,7.7C17.7,7.3 18.3,7.3 18.7,7.7C19.1,8.1 19.1,8.7 18.7,9.1L11.3,16.7C10.9,17.1 10.1,17.1 9.7,16.7Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.theme-dark .code-block {
|
||||
color: #d4d4d4;
|
||||
}
|
||||
|
||||
/* 代码高亮 */
|
||||
.theme-dark .keyword { color: #c586c0; }
|
||||
.theme-dark .function { color: #dcdcaa; }
|
||||
.theme-dark .variable { color: #9cdcfe; }
|
||||
.theme-dark .string { color: #ce9178; }
|
||||
.theme-dark .comment { color: #6a9955; }
|
||||
.theme-dark .class { color: #4ec9b0; }
|
||||
.theme-dark .parameter { color: #9cdcfe; }
|
||||
.theme-dark .built-in { color: #4ec9b0; }
|
||||
|
||||
/* 浅色主题代码高亮 */
|
||||
.keyword { color: #af00db; }
|
||||
.function { color: #795e26; }
|
||||
.variable { color: #001080; }
|
||||
.string { color: #a31515; }
|
||||
.comment { color: #008000; }
|
||||
.class { color: #267f99; }
|
||||
.parameter { color: #001080; }
|
||||
.built-in { color: #267f99; }
|
||||
|
||||
.preview-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
border: none;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.theme-dark .light-theme-img {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.theme-dark .dark-theme-img {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body:not(.theme-dark) .dark-theme-img {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
body:not(.theme-dark) .light-theme-img {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
/* 技术栈列表 */
|
||||
.tech-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 技术栈列表 */
|
||||
.tech-item {
|
||||
padding: 15px;
|
||||
margin-bottom: 15px;
|
||||
border: 2px solid var(--border-color);
|
||||
background-color: rgba(var(--light-bg-rgb), 0.7);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tech-icon {
|
||||
margin-right: 15px;
|
||||
color: var(--secondary-color);
|
||||
font-size: 20px;
|
||||
width: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tech-name {
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.tech-desc {
|
||||
font-size: 14px;
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
/* 页脚 */
|
||||
.footer {
|
||||
border-top: 2px solid var(--border-color);
|
||||
padding: 20px 0;
|
||||
margin-top: 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
background-color: transparent;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
margin: 0 0 15px 0;
|
||||
font-size: 14px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.footer-link {
|
||||
color: var(--secondary-color);
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
.footer-link:hover {
|
||||
color: var(--primary-color);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.button-group {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.features-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.card-header {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.card-controls {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.logo-frame {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.logo-image {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 针对移动设备的响应式优化 */
|
||||
@media (max-width: 768px) {
|
||||
body {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.card-controls {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
/* 预览区域优化 */
|
||||
.preview-content {
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.code-block {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.block-header {
|
||||
padding: 6px 10px;
|
||||
}
|
||||
|
||||
/* 日志界面导航链接优化 */
|
||||
.nav-links {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.nav-links .btn {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
/* 特性卡片优化 */
|
||||
.features-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
/* 预览窗口优化 */
|
||||
.preview-container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.preview-window {
|
||||
margin-bottom: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 技术栈列表小屏幕优化 */
|
||||
.tech-item {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.tech-desc {
|
||||
width: 100%;
|
||||
padding-left: 40px; /* 图标宽度+右边距 */
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
/* 日志界面资源列表项优化 */
|
||||
.asset-item {
|
||||
flex-wrap: wrap;
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.asset-name {
|
||||
width: 100%;
|
||||
word-break: break-all;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.asset-size {
|
||||
order: 2;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.download-btn {
|
||||
order: 3;
|
||||
margin-left: 0;
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
/* 页脚链接优化 */
|
||||
.footer {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
margin-top: 15px;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
BIN
docs/font/ibm-plex-mono/-F63fjptAgt5VM-kVkqdyU8n1i8q1w.woff2
Normal file
BIN
docs/font/ibm-plex-mono/-F63fjptAgt5VM-kVkqdyU8n1i8q1w.woff2
Normal file
Binary file not shown.
BIN
docs/font/ibm-plex-mono/-F63fjptAgt5VM-kVkqdyU8n1iAq129k.woff2
Normal file
BIN
docs/font/ibm-plex-mono/-F63fjptAgt5VM-kVkqdyU8n1iAq129k.woff2
Normal file
Binary file not shown.
BIN
docs/font/ibm-plex-mono/-F63fjptAgt5VM-kVkqdyU8n1iEq129k.woff2
Normal file
BIN
docs/font/ibm-plex-mono/-F63fjptAgt5VM-kVkqdyU8n1iEq129k.woff2
Normal file
Binary file not shown.
BIN
docs/font/ibm-plex-mono/-F63fjptAgt5VM-kVkqdyU8n1iIq129k.woff2
Normal file
BIN
docs/font/ibm-plex-mono/-F63fjptAgt5VM-kVkqdyU8n1iIq129k.woff2
Normal file
Binary file not shown.
BIN
docs/font/ibm-plex-mono/-F63fjptAgt5VM-kVkqdyU8n1isq129k.woff2
Normal file
BIN
docs/font/ibm-plex-mono/-F63fjptAgt5VM-kVkqdyU8n1isq129k.woff2
Normal file
Binary file not shown.
BIN
docs/font/space-mono/i7dPIFZifjKcF5UAWdDRYE58RWq7.woff2
Normal file
BIN
docs/font/space-mono/i7dPIFZifjKcF5UAWdDRYE58RWq7.woff2
Normal file
Binary file not shown.
BIN
docs/font/space-mono/i7dPIFZifjKcF5UAWdDRYE98RWq7.woff2
Normal file
BIN
docs/font/space-mono/i7dPIFZifjKcF5UAWdDRYE98RWq7.woff2
Normal file
Binary file not shown.
BIN
docs/font/space-mono/i7dPIFZifjKcF5UAWdDRYEF8RQ.woff2
Normal file
BIN
docs/font/space-mono/i7dPIFZifjKcF5UAWdDRYEF8RQ.woff2
Normal file
Binary file not shown.
BIN
docs/img/favicon.ico
Normal file
BIN
docs/img/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.9 KiB |
BIN
docs/img/logo.png
Normal file
BIN
docs/img/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.9 KiB |
BIN
docs/img/screenshot-dark.png
Normal file
BIN
docs/img/screenshot-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
BIN
docs/img/screenshot-light.png
Normal file
BIN
docs/img/screenshot-light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
256
docs/index.html
Normal file
256
docs/index.html
Normal file
@@ -0,0 +1,256 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>voidraft - An elegant text snippet recording tool designed for developers.</title>
|
||||
<meta name="description" content="voidraft is an elegant text snippet recording tool designed for developers. Features multi-language code blocks, syntax highlighting, code formatting, custom themes, and more.">
|
||||
<meta name="keywords" content="text editor, code snippets, developer tools, syntax highlighting, code formatting, multi-language, voidraft">
|
||||
<meta name="author" content="voidraft Team">
|
||||
<meta name="robots" content="index, follow">
|
||||
<link rel="canonical" href="https://landaiqing.github.io/voidraft/">
|
||||
|
||||
<!-- Internationalization / hreflang -->
|
||||
<link rel="alternate" hreflang="en" href="https://landaiqing.github.io/voidraft/">
|
||||
<link rel="alternate" hreflang="zh" href="https://landaiqing.github.io/voidraft/?lang=zh">
|
||||
<link rel="alternate" hreflang="x-default" href="https://landaiqing.github.io/voidraft/">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://landaiqing.github.io/voidraft/">
|
||||
<meta property="og:title" content="voidraft - An elegant text snippet recording tool designed for developers">
|
||||
<meta property="og:description" content="voidraft is an elegant text snippet recording tool designed for developers. Features multi-language code blocks, syntax highlighting, code formatting, custom themes, and more.">
|
||||
<meta property="og:image" content="https://landaiqing.github.io/voidraft/img/screenshot-dark.png">
|
||||
<meta property="og:site_name" content="voidraft">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:url" content="https://landaiqing.github.io/voidraft/">
|
||||
<meta property="twitter:title" content="voidraft - An elegant text snippet recording tool designed for developers">
|
||||
<meta property="twitter:description" content="voidraft is an elegant text snippet recording tool designed for developers. Features multi-language code blocks, syntax highlighting, code formatting, custom themes, and more.">
|
||||
<meta property="twitter:image" content="https://landaiqing.github.io/voidraft/img/screenshot-dark.png">
|
||||
|
||||
<link rel="stylesheet" href="./css/styles.css">
|
||||
<link rel="icon" href="./img/favicon.ico" type="image/x-icon">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
|
||||
<!-- Structured Data -->
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "SoftwareApplication",
|
||||
"name": "voidraft",
|
||||
"description": "An elegant text snippet recording tool designed for developers. Features multi-language code blocks, syntax highlighting, code formatting, custom themes, and more.",
|
||||
"url": "https://landaiqing.github.io/voidraft/",
|
||||
"downloadUrl": "https://github.com/landaiqing/voidraft/releases",
|
||||
"author": {
|
||||
"@type": "Organization",
|
||||
"name": "voidraft"
|
||||
},
|
||||
"operatingSystem": ["Windows", "macOS", "Linux"],
|
||||
"applicationCategory": "DeveloperApplication",
|
||||
"offers": {
|
||||
"@type": "Offer",
|
||||
"price": "0",
|
||||
"priceCurrency": "USD"
|
||||
},
|
||||
"screenshot": "https://landaiqing.github.io/voidraft/img/screenshot-dark.png",
|
||||
"softwareVersion": "Latest",
|
||||
"programmingLanguage": ["Go", "TypeScript", "Vue.js"],
|
||||
"codeRepository": "https://github.com/landaiqing/voidraft"
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body class="theme-dark">
|
||||
<div class="container">
|
||||
<!-- 主卡片 -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h1 class="card-title">voidraft</h1>
|
||||
<div class="card-controls">
|
||||
<button id="theme-toggle" class="btn btn-secondary" title="切换主题">
|
||||
<i class="fas fa-sun"></i> <span data-en="Theme" data-zh="主题">Theme</span>
|
||||
</button>
|
||||
<button id="lang-toggle" class="btn btn-secondary">
|
||||
<i class="fas fa-language"></i> 中/EN
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<!-- Logo和介绍 -->
|
||||
<div class="logo-container">
|
||||
<div class="logo-frame">
|
||||
<img src="img/logo.png" alt="voidraft Logo" class="logo-image">
|
||||
</div>
|
||||
<h2 class="logo-text" data-en="voidraft" data-zh="voidraft">voidraft</h2>
|
||||
<p class="tagline" data-en="An elegant text snippet recording tool" data-zh="优雅的文本片段记录工具">An elegant text snippet recording tool</p>
|
||||
</div>
|
||||
|
||||
<div class="intro-box">
|
||||
<p class="intro-text" data-en="Designed for developers to record, organize, and manage various text snippets anytime, anywhere." data-zh="专为开发者打造,随时随地记录、整理和管理各种文本片段。">Designed for developers to record, organize, and manage various text snippets anytime, anywhere.</p>
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
<a href="https://github.com/landaiqing/voidraft/releases" class="btn" data-en="Download" data-zh="下载">
|
||||
<i class="fas fa-download"></i> Download
|
||||
</a>
|
||||
<a href="https://github.com/landaiqing/voidraft" class="btn btn-secondary" data-en="Source Code" data-zh="源代码">
|
||||
<i class="fab fa-github"></i> Source Code
|
||||
</a>
|
||||
<a href="changelog.html" class="btn btn-secondary" data-en="Changelog" data-zh="更新日志">
|
||||
<i class="fas fa-history"></i> Changelog
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- 特性部分 -->
|
||||
<h2 data-en="Core Features" data-zh="核心特性">Core Features</h2>
|
||||
|
||||
<div class="features-grid">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-code"></i>
|
||||
</div>
|
||||
<h3 class="feature-title" data-en="Developer-Friendly" data-zh="开发者友好">Developer-Friendly</h3>
|
||||
<p class="feature-desc" data-en="Multi-language code blocks with syntax highlighting for 30+ programming languages" data-zh="多语言代码块支持,为30+种编程语言提供语法高亮">Multi-language code blocks with syntax highlighting for 30+ programming languages</p>
|
||||
</div>
|
||||
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-magic"></i>
|
||||
</div>
|
||||
<h3 class="feature-title" data-en="Code Formatting" data-zh="代码格式化">Code Formatting</h3>
|
||||
<p class="feature-desc" data-en="Built-in Prettier support for one-click code beautification" data-zh="内置Prettier支持,一键美化代码">Built-in Prettier support for one-click code beautification</p>
|
||||
</div>
|
||||
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-palette"></i>
|
||||
</div>
|
||||
<h3 class="feature-title" data-en="Custom Themes" data-zh="自定义主题">Custom Themes</h3>
|
||||
<p class="feature-desc" data-en="Dark/Light themes with full customization options" data-zh="深色/浅色主题,支持完全自定义">Dark/Light themes with full customization options</p>
|
||||
</div>
|
||||
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-clone"></i>
|
||||
</div>
|
||||
<h3 class="feature-title" data-en="Multi-Window" data-zh="多窗口支持">Multi-Window</h3>
|
||||
<p class="feature-desc" data-en="Edit multiple documents simultaneously" data-zh="同时编辑多个文档">Edit multiple documents simultaneously</p>
|
||||
</div>
|
||||
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-layer-group"></i>
|
||||
</div>
|
||||
<h3 class="feature-title" data-en="Block Editing" data-zh="块状编辑">Block Editing</h3>
|
||||
<p class="feature-desc" data-en="Split content into independent code blocks with different language settings" data-zh="将内容分割为独立的代码块,每个块可设置不同语言">Split content into independent code blocks with different language settings</p>
|
||||
</div>
|
||||
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-puzzle-piece"></i>
|
||||
</div>
|
||||
<h3 class="feature-title" data-en="Extensions" data-zh="丰富扩展">Extensions</h3>
|
||||
<p class="feature-desc" data-en="Rainbow brackets, VSCode-style search, color picker, translation tool, and more" data-zh="彩虹括号、VSCode风格搜索、颜色选择器、翻译工具等多种扩展">Rainbow brackets, VSCode-style search, color picker, translation tool, and more</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 预览部分 -->
|
||||
<h2 data-en="Preview" data-zh="预览">Preview</h2>
|
||||
|
||||
<div class="preview-container">
|
||||
<div class="preview-window">
|
||||
<div class="preview-header">
|
||||
<div class="preview-controls">
|
||||
<span class="preview-btn"></span>
|
||||
<span class="preview-btn"></span>
|
||||
<span class="preview-btn"></span>
|
||||
</div>
|
||||
<div class="preview-title">voidraft</div>
|
||||
</div>
|
||||
<div class="preview-content">
|
||||
<div class="code-block-wrapper">
|
||||
<div class="block-header">
|
||||
<div class="block-language">javascript</div>
|
||||
</div>
|
||||
<pre class="code-block">
|
||||
<span class="keyword">function</span> <span class="function">createDocument</span>() {
|
||||
<span class="keyword">const</span> <span class="variable">doc</span> = <span class="keyword">new</span> <span class="class">Document</span>();
|
||||
|
||||
<span class="variable">doc</span>.<span class="function">addCodeBlock</span>(<span class="string">'javascript'</span>, <span class="string">`
|
||||
<span class="keyword">function</span> <span class="function">greeting</span>(<span class="parameter">name</span>) {
|
||||
<span class="keyword">return</span> <span class="string">`Hello, </span>${<span class="parameter">name</span>}<span class="string">!`</span>;
|
||||
}
|
||||
|
||||
<span class="built-in">console</span>.<span class="function">log</span>(<span class="function">greeting</span>(<span class="string">'World'</span>));
|
||||
`</span>);
|
||||
|
||||
<span class="keyword">return</span> <span class="variable">doc</span>;
|
||||
}</pre>
|
||||
</div>
|
||||
|
||||
<div class="code-block-wrapper" style="margin-top: 10px;">
|
||||
<div class="block-header">
|
||||
<div class="block-language">text</div>
|
||||
</div>
|
||||
<pre class="code-block">
|
||||
<span class="comment">// voidraft - An elegant text snippet recording tool</span>
|
||||
<span class="comment">// Multi-language support | Code formatting | Custom themes</span>
|
||||
<span class="comment">// A modern text editor designed for developers</span></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="preview-window">
|
||||
<img src="img/screenshot-dark.png" alt="voidraft 界面预览" class="preview-image dark-theme-img">
|
||||
<img src="img/screenshot-light.png" alt="voidraft 界面预览" class="preview-image light-theme-img" style="display: none;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 技术栈部分 -->
|
||||
<h2 data-en="Technical Stack" data-zh="技术栈">Technical Stack</h2>
|
||||
|
||||
<ul class="tech-list">
|
||||
<li class="tech-item">
|
||||
<div class="tech-icon"><i class="fas fa-desktop"></i></div>
|
||||
<span class="tech-name">Wails3</span>
|
||||
<span class="tech-desc" data-en="Cross-platform desktop application framework" data-zh="跨平台桌面应用框架">Cross-platform desktop application framework</span>
|
||||
</li>
|
||||
<li class="tech-item">
|
||||
<div class="tech-icon"><i class="fas fa-cogs"></i></div>
|
||||
<span class="tech-name">Go 1.21+</span>
|
||||
<span class="tech-desc" data-en="Fast and efficient backend language" data-zh="快速高效的后端语言">Fast and efficient backend language</span>
|
||||
</li>
|
||||
<li class="tech-item">
|
||||
<div class="tech-icon"><i class="fab fa-vuejs"></i></div>
|
||||
<span class="tech-name">Vue 3 + TypeScript</span>
|
||||
<span class="tech-desc" data-en="Modern frontend framework" data-zh="现代化前端框架">Modern frontend framework</span>
|
||||
</li>
|
||||
<li class="tech-item">
|
||||
<div class="tech-icon"><i class="fas fa-edit"></i></div>
|
||||
<span class="tech-name">CodeMirror 6</span>
|
||||
<span class="tech-desc" data-en="Modern code editor with extension support" data-zh="支持扩展的现代化代码编辑器">Modern code editor with extension support</span>
|
||||
</li>
|
||||
<li class="tech-item">
|
||||
<div class="tech-icon"><i class="fas fa-database"></i></div>
|
||||
<span class="tech-name">SQLite</span>
|
||||
<span class="tech-desc" data-en="Lightweight database for document storage" data-zh="轻量级文档存储数据库">Lightweight database for document storage</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 页脚 -->
|
||||
<footer class="footer">
|
||||
<p class="footer-text" data-en="© 2025 voidraft - An elegant text snippet recording tool designed for developers" data-zh="© 2025 voidraft - 专为开发者打造的优雅文本片段记录工具">© 2025 voidraft - An elegant text snippet recording tool designed for developers</p>
|
||||
<div class="footer-links">
|
||||
<a href="https://github.com/landaiqing/voidraft" target="_blank" class="footer-link">GitHub</a>
|
||||
<a href="https://github.com/landaiqing/voidraft/issues" target="_blank" class="footer-link" data-en="Issues" data-zh="问题反馈">Issues</a>
|
||||
<a href="https://github.com/landaiqing/voidraft/releases" target="_blank" class="footer-link" data-en="Releases" data-zh="版本发布">Releases</a>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
705
docs/js/changelog.js
Normal file
705
docs/js/changelog.js
Normal file
@@ -0,0 +1,705 @@
|
||||
/**
|
||||
* voidraft - Changelog Script
|
||||
* 从GitHub API获取发布信息,支持Gitea备用源
|
||||
*/
|
||||
|
||||
/**
|
||||
* 仓库配置类
|
||||
*/
|
||||
class RepositoryConfig {
|
||||
constructor() {
|
||||
this.repos = {
|
||||
github: {
|
||||
owner: 'landaiqing',
|
||||
name: 'voidraft',
|
||||
apiUrl: 'https://api.github.com/repos/landaiqing/voidraft/releases',
|
||||
releasesUrl: 'https://github.com/landaiqing/voidraft/releases'
|
||||
},
|
||||
gitea: {
|
||||
owner: 'landaiqing',
|
||||
name: 'voidraft',
|
||||
domain: 'git.landaiqing.cn',
|
||||
apiUrl: 'https://git.landaiqing.cn/api/v1/repos/landaiqing/voidraft/releases',
|
||||
releasesUrl: 'https://git.landaiqing.cn/landaiqing/voidraft/releases'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取仓库配置
|
||||
* @param {string} source - 'github' 或 'gitea'
|
||||
*/
|
||||
getRepo(source) {
|
||||
return this.repos[source];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有仓库配置
|
||||
*/
|
||||
getAllRepos() {
|
||||
return this.repos;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 国际化消息管理类
|
||||
*/
|
||||
class I18nMessages {
|
||||
constructor() {
|
||||
this.messages = {
|
||||
loading: {
|
||||
en: 'Loading releases...',
|
||||
zh: '正在加载版本信息...'
|
||||
},
|
||||
noReleases: {
|
||||
en: 'No release information found',
|
||||
zh: '没有找到版本发布信息'
|
||||
},
|
||||
fetchError: {
|
||||
en: 'Failed to load release information. Please try again later.',
|
||||
zh: '无法获取版本信息,请稍后再试'
|
||||
},
|
||||
githubApiError: {
|
||||
en: 'GitHub API returned an error status: ',
|
||||
zh: 'GitHub API返回错误状态: '
|
||||
},
|
||||
giteaApiError: {
|
||||
en: 'Gitea API returned an error status: ',
|
||||
zh: 'Gitea API返回错误状态: '
|
||||
},
|
||||
dataSource: {
|
||||
en: 'Data source: ',
|
||||
zh: '数据来源: '
|
||||
},
|
||||
downloads: {
|
||||
en: 'Downloads',
|
||||
zh: '下载资源'
|
||||
},
|
||||
download: {
|
||||
en: 'Download',
|
||||
zh: '下载'
|
||||
},
|
||||
preRelease: {
|
||||
en: 'Pre-release',
|
||||
zh: '预发布'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取消息
|
||||
* @param {string} key - 消息键
|
||||
* @param {string} lang - 语言代码
|
||||
*/
|
||||
getMessage(key, lang = 'en') {
|
||||
return this.messages[key] && this.messages[key][lang] || this.messages[key]['en'] || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前语言
|
||||
*/
|
||||
getCurrentLang() {
|
||||
return window.currentLang || 'en';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* API客户端类
|
||||
*/
|
||||
class APIClient {
|
||||
constructor(repositoryConfig, i18nMessages) {
|
||||
this.repositoryConfig = repositoryConfig;
|
||||
this.i18nMessages = i18nMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从指定源获取发布信息
|
||||
* @param {string} source - 'github' 或 'gitea'
|
||||
*/
|
||||
async fetchReleases(source) {
|
||||
const repo = this.repositoryConfig.getRepo(source);
|
||||
const errorMessageKey = source === 'github' ? 'githubApiError' : 'giteaApiError';
|
||||
|
||||
const options = {
|
||||
headers: { 'Accept': 'application/json' }
|
||||
};
|
||||
|
||||
if (source === 'github') {
|
||||
return this.fetchFromGitHub(repo, options, errorMessageKey);
|
||||
} else {
|
||||
return this.fetchFromGitea(repo, options, errorMessageKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从GitHub获取数据
|
||||
* @param {Object} repo - 仓库配置
|
||||
* @param {Object} options - 请求选项
|
||||
* @param {string} errorMessageKey - 错误消息键
|
||||
*/
|
||||
async fetchFromGitHub(repo, options, errorMessageKey) {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 10000);
|
||||
|
||||
options.signal = controller.signal;
|
||||
options.headers['Accept'] = 'application/vnd.github.v3+json';
|
||||
|
||||
try {
|
||||
const response = await fetch(repo.apiUrl, options);
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`${this.i18nMessages.getMessage(errorMessageKey, this.i18nMessages.getCurrentLang())}${response.status}`);
|
||||
}
|
||||
|
||||
const releases = await response.json();
|
||||
|
||||
if (!releases || releases.length === 0) {
|
||||
throw new Error(this.i18nMessages.getMessage('noReleases', this.i18nMessages.getCurrentLang()));
|
||||
}
|
||||
|
||||
return releases;
|
||||
} catch (error) {
|
||||
clearTimeout(timeoutId);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从Gitea获取数据
|
||||
* @param {Object} repo - 仓库配置
|
||||
* @param {Object} options - 请求选项
|
||||
* @param {string} errorMessageKey - 错误消息键
|
||||
*/
|
||||
async fetchFromGitea(repo, options, errorMessageKey) {
|
||||
const response = await fetch(repo.apiUrl, options);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`${this.i18nMessages.getMessage(errorMessageKey, this.i18nMessages.getCurrentLang())}${response.status}`);
|
||||
}
|
||||
|
||||
const releases = await response.json();
|
||||
|
||||
if (!releases || releases.length === 0) {
|
||||
throw new Error(this.i18nMessages.getMessage('noReleases', this.i18nMessages.getCurrentLang()));
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UI管理类
|
||||
*/
|
||||
class UIManager {
|
||||
constructor(i18nMessages) {
|
||||
this.i18nMessages = i18nMessages;
|
||||
this.elements = {
|
||||
loading: document.getElementById('loading'),
|
||||
changelog: document.getElementById('changelog'),
|
||||
error: document.getElementById('error-message')
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示加载状态
|
||||
*/
|
||||
showLoading() {
|
||||
this.elements.loading.style.display = 'block';
|
||||
this.elements.error.style.display = 'none';
|
||||
this.elements.changelog.innerHTML = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏加载状态
|
||||
*/
|
||||
hideLoading() {
|
||||
this.elements.loading.style.display = 'none';
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示错误消息
|
||||
* @param {string} message - 错误消息
|
||||
*/
|
||||
showError(message) {
|
||||
const errorMessageElement = this.elements.error.querySelector('p');
|
||||
if (errorMessageElement) {
|
||||
errorMessageElement.textContent = message;
|
||||
} else {
|
||||
this.elements.error.textContent = message;
|
||||
}
|
||||
this.elements.error.style.display = 'block';
|
||||
this.hideLoading();
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示发布信息
|
||||
* @param {Array} releases - 发布信息数组
|
||||
* @param {string} source - 数据源
|
||||
*/
|
||||
displayReleases(releases, source) {
|
||||
this.hideLoading();
|
||||
|
||||
// 清除现有内容
|
||||
this.elements.changelog.innerHTML = '';
|
||||
|
||||
// 创建数据源元素
|
||||
const sourceElement = this.createSourceElement(source);
|
||||
this.elements.changelog.appendChild(sourceElement);
|
||||
|
||||
// 创建发布信息元素
|
||||
releases.forEach(release => {
|
||||
const releaseElement = this.createReleaseElement(release, source);
|
||||
this.elements.changelog.appendChild(releaseElement);
|
||||
});
|
||||
|
||||
this.elements.changelog.style.display = 'block';
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建数据源元素
|
||||
* @param {string} source - 数据源
|
||||
*/
|
||||
createSourceElement(source) {
|
||||
const sourceElement = document.createElement('div');
|
||||
sourceElement.className = 'data-source';
|
||||
|
||||
// 创建带有国际化支持的源标签
|
||||
const sourceLabel = document.createElement('span');
|
||||
sourceLabel.setAttribute('data-en', this.i18nMessages.getMessage('dataSource', 'en'));
|
||||
sourceLabel.setAttribute('data-zh', this.i18nMessages.getMessage('dataSource', 'zh'));
|
||||
sourceLabel.textContent = this.i18nMessages.getMessage('dataSource', this.i18nMessages.getCurrentLang());
|
||||
|
||||
// 创建链接
|
||||
const sourceLink = document.createElement('a');
|
||||
const repositoryConfig = new RepositoryConfig();
|
||||
sourceLink.href = repositoryConfig.getRepo(source).releasesUrl;
|
||||
sourceLink.textContent = source === 'github' ? 'GitHub' : 'Gitea';
|
||||
sourceLink.target = '_blank';
|
||||
|
||||
// 组装元素
|
||||
sourceElement.appendChild(sourceLabel);
|
||||
sourceElement.appendChild(sourceLink);
|
||||
|
||||
return sourceElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建发布信息元素
|
||||
* @param {Object} release - 发布信息对象
|
||||
* @param {string} source - 数据源
|
||||
*/
|
||||
createReleaseElement(release, source) {
|
||||
const releaseElement = document.createElement('div');
|
||||
releaseElement.className = 'release';
|
||||
|
||||
// 格式化发布日期
|
||||
const releaseDate = new Date(release.published_at || release.created_at);
|
||||
const formattedDate = DateFormatter.formatDate(releaseDate);
|
||||
|
||||
// 创建头部
|
||||
const headerElement = this.createReleaseHeader(release, formattedDate);
|
||||
releaseElement.appendChild(headerElement);
|
||||
|
||||
// 添加发布说明
|
||||
if (release.body) {
|
||||
const descriptionElement = document.createElement('div');
|
||||
descriptionElement.className = 'release-description markdown-content';
|
||||
descriptionElement.innerHTML = MarkdownParser.parseMarkdown(release.body);
|
||||
releaseElement.appendChild(descriptionElement);
|
||||
}
|
||||
|
||||
// 添加下载资源
|
||||
const assets = AssetManager.getAssetsFromRelease(release, source);
|
||||
if (assets && assets.length > 0) {
|
||||
const assetsElement = this.createAssetsElement(assets);
|
||||
releaseElement.appendChild(assetsElement);
|
||||
}
|
||||
|
||||
return releaseElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建发布信息头部
|
||||
*/
|
||||
createReleaseHeader(release, formattedDate) {
|
||||
const headerElement = document.createElement('div');
|
||||
headerElement.className = 'release-header';
|
||||
|
||||
// 版本元素
|
||||
const versionElement = document.createElement('div');
|
||||
versionElement.className = 'release-version';
|
||||
|
||||
// 版本文本
|
||||
const versionText = document.createElement('span');
|
||||
versionText.textContent = release.name || release.tag_name;
|
||||
versionElement.appendChild(versionText);
|
||||
|
||||
// 预发布标记
|
||||
if (release.prerelease) {
|
||||
const preReleaseTag = document.createElement('span');
|
||||
preReleaseTag.className = 'release-badge pre-release';
|
||||
preReleaseTag.setAttribute('data-en', this.i18nMessages.getMessage('preRelease', 'en'));
|
||||
preReleaseTag.setAttribute('data-zh', this.i18nMessages.getMessage('preRelease', 'zh'));
|
||||
preReleaseTag.textContent = this.i18nMessages.getMessage('preRelease', this.i18nMessages.getCurrentLang());
|
||||
versionElement.appendChild(preReleaseTag);
|
||||
}
|
||||
|
||||
// 日期元素
|
||||
const dateElement = document.createElement('div');
|
||||
dateElement.className = 'release-date';
|
||||
dateElement.textContent = formattedDate;
|
||||
|
||||
headerElement.appendChild(versionElement);
|
||||
headerElement.appendChild(dateElement);
|
||||
|
||||
return headerElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建资源文件元素
|
||||
* @param {Array} assets - 资源文件数组
|
||||
*/
|
||||
createAssetsElement(assets) {
|
||||
const assetsElement = document.createElement('div');
|
||||
assetsElement.className = 'release-assets';
|
||||
|
||||
// 资源标题
|
||||
const assetsTitle = document.createElement('div');
|
||||
assetsTitle.className = 'release-assets-title';
|
||||
assetsTitle.setAttribute('data-en', this.i18nMessages.getMessage('downloads', 'en'));
|
||||
assetsTitle.setAttribute('data-zh', this.i18nMessages.getMessage('downloads', 'zh'));
|
||||
assetsTitle.textContent = this.i18nMessages.getMessage('downloads', this.i18nMessages.getCurrentLang());
|
||||
|
||||
// 资源列表
|
||||
const assetList = document.createElement('ul');
|
||||
assetList.className = 'asset-list';
|
||||
|
||||
// 添加每个资源
|
||||
assets.forEach(asset => {
|
||||
const assetItem = this.createAssetItem(asset);
|
||||
assetList.appendChild(assetItem);
|
||||
});
|
||||
|
||||
assetsElement.appendChild(assetsTitle);
|
||||
assetsElement.appendChild(assetList);
|
||||
|
||||
return assetsElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建资源文件项
|
||||
* @param {Object} asset - 资源文件对象
|
||||
*/
|
||||
createAssetItem(asset) {
|
||||
const assetItem = document.createElement('li');
|
||||
assetItem.className = 'asset-item';
|
||||
|
||||
// 文件图标
|
||||
const iconElement = document.createElement('i');
|
||||
iconElement.className = `asset-icon fas fa-${FileIconHelper.getFileIcon(asset.name)}`;
|
||||
|
||||
// 文件名
|
||||
const nameElement = document.createElement('span');
|
||||
nameElement.className = 'asset-name';
|
||||
nameElement.textContent = asset.name;
|
||||
|
||||
// 文件大小
|
||||
const sizeElement = document.createElement('span');
|
||||
sizeElement.className = 'asset-size';
|
||||
sizeElement.textContent = FileSizeFormatter.formatFileSize(asset.size);
|
||||
|
||||
// 下载链接
|
||||
const downloadLink = document.createElement('a');
|
||||
downloadLink.className = 'download-btn';
|
||||
downloadLink.href = asset.browser_download_url;
|
||||
downloadLink.target = '_blank';
|
||||
downloadLink.setAttribute('data-en', this.i18nMessages.getMessage('download', 'en'));
|
||||
downloadLink.setAttribute('data-zh', this.i18nMessages.getMessage('download', 'zh'));
|
||||
downloadLink.textContent = this.i18nMessages.getMessage('download', this.i18nMessages.getCurrentLang());
|
||||
|
||||
// 组装资源项
|
||||
assetItem.appendChild(iconElement);
|
||||
assetItem.appendChild(nameElement);
|
||||
assetItem.appendChild(sizeElement);
|
||||
assetItem.appendChild(downloadLink);
|
||||
|
||||
return assetItem;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源管理器类
|
||||
*/
|
||||
class AssetManager {
|
||||
/**
|
||||
* 从发布信息中获取资源文件
|
||||
* @param {Object} release - 发布信息对象
|
||||
* @param {string} source - 数据源
|
||||
*/
|
||||
static getAssetsFromRelease(release, source) {
|
||||
let assets = [];
|
||||
|
||||
if (source === 'github') {
|
||||
assets = release.assets || [];
|
||||
} else { // Gitea
|
||||
assets = release.assets || [];
|
||||
// 检查Gitea特定的资源结构
|
||||
if (!assets.length && release.attachments) {
|
||||
assets = release.attachments.map(attachment => ({
|
||||
name: attachment.name,
|
||||
size: attachment.size,
|
||||
browser_download_url: attachment.browser_download_url
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
return assets;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件图标助手类
|
||||
*/
|
||||
class FileIconHelper {
|
||||
/**
|
||||
* 根据文件扩展名获取图标
|
||||
* @param {string} filename - 文件名
|
||||
*/
|
||||
static getFileIcon(filename) {
|
||||
const extension = filename.split('.').pop().toLowerCase();
|
||||
|
||||
const iconMap = {
|
||||
'exe': 'download',
|
||||
'msi': 'download',
|
||||
'dmg': 'download',
|
||||
'pkg': 'download',
|
||||
'deb': 'download',
|
||||
'rpm': 'download',
|
||||
'tar': 'file-archive',
|
||||
'gz': 'file-archive',
|
||||
'zip': 'file-archive',
|
||||
'7z': 'file-archive',
|
||||
'rar': 'file-archive',
|
||||
'pdf': 'file-pdf',
|
||||
'txt': 'file-alt',
|
||||
'md': 'file-alt',
|
||||
'json': 'file-code',
|
||||
'xml': 'file-code',
|
||||
'yml': 'file-code',
|
||||
'yaml': 'file-code'
|
||||
};
|
||||
|
||||
return iconMap[extension] || 'file';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件大小格式化器类
|
||||
*/
|
||||
class FileSizeFormatter {
|
||||
/**
|
||||
* 格式化文件大小
|
||||
* @param {number} bytes - 字节数
|
||||
*/
|
||||
static formatFileSize(bytes) {
|
||||
if (!bytes) return '';
|
||||
|
||||
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||
|
||||
return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期格式化器类
|
||||
*/
|
||||
class DateFormatter {
|
||||
/**
|
||||
* 格式化日期
|
||||
* @param {Date} date - 日期对象
|
||||
*/
|
||||
static formatDate(date) {
|
||||
const options = {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
};
|
||||
|
||||
const lang = window.currentLang || 'en';
|
||||
const locale = lang === 'zh' ? 'zh-CN' : 'en-US';
|
||||
|
||||
return date.toLocaleDateString(locale, options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Markdown解析器类
|
||||
*/
|
||||
class MarkdownParser {
|
||||
/**
|
||||
* 简单的Markdown解析
|
||||
* @param {string} markdown - Markdown文本
|
||||
*/
|
||||
static parseMarkdown(markdown) {
|
||||
if (!markdown) return '';
|
||||
|
||||
// 预处理:保留原始换行符,用特殊标记替换
|
||||
const preservedLineBreaks = '___LINE_BREAK___';
|
||||
markdown = markdown.replace(/\n/g, preservedLineBreaks);
|
||||
|
||||
// 引用块 - > text
|
||||
markdown = markdown.replace(/>\s*(.*?)(?=>|$)/g, '<blockquote>$1</blockquote>');
|
||||
markdown = markdown.replace(/>\s*(.*?)(?=>|$)/g, '<blockquote>$1</blockquote>');
|
||||
|
||||
// 链接 - [text](url)
|
||||
markdown = markdown.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>');
|
||||
|
||||
// 标题 - # Heading
|
||||
markdown = markdown.replace(/^### (.*?)(?=___LINE_BREAK___|$)/gm, '<h3>$1</h3>');
|
||||
markdown = markdown.replace(/^## (.*?)(?=___LINE_BREAK___|$)/gm, '<h2>$1</h2>');
|
||||
markdown = markdown.replace(/^# (.*?)(?=___LINE_BREAK___|$)/gm, '<h1>$1</h1>');
|
||||
|
||||
// 粗体 - **text**
|
||||
markdown = markdown.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
|
||||
|
||||
// 斜体 - *text*
|
||||
markdown = markdown.replace(/\*(.*?)\*/g, '<em>$1</em>');
|
||||
|
||||
// 代码块 - ```code```
|
||||
markdown = markdown.replace(/```([\s\S]*?)```/g, '<pre><code>$1</code></pre>');
|
||||
|
||||
// 行内代码 - `code`
|
||||
markdown = markdown.replace(/`([^`]+)`/g, '<code>$1</code>');
|
||||
|
||||
// 处理列表项
|
||||
// 先将每个列表项转换为HTML
|
||||
markdown = markdown.replace(/- (.*?)(?=___LINE_BREAK___- |___LINE_BREAK___$|$)/g, '<li>$1</li>');
|
||||
markdown = markdown.replace(/\* (.*?)(?=___LINE_BREAK___\* |___LINE_BREAK___$|$)/g, '<li>$1</li>');
|
||||
markdown = markdown.replace(/\d+\. (.*?)(?=___LINE_BREAK___\d+\. |___LINE_BREAK___$|$)/g, '<li>$1</li>');
|
||||
|
||||
// 然后将连续的列表项包装在ul或ol中
|
||||
const listItemRegex = /<li>.*?<\/li>/g;
|
||||
const listItems = markdown.match(listItemRegex) || [];
|
||||
|
||||
if (listItems.length > 0) {
|
||||
// 将连续的列表项组合在一起
|
||||
let lastIndex = 0;
|
||||
let result = '';
|
||||
let inList = false;
|
||||
|
||||
listItems.forEach(item => {
|
||||
const itemIndex = markdown.indexOf(item, lastIndex);
|
||||
|
||||
// 添加列表项之前的内容
|
||||
if (itemIndex > lastIndex) {
|
||||
result += markdown.substring(lastIndex, itemIndex);
|
||||
}
|
||||
|
||||
// 如果不在列表中,开始一个新列表
|
||||
if (!inList) {
|
||||
result += '<ul>';
|
||||
inList = true;
|
||||
}
|
||||
|
||||
// 添加列表项
|
||||
result += item;
|
||||
|
||||
// 更新lastIndex
|
||||
lastIndex = itemIndex + item.length;
|
||||
|
||||
// 检查下一个内容是否是列表项
|
||||
const nextItemIndex = markdown.indexOf('<li>', lastIndex);
|
||||
if (nextItemIndex === -1 || nextItemIndex > lastIndex + 20) { // 如果下一个列表项不紧邻
|
||||
result += '</ul>';
|
||||
inList = false;
|
||||
}
|
||||
});
|
||||
|
||||
// 添加剩余内容
|
||||
if (lastIndex < markdown.length) {
|
||||
result += markdown.substring(lastIndex);
|
||||
}
|
||||
|
||||
markdown = result;
|
||||
}
|
||||
|
||||
// 处理水平分隔线
|
||||
markdown = markdown.replace(/---/g, '<hr>');
|
||||
|
||||
// 恢复换行符
|
||||
markdown = markdown.replace(/___LINE_BREAK___/g, '<br>');
|
||||
|
||||
// 处理段落
|
||||
markdown = markdown.replace(/<br><br>/g, '</p><p>');
|
||||
|
||||
// 包装在段落标签中
|
||||
if (!markdown.startsWith('<p>')) {
|
||||
markdown = `<p>${markdown}</p>`;
|
||||
}
|
||||
|
||||
return markdown;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新日志主应用类
|
||||
*/
|
||||
class ChangelogApp {
|
||||
constructor() {
|
||||
this.repositoryConfig = new RepositoryConfig();
|
||||
this.i18nMessages = new I18nMessages();
|
||||
this.apiClient = new APIClient(this.repositoryConfig, this.i18nMessages);
|
||||
this.uiManager = new UIManager(this.i18nMessages);
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化应用
|
||||
*/
|
||||
init() {
|
||||
this.uiManager.showLoading();
|
||||
|
||||
// 首先尝试GitHub API
|
||||
this.apiClient.fetchReleases('github')
|
||||
.then(releases => {
|
||||
this.uiManager.displayReleases(releases, 'github');
|
||||
})
|
||||
.catch(() => {
|
||||
// GitHub失败时尝试Gitea
|
||||
return this.apiClient.fetchReleases('gitea')
|
||||
.then(releases => {
|
||||
this.uiManager.displayReleases(releases, 'gitea');
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('获取发布信息失败:', error);
|
||||
this.uiManager.showError(this.i18nMessages.getMessage('fetchError', this.i18nMessages.getCurrentLang()));
|
||||
});
|
||||
|
||||
// 监听语言变化事件
|
||||
document.addEventListener('languageChanged', () => this.updateUI());
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新UI元素(当语言变化时)
|
||||
*/
|
||||
updateUI() {
|
||||
const elementsToUpdate = document.querySelectorAll('[data-en][data-zh]');
|
||||
const currentLang = this.i18nMessages.getCurrentLang();
|
||||
|
||||
elementsToUpdate.forEach(element => {
|
||||
const text = element.getAttribute(`data-${currentLang}`);
|
||||
if (text) {
|
||||
element.textContent = text;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 当DOM加载完成时初始化应用
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
new ChangelogApp();
|
||||
});
|
||||
443
docs/js/script.js
Normal file
443
docs/js/script.js
Normal file
@@ -0,0 +1,443 @@
|
||||
/**
|
||||
* voidraft - Website Script
|
||||
*/
|
||||
|
||||
/**
|
||||
* 主题管理类
|
||||
*/
|
||||
class ThemeManager {
|
||||
constructor() {
|
||||
this.themeToggle = document.getElementById('theme-toggle');
|
||||
this.currentTheme = this.getInitialTheme();
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取初始主题
|
||||
*/
|
||||
getInitialTheme() {
|
||||
const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
const savedTheme = localStorage.getItem('theme');
|
||||
return savedTheme || (prefersDarkScheme.matches ? 'dark' : 'light');
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化主题管理器
|
||||
*/
|
||||
init() {
|
||||
if (!this.themeToggle) return;
|
||||
|
||||
this.setTheme(this.currentTheme);
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定事件
|
||||
*/
|
||||
bindEvents() {
|
||||
this.themeToggle.addEventListener('click', () => {
|
||||
this.toggleTheme();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换主题
|
||||
*/
|
||||
toggleTheme() {
|
||||
document.body.classList.add('theme-transition');
|
||||
|
||||
const newTheme = this.currentTheme === 'dark' ? 'light' : 'dark';
|
||||
this.setTheme(newTheme);
|
||||
this.saveTheme(newTheme);
|
||||
|
||||
setTimeout(() => document.body.classList.remove('theme-transition'), 300);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置主题
|
||||
* @param {string} theme - 'dark' 或 'light'
|
||||
*/
|
||||
setTheme(theme) {
|
||||
this.currentTheme = theme;
|
||||
const isDark = theme === 'dark';
|
||||
|
||||
document.body.classList.toggle('theme-dark', isDark);
|
||||
document.body.classList.toggle('theme-light', !isDark);
|
||||
|
||||
this.updateToggleIcon(isDark);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新切换按钮图标
|
||||
* @param {boolean} isDark - 是否为暗色主题
|
||||
*/
|
||||
updateToggleIcon(isDark) {
|
||||
if (this.themeToggle) {
|
||||
const icon = this.themeToggle.querySelector('i');
|
||||
if (icon) {
|
||||
icon.className = isDark ? 'fas fa-sun' : 'fas fa-moon';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存主题到本地存储
|
||||
* @param {string} theme - 主题名称
|
||||
*/
|
||||
saveTheme(theme) {
|
||||
localStorage.setItem('theme', theme);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 语言管理类
|
||||
*/
|
||||
class LanguageManager {
|
||||
constructor() {
|
||||
this.langToggle = document.getElementById('lang-toggle');
|
||||
this.currentLang = this.getInitialLanguage();
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取初始语言
|
||||
*/
|
||||
getInitialLanguage() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const urlLang = urlParams.get('lang');
|
||||
const savedLang = localStorage.getItem('lang');
|
||||
const browserLang = navigator.language.startsWith('zh') ? 'zh' : 'en';
|
||||
return urlLang || savedLang || browserLang;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化语言管理器
|
||||
*/
|
||||
init() {
|
||||
if (!this.langToggle) return;
|
||||
|
||||
window.currentLang = this.currentLang;
|
||||
this.setLanguage(this.currentLang);
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定事件
|
||||
*/
|
||||
bindEvents() {
|
||||
this.langToggle.addEventListener('click', () => {
|
||||
this.toggleLanguage();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换语言
|
||||
*/
|
||||
toggleLanguage() {
|
||||
document.body.classList.add('lang-transition');
|
||||
|
||||
const newLang = this.currentLang === 'zh' ? 'en' : 'zh';
|
||||
this.setLanguage(newLang);
|
||||
this.saveLanguage(newLang);
|
||||
this.updateURL(newLang);
|
||||
this.notifyLanguageChange(newLang);
|
||||
|
||||
setTimeout(() => document.body.classList.remove('lang-transition'), 300);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置页面语言
|
||||
* @param {string} lang - 'zh' 或 'en'
|
||||
*/
|
||||
setLanguage(lang) {
|
||||
this.currentLang = lang;
|
||||
window.currentLang = lang;
|
||||
|
||||
this.updatePageElements(lang);
|
||||
this.updateHTMLLang(lang);
|
||||
this.updateToggleButton(lang);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新页面元素文本
|
||||
* @param {string} lang - 语言代码
|
||||
*/
|
||||
updatePageElements(lang) {
|
||||
document.querySelectorAll('[data-zh][data-en]').forEach(el => {
|
||||
el.textContent = el.getAttribute(`data-${lang}`);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新HTML语言属性
|
||||
* @param {string} lang - 语言代码
|
||||
*/
|
||||
updateHTMLLang(lang) {
|
||||
document.documentElement.lang = lang === 'zh' ? 'zh-CN' : 'en';
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新切换按钮文本
|
||||
* @param {string} lang - 语言代码
|
||||
*/
|
||||
updateToggleButton(lang) {
|
||||
if (this.langToggle) {
|
||||
const text = lang === 'zh' ? 'EN/中' : '中/EN';
|
||||
this.langToggle.innerHTML = `<i class="fas fa-language"></i> ${text}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存语言到本地存储
|
||||
* @param {string} lang - 语言代码
|
||||
*/
|
||||
saveLanguage(lang) {
|
||||
localStorage.setItem('lang', lang);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新URL参数
|
||||
* @param {string} lang - 语言代码
|
||||
*/
|
||||
updateURL(lang) {
|
||||
const newUrl = new URL(window.location);
|
||||
if (lang === 'zh') {
|
||||
newUrl.searchParams.set('lang', 'zh');
|
||||
} else {
|
||||
newUrl.searchParams.delete('lang');
|
||||
}
|
||||
window.history.replaceState({}, '', newUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知语言变更
|
||||
* @param {string} lang - 语言代码
|
||||
*/
|
||||
notifyLanguageChange(lang) {
|
||||
window.dispatchEvent(new CustomEvent('languageChanged', { detail: { lang } }));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前语言
|
||||
*/
|
||||
getCurrentLanguage() {
|
||||
return this.currentLang;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SEO管理类
|
||||
*/
|
||||
class SEOManager {
|
||||
constructor(languageManager) {
|
||||
this.languageManager = languageManager;
|
||||
this.metaTexts = {
|
||||
en: {
|
||||
description: 'voidraft is an elegant text snippet recording tool designed for developers. Features multi-language code blocks, syntax highlighting, code formatting, custom themes, and more.',
|
||||
title: 'voidraft - An elegant text snippet recording tool designed for developers.',
|
||||
ogTitle: 'voidraft - An elegant text snippet recording tool designed for developers'
|
||||
},
|
||||
zh: {
|
||||
description: 'voidraft 是专为开发者打造的优雅文本片段记录工具。支持多语言代码块、语法高亮、代码格式化、自定义主题等功能。',
|
||||
title: 'voidraft - 专为开发者打造的优雅文本片段记录工具',
|
||||
ogTitle: 'voidraft - 专为开发者打造的优雅文本片段记录工具'
|
||||
}
|
||||
};
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化SEO管理器
|
||||
*/
|
||||
init() {
|
||||
this.bindEvents();
|
||||
this.updateMetaTags(this.languageManager.getCurrentLanguage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定事件
|
||||
*/
|
||||
bindEvents() {
|
||||
window.addEventListener('languageChanged', (event) => {
|
||||
this.updateMetaTags(event.detail.lang);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新SEO元标签
|
||||
* @param {string} lang - 当前语言
|
||||
*/
|
||||
updateMetaTags(lang) {
|
||||
const texts = this.metaTexts[lang];
|
||||
|
||||
this.updateMetaDescription(texts.description);
|
||||
this.updateOpenGraphTags(texts.ogTitle, texts.description);
|
||||
this.updateTwitterCardTags(texts.ogTitle, texts.description);
|
||||
this.updatePageTitle(texts.title);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新meta描述
|
||||
* @param {string} description - 描述文本
|
||||
*/
|
||||
updateMetaDescription(description) {
|
||||
const metaDesc = document.querySelector('meta[name="description"]');
|
||||
if (metaDesc) {
|
||||
metaDesc.content = description;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新Open Graph标签
|
||||
* @param {string} title - 标题
|
||||
* @param {string} description - 描述
|
||||
*/
|
||||
updateOpenGraphTags(title, description) {
|
||||
const ogTitle = document.querySelector('meta[property="og:title"]');
|
||||
const ogDesc = document.querySelector('meta[property="og:description"]');
|
||||
|
||||
if (ogTitle) ogTitle.content = title;
|
||||
if (ogDesc) ogDesc.content = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新Twitter Card标签
|
||||
* @param {string} title - 标题
|
||||
* @param {string} description - 描述
|
||||
*/
|
||||
updateTwitterCardTags(title, description) {
|
||||
const twitterTitle = document.querySelector('meta[property="twitter:title"]');
|
||||
const twitterDesc = document.querySelector('meta[property="twitter:description"]');
|
||||
|
||||
if (twitterTitle) twitterTitle.content = title;
|
||||
if (twitterDesc) twitterDesc.content = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新页面标题
|
||||
* @param {string} title - 标题
|
||||
*/
|
||||
updatePageTitle(title) {
|
||||
document.title = title;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UI效果管理类
|
||||
*/
|
||||
class UIEffects {
|
||||
constructor() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化UI效果
|
||||
*/
|
||||
init() {
|
||||
this.initCardEffects();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化卡片悬停效果
|
||||
*/
|
||||
initCardEffects() {
|
||||
const cards = document.querySelectorAll('.feature-card');
|
||||
|
||||
cards.forEach(card => {
|
||||
card.addEventListener('mouseenter', () => {
|
||||
this.animateCardHover(card, true);
|
||||
});
|
||||
|
||||
card.addEventListener('mouseleave', () => {
|
||||
this.animateCardHover(card, false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 卡片悬停动画
|
||||
* @param {Element} card - 卡片元素
|
||||
* @param {boolean} isHover - 是否悬停
|
||||
*/
|
||||
animateCardHover(card, isHover) {
|
||||
if (isHover) {
|
||||
card.style.transform = 'translateY(-8px)';
|
||||
card.style.boxShadow = '7px 7px 0 var(--shadow-color)';
|
||||
} else {
|
||||
card.style.transform = 'translateY(0)';
|
||||
card.style.boxShadow = '5px 5px 0 var(--shadow-color)';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* voidraft主应用类
|
||||
*/
|
||||
class voidraftApp {
|
||||
constructor() {
|
||||
this.themeManager = null;
|
||||
this.languageManager = null;
|
||||
this.seoManager = null;
|
||||
this.uiEffects = null;
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化应用
|
||||
*/
|
||||
init() {
|
||||
this.initializeManagers();
|
||||
this.showConsoleBranding();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化各个管理器
|
||||
*/
|
||||
initializeManagers() {
|
||||
this.themeManager = new ThemeManager();
|
||||
this.languageManager = new LanguageManager();
|
||||
this.seoManager = new SEOManager(this.languageManager);
|
||||
this.uiEffects = new UIEffects();
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示控制台品牌信息
|
||||
*/
|
||||
showConsoleBranding() {
|
||||
console.log('%c voidraft', 'color: #ff006e; font-size: 20px; font-family: "Space Mono", monospace;');
|
||||
console.log('%c An elegant text snippet recording tool designed for developers.', 'color: #073B4C; font-family: "Space Mono", monospace;');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取主题管理器
|
||||
*/
|
||||
getThemeManager() {
|
||||
return this.themeManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取语言管理器
|
||||
*/
|
||||
getLanguageManager() {
|
||||
return this.languageManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取SEO管理器
|
||||
*/
|
||||
getSEOManager() {
|
||||
return this.seoManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取UI效果管理器
|
||||
*/
|
||||
getUIEffects() {
|
||||
return this.uiEffects;
|
||||
}
|
||||
}
|
||||
|
||||
// 当DOM加载完成时初始化应用
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
window.voidRaftApp = new voidraftApp();
|
||||
});
|
||||
@@ -1,37 +0,0 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import {Create as $Create} from "@wailsio/runtime";
|
||||
|
||||
/**
|
||||
* DB is a database handle representing a pool of zero or more
|
||||
* underlying connections. It's safe for concurrent use by multiple
|
||||
* goroutines.
|
||||
*
|
||||
* The sql package creates and frees connections automatically; it
|
||||
* also maintains a free pool of idle connections. If the database has
|
||||
* a concept of per-connection state, such state can be reliably observed
|
||||
* within a transaction ([Tx]) or connection ([Conn]). Once [DB.Begin] is called, the
|
||||
* returned [Tx] is bound to a single connection. Once [Tx.Commit] or
|
||||
* [Tx.Rollback] is called on the transaction, that transaction's
|
||||
* connection is returned to [DB]'s idle connection pool. The pool size
|
||||
* can be controlled with [DB.SetMaxIdleConns].
|
||||
*/
|
||||
export class DB {
|
||||
|
||||
/** Creates a new DB instance. */
|
||||
constructor($$source: Partial<DB> = {}) {
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new DB instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): DB {
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
return new DB($$parsedSource as Partial<DB>);
|
||||
}
|
||||
}
|
||||
@@ -11,15 +11,55 @@ import * as slog$0 from "../../../../../../log/slog/models.js";
|
||||
|
||||
export class App {
|
||||
/**
|
||||
* The main application menu
|
||||
* Manager pattern for organized API
|
||||
*/
|
||||
"ApplicationMenu": Menu | null;
|
||||
"Window": WindowManager | null;
|
||||
"ContextMenu": ContextMenuManager | null;
|
||||
"KeyBinding": KeyBindingManager | null;
|
||||
"Browser": BrowserManager | null;
|
||||
"Env": EnvironmentManager | null;
|
||||
"Dialog": DialogManager | null;
|
||||
"Event": EventManager | null;
|
||||
"Menu": MenuManager | null;
|
||||
"Screen": ScreenManager | null;
|
||||
"Clipboard": ClipboardManager | null;
|
||||
"SystemTray": SystemTrayManager | null;
|
||||
"Logger": slog$0.Logger | null;
|
||||
|
||||
/** Creates a new App instance. */
|
||||
constructor($$source: Partial<App> = {}) {
|
||||
if (!("ApplicationMenu" in $$source)) {
|
||||
this["ApplicationMenu"] = null;
|
||||
if (!("Window" in $$source)) {
|
||||
this["Window"] = null;
|
||||
}
|
||||
if (!("ContextMenu" in $$source)) {
|
||||
this["ContextMenu"] = null;
|
||||
}
|
||||
if (!("KeyBinding" in $$source)) {
|
||||
this["KeyBinding"] = null;
|
||||
}
|
||||
if (!("Browser" in $$source)) {
|
||||
this["Browser"] = null;
|
||||
}
|
||||
if (!("Env" in $$source)) {
|
||||
this["Env"] = null;
|
||||
}
|
||||
if (!("Dialog" in $$source)) {
|
||||
this["Dialog"] = null;
|
||||
}
|
||||
if (!("Event" in $$source)) {
|
||||
this["Event"] = null;
|
||||
}
|
||||
if (!("Menu" in $$source)) {
|
||||
this["Menu"] = null;
|
||||
}
|
||||
if (!("Screen" in $$source)) {
|
||||
this["Screen"] = null;
|
||||
}
|
||||
if (!("Clipboard" in $$source)) {
|
||||
this["Clipboard"] = null;
|
||||
}
|
||||
if (!("SystemTray" in $$source)) {
|
||||
this["SystemTray"] = null;
|
||||
}
|
||||
if (!("Logger" in $$source)) {
|
||||
this["Logger"] = null;
|
||||
@@ -34,31 +74,308 @@ export class App {
|
||||
static createFrom($$source: any = {}): App {
|
||||
const $$createField0_0 = $$createType1;
|
||||
const $$createField1_0 = $$createType3;
|
||||
const $$createField2_0 = $$createType5;
|
||||
const $$createField3_0 = $$createType7;
|
||||
const $$createField4_0 = $$createType9;
|
||||
const $$createField5_0 = $$createType11;
|
||||
const $$createField6_0 = $$createType13;
|
||||
const $$createField7_0 = $$createType15;
|
||||
const $$createField8_0 = $$createType17;
|
||||
const $$createField9_0 = $$createType19;
|
||||
const $$createField10_0 = $$createType21;
|
||||
const $$createField11_0 = $$createType23;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("ApplicationMenu" in $$parsedSource) {
|
||||
$$parsedSource["ApplicationMenu"] = $$createField0_0($$parsedSource["ApplicationMenu"]);
|
||||
if ("Window" in $$parsedSource) {
|
||||
$$parsedSource["Window"] = $$createField0_0($$parsedSource["Window"]);
|
||||
}
|
||||
if ("ContextMenu" in $$parsedSource) {
|
||||
$$parsedSource["ContextMenu"] = $$createField1_0($$parsedSource["ContextMenu"]);
|
||||
}
|
||||
if ("KeyBinding" in $$parsedSource) {
|
||||
$$parsedSource["KeyBinding"] = $$createField2_0($$parsedSource["KeyBinding"]);
|
||||
}
|
||||
if ("Browser" in $$parsedSource) {
|
||||
$$parsedSource["Browser"] = $$createField3_0($$parsedSource["Browser"]);
|
||||
}
|
||||
if ("Env" in $$parsedSource) {
|
||||
$$parsedSource["Env"] = $$createField4_0($$parsedSource["Env"]);
|
||||
}
|
||||
if ("Dialog" in $$parsedSource) {
|
||||
$$parsedSource["Dialog"] = $$createField5_0($$parsedSource["Dialog"]);
|
||||
}
|
||||
if ("Event" in $$parsedSource) {
|
||||
$$parsedSource["Event"] = $$createField6_0($$parsedSource["Event"]);
|
||||
}
|
||||
if ("Menu" in $$parsedSource) {
|
||||
$$parsedSource["Menu"] = $$createField7_0($$parsedSource["Menu"]);
|
||||
}
|
||||
if ("Screen" in $$parsedSource) {
|
||||
$$parsedSource["Screen"] = $$createField8_0($$parsedSource["Screen"]);
|
||||
}
|
||||
if ("Clipboard" in $$parsedSource) {
|
||||
$$parsedSource["Clipboard"] = $$createField9_0($$parsedSource["Clipboard"]);
|
||||
}
|
||||
if ("SystemTray" in $$parsedSource) {
|
||||
$$parsedSource["SystemTray"] = $$createField10_0($$parsedSource["SystemTray"]);
|
||||
}
|
||||
if ("Logger" in $$parsedSource) {
|
||||
$$parsedSource["Logger"] = $$createField1_0($$parsedSource["Logger"]);
|
||||
$$parsedSource["Logger"] = $$createField11_0($$parsedSource["Logger"]);
|
||||
}
|
||||
return new App($$parsedSource as Partial<App>);
|
||||
}
|
||||
}
|
||||
|
||||
export class Menu {
|
||||
/**
|
||||
* BrowserManager manages browser-related operations
|
||||
*/
|
||||
export class BrowserManager {
|
||||
|
||||
/** Creates a new Menu instance. */
|
||||
constructor($$source: Partial<Menu> = {}) {
|
||||
/** Creates a new BrowserManager instance. */
|
||||
constructor($$source: Partial<BrowserManager> = {}) {
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Menu instance from a string or object.
|
||||
* Creates a new BrowserManager instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): Menu {
|
||||
static createFrom($$source: any = {}): BrowserManager {
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
return new Menu($$parsedSource as Partial<Menu>);
|
||||
return new BrowserManager($$parsedSource as Partial<BrowserManager>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ClipboardManager manages clipboard operations
|
||||
*/
|
||||
export class ClipboardManager {
|
||||
|
||||
/** Creates a new ClipboardManager instance. */
|
||||
constructor($$source: Partial<ClipboardManager> = {}) {
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ClipboardManager instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): ClipboardManager {
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
return new ClipboardManager($$parsedSource as Partial<ClipboardManager>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ContextMenuManager manages all context menu operations
|
||||
*/
|
||||
export class ContextMenuManager {
|
||||
|
||||
/** Creates a new ContextMenuManager instance. */
|
||||
constructor($$source: Partial<ContextMenuManager> = {}) {
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ContextMenuManager instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): ContextMenuManager {
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
return new ContextMenuManager($$parsedSource as Partial<ContextMenuManager>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DialogManager manages dialog-related operations
|
||||
*/
|
||||
export class DialogManager {
|
||||
|
||||
/** Creates a new DialogManager instance. */
|
||||
constructor($$source: Partial<DialogManager> = {}) {
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new DialogManager instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): DialogManager {
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
return new DialogManager($$parsedSource as Partial<DialogManager>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* EnvironmentManager manages environment-related operations
|
||||
*/
|
||||
export class EnvironmentManager {
|
||||
|
||||
/** Creates a new EnvironmentManager instance. */
|
||||
constructor($$source: Partial<EnvironmentManager> = {}) {
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new EnvironmentManager instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): EnvironmentManager {
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
return new EnvironmentManager($$parsedSource as Partial<EnvironmentManager>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* EventManager manages event-related operations
|
||||
*/
|
||||
export class EventManager {
|
||||
|
||||
/** Creates a new EventManager instance. */
|
||||
constructor($$source: Partial<EventManager> = {}) {
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new EventManager instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): EventManager {
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
return new EventManager($$parsedSource as Partial<EventManager>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* KeyBindingManager manages all key binding operations
|
||||
*/
|
||||
export class KeyBindingManager {
|
||||
|
||||
/** Creates a new KeyBindingManager instance. */
|
||||
constructor($$source: Partial<KeyBindingManager> = {}) {
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new KeyBindingManager instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): KeyBindingManager {
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
return new KeyBindingManager($$parsedSource as Partial<KeyBindingManager>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MenuManager manages menu-related operations
|
||||
*/
|
||||
export class MenuManager {
|
||||
|
||||
/** Creates a new MenuManager instance. */
|
||||
constructor($$source: Partial<MenuManager> = {}) {
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new MenuManager instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): MenuManager {
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
return new MenuManager($$parsedSource as Partial<MenuManager>);
|
||||
}
|
||||
}
|
||||
|
||||
export class ScreenManager {
|
||||
|
||||
/** Creates a new ScreenManager instance. */
|
||||
constructor($$source: Partial<ScreenManager> = {}) {
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ScreenManager instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): ScreenManager {
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
return new ScreenManager($$parsedSource as Partial<ScreenManager>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceOptions provides optional parameters for calls to [NewService].
|
||||
*/
|
||||
export class ServiceOptions {
|
||||
/**
|
||||
* Name can be set to override the name of the service
|
||||
* for logging and debugging purposes.
|
||||
*
|
||||
* If empty, it will default
|
||||
* either to the value obtained through the [ServiceName] interface,
|
||||
* or to the type name.
|
||||
*/
|
||||
"Name": string;
|
||||
|
||||
/**
|
||||
* If the service instance implements [http.Handler],
|
||||
* it will be mounted on the internal asset server
|
||||
* at the prefix specified by Route.
|
||||
*/
|
||||
"Route": string;
|
||||
|
||||
/**
|
||||
* MarshalError will be called if non-nil
|
||||
* to marshal to JSON the error values returned by this service's methods.
|
||||
*
|
||||
* MarshalError is not allowed to fail,
|
||||
* but it may return a nil slice to fall back
|
||||
* to the globally configured error handler.
|
||||
*
|
||||
* If the returned slice is not nil, it must contain valid JSON.
|
||||
*/
|
||||
"MarshalError": any;
|
||||
|
||||
/** Creates a new ServiceOptions instance. */
|
||||
constructor($$source: Partial<ServiceOptions> = {}) {
|
||||
if (!("Name" in $$source)) {
|
||||
this["Name"] = "";
|
||||
}
|
||||
if (!("Route" in $$source)) {
|
||||
this["Route"] = "";
|
||||
}
|
||||
if (!("MarshalError" in $$source)) {
|
||||
this["MarshalError"] = null;
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ServiceOptions instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): ServiceOptions {
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
return new ServiceOptions($$parsedSource as Partial<ServiceOptions>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SystemTrayManager manages system tray-related operations
|
||||
*/
|
||||
export class SystemTrayManager {
|
||||
|
||||
/** Creates a new SystemTrayManager instance. */
|
||||
constructor($$source: Partial<SystemTrayManager> = {}) {
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SystemTrayManager instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): SystemTrayManager {
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
return new SystemTrayManager($$parsedSource as Partial<SystemTrayManager>);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,8 +396,48 @@ export class WebviewWindow {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WindowManager manages all window-related operations
|
||||
*/
|
||||
export class WindowManager {
|
||||
|
||||
/** Creates a new WindowManager instance. */
|
||||
constructor($$source: Partial<WindowManager> = {}) {
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new WindowManager instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): WindowManager {
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
return new WindowManager($$parsedSource as Partial<WindowManager>);
|
||||
}
|
||||
}
|
||||
|
||||
// Private type creation functions
|
||||
const $$createType0 = Menu.createFrom;
|
||||
const $$createType0 = WindowManager.createFrom;
|
||||
const $$createType1 = $Create.Nullable($$createType0);
|
||||
const $$createType2 = slog$0.Logger.createFrom;
|
||||
const $$createType2 = ContextMenuManager.createFrom;
|
||||
const $$createType3 = $Create.Nullable($$createType2);
|
||||
const $$createType4 = KeyBindingManager.createFrom;
|
||||
const $$createType5 = $Create.Nullable($$createType4);
|
||||
const $$createType6 = BrowserManager.createFrom;
|
||||
const $$createType7 = $Create.Nullable($$createType6);
|
||||
const $$createType8 = EnvironmentManager.createFrom;
|
||||
const $$createType9 = $Create.Nullable($$createType8);
|
||||
const $$createType10 = DialogManager.createFrom;
|
||||
const $$createType11 = $Create.Nullable($$createType10);
|
||||
const $$createType12 = EventManager.createFrom;
|
||||
const $$createType13 = $Create.Nullable($$createType12);
|
||||
const $$createType14 = MenuManager.createFrom;
|
||||
const $$createType15 = $Create.Nullable($$createType14);
|
||||
const $$createType16 = ScreenManager.createFrom;
|
||||
const $$createType17 = $Create.Nullable($$createType16);
|
||||
const $$createType18 = ClipboardManager.createFrom;
|
||||
const $$createType19 = $Create.Nullable($$createType18);
|
||||
const $$createType20 = SystemTrayManager.createFrom;
|
||||
const $$createType21 = $Create.Nullable($$createType20);
|
||||
const $$createType22 = slog$0.Logger.createFrom;
|
||||
const $$createType23 = $Create.Nullable($$createType22);
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
/**
|
||||
* Service represents the notifications service
|
||||
* @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 "../../application/models.js";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as $models from "./models.js";
|
||||
|
||||
/**
|
||||
* RemoveBadge removes the badge label from the application icon.
|
||||
*/
|
||||
export function RemoveBadge(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2374916939) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceName returns the name of the service.
|
||||
*/
|
||||
export function ServiceName(): Promise<string> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2428202016) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceShutdown is called when the service is unloaded.
|
||||
*/
|
||||
export function ServiceShutdown(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3893755233) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceStartup is called when the service is loaded.
|
||||
*/
|
||||
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(4078800764, options) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* SetBadge sets the badge label on the application icon.
|
||||
*/
|
||||
export function SetBadge(label: string): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(784276339, label) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
export function SetCustomBadge(label: string, options: $models.Options): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3058653106, label, options) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
import * as BadgeService from "./badgeservice.js";
|
||||
export {
|
||||
BadgeService
|
||||
};
|
||||
|
||||
export * from "./models.js";
|
||||
@@ -0,0 +1,58 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import {Create as $Create} from "@wailsio/runtime";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as color$0 from "../../../../../../../image/color/models.js";
|
||||
|
||||
export class Options {
|
||||
"TextColour": color$0.RGBA;
|
||||
"BackgroundColour": color$0.RGBA;
|
||||
"FontName": string;
|
||||
"FontSize": number;
|
||||
"SmallFontSize": number;
|
||||
|
||||
/** Creates a new Options instance. */
|
||||
constructor($$source: Partial<Options> = {}) {
|
||||
if (!("TextColour" in $$source)) {
|
||||
this["TextColour"] = (new color$0.RGBA());
|
||||
}
|
||||
if (!("BackgroundColour" in $$source)) {
|
||||
this["BackgroundColour"] = (new color$0.RGBA());
|
||||
}
|
||||
if (!("FontName" in $$source)) {
|
||||
this["FontName"] = "";
|
||||
}
|
||||
if (!("FontSize" in $$source)) {
|
||||
this["FontSize"] = 0;
|
||||
}
|
||||
if (!("SmallFontSize" in $$source)) {
|
||||
this["SmallFontSize"] = 0;
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Options instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): Options {
|
||||
const $$createField0_0 = $$createType0;
|
||||
const $$createField1_0 = $$createType0;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("TextColour" in $$parsedSource) {
|
||||
$$parsedSource["TextColour"] = $$createField0_0($$parsedSource["TextColour"]);
|
||||
}
|
||||
if ("BackgroundColour" in $$parsedSource) {
|
||||
$$parsedSource["BackgroundColour"] = $$createField1_0($$parsedSource["BackgroundColour"]);
|
||||
}
|
||||
return new Options($$parsedSource as Partial<Options>);
|
||||
}
|
||||
}
|
||||
|
||||
// Private type creation functions
|
||||
const $$createType0 = color$0.RGBA.createFrom;
|
||||
@@ -0,0 +1,9 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
import * as NotificationService from "./notificationservice.js";
|
||||
export {
|
||||
NotificationService
|
||||
};
|
||||
|
||||
export * from "./models.js";
|
||||
@@ -0,0 +1,107 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import {Create as $Create} from "@wailsio/runtime";
|
||||
|
||||
/**
|
||||
* NotificationAction represents an action button for a notification.
|
||||
*/
|
||||
export class NotificationAction {
|
||||
"id"?: string;
|
||||
"title"?: string;
|
||||
|
||||
/**
|
||||
* (macOS-specific)
|
||||
*/
|
||||
"destructive"?: boolean;
|
||||
|
||||
/** Creates a new NotificationAction instance. */
|
||||
constructor($$source: Partial<NotificationAction> = {}) {
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new NotificationAction instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): NotificationAction {
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
return new NotificationAction($$parsedSource as Partial<NotificationAction>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* NotificationCategory groups actions for notifications.
|
||||
*/
|
||||
export class NotificationCategory {
|
||||
"id"?: string;
|
||||
"actions"?: NotificationAction[];
|
||||
"hasReplyField"?: boolean;
|
||||
"replyPlaceholder"?: string;
|
||||
"replyButtonTitle"?: string;
|
||||
|
||||
/** Creates a new NotificationCategory instance. */
|
||||
constructor($$source: Partial<NotificationCategory> = {}) {
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new NotificationCategory instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): NotificationCategory {
|
||||
const $$createField1_0 = $$createType1;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("actions" in $$parsedSource) {
|
||||
$$parsedSource["actions"] = $$createField1_0($$parsedSource["actions"]);
|
||||
}
|
||||
return new NotificationCategory($$parsedSource as Partial<NotificationCategory>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* NotificationOptions contains configuration for a notification
|
||||
*/
|
||||
export class NotificationOptions {
|
||||
"id": string;
|
||||
"title": string;
|
||||
|
||||
/**
|
||||
* (macOS and Linux only)
|
||||
*/
|
||||
"subtitle"?: string;
|
||||
"body"?: string;
|
||||
"categoryId"?: string;
|
||||
"data"?: { [_: string]: any };
|
||||
|
||||
/** Creates a new NotificationOptions instance. */
|
||||
constructor($$source: Partial<NotificationOptions> = {}) {
|
||||
if (!("id" in $$source)) {
|
||||
this["id"] = "";
|
||||
}
|
||||
if (!("title" in $$source)) {
|
||||
this["title"] = "";
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new NotificationOptions instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): NotificationOptions {
|
||||
const $$createField5_0 = $$createType2;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("data" in $$parsedSource) {
|
||||
$$parsedSource["data"] = $$createField5_0($$parsedSource["data"]);
|
||||
}
|
||||
return new NotificationOptions($$parsedSource as Partial<NotificationOptions>);
|
||||
}
|
||||
}
|
||||
|
||||
// Private type creation functions
|
||||
const $$createType0 = NotificationAction.createFrom;
|
||||
const $$createType1 = $Create.Array($$createType0);
|
||||
const $$createType2 = $Create.Map($Create.Any, $Create.Any);
|
||||
@@ -0,0 +1,110 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
/**
|
||||
* Service represents the notifications service
|
||||
* @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 "../../application/models.js";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as $models from "./models.js";
|
||||
|
||||
export function CheckNotificationAuthorization(): Promise<boolean> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2216952893) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* OnNotificationResponse registers a callback function that will be called when
|
||||
* a notification response is received from the user.
|
||||
*/
|
||||
export function OnNotificationResponse(callback: any): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1642697808, callback) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
export function RegisterNotificationCategory(category: $models.NotificationCategory): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2917562919, category) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
export function RemoveAllDeliveredNotifications(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3956282340) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
export function RemoveAllPendingNotifications(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(108821341) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
export function RemoveDeliveredNotification(identifier: string): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(975691940, identifier) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
export function RemoveNotification(identifier: string): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3966653866, identifier) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
export function RemoveNotificationCategory(categoryID: string): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2032615554, categoryID) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
export function RemovePendingNotification(identifier: string): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3729049703, identifier) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public methods that delegate to the implementation.
|
||||
*/
|
||||
export function RequestNotificationAuthorization(): Promise<boolean> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3933442950) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
export function SendNotification(options: $models.NotificationOptions): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3968228732, options) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
export function SendNotificationWithActions(options: $models.NotificationOptions): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1886542847, options) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceName returns the name of the service.
|
||||
*/
|
||||
export function ServiceName(): Promise<string> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2704532675) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceShutdown is called when the service is unloaded.
|
||||
*/
|
||||
export function ServiceShutdown(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2550195434) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceStartup is called when the service is loaded.
|
||||
*/
|
||||
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(4047820929, options) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
46
frontend/bindings/image/color/models.ts
Normal file
46
frontend/bindings/image/color/models.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import {Create as $Create} from "@wailsio/runtime";
|
||||
|
||||
/**
|
||||
* RGBA represents a traditional 32-bit alpha-premultiplied color, having 8
|
||||
* bits for each of red, green, blue and alpha.
|
||||
*
|
||||
* An alpha-premultiplied color component C has been scaled by alpha (A), so
|
||||
* has valid values 0 <= C <= A.
|
||||
*/
|
||||
export class RGBA {
|
||||
"R": number;
|
||||
"G": number;
|
||||
"B": number;
|
||||
"A": number;
|
||||
|
||||
/** Creates a new RGBA instance. */
|
||||
constructor($$source: Partial<RGBA> = {}) {
|
||||
if (!("R" in $$source)) {
|
||||
this["R"] = 0;
|
||||
}
|
||||
if (!("G" in $$source)) {
|
||||
this["G"] = 0;
|
||||
}
|
||||
if (!("B" in $$source)) {
|
||||
this["B"] = 0;
|
||||
}
|
||||
if (!("A" in $$source)) {
|
||||
this["A"] = 0;
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new RGBA instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): RGBA {
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
return new RGBA($$parsedSource as Partial<RGBA>);
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,11 @@ export class AppConfig {
|
||||
*/
|
||||
"updates": UpdatesConfig;
|
||||
|
||||
/**
|
||||
* Git备份设置
|
||||
*/
|
||||
"backup": GitBackupConfig;
|
||||
|
||||
/**
|
||||
* 配置元数据
|
||||
*/
|
||||
@@ -52,6 +57,9 @@ export class AppConfig {
|
||||
if (!("updates" in $$source)) {
|
||||
this["updates"] = (new UpdatesConfig());
|
||||
}
|
||||
if (!("backup" in $$source)) {
|
||||
this["backup"] = (new GitBackupConfig());
|
||||
}
|
||||
if (!("metadata" in $$source)) {
|
||||
this["metadata"] = (new ConfigMetadata());
|
||||
}
|
||||
@@ -68,6 +76,7 @@ export class AppConfig {
|
||||
const $$createField2_0 = $$createType2;
|
||||
const $$createField3_0 = $$createType3;
|
||||
const $$createField4_0 = $$createType4;
|
||||
const $$createField5_0 = $$createType5;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("general" in $$parsedSource) {
|
||||
$$parsedSource["general"] = $$createField0_0($$parsedSource["general"]);
|
||||
@@ -81,8 +90,11 @@ export class AppConfig {
|
||||
if ("updates" in $$parsedSource) {
|
||||
$$parsedSource["updates"] = $$createField3_0($$parsedSource["updates"]);
|
||||
}
|
||||
if ("backup" in $$parsedSource) {
|
||||
$$parsedSource["backup"] = $$createField4_0($$parsedSource["backup"]);
|
||||
}
|
||||
if ("metadata" in $$parsedSource) {
|
||||
$$parsedSource["metadata"] = $$createField4_0($$parsedSource["metadata"]);
|
||||
$$parsedSource["metadata"] = $$createField5_0($$parsedSource["metadata"]);
|
||||
}
|
||||
return new AppConfig($$parsedSource as Partial<AppConfig>);
|
||||
}
|
||||
@@ -123,6 +135,25 @@ export class AppearanceConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Git备份相关类型定义
|
||||
*
|
||||
* AuthMethod 定义Git认证方式
|
||||
*/
|
||||
export enum AuthMethod {
|
||||
/**
|
||||
* The Go zero value for the underlying type of the enum.
|
||||
*/
|
||||
$zero = "",
|
||||
|
||||
/**
|
||||
* 认证方式
|
||||
*/
|
||||
Token = "token",
|
||||
SSHKey = "ssh_key",
|
||||
UserPass = "user_pass",
|
||||
};
|
||||
|
||||
/**
|
||||
* ConfigMetadata 配置元数据
|
||||
*/
|
||||
@@ -169,6 +200,11 @@ export class Document {
|
||||
"updatedAt": time$0.Time;
|
||||
"is_deleted": boolean;
|
||||
|
||||
/**
|
||||
* 锁定标志,锁定的文档无法被删除
|
||||
*/
|
||||
"is_locked": boolean;
|
||||
|
||||
/** Creates a new Document instance. */
|
||||
constructor($$source: Partial<Document> = {}) {
|
||||
if (!("id" in $$source)) {
|
||||
@@ -189,6 +225,9 @@ export class Document {
|
||||
if (!("is_deleted" in $$source)) {
|
||||
this["is_deleted"] = false;
|
||||
}
|
||||
if (!("is_locked" in $$source)) {
|
||||
this["is_locked"] = false;
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
@@ -334,7 +373,7 @@ export class Extension {
|
||||
* Creates a new Extension instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): Extension {
|
||||
const $$createField3_0 = $$createType5;
|
||||
const $$createField3_0 = $$createType6;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("config" in $$parsedSource) {
|
||||
$$parsedSource["config"] = $$createField3_0($$parsedSource["config"]);
|
||||
@@ -428,6 +467,12 @@ export class GeneralConfig {
|
||||
*/
|
||||
"startAtLogin": boolean;
|
||||
|
||||
/**
|
||||
* 窗口吸附设置
|
||||
* 是否启用窗口吸附功能(阈值现在是自适应的)
|
||||
*/
|
||||
"enableWindowSnap": boolean;
|
||||
|
||||
/**
|
||||
* 全局热键设置
|
||||
* 是否启用全局热键
|
||||
@@ -453,6 +498,9 @@ export class GeneralConfig {
|
||||
if (!("startAtLogin" in $$source)) {
|
||||
this["startAtLogin"] = false;
|
||||
}
|
||||
if (!("enableWindowSnap" in $$source)) {
|
||||
this["enableWindowSnap"] = false;
|
||||
}
|
||||
if (!("enableGlobalHotkey" in $$source)) {
|
||||
this["enableGlobalHotkey"] = false;
|
||||
}
|
||||
@@ -467,15 +515,64 @@ export class GeneralConfig {
|
||||
* Creates a new GeneralConfig instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): GeneralConfig {
|
||||
const $$createField5_0 = $$createType7;
|
||||
const $$createField6_0 = $$createType8;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("globalHotkey" in $$parsedSource) {
|
||||
$$parsedSource["globalHotkey"] = $$createField5_0($$parsedSource["globalHotkey"]);
|
||||
$$parsedSource["globalHotkey"] = $$createField6_0($$parsedSource["globalHotkey"]);
|
||||
}
|
||||
return new GeneralConfig($$parsedSource as Partial<GeneralConfig>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GitBackupConfig Git备份配置
|
||||
*/
|
||||
export class GitBackupConfig {
|
||||
"enabled": boolean;
|
||||
"repo_url": string;
|
||||
"auth_method": AuthMethod;
|
||||
"username"?: string;
|
||||
"password"?: string;
|
||||
"token"?: string;
|
||||
"ssh_key_path"?: string;
|
||||
"ssh_key_passphrase"?: string;
|
||||
|
||||
/**
|
||||
* 分钟
|
||||
*/
|
||||
"backup_interval": number;
|
||||
"auto_backup": boolean;
|
||||
|
||||
/** Creates a new GitBackupConfig instance. */
|
||||
constructor($$source: Partial<GitBackupConfig> = {}) {
|
||||
if (!("enabled" in $$source)) {
|
||||
this["enabled"] = false;
|
||||
}
|
||||
if (!("repo_url" in $$source)) {
|
||||
this["repo_url"] = "";
|
||||
}
|
||||
if (!("auth_method" in $$source)) {
|
||||
this["auth_method"] = ("" as AuthMethod);
|
||||
}
|
||||
if (!("backup_interval" in $$source)) {
|
||||
this["backup_interval"] = 0;
|
||||
}
|
||||
if (!("auto_backup" in $$source)) {
|
||||
this["auto_backup"] = false;
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GitBackupConfig instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): GitBackupConfig {
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
return new GitBackupConfig($$parsedSource as Partial<GitBackupConfig>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GiteaConfig Gitea配置
|
||||
*/
|
||||
@@ -963,84 +1060,6 @@ export enum KeyBindingCommand {
|
||||
TextHighlightToggleCommand = "textHighlightToggle",
|
||||
};
|
||||
|
||||
/**
|
||||
* KeyBindingConfig 快捷键配置
|
||||
*/
|
||||
export class KeyBindingConfig {
|
||||
/**
|
||||
* 快捷键列表
|
||||
*/
|
||||
"keyBindings": KeyBinding[];
|
||||
|
||||
/**
|
||||
* 配置元数据
|
||||
*/
|
||||
"metadata": KeyBindingMetadata;
|
||||
|
||||
/** Creates a new KeyBindingConfig instance. */
|
||||
constructor($$source: Partial<KeyBindingConfig> = {}) {
|
||||
if (!("keyBindings" in $$source)) {
|
||||
this["keyBindings"] = [];
|
||||
}
|
||||
if (!("metadata" in $$source)) {
|
||||
this["metadata"] = (new KeyBindingMetadata());
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new KeyBindingConfig instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): KeyBindingConfig {
|
||||
const $$createField0_0 = $$createType9;
|
||||
const $$createField1_0 = $$createType10;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("keyBindings" in $$parsedSource) {
|
||||
$$parsedSource["keyBindings"] = $$createField0_0($$parsedSource["keyBindings"]);
|
||||
}
|
||||
if ("metadata" in $$parsedSource) {
|
||||
$$parsedSource["metadata"] = $$createField1_0($$parsedSource["metadata"]);
|
||||
}
|
||||
return new KeyBindingConfig($$parsedSource as Partial<KeyBindingConfig>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* KeyBindingMetadata 快捷键配置元数据
|
||||
*/
|
||||
export class KeyBindingMetadata {
|
||||
/**
|
||||
* 配置版本
|
||||
*/
|
||||
"version": string;
|
||||
|
||||
/**
|
||||
* 最后更新时间
|
||||
*/
|
||||
"lastUpdated": string;
|
||||
|
||||
/** Creates a new KeyBindingMetadata instance. */
|
||||
constructor($$source: Partial<KeyBindingMetadata> = {}) {
|
||||
if (!("version" in $$source)) {
|
||||
this["version"] = "";
|
||||
}
|
||||
if (!("lastUpdated" in $$source)) {
|
||||
this["lastUpdated"] = "";
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new KeyBindingMetadata instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): KeyBindingMetadata {
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
return new KeyBindingMetadata($$parsedSource as Partial<KeyBindingMetadata>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* LanguageType 语言类型定义
|
||||
*/
|
||||
@@ -1106,6 +1125,279 @@ export enum TabType {
|
||||
TabTypeTab = "tab",
|
||||
};
|
||||
|
||||
/**
|
||||
* Theme 主题数据库模型
|
||||
*/
|
||||
export class Theme {
|
||||
"id": number;
|
||||
"name": string;
|
||||
"type": ThemeType;
|
||||
"colors": ThemeColorConfig;
|
||||
"isDefault": boolean;
|
||||
"createdAt": time$0.Time;
|
||||
"updatedAt": time$0.Time;
|
||||
|
||||
/** Creates a new Theme instance. */
|
||||
constructor($$source: Partial<Theme> = {}) {
|
||||
if (!("id" in $$source)) {
|
||||
this["id"] = 0;
|
||||
}
|
||||
if (!("name" in $$source)) {
|
||||
this["name"] = "";
|
||||
}
|
||||
if (!("type" in $$source)) {
|
||||
this["type"] = ("" as ThemeType);
|
||||
}
|
||||
if (!("colors" in $$source)) {
|
||||
this["colors"] = (new ThemeColorConfig());
|
||||
}
|
||||
if (!("isDefault" in $$source)) {
|
||||
this["isDefault"] = false;
|
||||
}
|
||||
if (!("createdAt" in $$source)) {
|
||||
this["createdAt"] = null;
|
||||
}
|
||||
if (!("updatedAt" in $$source)) {
|
||||
this["updatedAt"] = null;
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Theme instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): Theme {
|
||||
const $$createField3_0 = $$createType9;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("colors" in $$parsedSource) {
|
||||
$$parsedSource["colors"] = $$createField3_0($$parsedSource["colors"]);
|
||||
}
|
||||
return new Theme($$parsedSource as Partial<Theme>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ThemeColorConfig 主题颜色配置
|
||||
*/
|
||||
export class ThemeColorConfig {
|
||||
/**
|
||||
* 基础色调
|
||||
* 主背景色
|
||||
*/
|
||||
"background": string;
|
||||
|
||||
/**
|
||||
* 次要背景色
|
||||
*/
|
||||
"backgroundSecondary": string;
|
||||
|
||||
/**
|
||||
* 面板背景
|
||||
*/
|
||||
"surface": string;
|
||||
|
||||
/**
|
||||
* 主文本色
|
||||
*/
|
||||
"foreground": string;
|
||||
|
||||
/**
|
||||
* 次要文本色
|
||||
*/
|
||||
"foregroundSecondary": string;
|
||||
|
||||
/**
|
||||
* 语法高亮
|
||||
* 注释色
|
||||
*/
|
||||
"comment": string;
|
||||
|
||||
/**
|
||||
* 关键字
|
||||
*/
|
||||
"keyword": string;
|
||||
|
||||
/**
|
||||
* 字符串
|
||||
*/
|
||||
"string": string;
|
||||
|
||||
/**
|
||||
* 函数名
|
||||
*/
|
||||
"function": string;
|
||||
|
||||
/**
|
||||
* 数字
|
||||
*/
|
||||
"number": string;
|
||||
|
||||
/**
|
||||
* 操作符
|
||||
*/
|
||||
"operator": string;
|
||||
|
||||
/**
|
||||
* 变量
|
||||
*/
|
||||
"variable": string;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
"type": string;
|
||||
|
||||
/**
|
||||
* 界面元素
|
||||
* 光标
|
||||
*/
|
||||
"cursor": string;
|
||||
|
||||
/**
|
||||
* 选中背景
|
||||
*/
|
||||
"selection": string;
|
||||
|
||||
/**
|
||||
* 失焦选中背景
|
||||
*/
|
||||
"selectionBlur": string;
|
||||
|
||||
/**
|
||||
* 当前行高亮
|
||||
*/
|
||||
"activeLine": string;
|
||||
|
||||
/**
|
||||
* 行号
|
||||
*/
|
||||
"lineNumber": string;
|
||||
|
||||
/**
|
||||
* 活动行号
|
||||
*/
|
||||
"activeLineNumber": string;
|
||||
|
||||
/**
|
||||
* 边框分割线
|
||||
* 边框色
|
||||
*/
|
||||
"borderColor": string;
|
||||
|
||||
/**
|
||||
* 浅色边框
|
||||
*/
|
||||
"borderLight": string;
|
||||
|
||||
/**
|
||||
* 搜索匹配
|
||||
* 搜索匹配
|
||||
*/
|
||||
"searchMatch": string;
|
||||
|
||||
/**
|
||||
* 匹配括号
|
||||
*/
|
||||
"matchingBracket": string;
|
||||
|
||||
/** Creates a new ThemeColorConfig instance. */
|
||||
constructor($$source: Partial<ThemeColorConfig> = {}) {
|
||||
if (!("background" in $$source)) {
|
||||
this["background"] = "";
|
||||
}
|
||||
if (!("backgroundSecondary" in $$source)) {
|
||||
this["backgroundSecondary"] = "";
|
||||
}
|
||||
if (!("surface" in $$source)) {
|
||||
this["surface"] = "";
|
||||
}
|
||||
if (!("foreground" in $$source)) {
|
||||
this["foreground"] = "";
|
||||
}
|
||||
if (!("foregroundSecondary" in $$source)) {
|
||||
this["foregroundSecondary"] = "";
|
||||
}
|
||||
if (!("comment" in $$source)) {
|
||||
this["comment"] = "";
|
||||
}
|
||||
if (!("keyword" in $$source)) {
|
||||
this["keyword"] = "";
|
||||
}
|
||||
if (!("string" in $$source)) {
|
||||
this["string"] = "";
|
||||
}
|
||||
if (!("function" in $$source)) {
|
||||
this["function"] = "";
|
||||
}
|
||||
if (!("number" in $$source)) {
|
||||
this["number"] = "";
|
||||
}
|
||||
if (!("operator" in $$source)) {
|
||||
this["operator"] = "";
|
||||
}
|
||||
if (!("variable" in $$source)) {
|
||||
this["variable"] = "";
|
||||
}
|
||||
if (!("type" in $$source)) {
|
||||
this["type"] = "";
|
||||
}
|
||||
if (!("cursor" in $$source)) {
|
||||
this["cursor"] = "";
|
||||
}
|
||||
if (!("selection" in $$source)) {
|
||||
this["selection"] = "";
|
||||
}
|
||||
if (!("selectionBlur" in $$source)) {
|
||||
this["selectionBlur"] = "";
|
||||
}
|
||||
if (!("activeLine" in $$source)) {
|
||||
this["activeLine"] = "";
|
||||
}
|
||||
if (!("lineNumber" in $$source)) {
|
||||
this["lineNumber"] = "";
|
||||
}
|
||||
if (!("activeLineNumber" in $$source)) {
|
||||
this["activeLineNumber"] = "";
|
||||
}
|
||||
if (!("borderColor" in $$source)) {
|
||||
this["borderColor"] = "";
|
||||
}
|
||||
if (!("borderLight" in $$source)) {
|
||||
this["borderLight"] = "";
|
||||
}
|
||||
if (!("searchMatch" in $$source)) {
|
||||
this["searchMatch"] = "";
|
||||
}
|
||||
if (!("matchingBracket" in $$source)) {
|
||||
this["matchingBracket"] = "";
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ThemeColorConfig instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): ThemeColorConfig {
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
return new ThemeColorConfig($$parsedSource as Partial<ThemeColorConfig>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ThemeType 主题类型枚举
|
||||
*/
|
||||
export enum ThemeType {
|
||||
/**
|
||||
* The Go zero value for the underlying type of the enum.
|
||||
*/
|
||||
$zero = "",
|
||||
|
||||
ThemeTypeDark = "dark",
|
||||
ThemeTypeLight = "light",
|
||||
};
|
||||
|
||||
/**
|
||||
* UpdateSourceType 更新源类型
|
||||
*/
|
||||
@@ -1204,8 +1496,8 @@ export class UpdatesConfig {
|
||||
* Creates a new UpdatesConfig instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): UpdatesConfig {
|
||||
const $$createField6_0 = $$createType11;
|
||||
const $$createField7_0 = $$createType12;
|
||||
const $$createField6_0 = $$createType10;
|
||||
const $$createField7_0 = $$createType11;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("github" in $$parsedSource) {
|
||||
$$parsedSource["github"] = $$createField6_0($$parsedSource["github"]);
|
||||
@@ -1222,17 +1514,16 @@ const $$createType0 = GeneralConfig.createFrom;
|
||||
const $$createType1 = EditingConfig.createFrom;
|
||||
const $$createType2 = AppearanceConfig.createFrom;
|
||||
const $$createType3 = UpdatesConfig.createFrom;
|
||||
const $$createType4 = ConfigMetadata.createFrom;
|
||||
var $$createType5 = (function $$initCreateType5(...args): any {
|
||||
if ($$createType5 === $$initCreateType5) {
|
||||
$$createType5 = $$createType6;
|
||||
const $$createType4 = GitBackupConfig.createFrom;
|
||||
const $$createType5 = ConfigMetadata.createFrom;
|
||||
var $$createType6 = (function $$initCreateType6(...args): any {
|
||||
if ($$createType6 === $$initCreateType6) {
|
||||
$$createType6 = $$createType7;
|
||||
}
|
||||
return $$createType5(...args);
|
||||
return $$createType6(...args);
|
||||
});
|
||||
const $$createType6 = $Create.Map($Create.Any, $Create.Any);
|
||||
const $$createType7 = HotkeyCombo.createFrom;
|
||||
const $$createType8 = KeyBinding.createFrom;
|
||||
const $$createType9 = $Create.Array($$createType8);
|
||||
const $$createType10 = KeyBindingMetadata.createFrom;
|
||||
const $$createType11 = GithubConfig.createFrom;
|
||||
const $$createType12 = GiteaConfig.createFrom;
|
||||
const $$createType7 = $Create.Map($Create.Any, $Create.Any);
|
||||
const $$createType8 = HotkeyCombo.createFrom;
|
||||
const $$createType9 = ThemeColorConfig.createFrom;
|
||||
const $$createType10 = GithubConfig.createFrom;
|
||||
const $$createType11 = GiteaConfig.createFrom;
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// 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 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* PushToRemote 推送本地更改到远程仓库
|
||||
*/
|
||||
export function PushToRemote(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(262644139) 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
@@ -34,6 +34,30 @@ export function GetConfig(): Promise<models$0.AppConfig | null> & { cancel(): vo
|
||||
return $typingPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetConfigDir 获取配置目录
|
||||
*/
|
||||
export function GetConfigDir(): Promise<string> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2275626561) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetSettingsPath 获取设置文件路径
|
||||
*/
|
||||
export function GetSettingsPath(): Promise<string> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2175583370) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* MigrateConfig 执行配置迁移
|
||||
*/
|
||||
export function MigrateConfig(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(434292783) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ResetConfig 强制重置所有配置为默认值
|
||||
*/
|
||||
@@ -42,6 +66,14 @@ export function ResetConfig(): Promise<void> & { cancel(): void } {
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceShutdown 关闭服务
|
||||
*/
|
||||
export function ServiceShutdown(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3963562361) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 设置配置项
|
||||
*/
|
||||
@@ -50,6 +82,14 @@ export function Set(key: string, value: any): Promise<void> & { cancel(): void }
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* SetBackupConfigChangeCallback 设置备份配置变更回调
|
||||
*/
|
||||
export function SetBackupConfigChangeCallback(callback: any): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3264871659, callback) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* SetDataPathChangeCallback 设置数据路径配置变更回调
|
||||
*/
|
||||
@@ -66,6 +106,14 @@ export function SetHotkeyChangeCallback(callback: any): Promise<void> & { cancel
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* SetWindowSnapConfigChangeCallback 设置窗口吸附配置变更回调
|
||||
*/
|
||||
export function SetWindowSnapConfigChangeCallback(callback: any): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2324961653, callback) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
// Private type creation functions
|
||||
const $$createType0 = models$0.AppConfig.createFrom;
|
||||
const $$createType1 = $Create.Nullable($$createType0);
|
||||
|
||||
@@ -12,19 +12,7 @@ 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 sql$0 from "../../../database/sql/models.js";
|
||||
|
||||
/**
|
||||
* GetDB returns the database connection
|
||||
*/
|
||||
export function GetDB(): Promise<sql$0.DB | null> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(228760371) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
return $$createType1($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
return $typingPromise;
|
||||
}
|
||||
import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js";
|
||||
|
||||
/**
|
||||
* OnDataPathChanged handles data path changes
|
||||
@@ -34,6 +22,26 @@ export function OnDataPathChanged(): Promise<void> & { cancel(): void } {
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
// Private type creation functions
|
||||
const $$createType0 = sql$0.DB.createFrom;
|
||||
const $$createType1 = $Create.Nullable($$createType0);
|
||||
/**
|
||||
* RegisterModel 注册模型与表的映射关系
|
||||
*/
|
||||
export function RegisterModel(tableName: string, model: any): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(175397515, tableName, model) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceShutdown shuts down the service when the application closes
|
||||
*/
|
||||
export function ServiceShutdown(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3907893632) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceStartup initializes the service when the application starts
|
||||
*/
|
||||
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2067840771, options) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,14 @@ export function SelectDirectory(): Promise<string> & { cancel(): void } {
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* SelectFile 打开文件选择对话框
|
||||
*/
|
||||
export function SelectFile(): Promise<string> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(37302920) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* SetWindow 设置绑定的窗口
|
||||
*/
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
// @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";
|
||||
@@ -78,6 +81,14 @@ export function ListDeletedDocumentsMeta(): Promise<(models$0.Document | null)[]
|
||||
return $typingPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* LockDocument 锁定文档,防止删除
|
||||
*/
|
||||
export function LockDocument(id: number): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1889494473, id) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* RestoreDocument restores a deleted document
|
||||
*/
|
||||
@@ -86,6 +97,22 @@ export function RestoreDocument(id: number): Promise<void> & { cancel(): void }
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceStartup initializes the service when the application starts
|
||||
*/
|
||||
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1474135487, options) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* UnlockDocument 解锁文档
|
||||
*/
|
||||
export function UnlockDocument(id: number): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(222307930, id) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* UpdateDocumentContent updates the content of a document
|
||||
*/
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
// @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";
|
||||
@@ -42,6 +45,14 @@ export function ResetExtensionToDefault(id: models$0.ExtensionID): Promise<void>
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceStartup 启动时调用
|
||||
*/
|
||||
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(40324057, options) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* UpdateExtensionEnabled 更新扩展启用状态
|
||||
*/
|
||||
|
||||
@@ -32,8 +32,8 @@ export function GetCurrentHotkey(): Promise<models$0.HotkeyCombo | null> & { can
|
||||
/**
|
||||
* Initialize 初始化热键服务
|
||||
*/
|
||||
export function Initialize(app: application$0.App | null): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3671360458, app) as any;
|
||||
export function Initialize(app: application$0.App | null, mainWindow: application$0.WebviewWindow | null): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3671360458, app, mainWindow) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export function RegisterHotkey(hotkey: models$0.HotkeyCombo | null): Promise<voi
|
||||
}
|
||||
|
||||
/**
|
||||
* OnShutdown 关闭服务
|
||||
* ServiceShutdown 关闭服务
|
||||
*/
|
||||
export function ServiceShutdown(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(157291181) as any;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 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";
|
||||
@@ -12,9 +13,13 @@ 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 TrayService from "./trayservice.js";
|
||||
import * as WindowService from "./windowservice.js";
|
||||
export {
|
||||
BackupService,
|
||||
ConfigService,
|
||||
DatabaseService,
|
||||
DialogService,
|
||||
@@ -26,8 +31,11 @@ export {
|
||||
SelfUpdateService,
|
||||
StartupService,
|
||||
SystemService,
|
||||
TestService,
|
||||
ThemeService,
|
||||
TranslationService,
|
||||
TrayService
|
||||
TrayService,
|
||||
WindowService
|
||||
};
|
||||
|
||||
export * from "./models.js";
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
// @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";
|
||||
@@ -27,19 +30,13 @@ export function GetAllKeyBindings(): Promise<models$0.KeyBinding[]> & { cancel()
|
||||
}
|
||||
|
||||
/**
|
||||
* GetKeyBindingConfig 获取完整快捷键配置
|
||||
* ServiceStartup 启动时调用
|
||||
*/
|
||||
export function GetKeyBindingConfig(): Promise<models$0.KeyBindingConfig | null> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3804318356) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
return $$createType3($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
return $typingPromise;
|
||||
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2057121990, options) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
// Private type creation functions
|
||||
const $$createType0 = models$0.KeyBinding.createFrom;
|
||||
const $$createType1 = $Create.Array($$createType0);
|
||||
const $$createType2 = models$0.KeyBindingConfig.createFrom;
|
||||
const $$createType3 = $Create.Nullable($$createType2);
|
||||
|
||||
@@ -42,5 +42,13 @@ export function MigrateDirectory(srcPath: string, dstPath: string): Promise<void
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceShutdown 服务关闭
|
||||
*/
|
||||
export function ServiceShutdown(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3472042605) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
// Private type creation functions
|
||||
const $$createType0 = $models.MigrationProgress.createFrom;
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
// @ts-ignore: Unused imports
|
||||
import {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";
|
||||
|
||||
/**
|
||||
* MemoryStats 内存统计信息
|
||||
*/
|
||||
@@ -197,3 +201,63 @@ export class SelfUpdateResult {
|
||||
return new SelfUpdateResult($$parsedSource as Partial<SelfUpdateResult>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WindowInfo 窗口信息(简化版)
|
||||
*/
|
||||
export class WindowInfo {
|
||||
"Window": application$0.WebviewWindow | null;
|
||||
"DocumentID": number;
|
||||
"Title": string;
|
||||
|
||||
/** Creates a new WindowInfo instance. */
|
||||
constructor($$source: Partial<WindowInfo> = {}) {
|
||||
if (!("Window" in $$source)) {
|
||||
this["Window"] = null;
|
||||
}
|
||||
if (!("DocumentID" in $$source)) {
|
||||
this["DocumentID"] = 0;
|
||||
}
|
||||
if (!("Title" in $$source)) {
|
||||
this["Title"] = "";
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new WindowInfo instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): WindowInfo {
|
||||
const $$createField0_0 = $$createType1;
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
if ("Window" in $$parsedSource) {
|
||||
$$parsedSource["Window"] = $$createField0_0($$parsedSource["Window"]);
|
||||
}
|
||||
return new WindowInfo($$parsedSource as Partial<WindowInfo>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WindowSnapService 窗口吸附服务
|
||||
*/
|
||||
export class WindowSnapService {
|
||||
|
||||
/** Creates a new WindowSnapService instance. */
|
||||
constructor($$source: Partial<WindowSnapService> = {}) {
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new WindowSnapService instance from a string or object.
|
||||
*/
|
||||
static createFrom($$source: any = {}): WindowSnapService {
|
||||
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
|
||||
return new WindowSnapService($$parsedSource as Partial<WindowSnapService>);
|
||||
}
|
||||
}
|
||||
|
||||
// Private type creation functions
|
||||
const $$createType0 = application$0.WebviewWindow.createFrom;
|
||||
const $$createType1 = $Create.Nullable($$createType0);
|
||||
|
||||
55
frontend/bindings/voidraft/internal/services/testservice.ts
Normal file
55
frontend/bindings/voidraft/internal/services/testservice.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
/**
|
||||
* TestService 测试服务 - 仅在开发环境使用
|
||||
* @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";
|
||||
|
||||
/**
|
||||
* ClearAll 清除所有测试状态
|
||||
*/
|
||||
export function ClearAll(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2179720854) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceStartup 服务启动时调用
|
||||
*/
|
||||
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(617408198, options) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* TestBadge 测试Badge功能
|
||||
*/
|
||||
export function TestBadge(text: string): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(4242952145, text) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* TestNotification 测试通知功能
|
||||
*/
|
||||
export function TestNotification(title: string, subtitle: string, body: string): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1697553289, title, subtitle, body) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* TestUpdateNotification 测试更新通知
|
||||
*/
|
||||
export function TestUpdateNotification(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3091730060) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
104
frontend/bindings/voidraft/internal/services/themeservice.ts
Normal file
104
frontend/bindings/voidraft/internal/services/themeservice.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
/**
|
||||
* ThemeService 主题服务
|
||||
* @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";
|
||||
|
||||
/**
|
||||
* CreateTheme 创建新主题
|
||||
*/
|
||||
export function CreateTheme(theme: models$0.Theme | null): Promise<models$0.Theme | null> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3274757686, theme) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
return $$createType1($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
return $typingPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetAllThemes 获取所有主题
|
||||
*/
|
||||
export function GetAllThemes(): Promise<(models$0.Theme | null)[]> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2425053076) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
return $$createType2($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
return $typingPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetDefaultThemes 获取默认主题
|
||||
*/
|
||||
export function GetDefaultThemes(): Promise<{ [_: string]: models$0.Theme | null }> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3801788118) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
return $$createType3($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
return $typingPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetThemeByType 根据类型获取默认主题
|
||||
*/
|
||||
export function GetThemeByType(themeType: models$0.ThemeType): Promise<models$0.Theme | null> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1680465265, themeType) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
return $$createType1($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
return $typingPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ResetThemeColors 重置主题颜色为默认值
|
||||
*/
|
||||
export function ResetThemeColors(themeType: models$0.ThemeType): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(342461245, themeType) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceShutdown 服务关闭
|
||||
*/
|
||||
export function ServiceShutdown(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1676749034) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceStartup 服务启动时初始化
|
||||
*/
|
||||
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2915959937, options) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* UpdateThemeColors 更新主题颜色
|
||||
*/
|
||||
export function UpdateThemeColors(themeType: models$0.ThemeType, colors: models$0.ThemeColorConfig): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2750902529, themeType, colors) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
// Private type creation functions
|
||||
const $$createType0 = models$0.Theme.createFrom;
|
||||
const $$createType1 = $Create.Nullable($$createType0);
|
||||
const $$createType2 = $Create.Array($$createType1);
|
||||
const $$createType3 = $Create.Map($Create.Any, $$createType1);
|
||||
@@ -0,0 +1,75 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
/**
|
||||
* WindowService 窗口管理服务(专注于窗口生命周期管理)
|
||||
* @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 from "./models.js";
|
||||
|
||||
/**
|
||||
* GetOpenWindows 获取所有打开的窗口信息
|
||||
*/
|
||||
export function GetOpenWindows(): Promise<$models.WindowInfo[]> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1464997251) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
return $$createType1($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
return $typingPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* IsDocumentWindowOpen 检查指定文档的窗口是否已打开
|
||||
*/
|
||||
export function IsDocumentWindowOpen(documentID: number): Promise<boolean> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1735611839, documentID) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* OpenDocumentWindow 为指定文档ID打开新窗口
|
||||
*/
|
||||
export function OpenDocumentWindow(documentID: number): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(494716471, documentID) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ServiceShutdown 实现服务关闭接口
|
||||
*/
|
||||
export function ServiceShutdown(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(202192783) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* SetAppReferences 设置应用和主窗口引用
|
||||
*/
|
||||
export function SetAppReferences(app: application$0.App | null, mainWindow: application$0.WebviewWindow | null): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1120840759, app, mainWindow) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* SetWindowSnapService 设置窗口吸附服务引用
|
||||
*/
|
||||
export function SetWindowSnapService(snapService: $models.WindowSnapService | null): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1105193745, snapService) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
// Private type creation functions
|
||||
const $$createType0 = $models.WindowInfo.createFrom;
|
||||
const $$createType1 = $Create.Array($$createType0);
|
||||
1
frontend/components.d.ts
vendored
1
frontend/components.d.ts
vendored
@@ -11,6 +11,7 @@ declare module 'vue' {
|
||||
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']
|
||||
LoadingScreen: typeof import('./src/components/loading/LoadingScreen.vue')['default']
|
||||
MacOSTitleBar: typeof import('./src/components/titlebar/MacOSTitleBar.vue')['default']
|
||||
MemoryMonitor: typeof import('./src/components/monitor/MemoryMonitor.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
|
||||
3303
frontend/package-lock.json
generated
3303
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -5,77 +5,92 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host --mode development",
|
||||
"build:dev": "vue-tsc && vite build --minify false --mode development",
|
||||
"build": "vue-tsc && vite build --mode production",
|
||||
"build:dev": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" vue-tsc && cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" vite build --minify false --mode development",
|
||||
"build": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" vue-tsc && cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" vite build --mode production",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint",
|
||||
"lint:fix": "eslint --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.18.6",
|
||||
"@codemirror/autocomplete": "^6.18.7",
|
||||
"@codemirror/commands": "^6.8.1",
|
||||
"@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.9",
|
||||
"@codemirror/lang-html": "^6.4.10",
|
||||
"@codemirror/lang-java": "^6.0.2",
|
||||
"@codemirror/lang-javascript": "^6.2.4",
|
||||
"@codemirror/lang-json": "^6.0.2",
|
||||
"@codemirror/lang-less": "^6.0.2",
|
||||
"@codemirror/lang-lezer": "^6.0.2",
|
||||
"@codemirror/lang-liquid": "^6.2.3",
|
||||
"@codemirror/lang-markdown": "^6.3.3",
|
||||
"@codemirror/lang-liquid": "^6.3.0",
|
||||
"@codemirror/lang-markdown": "^6.3.4",
|
||||
"@codemirror/lang-php": "^6.0.2",
|
||||
"@codemirror/lang-python": "^6.2.1",
|
||||
"@codemirror/lang-rust": "^6.0.2",
|
||||
"@codemirror/lang-sass": "^6.0.2",
|
||||
"@codemirror/lang-sql": "^6.9.0",
|
||||
"@codemirror/lang-sql": "^6.9.1",
|
||||
"@codemirror/lang-vue": "^0.1.3",
|
||||
"@codemirror/lang-wast": "^6.0.2",
|
||||
"@codemirror/lang-xml": "^6.1.0",
|
||||
"@codemirror/lang-yaml": "^6.1.2",
|
||||
"@codemirror/language": "^6.11.2",
|
||||
"@codemirror/language": "^6.11.3",
|
||||
"@codemirror/language-data": "^6.5.1",
|
||||
"@codemirror/legacy-modes": "^6.5.1",
|
||||
"@codemirror/lint": "^6.8.5",
|
||||
"@codemirror/search": "^6.5.11",
|
||||
"@codemirror/state": "^6.5.2",
|
||||
"@codemirror/view": "^6.38.0",
|
||||
"@codemirror/view": "^6.38.2",
|
||||
"@cospaia/prettier-plugin-clojure": "^0.0.2",
|
||||
"@lezer/highlight": "^1.2.1",
|
||||
"@lezer/lr": "^1.4.2",
|
||||
"@prettier/plugin-xml": "^3.4.2",
|
||||
"@reteps/dockerfmt": "^0.3.6",
|
||||
"@toml-tools/lexer": "^1.0.0",
|
||||
"@toml-tools/parser": "^1.0.0",
|
||||
"codemirror": "^6.0.2",
|
||||
"codemirror-lang-elixir": "^4.0.0",
|
||||
"colors-named": "^1.0.2",
|
||||
"colors-named-hex": "^1.0.2",
|
||||
"franc-min": "^6.2.0",
|
||||
"groovy-beautify": "^0.0.17",
|
||||
"hsl-matcher": "^1.2.4",
|
||||
"lezer": "^0.13.5",
|
||||
"java-parser": "^3.0.1",
|
||||
"jinx-rust": "^0.1.6",
|
||||
"jsox": "^1.2.123",
|
||||
"linguist-languages": "^9.0.0",
|
||||
"node-sql-parser": "^5.3.12",
|
||||
"php-parser": "^3.2.5",
|
||||
"pinia": "^3.0.3",
|
||||
"pinia-plugin-persistedstate": "^4.4.1",
|
||||
"pinia-plugin-persistedstate": "^4.5.0",
|
||||
"prettier": "^3.6.2",
|
||||
"remarkable": "^2.0.1",
|
||||
"sass": "^1.89.2",
|
||||
"vue": "^3.5.17",
|
||||
"vue-i18n": "^11.1.9",
|
||||
"sass": "^1.92.1",
|
||||
"sh-syntax": "^0.5.8",
|
||||
"sql-formatter": "^15.6.9",
|
||||
"vue": "^3.5.21",
|
||||
"vue-i18n": "^11.1.12",
|
||||
"vue-pick-colors": "^1.8.0",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.30.1",
|
||||
"@eslint/js": "^9.35.0",
|
||||
"@lezer/generator": "^1.8.0",
|
||||
"@types/lodash": "^4.17.20",
|
||||
"@types/node": "^24.0.10",
|
||||
"@types/node": "^24.3.1",
|
||||
"@types/remarkable": "^2.0.8",
|
||||
"@vitejs/plugin-vue": "^6.0.0",
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"@wailsio/runtime": "latest",
|
||||
"eslint": "^9.30.1",
|
||||
"eslint-plugin-vue": "^10.3.0",
|
||||
"globals": "^16.3.0",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.35.1",
|
||||
"unplugin-vue-components": "^28.8.0",
|
||||
"vite": "^7.0.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^9.35.0",
|
||||
"eslint-plugin-vue": "^10.4.0",
|
||||
"globals": "^16.4.0",
|
||||
"rollup-plugin-visualizer": "^6.0.3",
|
||||
"typescript": "^5.9.2",
|
||||
"typescript-eslint": "^8.43.0",
|
||||
"unplugin-vue-components": "^29.0.0",
|
||||
"vite": "^7.1.5",
|
||||
"vite-plugin-node-polyfills": "^0.24.0",
|
||||
"vue-eslint-parser": "^10.2.0",
|
||||
"vue-tsc": "^3.0.1"
|
||||
"vue-tsc": "^3.0.6"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
frontend/public/go-format.wasm
Normal file
BIN
frontend/public/go-format.wasm
Normal file
Binary file not shown.
@@ -1,10 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from 'vue';
|
||||
import { useConfigStore } from '@/stores/configStore';
|
||||
import { useSystemStore } from '@/stores/systemStore';
|
||||
import { useKeybindingStore } from '@/stores/keybindingStore';
|
||||
import { useThemeStore } from '@/stores/themeStore';
|
||||
import { useUpdateStore } from '@/stores/updateStore';
|
||||
import {onMounted} from 'vue';
|
||||
import {useConfigStore} from '@/stores/configStore';
|
||||
import {useSystemStore} from '@/stores/systemStore';
|
||||
import {useKeybindingStore} from '@/stores/keybindingStore';
|
||||
import {useThemeStore} from '@/stores/themeStore';
|
||||
import {useUpdateStore} from '@/stores/updateStore';
|
||||
import {useBackupStore} from '@/stores/backupStore';
|
||||
import WindowTitleBar from '@/components/titlebar/WindowTitleBar.vue';
|
||||
|
||||
const configStore = useConfigStore();
|
||||
@@ -12,6 +13,7 @@ const systemStore = useSystemStore();
|
||||
const keybindingStore = useKeybindingStore();
|
||||
const themeStore = useThemeStore();
|
||||
const updateStore = useUpdateStore();
|
||||
const backupStore = useBackupStore();
|
||||
|
||||
// 应用启动时加载配置和初始化系统信息
|
||||
onMounted(async () => {
|
||||
@@ -26,6 +28,9 @@ onMounted(async () => {
|
||||
await configStore.initializeLanguage();
|
||||
themeStore.initializeTheme();
|
||||
|
||||
// 初始化备份服务
|
||||
await backupStore.initialize();
|
||||
|
||||
// 启动时检查更新
|
||||
await updateStore.checkOnStartup();
|
||||
});
|
||||
@@ -33,7 +38,7 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<WindowTitleBar />
|
||||
<WindowTitleBar/>
|
||||
<div class="app-content">
|
||||
<router-view/>
|
||||
</div>
|
||||
|
||||
@@ -28,6 +28,11 @@
|
||||
--dark-danger-color: #ff6b6b;
|
||||
--dark-bg-primary: #1a1a1a;
|
||||
--dark-bg-hover: #2a2a2a;
|
||||
--dark-loading-bg-gradient: radial-gradient(#222922, #000500);
|
||||
--dark-loading-color: #fff;
|
||||
--dark-loading-glow: 0 0 10px rgba(50, 255, 50, 0.5), 0 0 5px rgba(100, 255, 100, 0.5);
|
||||
--dark-loading-done-color: #6f6;
|
||||
--dark-loading-overlay: linear-gradient(transparent 0%, rgba(10, 16, 10, 0.5) 50%);
|
||||
|
||||
/* 浅色主题颜色变量 */
|
||||
--light-toolbar-bg: #f8f9fa;
|
||||
@@ -55,6 +60,11 @@
|
||||
--light-danger-color: #dc3545;
|
||||
--light-bg-primary: #ffffff;
|
||||
--light-bg-hover: #f1f3f4;
|
||||
--light-loading-bg-gradient: radial-gradient(#f0f6f0, #e5efe5);
|
||||
--light-loading-color: #1a3c1a;
|
||||
--light-loading-glow: 0 0 10px rgba(0, 160, 0, 0.3), 0 0 5px rgba(0, 120, 0, 0.2);
|
||||
--light-loading-done-color: #008800;
|
||||
--light-loading-overlay: linear-gradient(transparent 0%, rgba(220, 240, 220, 0.5) 50%);
|
||||
|
||||
/* 默认使用深色主题 */
|
||||
--toolbar-bg: var(--dark-toolbar-bg);
|
||||
@@ -83,6 +93,12 @@
|
||||
--text-danger: var(--dark-danger-color);
|
||||
--bg-primary: var(--dark-bg-primary);
|
||||
--bg-hover: var(--dark-bg-hover);
|
||||
--voidraft-bg-gradient: var(--dark-loading-bg-gradient);
|
||||
--voidraft-loading-color: var(--dark-loading-color);
|
||||
--voidraft-loading-glow: var(--dark-loading-glow);
|
||||
--voidraft-loading-done-color: var(--dark-loading-done-color);
|
||||
--voidraft-loading-overlay: var(--dark-loading-overlay);
|
||||
--voidraft-mono-font: "HarmonyOS Sans Mono", monospace;
|
||||
|
||||
color-scheme: light dark;
|
||||
}
|
||||
@@ -116,6 +132,11 @@
|
||||
--text-danger: var(--dark-danger-color);
|
||||
--bg-primary: var(--dark-bg-primary);
|
||||
--bg-hover: var(--dark-bg-hover);
|
||||
--voidraft-bg-gradient: var(--dark-loading-bg-gradient);
|
||||
--voidraft-loading-color: var(--dark-loading-color);
|
||||
--voidraft-loading-glow: var(--dark-loading-glow);
|
||||
--voidraft-loading-done-color: var(--dark-loading-done-color);
|
||||
--voidraft-loading-overlay: var(--dark-loading-overlay);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,6 +169,11 @@
|
||||
--text-danger: var(--light-danger-color);
|
||||
--bg-primary: var(--light-bg-primary);
|
||||
--bg-hover: var(--light-bg-hover);
|
||||
--voidraft-bg-gradient: var(--light-loading-bg-gradient);
|
||||
--voidraft-loading-color: var(--light-loading-color);
|
||||
--voidraft-loading-glow: var(--light-loading-glow);
|
||||
--voidraft-loading-done-color: var(--light-loading-done-color);
|
||||
--voidraft-loading-overlay: var(--light-loading-overlay);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,6 +205,11 @@
|
||||
--text-danger: var(--light-danger-color);
|
||||
--bg-primary: var(--light-bg-primary);
|
||||
--bg-hover: var(--light-bg-hover);
|
||||
--voidraft-bg-gradient: var(--light-loading-bg-gradient);
|
||||
--voidraft-loading-color: var(--light-loading-color);
|
||||
--voidraft-loading-glow: var(--light-loading-glow);
|
||||
--voidraft-loading-done-color: var(--light-loading-done-color);
|
||||
--voidraft-loading-overlay: var(--light-loading-overlay);
|
||||
}
|
||||
|
||||
/* 手动选择深色主题 */
|
||||
@@ -207,4 +238,11 @@
|
||||
--selection-bg: var(--dark-selection-bg);
|
||||
--selection-text: var(--dark-selection-text);
|
||||
--text-danger: var(--dark-danger-color);
|
||||
--bg-primary: var(--dark-bg-primary);
|
||||
--bg-hover: var(--dark-bg-hover);
|
||||
--voidraft-bg-gradient: var(--dark-loading-bg-gradient);
|
||||
--voidraft-loading-color: var(--dark-loading-color);
|
||||
--voidraft-loading-glow: var(--dark-loading-glow);
|
||||
--voidraft-loading-done-color: var(--dark-loading-done-color);
|
||||
--voidraft-loading-overlay: var(--dark-loading-overlay);
|
||||
}
|
||||
117
frontend/src/common/prettier/plugins/clang/CustomFileSystem.cc
Normal file
117
frontend/src/common/prettier/plugins/clang/CustomFileSystem.cc
Normal file
@@ -0,0 +1,117 @@
|
||||
#include "CustomFileSystem.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include <emscripten.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::vfs;
|
||||
|
||||
namespace {
|
||||
|
||||
bool isRunningOnWindows() {
|
||||
return EM_ASM_INT({return process.platform == 'win32' ? 1 : 0}) == 1;
|
||||
}
|
||||
|
||||
std::error_code current_path(SmallVectorImpl<char> &result) {
|
||||
result.clear();
|
||||
|
||||
const char *pwd = ::getenv("PWD");
|
||||
result.append(pwd, pwd + strlen(pwd));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace llvm {
|
||||
namespace vfs {
|
||||
|
||||
sys::path::Style getPathStyle() {
|
||||
static sys::path::Style cachedStyle = sys::path::Style::native;
|
||||
|
||||
if (cachedStyle == sys::path::Style::native) {
|
||||
cachedStyle = isRunningOnWindows() ? sys::path::Style::windows
|
||||
: sys::path::Style::posix;
|
||||
}
|
||||
|
||||
return cachedStyle;
|
||||
}
|
||||
|
||||
void make_absolute(const Twine ¤t_directory,
|
||||
SmallVectorImpl<char> &path) {
|
||||
StringRef p(path.data(), path.size());
|
||||
|
||||
auto pathStyle = getPathStyle();
|
||||
|
||||
bool rootDirectory = sys::path::has_root_directory(p, pathStyle);
|
||||
bool rootName = sys::path::has_root_name(p, pathStyle);
|
||||
|
||||
// Already absolute.
|
||||
if ((rootName || is_style_posix(pathStyle)) && rootDirectory)
|
||||
return;
|
||||
|
||||
// All of the following conditions will need the current directory.
|
||||
SmallString<128> current_dir;
|
||||
current_directory.toVector(current_dir);
|
||||
|
||||
// Relative path. Prepend the current directory.
|
||||
if (!rootName && !rootDirectory) {
|
||||
// Append path to the current directory.
|
||||
sys::path::append(current_dir, pathStyle, p);
|
||||
// Set path to the result.
|
||||
path.swap(current_dir);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!rootName && rootDirectory) {
|
||||
StringRef cdrn = sys::path::root_name(current_dir, pathStyle);
|
||||
SmallString<128> curDirRootName(cdrn.begin(), cdrn.end());
|
||||
sys::path::append(curDirRootName, pathStyle, p);
|
||||
// Set path to the result.
|
||||
path.swap(curDirRootName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rootName && !rootDirectory) {
|
||||
StringRef pRootName = sys::path::root_name(p, pathStyle);
|
||||
StringRef bRootDirectory =
|
||||
sys::path::root_directory(current_dir, pathStyle);
|
||||
StringRef bRelativePath = sys::path::relative_path(current_dir, pathStyle);
|
||||
StringRef pRelativePath = sys::path::relative_path(p, pathStyle);
|
||||
|
||||
SmallString<128> res;
|
||||
sys::path::append(res, pathStyle, pRootName, bRootDirectory, bRelativePath,
|
||||
pRelativePath);
|
||||
path.swap(res);
|
||||
return;
|
||||
}
|
||||
|
||||
llvm_unreachable("All rootName and rootDirectory combinations should have "
|
||||
"occurred above!");
|
||||
}
|
||||
|
||||
std::error_code make_absolute(SmallVectorImpl<char> &path) {
|
||||
if (sys::path::is_absolute(path, getPathStyle()))
|
||||
return {};
|
||||
|
||||
SmallString<128> current_dir;
|
||||
if (std::error_code ec = current_path(current_dir))
|
||||
return ec;
|
||||
|
||||
make_absolute(current_dir, path);
|
||||
return {};
|
||||
}
|
||||
|
||||
CustomFileSystem::CustomFileSystem(IntrusiveRefCntPtr<FileSystem> FS)
|
||||
: ProxyFileSystem(std::move(FS)) {}
|
||||
|
||||
std::error_code
|
||||
CustomFileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
|
||||
return make_absolute(Path);
|
||||
}
|
||||
|
||||
} // namespace vfs
|
||||
} // namespace llvm
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef CUSTOM_FILE_SYSTEM_H
|
||||
#define CUSTOM_FILE_SYSTEM_H
|
||||
|
||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/Support/ErrorOr.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/VirtualFileSystem.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace vfs {
|
||||
|
||||
sys::path::Style getPathStyle();
|
||||
std::error_code make_absolute(SmallVectorImpl<char> &path);
|
||||
|
||||
class CustomFileSystem : public ProxyFileSystem {
|
||||
public:
|
||||
CustomFileSystem(IntrusiveRefCntPtr<FileSystem> FS);
|
||||
|
||||
std::error_code makeAbsolute(SmallVectorImpl<char> &Path) const override;
|
||||
};
|
||||
|
||||
} // namespace vfs
|
||||
} // namespace llvm
|
||||
|
||||
#endif // CUSTOM_FILE_SYSTEM_H
|
||||
26
frontend/src/common/prettier/plugins/clang/binding.cc
Normal file
26
frontend/src/common/prettier/plugins/clang/binding.cc
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "lib.h"
|
||||
#include <emscripten/bind.h>
|
||||
|
||||
using namespace emscripten;
|
||||
|
||||
EMSCRIPTEN_BINDINGS(my_module) {
|
||||
register_vector<unsigned>("RangeList");
|
||||
|
||||
value_object<Result>("Result")
|
||||
.field("error", &Result::error)
|
||||
.field("content", &Result::content);
|
||||
|
||||
function<std::string>("version", &version);
|
||||
function<Result, const std::string, const std::string, const std::string>(
|
||||
"format", &format);
|
||||
function<Result, const std::string, const std::string, const std::string,
|
||||
const std::vector<unsigned>>("format_byte", &format_byte);
|
||||
function<Result, const std::string, const std::string, const std::string,
|
||||
const std::vector<unsigned>>("format_line", &format_line);
|
||||
function<void, const std::string>("set_fallback_style", &set_fallback_style);
|
||||
function<void, bool>("set_sort_includes", &set_sort_includes);
|
||||
function<Result, const std::string, const std::string, const std::string>(
|
||||
"dump_config", &dump_config);
|
||||
}
|
||||
|
||||
int main(void) {}
|
||||
File diff suppressed because one or more lines are too long
BIN
frontend/src/common/prettier/plugins/clang/clang-format-cli.wasm
Normal file
BIN
frontend/src/common/prettier/plugins/clang/clang-format-cli.wasm
Normal file
Binary file not shown.
197
frontend/src/common/prettier/plugins/clang/clang-format-diff.py
Normal file
197
frontend/src/common/prettier/plugins/clang/clang-format-diff.py
Normal file
@@ -0,0 +1,197 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# ===- clang-format-diff.py - ClangFormat Diff Reformatter ----*- python -*--===#
|
||||
#
|
||||
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
# See https://llvm.org/LICENSE.txt for license information.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
#
|
||||
# ===------------------------------------------------------------------------===#
|
||||
|
||||
"""
|
||||
This script reads input from a unified diff and reformats all the changed
|
||||
lines. This is useful to reformat all the lines touched by a specific patch.
|
||||
Example usage for git/svn users:
|
||||
|
||||
git diff -U0 --no-color --relative HEAD^ | {clang_format_diff} -p1 -i
|
||||
svn diff --diff-cmd=diff -x-U0 | {clang_format_diff} -i
|
||||
|
||||
It should be noted that the filename contained in the diff is used unmodified
|
||||
to determine the source file to update. Users calling this script directly
|
||||
should be careful to ensure that the path in the diff is correct relative to the
|
||||
current working directory.
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
import argparse
|
||||
import difflib
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
if sys.version_info.major >= 3:
|
||||
from io import StringIO
|
||||
else:
|
||||
from io import BytesIO as StringIO
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description=__doc__.format(clang_format_diff="%(prog)s"),
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="apply edits to files instead of displaying a diff",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
metavar="NUM",
|
||||
default=0,
|
||||
help="strip the smallest prefix containing P slashes",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-regex",
|
||||
metavar="PATTERN",
|
||||
default=None,
|
||||
help="custom pattern selecting file paths to reformat "
|
||||
"(case sensitive, overrides -iregex)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-iregex",
|
||||
metavar="PATTERN",
|
||||
default=r".*\.(?:cpp|cc|c\+\+|cxx|cppm|ccm|cxxm|c\+\+m|c|cl|h|hh|hpp"
|
||||
r"|hxx|m|mm|inc|js|ts|proto|protodevel|java|cs|json|ipynb|s?vh?)",
|
||||
help="custom pattern selecting file paths to reformat "
|
||||
"(case insensitive, overridden by -regex)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-sort-includes",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="let clang-format sort include blocks",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--verbose",
|
||||
action="store_true",
|
||||
help="be more verbose, ineffective without -i",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-style",
|
||||
help="formatting style to apply (LLVM, GNU, Google, Chromium, "
|
||||
"Microsoft, Mozilla, WebKit)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-fallback-style",
|
||||
help="The name of the predefined style used as a"
|
||||
"fallback in case clang-format is invoked with"
|
||||
"-style=file, but can not find the .clang-format"
|
||||
"file to use.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-binary",
|
||||
default="clang-format",
|
||||
help="location of binary to use for clang-format",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Extract changed lines for each file.
|
||||
filename = None
|
||||
lines_by_file = {}
|
||||
for line in sys.stdin:
|
||||
match = re.search(r"^\+\+\+\ (.*?/){%s}(.+)" % args.p, line.rstrip())
|
||||
if match:
|
||||
filename = match.group(2)
|
||||
if filename is None:
|
||||
continue
|
||||
|
||||
if args.regex is not None:
|
||||
if not re.match("^%s$" % args.regex, filename):
|
||||
continue
|
||||
else:
|
||||
if not re.match("^%s$" % args.iregex, filename, re.IGNORECASE):
|
||||
continue
|
||||
|
||||
match = re.search(r"^@@.*\+(\d+)(?:,(\d+))?", line)
|
||||
if match:
|
||||
start_line = int(match.group(1))
|
||||
line_count = 1
|
||||
if match.group(2):
|
||||
line_count = int(match.group(2))
|
||||
# The input is something like
|
||||
#
|
||||
# @@ -1, +0,0 @@
|
||||
#
|
||||
# which means no lines were added.
|
||||
if line_count == 0:
|
||||
continue
|
||||
# Also format lines range if line_count is 0 in case of deleting
|
||||
# surrounding statements.
|
||||
end_line = start_line
|
||||
if line_count != 0:
|
||||
end_line += line_count - 1
|
||||
lines_by_file.setdefault(filename, []).extend(
|
||||
["--lines", str(start_line) + ":" + str(end_line)]
|
||||
)
|
||||
|
||||
# Reformat files containing changes in place.
|
||||
has_diff = False
|
||||
for filename, lines in lines_by_file.items():
|
||||
if args.i and args.verbose:
|
||||
print("Formatting {}".format(filename))
|
||||
command = [args.binary, filename]
|
||||
if args.i:
|
||||
command.append("-i")
|
||||
if args.sort_includes:
|
||||
command.append("--sort-includes")
|
||||
command.extend(lines)
|
||||
if args.style:
|
||||
command.extend(["--style", args.style])
|
||||
if args.fallback_style:
|
||||
command.extend(["--fallback-style", args.fallback_style])
|
||||
|
||||
try:
|
||||
p = subprocess.Popen(
|
||||
command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=None,
|
||||
stdin=subprocess.PIPE,
|
||||
universal_newlines=True,
|
||||
)
|
||||
except OSError as e:
|
||||
# Give the user more context when clang-format isn't
|
||||
# found/isn't executable, etc.
|
||||
raise RuntimeError(
|
||||
'Failed to run "%s" - %s"' % (" ".join(command), e.strerror)
|
||||
)
|
||||
|
||||
stdout, _stderr = p.communicate()
|
||||
if p.returncode != 0:
|
||||
return p.returncode
|
||||
|
||||
if not args.i:
|
||||
with open(filename) as f:
|
||||
code = f.readlines()
|
||||
formatted_code = StringIO(stdout).readlines()
|
||||
diff = difflib.unified_diff(
|
||||
code,
|
||||
formatted_code,
|
||||
filename,
|
||||
filename,
|
||||
"(before formatting)",
|
||||
"(after formatting)",
|
||||
)
|
||||
diff_string = "".join(diff)
|
||||
if len(diff_string) > 0:
|
||||
has_diff = True
|
||||
sys.stdout.write(diff_string)
|
||||
|
||||
if has_diff:
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,10 @@
|
||||
import fs from "node:fs/promises";
|
||||
import initAsync from "./clang-format.js";
|
||||
|
||||
const wasm = new URL("./clang-format.wasm", import.meta.url);
|
||||
|
||||
export default function (init = fs.readFile(wasm)) {
|
||||
return initAsync(init);
|
||||
}
|
||||
|
||||
export * from "./clang-format.js";
|
||||
@@ -0,0 +1,8 @@
|
||||
import initAsync from "./clang-format.js";
|
||||
import wasm from "./clang-format.wasm?url";
|
||||
|
||||
export default function (input = wasm) {
|
||||
return initAsync(input);
|
||||
}
|
||||
|
||||
export * from "./clang-format.js";
|
||||
175
frontend/src/common/prettier/plugins/clang/clang-format.d.ts
vendored
Normal file
175
frontend/src/common/prettier/plugins/clang/clang-format.d.ts
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
export type InitInput =
|
||||
| RequestInfo
|
||||
| URL
|
||||
| Response
|
||||
| BufferSource
|
||||
| WebAssembly.Module;
|
||||
|
||||
export default function init(input?: InitInput): Promise<void>;
|
||||
|
||||
/**
|
||||
* The style to use for formatting.
|
||||
* Supported style values are:
|
||||
* - `LLVM` - A style complying with the LLVM coding standards.
|
||||
* - `Google` - A style complying with Google’s C++ style guide.
|
||||
* - `Chromium` - A style complying with Chromium’s style guide.
|
||||
* - `Mozilla` - A style complying with Mozilla’s style guide.
|
||||
* - `WebKit` - A style complying with WebKit’s style guide.
|
||||
* - `Microsoft` - A style complying with Microsoft’s style guide.
|
||||
* - `GNU` - A style complying with the GNU coding standards.
|
||||
* - A string starting with `{`, for example: `{BasedOnStyle: Chromium, IndentWidth: 4, ...}`.
|
||||
* - A string which represents `.clang-format` content.
|
||||
*
|
||||
*/
|
||||
export type Style =
|
||||
| "LLVM"
|
||||
| "Google"
|
||||
| "Chromium"
|
||||
| "Mozilla"
|
||||
| "WebKit"
|
||||
| "Microsoft"
|
||||
| "GNU"
|
||||
| (string & {});
|
||||
|
||||
/**
|
||||
* The filename to use for determining the language.
|
||||
*/
|
||||
export type Filename =
|
||||
| "main.c"
|
||||
| "main.cc"
|
||||
| "main.cxx"
|
||||
| "main.cpp"
|
||||
| "main.java"
|
||||
| "main.js"
|
||||
| "main.mjs"
|
||||
| "main.ts"
|
||||
| "main.json"
|
||||
| "main.m"
|
||||
| "main.mm"
|
||||
| "main.proto"
|
||||
| "main.cs"
|
||||
| (string & {});
|
||||
|
||||
/**
|
||||
* Formats the given content using the specified style.
|
||||
*
|
||||
* @param {string} content - The content to format.
|
||||
* @param {Filename} filename - The filename to use for determining the language.
|
||||
* @param {Style} style - The style to use for formatting.
|
||||
* Supported style values are:
|
||||
* - `LLVM` - A style complying with the LLVM coding standards.
|
||||
* - `Google` - A style complying with Google’s C++ style guide.
|
||||
* - `Chromium` - A style complying with Chromium’s style guide.
|
||||
* - `Mozilla` - A style complying with Mozilla’s style guide.
|
||||
* - `WebKit` - A style complying with WebKit’s style guide.
|
||||
* - `Microsoft` - A style complying with Microsoft’s style guide.
|
||||
* - `GNU` - A style complying with the GNU coding standards.
|
||||
* - A string starting with `{`, for example: `{BasedOnStyle: Chromium, IndentWidth: 4, ...}`.
|
||||
* - A string which represents `.clang-format` content.
|
||||
*
|
||||
* @returns {string} The formatted content.
|
||||
* @throws {Error}
|
||||
*
|
||||
* @see {@link https://clang.llvm.org/docs/ClangFormatStyleOptions.html}
|
||||
*/
|
||||
export declare function format(
|
||||
content: string,
|
||||
filename?: Filename,
|
||||
style?: Style,
|
||||
): string;
|
||||
|
||||
/**
|
||||
* Both the startLine and endLine are 1-based.
|
||||
*/
|
||||
export type LineRange = [startLine: number, endLine: number];
|
||||
|
||||
/**
|
||||
* Both the offset and length are measured in bytes.
|
||||
*/
|
||||
export type ByteRange = [offset: number, length: number];
|
||||
|
||||
/**
|
||||
* Formats the specified range of lines in the given content using the specified style.
|
||||
*
|
||||
* @param {string} content - The content to format.
|
||||
* @param {LineRange[]} range - Array<[startLine, endLine]> - The range of lines to format.
|
||||
* Both startLine and endLine are 1-based.
|
||||
* Multiple ranges can be formatted by specifying several lines arguments.
|
||||
* @param {Filename} filename - The filename to use for determining the language.
|
||||
* @param {Style} style - The style to use for formatting.
|
||||
* Supported style values are:
|
||||
* - `LLVM` - A style complying with the LLVM coding standards.
|
||||
* - `Google` - A style complying with Google’s C++ style guide.
|
||||
* - `Chromium` - A style complying with Chromium’s style guide.
|
||||
* - `Mozilla` - A style complying with Mozilla’s style guide.
|
||||
* - `WebKit` - A style complying with WebKit’s style guide.
|
||||
* - `Microsoft` - A style complying with Microsoft’s style guide.
|
||||
* - `GNU` - A style complying with the GNU coding standards.
|
||||
* - A string starting with `{`, for example: `{BasedOnStyle: Chromium, IndentWidth: 4, ...}`.
|
||||
* - A string which represents `.clang-format` content.
|
||||
*
|
||||
* @returns {string} The formatted content.
|
||||
* @throws {Error}
|
||||
*
|
||||
* @see {@link https://clang.llvm.org/docs/ClangFormatStyleOptions.html}
|
||||
*/
|
||||
export declare function format_line_range(
|
||||
content: string,
|
||||
range: ByteRange[] | [[offset: number]],
|
||||
filename?: Filename,
|
||||
style?: Style,
|
||||
): string;
|
||||
|
||||
/**
|
||||
* @deprecated Use `format_line_range` instead.
|
||||
*/
|
||||
export declare function formatLineRange(
|
||||
content: string,
|
||||
range: ByteRange[] | [[offset: number]],
|
||||
filename?: Filename,
|
||||
style?: Style,
|
||||
): string;
|
||||
|
||||
/**
|
||||
* Formats the specified range of bytes in the given content using the specified style.
|
||||
*
|
||||
* @param {string} content - The content to format.
|
||||
* @param {ByteRange[]} range - Array<[offset, length]> - The range of bytes to format.
|
||||
* @param {Filename} filename - The filename to use for determining the language.
|
||||
* @param {Style} style - The style to use for formatting.
|
||||
* Supported style values are:
|
||||
* - `LLVM` - A style complying with the LLVM coding standards.
|
||||
* - `Google` - A style complying with Google’s C++ style guide.
|
||||
* - `Chromium` - A style complying with Chromium’s style guide.
|
||||
* - `Mozilla` - A style complying with Mozilla’s style guide.
|
||||
* - `WebKit` - A style complying with WebKit’s style guide.
|
||||
* - `Microsoft` - A style complying with Microsoft’s style guide.
|
||||
* - `GNU` - A style complying with the GNU coding standards.
|
||||
* - A string starting with `{`, for example: `{BasedOnStyle: Chromium, IndentWidth: 4, ...}`.
|
||||
* - A string which represents `.clang-format` content.
|
||||
*
|
||||
* @returns {string} The formatted content.
|
||||
* @throws {Error}
|
||||
*
|
||||
* @see {@link https://clang.llvm.org/docs/ClangFormatStyleOptions.html}
|
||||
*/
|
||||
export declare function format_byte_range(
|
||||
content: string,
|
||||
range: LineRange[],
|
||||
filename?: Filename,
|
||||
style?: Style,
|
||||
): string;
|
||||
|
||||
/**
|
||||
* @deprecated Use `format_byte_range` instead.
|
||||
*/
|
||||
export declare function formatByteRange(
|
||||
content: string,
|
||||
range: LineRange[],
|
||||
filename?: Filename,
|
||||
style?: Style,
|
||||
): string;
|
||||
|
||||
export declare function version(): string;
|
||||
|
||||
export declare function set_fallback_style(style: Style): void;
|
||||
155
frontend/src/common/prettier/plugins/clang/clang-format.js
Normal file
155
frontend/src/common/prettier/plugins/clang/clang-format.js
Normal file
File diff suppressed because one or more lines are too long
BIN
frontend/src/common/prettier/plugins/clang/clang-format.wasm
Normal file
BIN
frontend/src/common/prettier/plugins/clang/clang-format.wasm
Normal file
Binary file not shown.
3
frontend/src/common/prettier/plugins/clang/cli-pre.js
Normal file
3
frontend/src/common/prettier/plugins/clang/cli-pre.js
Normal file
@@ -0,0 +1,3 @@
|
||||
Module.preRun = function customPreRun() {
|
||||
ENV.PWD = process.cwd();
|
||||
}
|
||||
748
frontend/src/common/prettier/plugins/clang/cli.cc
Normal file
748
frontend/src/common/prettier/plugins/clang/cli.cc
Normal file
@@ -0,0 +1,748 @@
|
||||
//===-- clang-format/ClangFormat.cpp - Clang format tool ------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// This file implements a clang-format tool that automatically formats
|
||||
/// (fragments of) C++ code.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/../../lib/Format/MatchFilePath.h"
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/DiagnosticOptions.h"
|
||||
#include "clang/Basic/FileManager.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Basic/Version.h"
|
||||
#include "clang/Format/Format.h"
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/InitLLVM.h"
|
||||
#include "llvm/Support/Process.h"
|
||||
#include <fstream>
|
||||
|
||||
#include "CustomFileSystem.h"
|
||||
|
||||
using namespace llvm;
|
||||
using clang::tooling::Replacements;
|
||||
|
||||
static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
|
||||
|
||||
// Mark all our options with this category, everything else (except for -version
|
||||
// and -help) will be hidden.
|
||||
static cl::OptionCategory ClangFormatCategory("Clang-format options");
|
||||
|
||||
static cl::list<unsigned>
|
||||
Offsets("offset",
|
||||
cl::desc("Format a range starting at this byte offset.\n"
|
||||
"Multiple ranges can be formatted by specifying\n"
|
||||
"several -offset and -length pairs.\n"
|
||||
"Can only be used with one input file."),
|
||||
cl::cat(ClangFormatCategory));
|
||||
static cl::list<unsigned>
|
||||
Lengths("length",
|
||||
cl::desc("Format a range of this length (in bytes).\n"
|
||||
"Multiple ranges can be formatted by specifying\n"
|
||||
"several -offset and -length pairs.\n"
|
||||
"When only a single -offset is specified without\n"
|
||||
"-length, clang-format will format up to the end\n"
|
||||
"of the file.\n"
|
||||
"Can only be used with one input file."),
|
||||
cl::cat(ClangFormatCategory));
|
||||
static cl::list<std::string>
|
||||
LineRanges("lines",
|
||||
cl::desc("<start line>:<end line> - format a range of\n"
|
||||
"lines (both 1-based).\n"
|
||||
"Multiple ranges can be formatted by specifying\n"
|
||||
"several -lines arguments.\n"
|
||||
"Can't be used with -offset and -length.\n"
|
||||
"Can only be used with one input file."),
|
||||
cl::cat(ClangFormatCategory));
|
||||
static cl::opt<std::string>
|
||||
Style("style", cl::desc(clang::format::StyleOptionHelpDescription),
|
||||
cl::init(clang::format::DefaultFormatStyle),
|
||||
cl::cat(ClangFormatCategory));
|
||||
static cl::opt<std::string>
|
||||
FallbackStyle("fallback-style",
|
||||
cl::desc("The name of the predefined style used as a\n"
|
||||
"fallback in case clang-format is invoked with\n"
|
||||
"-style=file, but can not find the .clang-format\n"
|
||||
"file to use. Defaults to 'LLVM'.\n"
|
||||
"Use -fallback-style=none to skip formatting."),
|
||||
cl::init(clang::format::DefaultFallbackStyle),
|
||||
cl::cat(ClangFormatCategory));
|
||||
|
||||
static cl::opt<std::string> AssumeFileName(
|
||||
"assume-filename",
|
||||
cl::desc("Set filename used to determine the language and to find\n"
|
||||
".clang-format file.\n"
|
||||
"Only used when reading from stdin.\n"
|
||||
"If this is not passed, the .clang-format file is searched\n"
|
||||
"relative to the current working directory when reading stdin.\n"
|
||||
"Unrecognized filenames are treated as C++.\n"
|
||||
"supported:\n"
|
||||
" CSharp: .cs\n"
|
||||
" Java: .java\n"
|
||||
" JavaScript: .js .mjs .cjs .ts\n"
|
||||
" Json: .json .ipynb\n"
|
||||
" Objective-C: .m .mm\n"
|
||||
" Proto: .proto .protodevel\n"
|
||||
" TableGen: .td\n"
|
||||
" TextProto: .txtpb .textpb .pb.txt .textproto .asciipb\n"
|
||||
" Verilog: .sv .svh .v .vh"),
|
||||
cl::init("<stdin>"), cl::cat(ClangFormatCategory));
|
||||
|
||||
static cl::opt<bool> Inplace("i",
|
||||
cl::desc("Inplace edit <file>s, if specified."),
|
||||
cl::cat(ClangFormatCategory));
|
||||
|
||||
static cl::opt<bool> OutputXML("output-replacements-xml",
|
||||
cl::desc("Output replacements as XML."),
|
||||
cl::cat(ClangFormatCategory));
|
||||
static cl::opt<bool>
|
||||
DumpConfig("dump-config",
|
||||
cl::desc("Dump configuration options to stdout and exit.\n"
|
||||
"Can be used with -style option."),
|
||||
cl::cat(ClangFormatCategory));
|
||||
static cl::opt<unsigned>
|
||||
Cursor("cursor",
|
||||
cl::desc("The position of the cursor when invoking\n"
|
||||
"clang-format from an editor integration"),
|
||||
cl::init(0), cl::cat(ClangFormatCategory));
|
||||
|
||||
static cl::opt<bool>
|
||||
SortIncludes("sort-includes",
|
||||
cl::desc("If set, overrides the include sorting behavior\n"
|
||||
"determined by the SortIncludes style flag"),
|
||||
cl::cat(ClangFormatCategory));
|
||||
|
||||
static cl::opt<std::string> QualifierAlignment(
|
||||
"qualifier-alignment",
|
||||
cl::desc("If set, overrides the qualifier alignment style\n"
|
||||
"determined by the QualifierAlignment style flag"),
|
||||
cl::init(""), cl::cat(ClangFormatCategory));
|
||||
|
||||
static cl::opt<std::string> Files(
|
||||
"files",
|
||||
cl::desc("A file containing a list of files to process, one per line."),
|
||||
cl::value_desc("filename"), cl::init(""), cl::cat(ClangFormatCategory));
|
||||
|
||||
static cl::opt<bool>
|
||||
Verbose("verbose", cl::desc("If set, shows the list of processed files"),
|
||||
cl::cat(ClangFormatCategory));
|
||||
|
||||
// Use --dry-run to match other LLVM tools when you mean do it but don't
|
||||
// actually do it
|
||||
static cl::opt<bool>
|
||||
DryRun("dry-run",
|
||||
cl::desc("If set, do not actually make the formatting changes"),
|
||||
cl::cat(ClangFormatCategory));
|
||||
|
||||
// Use -n as a common command as an alias for --dry-run. (git and make use -n)
|
||||
static cl::alias DryRunShort("n", cl::desc("Alias for --dry-run"),
|
||||
cl::cat(ClangFormatCategory), cl::aliasopt(DryRun),
|
||||
cl::NotHidden);
|
||||
|
||||
// Emulate being able to turn on/off the warning.
|
||||
static cl::opt<bool>
|
||||
WarnFormat("Wclang-format-violations",
|
||||
cl::desc("Warnings about individual formatting changes needed. "
|
||||
"Used only with --dry-run or -n"),
|
||||
cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden);
|
||||
|
||||
static cl::opt<bool>
|
||||
NoWarnFormat("Wno-clang-format-violations",
|
||||
cl::desc("Do not warn about individual formatting changes "
|
||||
"needed. Used only with --dry-run or -n"),
|
||||
cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden);
|
||||
|
||||
static cl::opt<unsigned> ErrorLimit(
|
||||
"ferror-limit",
|
||||
cl::desc("Set the maximum number of clang-format errors to emit\n"
|
||||
"before stopping (0 = no limit).\n"
|
||||
"Used only with --dry-run or -n"),
|
||||
cl::init(0), cl::cat(ClangFormatCategory));
|
||||
|
||||
static cl::opt<bool>
|
||||
WarningsAsErrors("Werror",
|
||||
cl::desc("If set, changes formatting warnings to errors"),
|
||||
cl::cat(ClangFormatCategory));
|
||||
|
||||
namespace {
|
||||
enum class WNoError { Unknown };
|
||||
}
|
||||
|
||||
static cl::bits<WNoError> WNoErrorList(
|
||||
"Wno-error",
|
||||
cl::desc("If set, don't error out on the specified warning type."),
|
||||
cl::values(
|
||||
clEnumValN(WNoError::Unknown, "unknown",
|
||||
"If set, unknown format options are only warned about.\n"
|
||||
"This can be used to enable formatting, even if the\n"
|
||||
"configuration contains unknown (newer) options.\n"
|
||||
"Use with caution, as this might lead to dramatically\n"
|
||||
"differing format depending on an option being\n"
|
||||
"supported or not.")),
|
||||
cl::cat(ClangFormatCategory));
|
||||
|
||||
static cl::opt<bool>
|
||||
ShowColors("fcolor-diagnostics",
|
||||
cl::desc("If set, and on a color-capable terminal controls "
|
||||
"whether or not to print diagnostics in color"),
|
||||
cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden);
|
||||
|
||||
static cl::opt<bool>
|
||||
NoShowColors("fno-color-diagnostics",
|
||||
cl::desc("If set, and on a color-capable terminal controls "
|
||||
"whether or not to print diagnostics in color"),
|
||||
cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden);
|
||||
|
||||
static cl::list<std::string> FileNames(cl::Positional,
|
||||
cl::desc("[@<file>] [<file> ...]"),
|
||||
cl::cat(ClangFormatCategory));
|
||||
|
||||
static cl::opt<bool> FailOnIncompleteFormat(
|
||||
"fail-on-incomplete-format",
|
||||
cl::desc("If set, fail with exit code 1 on incomplete format."),
|
||||
cl::init(false), cl::cat(ClangFormatCategory));
|
||||
|
||||
static cl::opt<bool> ListIgnored("list-ignored",
|
||||
cl::desc("List ignored files."),
|
||||
cl::cat(ClangFormatCategory), cl::Hidden);
|
||||
|
||||
namespace clang {
|
||||
namespace format {
|
||||
|
||||
static FileID createInMemoryFile(StringRef FileName, MemoryBufferRef Source,
|
||||
SourceManager &Sources, FileManager &Files,
|
||||
llvm::vfs::InMemoryFileSystem *MemFS) {
|
||||
MemFS->addFileNoOwn(FileName, 0, Source);
|
||||
auto File = Files.getOptionalFileRef(FileName);
|
||||
assert(File && "File not added to MemFS?");
|
||||
return Sources.createFileID(*File, SourceLocation(), SrcMgr::C_User);
|
||||
}
|
||||
|
||||
// Parses <start line>:<end line> input to a pair of line numbers.
|
||||
// Returns true on error.
|
||||
static bool parseLineRange(StringRef Input, unsigned &FromLine,
|
||||
unsigned &ToLine) {
|
||||
std::pair<StringRef, StringRef> LineRange = Input.split(':');
|
||||
return LineRange.first.getAsInteger(0, FromLine) ||
|
||||
LineRange.second.getAsInteger(0, ToLine);
|
||||
}
|
||||
|
||||
static bool fillRanges(MemoryBuffer *Code,
|
||||
std::vector<tooling::Range> &Ranges) {
|
||||
IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
|
||||
new llvm::vfs::InMemoryFileSystem);
|
||||
FileManager Files(FileSystemOptions(), InMemoryFileSystem);
|
||||
DiagnosticOptions DiagOpts;
|
||||
DiagnosticsEngine Diagnostics(
|
||||
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), DiagOpts);
|
||||
SourceManager Sources(Diagnostics, Files);
|
||||
const auto ID = createInMemoryFile("<irrelevant>", *Code, Sources, Files,
|
||||
InMemoryFileSystem.get());
|
||||
if (!LineRanges.empty()) {
|
||||
if (!Offsets.empty() || !Lengths.empty()) {
|
||||
errs() << "error: cannot use -lines with -offset/-length\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto &LineRange : LineRanges) {
|
||||
unsigned FromLine, ToLine;
|
||||
if (parseLineRange(LineRange, FromLine, ToLine)) {
|
||||
errs() << "error: invalid <start line>:<end line> pair\n";
|
||||
return true;
|
||||
}
|
||||
if (FromLine < 1) {
|
||||
errs() << "error: start line should be at least 1\n";
|
||||
return true;
|
||||
}
|
||||
if (FromLine > ToLine) {
|
||||
errs() << "error: start line should not exceed end line\n";
|
||||
return true;
|
||||
}
|
||||
const auto Start = Sources.translateLineCol(ID, FromLine, 1);
|
||||
const auto End = Sources.translateLineCol(ID, ToLine, UINT_MAX);
|
||||
if (Start.isInvalid() || End.isInvalid())
|
||||
return true;
|
||||
const auto Offset = Sources.getFileOffset(Start);
|
||||
const auto Length = Sources.getFileOffset(End) - Offset;
|
||||
Ranges.push_back(tooling::Range(Offset, Length));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Offsets.empty())
|
||||
Offsets.push_back(0);
|
||||
const bool EmptyLengths = Lengths.empty();
|
||||
unsigned Length = 0;
|
||||
if (Offsets.size() == 1 && EmptyLengths) {
|
||||
Length = Sources.getFileOffset(Sources.getLocForEndOfFile(ID)) - Offsets[0];
|
||||
} else if (Offsets.size() != Lengths.size()) {
|
||||
errs() << "error: number of -offset and -length arguments must match.\n";
|
||||
return true;
|
||||
}
|
||||
for (unsigned I = 0, E = Offsets.size(), CodeSize = Code->getBufferSize();
|
||||
I < E; ++I) {
|
||||
const auto Offset = Offsets[I];
|
||||
if (Offset >= CodeSize) {
|
||||
errs() << "error: offset " << Offset << " is outside the file\n";
|
||||
return true;
|
||||
}
|
||||
if (!EmptyLengths)
|
||||
Length = Lengths[I];
|
||||
if (Offset + Length > CodeSize) {
|
||||
errs() << "error: invalid length " << Length << ", offset + length ("
|
||||
<< Offset + Length << ") is outside the file.\n";
|
||||
return true;
|
||||
}
|
||||
Ranges.push_back(tooling::Range(Offset, Length));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void outputReplacementXML(StringRef Text) {
|
||||
// FIXME: When we sort includes, we need to make sure the stream is correct
|
||||
// utf-8.
|
||||
size_t From = 0;
|
||||
size_t Index;
|
||||
while ((Index = Text.find_first_of("\n\r<&", From)) != StringRef::npos) {
|
||||
outs() << Text.substr(From, Index - From);
|
||||
switch (Text[Index]) {
|
||||
case '\n':
|
||||
outs() << " ";
|
||||
break;
|
||||
case '\r':
|
||||
outs() << " ";
|
||||
break;
|
||||
case '<':
|
||||
outs() << "<";
|
||||
break;
|
||||
case '&':
|
||||
outs() << "&";
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("Unexpected character encountered!");
|
||||
}
|
||||
From = Index + 1;
|
||||
}
|
||||
outs() << Text.substr(From);
|
||||
}
|
||||
|
||||
static void outputReplacementsXML(const Replacements &Replaces) {
|
||||
for (const auto &R : Replaces) {
|
||||
outs() << "<replacement "
|
||||
<< "offset='" << R.getOffset() << "' "
|
||||
<< "length='" << R.getLength() << "'>";
|
||||
outputReplacementXML(R.getReplacementText());
|
||||
outs() << "</replacement>\n";
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
emitReplacementWarnings(const Replacements &Replaces, StringRef AssumedFileName,
|
||||
const std::unique_ptr<llvm::MemoryBuffer> &Code) {
|
||||
unsigned Errors = 0;
|
||||
if (WarnFormat && !NoWarnFormat) {
|
||||
SourceMgr Mgr;
|
||||
const char *StartBuf = Code->getBufferStart();
|
||||
|
||||
Mgr.AddNewSourceBuffer(
|
||||
MemoryBuffer::getMemBuffer(StartBuf, AssumedFileName), SMLoc());
|
||||
for (const auto &R : Replaces) {
|
||||
SMDiagnostic Diag = Mgr.GetMessage(
|
||||
SMLoc::getFromPointer(StartBuf + R.getOffset()),
|
||||
WarningsAsErrors ? SourceMgr::DiagKind::DK_Error
|
||||
: SourceMgr::DiagKind::DK_Warning,
|
||||
"code should be clang-formatted [-Wclang-format-violations]");
|
||||
|
||||
Diag.print(nullptr, llvm::errs(), ShowColors && !NoShowColors);
|
||||
if (ErrorLimit && ++Errors >= ErrorLimit)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return WarningsAsErrors;
|
||||
}
|
||||
|
||||
static void outputXML(const Replacements &Replaces,
|
||||
const Replacements &FormatChanges,
|
||||
const FormattingAttemptStatus &Status,
|
||||
const cl::opt<unsigned> &Cursor,
|
||||
unsigned CursorPosition) {
|
||||
outs() << "<?xml version='1.0'?>\n<replacements "
|
||||
"xml:space='preserve' incomplete_format='"
|
||||
<< (Status.FormatComplete ? "false" : "true") << "'";
|
||||
if (!Status.FormatComplete)
|
||||
outs() << " line='" << Status.Line << "'";
|
||||
outs() << ">\n";
|
||||
if (Cursor.getNumOccurrences() != 0) {
|
||||
outs() << "<cursor>" << FormatChanges.getShiftedCodePosition(CursorPosition)
|
||||
<< "</cursor>\n";
|
||||
}
|
||||
|
||||
outputReplacementsXML(Replaces);
|
||||
outs() << "</replacements>\n";
|
||||
}
|
||||
|
||||
class ClangFormatDiagConsumer : public DiagnosticConsumer {
|
||||
virtual void anchor() {}
|
||||
|
||||
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
|
||||
const Diagnostic &Info) override {
|
||||
|
||||
SmallVector<char, 16> vec;
|
||||
Info.FormatDiagnostic(vec);
|
||||
errs() << "clang-format error:" << vec << "\n";
|
||||
}
|
||||
};
|
||||
|
||||
// Returns true on error.
|
||||
static bool format(StringRef FileName, bool ErrorOnIncompleteFormat = false) {
|
||||
const bool IsSTDIN = FileName == "-";
|
||||
if (!OutputXML && Inplace && IsSTDIN) {
|
||||
errs() << "error: cannot use -i when reading from stdin.\n";
|
||||
return true;
|
||||
}
|
||||
// On Windows, overwriting a file with an open file mapping doesn't work,
|
||||
// so read the whole file into memory when formatting in-place.
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
|
||||
!OutputXML && Inplace
|
||||
? MemoryBuffer::getFileAsStream(FileName)
|
||||
: MemoryBuffer::getFileOrSTDIN(FileName, /*IsText=*/true);
|
||||
if (std::error_code EC = CodeOrErr.getError()) {
|
||||
errs() << FileName << ": " << EC.message() << "\n";
|
||||
return true;
|
||||
}
|
||||
std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());
|
||||
if (Code->getBufferSize() == 0)
|
||||
return false; // Empty files are formatted correctly.
|
||||
|
||||
StringRef BufStr = Code->getBuffer();
|
||||
|
||||
const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr);
|
||||
|
||||
if (InvalidBOM) {
|
||||
errs() << "error: encoding with unsupported byte order mark \""
|
||||
<< InvalidBOM << "\" detected";
|
||||
if (!IsSTDIN)
|
||||
errs() << " in file '" << FileName << "'";
|
||||
errs() << ".\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<tooling::Range> Ranges;
|
||||
if (fillRanges(Code.get(), Ranges))
|
||||
return true;
|
||||
StringRef AssumedFileName = IsSTDIN ? AssumeFileName : FileName;
|
||||
if (AssumedFileName.empty()) {
|
||||
llvm::errs() << "error: empty filenames are not allowed\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
auto RealFS = vfs::getRealFileSystem();
|
||||
auto CustomFS = new vfs::CustomFileSystem(RealFS);
|
||||
IntrusiveRefCntPtr<vfs::FileSystem> CustomFSPtr(CustomFS);
|
||||
Expected<FormatStyle> FormatStyle =
|
||||
getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(),
|
||||
CustomFSPtr.get(), WNoErrorList.isSet(WNoError::Unknown));
|
||||
if (!FormatStyle) {
|
||||
llvm::errs() << toString(FormatStyle.takeError()) << "\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
StringRef QualifierAlignmentOrder = QualifierAlignment;
|
||||
|
||||
FormatStyle->QualifierAlignment =
|
||||
StringSwitch<FormatStyle::QualifierAlignmentStyle>(
|
||||
QualifierAlignmentOrder.lower())
|
||||
.Case("right", FormatStyle::QAS_Right)
|
||||
.Case("left", FormatStyle::QAS_Left)
|
||||
.Default(FormatStyle->QualifierAlignment);
|
||||
|
||||
if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Left) {
|
||||
FormatStyle->QualifierOrder = {"const", "volatile", "type"};
|
||||
} else if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Right) {
|
||||
FormatStyle->QualifierOrder = {"type", "const", "volatile"};
|
||||
} else if (QualifierAlignmentOrder.contains("type")) {
|
||||
FormatStyle->QualifierAlignment = FormatStyle::QAS_Custom;
|
||||
SmallVector<StringRef> Qualifiers;
|
||||
QualifierAlignmentOrder.split(Qualifiers, " ", /*MaxSplit=*/-1,
|
||||
/*KeepEmpty=*/false);
|
||||
FormatStyle->QualifierOrder = {Qualifiers.begin(), Qualifiers.end()};
|
||||
}
|
||||
|
||||
if (SortIncludes.getNumOccurrences() != 0) {
|
||||
FormatStyle->SortIncludes = {};
|
||||
if (SortIncludes)
|
||||
FormatStyle->SortIncludes.Enabled = true;
|
||||
}
|
||||
unsigned CursorPosition = Cursor;
|
||||
Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges,
|
||||
AssumedFileName, &CursorPosition);
|
||||
|
||||
const bool IsJson = FormatStyle->isJson();
|
||||
|
||||
// To format JSON insert a variable to trick the code into thinking its
|
||||
// JavaScript.
|
||||
if (IsJson && !FormatStyle->DisableFormat) {
|
||||
auto Err =
|
||||
Replaces.add(tooling::Replacement(AssumedFileName, 0, 0, "x = "));
|
||||
if (Err)
|
||||
llvm::errs() << "Bad Json variable insertion\n";
|
||||
}
|
||||
|
||||
auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces);
|
||||
if (!ChangedCode) {
|
||||
llvm::errs() << toString(ChangedCode.takeError()) << "\n";
|
||||
return true;
|
||||
}
|
||||
// Get new affected ranges after sorting `#includes`.
|
||||
Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges);
|
||||
FormattingAttemptStatus Status;
|
||||
Replacements FormatChanges =
|
||||
reformat(*FormatStyle, *ChangedCode, Ranges, AssumedFileName, &Status);
|
||||
Replaces = Replaces.merge(FormatChanges);
|
||||
if (DryRun) {
|
||||
return Replaces.size() > (IsJson ? 1u : 0u) &&
|
||||
emitReplacementWarnings(Replaces, AssumedFileName, Code);
|
||||
}
|
||||
if (OutputXML) {
|
||||
outputXML(Replaces, FormatChanges, Status, Cursor, CursorPosition);
|
||||
} else {
|
||||
IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
|
||||
new llvm::vfs::InMemoryFileSystem);
|
||||
FileManager Files(FileSystemOptions(), InMemoryFileSystem);
|
||||
|
||||
DiagnosticOptions DiagOpts;
|
||||
ClangFormatDiagConsumer IgnoreDiagnostics;
|
||||
DiagnosticsEngine Diagnostics(
|
||||
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), DiagOpts,
|
||||
&IgnoreDiagnostics, false);
|
||||
SourceManager Sources(Diagnostics, Files);
|
||||
FileID ID = createInMemoryFile(AssumedFileName, *Code, Sources, Files,
|
||||
InMemoryFileSystem.get());
|
||||
Rewriter Rewrite(Sources, LangOptions());
|
||||
tooling::applyAllReplacements(Replaces, Rewrite);
|
||||
if (Inplace) {
|
||||
if (Rewrite.overwriteChangedFiles())
|
||||
return true;
|
||||
} else {
|
||||
if (Cursor.getNumOccurrences() != 0) {
|
||||
outs() << "{ \"Cursor\": "
|
||||
<< FormatChanges.getShiftedCodePosition(CursorPosition)
|
||||
<< ", \"IncompleteFormat\": "
|
||||
<< (Status.FormatComplete ? "false" : "true");
|
||||
if (!Status.FormatComplete)
|
||||
outs() << ", \"Line\": " << Status.Line;
|
||||
outs() << " }\n";
|
||||
}
|
||||
Rewrite.getEditBuffer(ID).write(outs());
|
||||
}
|
||||
}
|
||||
return ErrorOnIncompleteFormat && !Status.FormatComplete;
|
||||
}
|
||||
|
||||
} // namespace format
|
||||
} // namespace clang
|
||||
|
||||
static void PrintVersion(raw_ostream &OS) {
|
||||
OS << clang::getClangToolFullVersion("clang-format") << '\n';
|
||||
}
|
||||
|
||||
// Dump the configuration.
|
||||
static int dumpConfig() {
|
||||
std::unique_ptr<llvm::MemoryBuffer> Code;
|
||||
// We can't read the code to detect the language if there's no file name.
|
||||
if (!FileNames.empty()) {
|
||||
// Read in the code in case the filename alone isn't enough to detect the
|
||||
// language.
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
|
||||
MemoryBuffer::getFileOrSTDIN(FileNames[0], /*IsText=*/true);
|
||||
if (std::error_code EC = CodeOrErr.getError()) {
|
||||
llvm::errs() << EC.message() << "\n";
|
||||
return 1;
|
||||
}
|
||||
Code = std::move(CodeOrErr.get());
|
||||
}
|
||||
|
||||
auto RealFS = vfs::getRealFileSystem();
|
||||
auto CustomFS = new vfs::CustomFileSystem(RealFS);
|
||||
IntrusiveRefCntPtr<vfs::FileSystem> CustomFSPtr(CustomFS);
|
||||
|
||||
Expected<clang::format::FormatStyle> FormatStyle = clang::format::getStyle(
|
||||
Style,
|
||||
FileNames.empty() || FileNames[0] == "-" ? AssumeFileName : FileNames[0],
|
||||
FallbackStyle, Code ? Code->getBuffer() : "", CustomFSPtr.get());
|
||||
if (!FormatStyle) {
|
||||
llvm::errs() << toString(FormatStyle.takeError()) << "\n";
|
||||
return 1;
|
||||
}
|
||||
std::string Config = clang::format::configurationAsText(*FormatStyle);
|
||||
outs() << Config << "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
using String = SmallString<128>;
|
||||
static String IgnoreDir; // Directory of .clang-format-ignore file.
|
||||
static String PrevDir; // Directory of previous `FilePath`.
|
||||
static SmallVector<String> Patterns; // Patterns in .clang-format-ignore file.
|
||||
|
||||
// Check whether `FilePath` is ignored according to the nearest
|
||||
// .clang-format-ignore file based on the rules below:
|
||||
// - A blank line is skipped.
|
||||
// - Leading and trailing spaces of a line are trimmed.
|
||||
// - A line starting with a hash (`#`) is a comment.
|
||||
// - A non-comment line is a single pattern.
|
||||
// - The slash (`/`) is used as the directory separator.
|
||||
// - A pattern is relative to the directory of the .clang-format-ignore file (or
|
||||
// the root directory if the pattern starts with a slash).
|
||||
// - A pattern is negated if it starts with a bang (`!`).
|
||||
static bool isIgnored(StringRef FilePath) {
|
||||
using namespace llvm::sys::fs;
|
||||
if (!is_regular_file(FilePath))
|
||||
return false;
|
||||
|
||||
String Path;
|
||||
String AbsPath{FilePath};
|
||||
|
||||
auto PathStyle = vfs::getPathStyle();
|
||||
|
||||
using namespace llvm::sys::path;
|
||||
vfs::make_absolute(AbsPath);
|
||||
remove_dots(AbsPath, /*remove_dot_dot=*/true, PathStyle);
|
||||
|
||||
if (StringRef Dir{parent_path(AbsPath, PathStyle)}; PrevDir != Dir) {
|
||||
PrevDir = Dir;
|
||||
|
||||
for (;;) {
|
||||
Path = Dir;
|
||||
append(Path, PathStyle, ".clang-format-ignore");
|
||||
if (is_regular_file(Path))
|
||||
break;
|
||||
Dir = parent_path(Dir, PathStyle);
|
||||
if (Dir.empty())
|
||||
return false;
|
||||
}
|
||||
|
||||
IgnoreDir = convert_to_slash(Dir, PathStyle);
|
||||
|
||||
std::ifstream IgnoreFile{Path.c_str()};
|
||||
if (!IgnoreFile.good())
|
||||
return false;
|
||||
|
||||
Patterns.clear();
|
||||
|
||||
for (std::string Line; std::getline(IgnoreFile, Line);) {
|
||||
if (const auto Pattern{StringRef{Line}.trim()};
|
||||
// Skip empty and comment lines.
|
||||
!Pattern.empty() && Pattern[0] != '#') {
|
||||
Patterns.push_back(Pattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IgnoreDir.empty())
|
||||
return false;
|
||||
|
||||
const auto Pathname{convert_to_slash(AbsPath, PathStyle)};
|
||||
for (const auto &Pat : Patterns) {
|
||||
const bool IsNegated = Pat[0] == '!';
|
||||
StringRef Pattern{Pat};
|
||||
if (IsNegated)
|
||||
Pattern = Pattern.drop_front();
|
||||
|
||||
if (Pattern.empty())
|
||||
continue;
|
||||
|
||||
Pattern = Pattern.ltrim();
|
||||
|
||||
// `Pattern` is relative to `IgnoreDir` unless it starts with a slash.
|
||||
// This doesn't support patterns containing drive names (e.g. `C:`).
|
||||
if (Pattern[0] != '/') {
|
||||
Path = IgnoreDir;
|
||||
append(Path, Style::posix, Pattern);
|
||||
remove_dots(Path, /*remove_dot_dot=*/true, Style::posix);
|
||||
Pattern = Path;
|
||||
}
|
||||
|
||||
if (clang::format::matchFilePath(Pattern, Pathname) == !IsNegated)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
InitLLVM X(argc, argv);
|
||||
|
||||
cl::HideUnrelatedOptions(ClangFormatCategory);
|
||||
|
||||
cl::SetVersionPrinter(PrintVersion);
|
||||
cl::ParseCommandLineOptions(
|
||||
argc, argv,
|
||||
"A tool to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# "
|
||||
"code.\n\n"
|
||||
"If no arguments are specified, it formats the code from standard input\n"
|
||||
"and writes the result to the standard output.\n"
|
||||
"If <file>s are given, it reformats the files. If -i is specified\n"
|
||||
"together with <file>s, the files are edited in-place. Otherwise, the\n"
|
||||
"result is written to the standard output.\n");
|
||||
|
||||
if (Help) {
|
||||
cl::PrintHelpMessage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (DumpConfig)
|
||||
return dumpConfig();
|
||||
|
||||
if (!Files.empty()) {
|
||||
std::ifstream ExternalFileOfFiles{std::string(Files)};
|
||||
std::string Line;
|
||||
unsigned LineNo = 1;
|
||||
while (std::getline(ExternalFileOfFiles, Line)) {
|
||||
FileNames.push_back(Line);
|
||||
LineNo++;
|
||||
}
|
||||
errs() << "Clang-formatting " << LineNo << " files\n";
|
||||
}
|
||||
|
||||
if (FileNames.empty()) {
|
||||
if (isIgnored(AssumeFileName))
|
||||
return 0;
|
||||
return clang::format::format("-", FailOnIncompleteFormat);
|
||||
}
|
||||
|
||||
if (FileNames.size() > 1 &&
|
||||
(!Offsets.empty() || !Lengths.empty() || !LineRanges.empty())) {
|
||||
errs() << "error: -offset, -length and -lines can only be used for "
|
||||
"single file.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned FileNo = 1;
|
||||
bool Error = false;
|
||||
for (const auto &FileName : FileNames) {
|
||||
const bool Ignored = isIgnored(FileName);
|
||||
if (ListIgnored) {
|
||||
if (Ignored)
|
||||
outs() << FileName << '\n';
|
||||
continue;
|
||||
}
|
||||
if (Ignored)
|
||||
continue;
|
||||
if (Verbose) {
|
||||
errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] "
|
||||
<< FileName << "\n";
|
||||
}
|
||||
Error |= clang::format::format(FileName, FailOnIncompleteFormat);
|
||||
}
|
||||
return Error ? 1 : 0;
|
||||
}
|
||||
858
frontend/src/common/prettier/plugins/clang/git-clang-format
Normal file
858
frontend/src/common/prettier/plugins/clang/git-clang-format
Normal file
@@ -0,0 +1,858 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# ===- git-clang-format - ClangFormat Git Integration -------*- python -*--=== #
|
||||
#
|
||||
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
# See https://llvm.org/LICENSE.txt for license information.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
#
|
||||
# ===----------------------------------------------------------------------=== #
|
||||
|
||||
r"""
|
||||
clang-format git integration
|
||||
============================
|
||||
|
||||
This file provides a clang-format integration for git. Put it somewhere in your
|
||||
path and ensure that it is executable. Then, "git clang-format" will invoke
|
||||
clang-format on the changes in current files or a specific commit.
|
||||
|
||||
For further details, run:
|
||||
git clang-format -h
|
||||
|
||||
Requires Python version >=3.8
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
import argparse
|
||||
import collections
|
||||
import contextlib
|
||||
import errno
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
usage = "git clang-format [OPTIONS] [<commit>] [<commit>|--staged] [--] [<file>...]"
|
||||
|
||||
desc = """
|
||||
If zero or one commits are given, run clang-format on all lines that differ
|
||||
between the working directory and <commit>, which defaults to HEAD. Changes are
|
||||
only applied to the working directory, or in the stage/index.
|
||||
|
||||
Examples:
|
||||
To format staged changes, i.e everything that's been `git add`ed:
|
||||
git clang-format
|
||||
|
||||
To also format everything touched in the most recent commit:
|
||||
git clang-format HEAD~1
|
||||
|
||||
If you're on a branch off main, to format everything touched on your branch:
|
||||
git clang-format main
|
||||
|
||||
If two commits are given (requires --diff), run clang-format on all lines in the
|
||||
second <commit> that differ from the first <commit>.
|
||||
|
||||
The following git-config settings set the default of the corresponding option:
|
||||
clangFormat.binary
|
||||
clangFormat.commit
|
||||
clangFormat.extensions
|
||||
clangFormat.style
|
||||
"""
|
||||
|
||||
# Name of the temporary index file in which save the output of clang-format.
|
||||
# This file is created within the .git directory.
|
||||
temp_index_basename = "clang-format-index"
|
||||
|
||||
|
||||
Range = collections.namedtuple("Range", "start, count")
|
||||
|
||||
|
||||
def main():
|
||||
config = load_git_config()
|
||||
|
||||
# In order to keep '--' yet allow options after positionals, we need to
|
||||
# check for '--' ourselves. (Setting nargs='*' throws away the '--', while
|
||||
# nargs=argparse.REMAINDER disallows options after positionals.)
|
||||
argv = sys.argv[1:]
|
||||
try:
|
||||
idx = argv.index("--")
|
||||
except ValueError:
|
||||
dash_dash = []
|
||||
else:
|
||||
dash_dash = argv[idx:]
|
||||
argv = argv[:idx]
|
||||
|
||||
default_extensions = ",".join(
|
||||
[
|
||||
# From clang/lib/Frontend/FrontendOptions.cpp, all lower case
|
||||
"c",
|
||||
"h", # C
|
||||
"m", # ObjC
|
||||
"mm", # ObjC++
|
||||
"cc",
|
||||
"cp",
|
||||
"cpp",
|
||||
"c++",
|
||||
"cxx",
|
||||
"hh",
|
||||
"hpp",
|
||||
"hxx",
|
||||
"inc", # C++
|
||||
"ccm",
|
||||
"cppm",
|
||||
"cxxm",
|
||||
"c++m", # C++ Modules
|
||||
"cu",
|
||||
"cuh", # CUDA
|
||||
"cl", # OpenCL
|
||||
# Other languages that clang-format supports
|
||||
"proto",
|
||||
"protodevel", # Protocol Buffers
|
||||
"java", # Java
|
||||
"js",
|
||||
"mjs",
|
||||
"cjs", # JavaScript
|
||||
"ts", # TypeScript
|
||||
"cs", # C Sharp
|
||||
"json",
|
||||
"ipynb", # Json
|
||||
"sv",
|
||||
"svh",
|
||||
"v",
|
||||
"vh", # Verilog
|
||||
"td", # TableGen
|
||||
"txtpb",
|
||||
"textpb",
|
||||
"pb.txt",
|
||||
"textproto",
|
||||
"asciipb", # TextProto
|
||||
]
|
||||
)
|
||||
|
||||
p = argparse.ArgumentParser(
|
||||
usage=usage,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description=desc,
|
||||
)
|
||||
p.add_argument(
|
||||
"--binary",
|
||||
default=config.get("clangformat.binary", "clang-format"),
|
||||
help="path to clang-format",
|
||||
),
|
||||
p.add_argument(
|
||||
"--commit",
|
||||
default=config.get("clangformat.commit", "HEAD"),
|
||||
help="default commit to use if none is specified",
|
||||
),
|
||||
p.add_argument(
|
||||
"--diff",
|
||||
action="store_true",
|
||||
help="print a diff instead of applying the changes",
|
||||
)
|
||||
p.add_argument(
|
||||
"--diffstat",
|
||||
action="store_true",
|
||||
help="print a diffstat instead of applying the changes",
|
||||
)
|
||||
p.add_argument(
|
||||
"--extensions",
|
||||
default=config.get("clangformat.extensions", default_extensions),
|
||||
help=(
|
||||
"comma-separated list of file extensions to format, "
|
||||
"excluding the period and case-insensitive"
|
||||
),
|
||||
),
|
||||
p.add_argument(
|
||||
"-f",
|
||||
"--force",
|
||||
action="store_true",
|
||||
help="allow changes to unstaged files",
|
||||
)
|
||||
p.add_argument(
|
||||
"-p", "--patch", action="store_true", help="select hunks interactively"
|
||||
)
|
||||
p.add_argument(
|
||||
"-q",
|
||||
"--quiet",
|
||||
action="count",
|
||||
default=0,
|
||||
help="print less information",
|
||||
)
|
||||
p.add_argument(
|
||||
"--staged",
|
||||
"--cached",
|
||||
action="store_true",
|
||||
help="format lines in the stage instead of the working dir",
|
||||
)
|
||||
p.add_argument(
|
||||
"--style",
|
||||
default=config.get("clangformat.style", None),
|
||||
help="passed to clang-format",
|
||||
),
|
||||
p.add_argument(
|
||||
"-v",
|
||||
"--verbose",
|
||||
action="count",
|
||||
default=0,
|
||||
help="print extra information",
|
||||
)
|
||||
p.add_argument(
|
||||
"--diff_from_common_commit",
|
||||
action="store_true",
|
||||
help=(
|
||||
"diff from the last common commit for commits in "
|
||||
"separate branches rather than the exact point of the "
|
||||
"commits"
|
||||
),
|
||||
)
|
||||
# We gather all the remaining positional arguments into 'args' since we need
|
||||
# to use some heuristics to determine whether or not <commit> was present.
|
||||
# However, to print pretty messages, we make use of metavar and help.
|
||||
p.add_argument(
|
||||
"args",
|
||||
nargs="*",
|
||||
metavar="<commit>",
|
||||
help="revision from which to compute the diff",
|
||||
)
|
||||
p.add_argument(
|
||||
"ignored",
|
||||
nargs="*",
|
||||
metavar="<file>...",
|
||||
help="if specified, only consider differences in these files",
|
||||
)
|
||||
opts = p.parse_args(argv)
|
||||
|
||||
opts.verbose -= opts.quiet
|
||||
del opts.quiet
|
||||
|
||||
commits, files = interpret_args(opts.args, dash_dash, opts.commit)
|
||||
if len(commits) > 2:
|
||||
die("at most two commits allowed; %d given" % len(commits))
|
||||
if len(commits) == 2:
|
||||
if opts.staged:
|
||||
die("--staged is not allowed when two commits are given")
|
||||
if not opts.diff:
|
||||
die("--diff is required when two commits are given")
|
||||
elif opts.diff_from_common_commit:
|
||||
die("--diff_from_common_commit is only allowed when two commits are given")
|
||||
|
||||
if os.path.dirname(opts.binary):
|
||||
opts.binary = os.path.abspath(opts.binary)
|
||||
|
||||
changed_lines = compute_diff_and_extract_lines(
|
||||
commits, files, opts.staged, opts.diff_from_common_commit
|
||||
)
|
||||
if opts.verbose >= 1:
|
||||
ignored_files = set(changed_lines)
|
||||
filter_by_extension(changed_lines, opts.extensions.lower().split(","))
|
||||
# The computed diff outputs absolute paths, so we must cd before accessing
|
||||
# those files.
|
||||
cd_to_toplevel()
|
||||
filter_symlinks(changed_lines)
|
||||
filter_ignored_files(changed_lines, binary=opts.binary)
|
||||
if opts.verbose >= 1:
|
||||
ignored_files.difference_update(changed_lines)
|
||||
if ignored_files:
|
||||
print(
|
||||
"Ignoring the following files (wrong extension, symlink, or "
|
||||
"ignored by clang-format):"
|
||||
)
|
||||
for filename in ignored_files:
|
||||
print(" %s" % filename)
|
||||
if changed_lines:
|
||||
print("Running clang-format on the following files:")
|
||||
for filename in changed_lines:
|
||||
print(" %s" % filename)
|
||||
|
||||
if not changed_lines:
|
||||
if opts.verbose >= 0:
|
||||
print("no modified files to format")
|
||||
return 0
|
||||
|
||||
if len(commits) > 1:
|
||||
old_tree = commits[1]
|
||||
revision = old_tree
|
||||
elif opts.staged:
|
||||
old_tree = create_tree_from_index(changed_lines)
|
||||
revision = ""
|
||||
else:
|
||||
old_tree = create_tree_from_workdir(changed_lines)
|
||||
revision = None
|
||||
new_tree = run_clang_format_and_save_to_tree(
|
||||
changed_lines, revision, binary=opts.binary, style=opts.style
|
||||
)
|
||||
if opts.verbose >= 1:
|
||||
print("old tree: %s" % old_tree)
|
||||
print("new tree: %s" % new_tree)
|
||||
|
||||
if old_tree == new_tree:
|
||||
if opts.verbose >= 0:
|
||||
print("clang-format did not modify any files")
|
||||
return 0
|
||||
|
||||
if opts.diff:
|
||||
return print_diff(old_tree, new_tree)
|
||||
if opts.diffstat:
|
||||
return print_diffstat(old_tree, new_tree)
|
||||
|
||||
changed_files = apply_changes(
|
||||
old_tree, new_tree, force=opts.force, patch_mode=opts.patch
|
||||
)
|
||||
if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1:
|
||||
print("changed files:")
|
||||
for filename in changed_files:
|
||||
print(" %s" % filename)
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
def load_git_config(non_string_options=None):
|
||||
"""Return the git configuration as a dictionary.
|
||||
|
||||
All options are assumed to be strings unless in `non_string_options`, in
|
||||
which is a dictionary mapping option name (in lower case) to either "--bool"
|
||||
or "--int"."""
|
||||
if non_string_options is None:
|
||||
non_string_options = {}
|
||||
out = {}
|
||||
for entry in run("git", "config", "--list", "--null").split("\0"):
|
||||
if entry:
|
||||
if "\n" in entry:
|
||||
name, value = entry.split("\n", 1)
|
||||
else:
|
||||
# A setting with no '=' ('\n' with --null) is implicitly 'true'
|
||||
name = entry
|
||||
value = "true"
|
||||
if name in non_string_options:
|
||||
value = run("git", "config", non_string_options[name], name)
|
||||
out[name] = value
|
||||
return out
|
||||
|
||||
|
||||
def interpret_args(args, dash_dash, default_commit):
|
||||
"""Interpret `args` as "[commits] [--] [files]" and return (commits, files).
|
||||
|
||||
It is assumed that "--" and everything that follows has been removed from
|
||||
args and placed in `dash_dash`.
|
||||
|
||||
If "--" is present (i.e., `dash_dash` is non-empty), the arguments to its
|
||||
left (if present) are taken as commits. Otherwise, the arguments are
|
||||
checked from left to right if they are commits or files. If commits are not
|
||||
given, a list with `default_commit` is used."""
|
||||
if dash_dash:
|
||||
if len(args) == 0:
|
||||
commits = [default_commit]
|
||||
else:
|
||||
commits = args
|
||||
for commit in commits:
|
||||
object_type = get_object_type(commit)
|
||||
if object_type not in ("commit", "tag"):
|
||||
if object_type is None:
|
||||
die("'%s' is not a commit" % commit)
|
||||
else:
|
||||
die(
|
||||
"'%s' is a %s, but a commit was expected"
|
||||
% (commit, object_type)
|
||||
)
|
||||
files = dash_dash[1:]
|
||||
elif args:
|
||||
commits = []
|
||||
while args:
|
||||
if not disambiguate_revision(args[0]):
|
||||
break
|
||||
commits.append(args.pop(0))
|
||||
if not commits:
|
||||
commits = [default_commit]
|
||||
files = args
|
||||
else:
|
||||
commits = [default_commit]
|
||||
files = []
|
||||
return commits, files
|
||||
|
||||
|
||||
def disambiguate_revision(value):
|
||||
"""Returns True if `value` is a revision, False if it is a file, or dies."""
|
||||
# If `value` is ambiguous (neither a commit nor a file), the following
|
||||
# command will die with an appropriate error message.
|
||||
run("git", "rev-parse", value, verbose=False)
|
||||
object_type = get_object_type(value)
|
||||
if object_type is None:
|
||||
return False
|
||||
if object_type in ("commit", "tag"):
|
||||
return True
|
||||
die("`%s` is a %s, but a commit or filename was expected" % (value, object_type))
|
||||
|
||||
|
||||
def get_object_type(value):
|
||||
"""Returns a string description of an object's type, or None if it is not
|
||||
a valid git object."""
|
||||
cmd = ["git", "cat-file", "-t", value]
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
stdout, stderr = p.communicate()
|
||||
if p.returncode != 0:
|
||||
return None
|
||||
return convert_string(stdout.strip())
|
||||
|
||||
|
||||
def compute_diff_and_extract_lines(commits, files, staged, diff_common_commit):
|
||||
"""Calls compute_diff() followed by extract_lines()."""
|
||||
diff_process = compute_diff(commits, files, staged, diff_common_commit)
|
||||
changed_lines = extract_lines(diff_process.stdout)
|
||||
diff_process.stdout.close()
|
||||
diff_process.wait()
|
||||
if diff_process.returncode != 0:
|
||||
# Assume error was already printed to stderr.
|
||||
sys.exit(2)
|
||||
return changed_lines
|
||||
|
||||
|
||||
def compute_diff(commits, files, staged, diff_common_commit):
|
||||
"""Return a subprocess object producing the diff from `commits`.
|
||||
|
||||
The return value's `stdin` file object will produce a patch with the
|
||||
differences between the working directory (or stage if --staged is used) and
|
||||
the first commit if a single one was specified, or the difference between
|
||||
both specified commits, filtered on `files` (if non-empty).
|
||||
Zero context lines are used in the patch."""
|
||||
git_tool = "diff-index"
|
||||
extra_args = []
|
||||
if len(commits) == 2:
|
||||
git_tool = "diff-tree"
|
||||
if diff_common_commit:
|
||||
commits = [f"{commits[0]}...{commits[1]}"]
|
||||
elif staged:
|
||||
extra_args += ["--cached"]
|
||||
|
||||
cmd = ["git", git_tool, "-p", "-U0"] + extra_args + commits + ["--"]
|
||||
cmd.extend(files)
|
||||
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
p.stdin.close()
|
||||
return p
|
||||
|
||||
|
||||
def extract_lines(patch_file):
|
||||
"""Extract the changed lines in `patch_file`.
|
||||
|
||||
The return value is a dictionary mapping filename to a list of (start_line,
|
||||
line_count) pairs.
|
||||
|
||||
The input must have been produced with ``-U0``, meaning unidiff format with
|
||||
zero lines of context. The return value is a dict mapping filename to a
|
||||
list of line `Range`s."""
|
||||
matches = {}
|
||||
for line in patch_file:
|
||||
line = convert_string(line)
|
||||
match = re.search(r"^\+\+\+\ [^/]+/(.*)", line)
|
||||
if match:
|
||||
filename = match.group(1).rstrip("\r\n\t")
|
||||
match = re.search(r"^@@ -[0-9,]+ \+(\d+)(,(\d+))?", line)
|
||||
if match:
|
||||
start_line = int(match.group(1))
|
||||
line_count = 1
|
||||
if match.group(3):
|
||||
line_count = int(match.group(3))
|
||||
if line_count == 0:
|
||||
line_count = 1
|
||||
if start_line == 0:
|
||||
continue
|
||||
matches.setdefault(filename, []).append(Range(start_line, line_count))
|
||||
return matches
|
||||
|
||||
|
||||
def filter_by_extension(dictionary, allowed_extensions):
|
||||
"""Delete every key in `dictionary` that doesn't have an allowed extension.
|
||||
|
||||
`allowed_extensions` must be a collection of lowercase file extensions,
|
||||
excluding the period."""
|
||||
allowed_extensions = frozenset(allowed_extensions)
|
||||
for filename in list(dictionary.keys()):
|
||||
base_ext = filename.rsplit(".", 1)
|
||||
if len(base_ext) == 1 and "" in allowed_extensions:
|
||||
continue
|
||||
if len(base_ext) == 1 or base_ext[1].lower() not in allowed_extensions:
|
||||
del dictionary[filename]
|
||||
|
||||
|
||||
def filter_symlinks(dictionary):
|
||||
"""Delete every key in `dictionary` that is a symlink."""
|
||||
for filename in list(dictionary.keys()):
|
||||
if os.path.islink(filename):
|
||||
del dictionary[filename]
|
||||
|
||||
|
||||
def filter_ignored_files(dictionary, binary):
|
||||
"""Delete every key in `dictionary` that is ignored by clang-format."""
|
||||
ignored_files = run(binary, "-list-ignored", *dictionary.keys())
|
||||
if not ignored_files:
|
||||
return
|
||||
ignored_files = ignored_files.split("\n")
|
||||
for filename in ignored_files:
|
||||
del dictionary[filename]
|
||||
|
||||
|
||||
def cd_to_toplevel():
|
||||
"""Change to the top level of the git repository."""
|
||||
toplevel = run("git", "rev-parse", "--show-toplevel")
|
||||
os.chdir(toplevel)
|
||||
|
||||
|
||||
def create_tree_from_workdir(filenames):
|
||||
"""Create a new git tree with the given files from the working directory.
|
||||
|
||||
Returns the object ID (SHA-1) of the created tree."""
|
||||
return create_tree(filenames, "--stdin")
|
||||
|
||||
|
||||
def create_tree_from_index(filenames):
|
||||
# Copy the environment, because the files have to be read from the original
|
||||
# index.
|
||||
env = os.environ.copy()
|
||||
|
||||
def index_contents_generator():
|
||||
for filename in filenames:
|
||||
git_ls_files_cmd = [
|
||||
"git",
|
||||
"ls-files",
|
||||
"--stage",
|
||||
"-z",
|
||||
"--",
|
||||
filename,
|
||||
]
|
||||
git_ls_files = subprocess.Popen(
|
||||
git_ls_files_cmd,
|
||||
env=env,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
stdout = git_ls_files.communicate()[0]
|
||||
yield convert_string(stdout.split(b"\0")[0])
|
||||
|
||||
return create_tree(index_contents_generator(), "--index-info")
|
||||
|
||||
|
||||
def run_clang_format_and_save_to_tree(
|
||||
changed_lines, revision=None, binary="clang-format", style=None
|
||||
):
|
||||
"""Run clang-format on each file and save the result to a git tree.
|
||||
|
||||
Returns the object ID (SHA-1) of the created tree."""
|
||||
# Copy the environment when formatting the files in the index, because the
|
||||
# files have to be read from the original index.
|
||||
env = os.environ.copy() if revision == "" else None
|
||||
|
||||
def iteritems(container):
|
||||
try:
|
||||
return container.iteritems() # Python 2
|
||||
except AttributeError:
|
||||
return container.items() # Python 3
|
||||
|
||||
def index_info_generator():
|
||||
for filename, line_ranges in iteritems(changed_lines):
|
||||
if revision is not None:
|
||||
if len(revision) > 0:
|
||||
git_metadata_cmd = [
|
||||
"git",
|
||||
"ls-tree",
|
||||
"%s:%s" % (revision, os.path.dirname(filename)),
|
||||
os.path.basename(filename),
|
||||
]
|
||||
else:
|
||||
git_metadata_cmd = [
|
||||
"git",
|
||||
"ls-files",
|
||||
"--stage",
|
||||
"--",
|
||||
filename,
|
||||
]
|
||||
git_metadata = subprocess.Popen(
|
||||
git_metadata_cmd,
|
||||
env=env,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
stdout = git_metadata.communicate()[0]
|
||||
mode = oct(int(stdout.split()[0], 8))
|
||||
else:
|
||||
mode = oct(os.stat(filename).st_mode)
|
||||
# Adjust python3 octal format so that it matches what git expects
|
||||
if mode.startswith("0o"):
|
||||
mode = "0" + mode[2:]
|
||||
blob_id = clang_format_to_blob(
|
||||
filename,
|
||||
line_ranges,
|
||||
revision=revision,
|
||||
binary=binary,
|
||||
style=style,
|
||||
env=env,
|
||||
)
|
||||
yield "%s %s\t%s" % (mode, blob_id, filename)
|
||||
|
||||
return create_tree(index_info_generator(), "--index-info")
|
||||
|
||||
|
||||
def create_tree(input_lines, mode):
|
||||
"""Create a tree object from the given input.
|
||||
|
||||
If mode is '--stdin', it must be a list of filenames. If mode is
|
||||
'--index-info' is must be a list of values suitable for "git update-index
|
||||
--index-info", such as "<mode> <SP> <sha1> <TAB> <filename>". Any other
|
||||
mode is invalid."""
|
||||
assert mode in ("--stdin", "--index-info")
|
||||
cmd = ["git", "update-index", "--add", "-z", mode]
|
||||
with temporary_index_file():
|
||||
p = subprocess.Popen(cmd, stdin=subprocess.PIPE)
|
||||
for line in input_lines:
|
||||
p.stdin.write(to_bytes("%s\0" % line))
|
||||
p.stdin.close()
|
||||
if p.wait() != 0:
|
||||
die("`%s` failed" % " ".join(cmd))
|
||||
tree_id = run("git", "write-tree")
|
||||
return tree_id
|
||||
|
||||
|
||||
def clang_format_to_blob(
|
||||
filename,
|
||||
line_ranges,
|
||||
revision=None,
|
||||
binary="clang-format",
|
||||
style=None,
|
||||
env=None,
|
||||
):
|
||||
"""Run clang-format on the given file and save the result to a git blob.
|
||||
|
||||
Runs on the file in `revision` if not None, or on the file in the working
|
||||
directory if `revision` is None. Revision can be set to an empty string to
|
||||
run clang-format on the file in the index.
|
||||
|
||||
Returns the object ID (SHA-1) of the created blob."""
|
||||
clang_format_cmd = [binary]
|
||||
if style:
|
||||
clang_format_cmd.extend(["--style=" + style])
|
||||
clang_format_cmd.extend(
|
||||
[
|
||||
"--lines=%s:%s" % (start_line, start_line + line_count - 1)
|
||||
for start_line, line_count in line_ranges
|
||||
]
|
||||
)
|
||||
if revision is not None:
|
||||
clang_format_cmd.extend(["--assume-filename=" + filename])
|
||||
git_show_cmd = [
|
||||
"git",
|
||||
"cat-file",
|
||||
"blob",
|
||||
"%s:%s" % (revision, filename),
|
||||
]
|
||||
git_show = subprocess.Popen(
|
||||
git_show_cmd, env=env, stdin=subprocess.PIPE, stdout=subprocess.PIPE
|
||||
)
|
||||
git_show.stdin.close()
|
||||
clang_format_stdin = git_show.stdout
|
||||
else:
|
||||
clang_format_cmd.extend([filename])
|
||||
git_show = None
|
||||
clang_format_stdin = subprocess.PIPE
|
||||
try:
|
||||
clang_format = subprocess.Popen(
|
||||
clang_format_cmd, stdin=clang_format_stdin, stdout=subprocess.PIPE
|
||||
)
|
||||
if clang_format_stdin == subprocess.PIPE:
|
||||
clang_format_stdin = clang_format.stdin
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENOENT:
|
||||
die('cannot find executable "%s"' % binary)
|
||||
else:
|
||||
raise
|
||||
clang_format_stdin.close()
|
||||
hash_object_cmd = [
|
||||
"git",
|
||||
"hash-object",
|
||||
"-w",
|
||||
"--path=" + filename,
|
||||
"--stdin",
|
||||
]
|
||||
hash_object = subprocess.Popen(
|
||||
hash_object_cmd, stdin=clang_format.stdout, stdout=subprocess.PIPE
|
||||
)
|
||||
clang_format.stdout.close()
|
||||
stdout = hash_object.communicate()[0]
|
||||
if hash_object.returncode != 0:
|
||||
die("`%s` failed" % " ".join(hash_object_cmd))
|
||||
if clang_format.wait() != 0:
|
||||
die("`%s` failed" % " ".join(clang_format_cmd))
|
||||
if git_show and git_show.wait() != 0:
|
||||
die("`%s` failed" % " ".join(git_show_cmd))
|
||||
return convert_string(stdout).rstrip("\r\n")
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def temporary_index_file(tree=None):
|
||||
"""Context manager for setting GIT_INDEX_FILE to a temporary file and
|
||||
deleting the file afterward."""
|
||||
index_path = create_temporary_index(tree)
|
||||
old_index_path = os.environ.get("GIT_INDEX_FILE")
|
||||
os.environ["GIT_INDEX_FILE"] = index_path
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
if old_index_path is None:
|
||||
del os.environ["GIT_INDEX_FILE"]
|
||||
else:
|
||||
os.environ["GIT_INDEX_FILE"] = old_index_path
|
||||
os.remove(index_path)
|
||||
|
||||
|
||||
def create_temporary_index(tree=None):
|
||||
"""Create a temporary index file and return the created file's path.
|
||||
|
||||
If `tree` is not None, use that as the tree to read in. Otherwise, an
|
||||
empty index is created."""
|
||||
gitdir = run("git", "rev-parse", "--git-dir")
|
||||
path = os.path.join(gitdir, temp_index_basename)
|
||||
if tree is None:
|
||||
tree = "--empty"
|
||||
run("git", "read-tree", "--index-output=" + path, tree)
|
||||
return path
|
||||
|
||||
|
||||
def print_diff(old_tree, new_tree):
|
||||
"""Print the diff between the two trees to stdout."""
|
||||
# We use the porcelain 'diff' and not plumbing 'diff-tree' because the
|
||||
# output is expected to be viewed by the user, and only the former does nice
|
||||
# things like color and pagination.
|
||||
#
|
||||
# We also only print modified files since `new_tree` only contains the files
|
||||
# that were modified, so unmodified files would show as deleted without the
|
||||
# filter.
|
||||
return subprocess.run(
|
||||
["git", "diff", "--diff-filter=M", "--exit-code", old_tree, new_tree]
|
||||
).returncode
|
||||
|
||||
|
||||
def print_diffstat(old_tree, new_tree):
|
||||
"""Print the diffstat between the two trees to stdout."""
|
||||
# We use the porcelain 'diff' and not plumbing 'diff-tree' because the
|
||||
# output is expected to be viewed by the user, and only the former does nice
|
||||
# things like color and pagination.
|
||||
#
|
||||
# We also only print modified files since `new_tree` only contains the files
|
||||
# that were modified, so unmodified files would show as deleted without the
|
||||
# filter.
|
||||
return subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"diff",
|
||||
"--diff-filter=M",
|
||||
"--exit-code",
|
||||
"--stat",
|
||||
old_tree,
|
||||
new_tree,
|
||||
]
|
||||
).returncode
|
||||
|
||||
|
||||
def apply_changes(old_tree, new_tree, force=False, patch_mode=False):
|
||||
"""Apply the changes in `new_tree` to the working directory.
|
||||
|
||||
Bails if there are local changes in those files and not `force`. If
|
||||
`patch_mode`, runs `git checkout --patch` to select hunks interactively."""
|
||||
changed_files = (
|
||||
run(
|
||||
"git",
|
||||
"diff-tree",
|
||||
"--diff-filter=M",
|
||||
"-r",
|
||||
"-z",
|
||||
"--name-only",
|
||||
old_tree,
|
||||
new_tree,
|
||||
)
|
||||
.rstrip("\0")
|
||||
.split("\0")
|
||||
)
|
||||
if not force:
|
||||
unstaged_files = run("git", "diff-files", "--name-status", *changed_files)
|
||||
if unstaged_files:
|
||||
print(
|
||||
"The following files would be modified but have unstaged changes:",
|
||||
file=sys.stderr,
|
||||
)
|
||||
print(unstaged_files, file=sys.stderr)
|
||||
print("Please commit, stage, or stash them first.", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
if patch_mode:
|
||||
# In patch mode, we could just as well create an index from the new tree
|
||||
# and checkout from that, but then the user will be presented with a
|
||||
# message saying "Discard ... from worktree". Instead, we use the old
|
||||
# tree as the index and checkout from new_tree, which gives the slightly
|
||||
# better message, "Apply ... to index and worktree". This is not quite
|
||||
# right, since it won't be applied to the user's index, but oh well.
|
||||
with temporary_index_file(old_tree):
|
||||
subprocess.run(["git", "checkout", "--patch", new_tree], check=True)
|
||||
index_tree = old_tree
|
||||
else:
|
||||
with temporary_index_file(new_tree):
|
||||
run("git", "checkout-index", "-f", "--", *changed_files)
|
||||
return changed_files
|
||||
|
||||
|
||||
def run(*args, **kwargs):
|
||||
stdin = kwargs.pop("stdin", "")
|
||||
verbose = kwargs.pop("verbose", True)
|
||||
strip = kwargs.pop("strip", True)
|
||||
for name in kwargs:
|
||||
raise TypeError("run() got an unexpected keyword argument '%s'" % name)
|
||||
p = subprocess.Popen(
|
||||
args,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
stdin=subprocess.PIPE,
|
||||
)
|
||||
stdout, stderr = p.communicate(input=stdin)
|
||||
|
||||
stdout = convert_string(stdout)
|
||||
stderr = convert_string(stderr)
|
||||
|
||||
if p.returncode == 0:
|
||||
if stderr:
|
||||
if verbose:
|
||||
print("`%s` printed to stderr:" % " ".join(args), file=sys.stderr)
|
||||
print(stderr.rstrip(), file=sys.stderr)
|
||||
if strip:
|
||||
stdout = stdout.rstrip("\r\n")
|
||||
return stdout
|
||||
if verbose:
|
||||
print("`%s` returned %s" % (" ".join(args), p.returncode), file=sys.stderr)
|
||||
if stderr:
|
||||
print(stderr.rstrip(), file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
def die(message):
|
||||
print("error:", message, file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
def to_bytes(str_input):
|
||||
# Encode to UTF-8 to get binary data.
|
||||
if isinstance(str_input, bytes):
|
||||
return str_input
|
||||
return str_input.encode("utf-8")
|
||||
|
||||
|
||||
def to_string(bytes_input):
|
||||
if isinstance(bytes_input, str):
|
||||
return bytes_input
|
||||
return bytes_input.encode("utf-8")
|
||||
|
||||
|
||||
def convert_string(bytes_input):
|
||||
try:
|
||||
return to_string(bytes_input.decode("utf-8"))
|
||||
except AttributeError: # 'str' object has no attribute 'decode'.
|
||||
return str(bytes_input)
|
||||
except UnicodeError:
|
||||
return str(bytes_input)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
155
frontend/src/common/prettier/plugins/clang/index.ts
Normal file
155
frontend/src/common/prettier/plugins/clang/index.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
* Prettier Plugin for C/C++ formatting using clang-format WebAssembly
|
||||
*
|
||||
* This plugin provides support for formatting C/C++ files using the clang-format WASM implementation.
|
||||
* It supports various C/C++ file extensions and common clang-format styles.
|
||||
*/
|
||||
import type { Plugin, Parser, Printer } from 'prettier';
|
||||
|
||||
// Import the clang-format WASM module
|
||||
import clangFormatInit, { format } from './clang-format-vite.js';
|
||||
|
||||
const parserName = 'clang';
|
||||
|
||||
// Language configuration
|
||||
const languages = [
|
||||
{
|
||||
name: 'C',
|
||||
aliases: ['c'],
|
||||
parsers: [parserName],
|
||||
extensions: ['.c', '.h'],
|
||||
aceMode: 'c_cpp',
|
||||
tmScope: 'source.c',
|
||||
linguistLanguageId: 50,
|
||||
vscodeLanguageIds: ['c']
|
||||
},
|
||||
{
|
||||
name: 'C++',
|
||||
aliases: ['cpp', 'cxx', 'cc'],
|
||||
parsers: [parserName],
|
||||
extensions: ['.cpp', '.cxx', '.cc', '.hpp', '.hxx', '.hh', '.C', '.H'],
|
||||
aceMode: 'c_cpp',
|
||||
tmScope: 'source.cpp',
|
||||
linguistLanguageId: 43,
|
||||
vscodeLanguageIds: ['cpp']
|
||||
},
|
||||
{
|
||||
name: 'Objective-C',
|
||||
aliases: ['objc', 'objectivec'],
|
||||
parsers: [parserName],
|
||||
extensions: ['.m', '.mm'],
|
||||
aceMode: 'objectivec',
|
||||
tmScope: 'source.objc',
|
||||
linguistLanguageId: 259,
|
||||
vscodeLanguageIds: ['objective-c']
|
||||
}
|
||||
];
|
||||
|
||||
// Parser configuration
|
||||
const clangParser: Parser<string> = {
|
||||
astFormat: parserName,
|
||||
parse: (text: string) => text,
|
||||
locStart: () => 0,
|
||||
locEnd: (node: string) => node.length,
|
||||
};
|
||||
|
||||
// Initialize clang-format WASM module
|
||||
let initPromise: Promise<void> | null = null;
|
||||
let isInitialized = false;
|
||||
|
||||
function initClangFormat(): Promise<void> {
|
||||
if (initPromise) {
|
||||
return initPromise;
|
||||
}
|
||||
|
||||
initPromise = (async () => {
|
||||
if (!isInitialized) {
|
||||
await clangFormatInit();
|
||||
isInitialized = true;
|
||||
}
|
||||
})();
|
||||
|
||||
return initPromise;
|
||||
}
|
||||
|
||||
// Printer configuration
|
||||
const clangPrinter: Printer<string> = {
|
||||
print: (path, options) => {
|
||||
try {
|
||||
if (!isInitialized) {
|
||||
console.warn('clang-format WASM module not initialized, returning original text');
|
||||
return (path as any).getValue ? (path as any).getValue() : path.node;
|
||||
}
|
||||
|
||||
const text = (path as any).getValue ? (path as any).getValue() : path.node;
|
||||
const style = getClangStyle(options);
|
||||
|
||||
// Format using clang-format (synchronous call)
|
||||
const formatted = format(text, undefined, style);
|
||||
|
||||
return formatted.trim();
|
||||
} catch (error) {
|
||||
console.warn('clang-format failed:', error);
|
||||
// Return original text if formatting fails
|
||||
return (path as any).getValue ? (path as any).getValue() : path.node;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
// 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';
|
||||
}
|
||||
|
||||
// Plugin options
|
||||
const options = {
|
||||
clangStyle: {
|
||||
since: '0.0.1',
|
||||
category: 'Format' as const,
|
||||
type: 'choice' as const,
|
||||
default: 'LLVM',
|
||||
description: 'The clang-format style to use',
|
||||
choices: [
|
||||
{ value: 'LLVM', description: 'LLVM coding standards' },
|
||||
{ value: 'Google', description: "Google's C++ style guide" },
|
||||
{ value: 'Chromium', description: "Chromium's style guide" },
|
||||
{ value: 'Mozilla', description: "Mozilla's style guide" },
|
||||
{ value: 'WebKit', description: "WebKit's style guide" },
|
||||
{ value: 'Microsoft', description: "Microsoft's style guide" },
|
||||
{ value: 'GNU', description: 'GNU coding standards' }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
// Plugin object
|
||||
const clangPlugin: Plugin = {
|
||||
languages,
|
||||
parsers: {
|
||||
[parserName]: clangParser,
|
||||
},
|
||||
printers: {
|
||||
[parserName]: clangPrinter,
|
||||
},
|
||||
options,
|
||||
};
|
||||
|
||||
// Initialize WASM module when plugin loads
|
||||
initClangFormat().catch(error => {
|
||||
console.warn('Failed to initialize clang-format WASM module:', error);
|
||||
});
|
||||
|
||||
export default clangPlugin;
|
||||
export { languages };
|
||||
export const parsers = clangPlugin.parsers;
|
||||
export const printers = clangPlugin.printers;
|
||||
323
frontend/src/common/prettier/plugins/clang/lib.cc
Normal file
323
frontend/src/common/prettier/plugins/clang/lib.cc
Normal file
@@ -0,0 +1,323 @@
|
||||
//===-- clang-format/ClangFormat.cpp - Clang format tool ------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// This file implements a clang-format tool that automatically formats
|
||||
/// (fragments of) C++ code.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lib.h"
|
||||
#include "clang/Basic/FileManager.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Basic/Version.h"
|
||||
#include "clang/Format/Format.h"
|
||||
#include "clang/Rewrite/Core/Rewriter.h"
|
||||
|
||||
using namespace llvm;
|
||||
using clang::tooling::Replacements;
|
||||
|
||||
static std::string FallbackStyle{clang::format::DefaultFallbackStyle};
|
||||
|
||||
static unsigned Cursor{0};
|
||||
|
||||
static bool SortIncludes{false};
|
||||
|
||||
static std::string QualifierAlignment{""};
|
||||
|
||||
static auto Ok(const std::string content) -> Result {
|
||||
return {false, std::move(content)};
|
||||
}
|
||||
|
||||
static auto Err(const std::string content) -> Result {
|
||||
return {true, std::move(content)};
|
||||
}
|
||||
|
||||
namespace clang {
|
||||
namespace format {
|
||||
|
||||
static FileID createInMemoryFile(StringRef FileName, MemoryBufferRef Source,
|
||||
SourceManager &Sources, FileManager &Files,
|
||||
llvm::vfs::InMemoryFileSystem *MemFS) {
|
||||
MemFS->addFileNoOwn(FileName, 0, Source);
|
||||
auto File = Files.getOptionalFileRef(FileName);
|
||||
assert(File && "File not added to MemFS?");
|
||||
return Sources.createFileID(*File, SourceLocation(), SrcMgr::C_User);
|
||||
}
|
||||
|
||||
static auto fillRanges(MemoryBuffer *Code, std::vector<tooling::Range> &Ranges)
|
||||
-> void {
|
||||
Ranges.push_back(tooling::Range(0, Code->getBuffer().size()));
|
||||
}
|
||||
|
||||
static auto isPredefinedStyle(StringRef style) -> bool {
|
||||
return StringSwitch<bool>(style.lower())
|
||||
.Cases("llvm", "chromium", "mozilla", "google", "webkit", "gnu",
|
||||
"microsoft", "none", "file", true)
|
||||
.Default(false);
|
||||
}
|
||||
|
||||
static auto format_range(const std::unique_ptr<llvm::MemoryBuffer> code,
|
||||
const std::string assumedFileName,
|
||||
const std::string style,
|
||||
std::vector<tooling::Range> ranges) -> Result {
|
||||
StringRef BufStr = code->getBuffer();
|
||||
|
||||
const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr);
|
||||
|
||||
if (InvalidBOM) {
|
||||
std::stringstream err;
|
||||
err << "encoding with unsupported byte order mark \"" << InvalidBOM
|
||||
<< "\" detected.";
|
||||
|
||||
return Err(err.str());
|
||||
}
|
||||
|
||||
StringRef AssumedFileName = assumedFileName;
|
||||
if (AssumedFileName.empty())
|
||||
AssumedFileName = "<stdin>";
|
||||
|
||||
IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
|
||||
new llvm::vfs::InMemoryFileSystem);
|
||||
FileManager Files(FileSystemOptions(), InMemoryFileSystem);
|
||||
|
||||
DiagnosticOptions DiagOpts;
|
||||
DiagnosticsEngine Diagnostics(
|
||||
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), DiagOpts);
|
||||
SourceManager Sources(Diagnostics, Files);
|
||||
|
||||
StringRef _style = style;
|
||||
|
||||
if (!_style.starts_with("{") && !isPredefinedStyle(_style)) {
|
||||
std::unique_ptr<llvm::MemoryBuffer> DotClangFormat =
|
||||
MemoryBuffer::getMemBuffer(style);
|
||||
|
||||
createInMemoryFile(".clang-format", *DotClangFormat.get(), Sources, Files,
|
||||
InMemoryFileSystem.get());
|
||||
_style = "file:.clang-format";
|
||||
}
|
||||
|
||||
llvm::Expected<FormatStyle> FormatStyle =
|
||||
getStyle(_style, AssumedFileName, FallbackStyle, code->getBuffer(),
|
||||
InMemoryFileSystem.get(), false);
|
||||
|
||||
InMemoryFileSystem.reset();
|
||||
|
||||
if (!FormatStyle) {
|
||||
std::string err = llvm::toString(FormatStyle.takeError());
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
StringRef QualifierAlignmentOrder = QualifierAlignment;
|
||||
|
||||
FormatStyle->QualifierAlignment =
|
||||
StringSwitch<FormatStyle::QualifierAlignmentStyle>(
|
||||
QualifierAlignmentOrder.lower())
|
||||
.Case("right", FormatStyle::QAS_Right)
|
||||
.Case("left", FormatStyle::QAS_Left)
|
||||
.Default(FormatStyle->QualifierAlignment);
|
||||
|
||||
if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Left) {
|
||||
FormatStyle->QualifierOrder = {"const", "volatile", "type"};
|
||||
} else if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Right) {
|
||||
FormatStyle->QualifierOrder = {"type", "const", "volatile"};
|
||||
} else if (QualifierAlignmentOrder.contains("type")) {
|
||||
FormatStyle->QualifierAlignment = FormatStyle::QAS_Custom;
|
||||
SmallVector<StringRef> Qualifiers;
|
||||
QualifierAlignmentOrder.split(Qualifiers, " ", /*MaxSplit=*/-1,
|
||||
/*KeepEmpty=*/false);
|
||||
FormatStyle->QualifierOrder = {Qualifiers.begin(), Qualifiers.end()};
|
||||
}
|
||||
|
||||
if (SortIncludes) {
|
||||
FormatStyle->SortIncludes = {};
|
||||
FormatStyle->SortIncludes.Enabled = true;
|
||||
}
|
||||
|
||||
unsigned CursorPosition = Cursor;
|
||||
Replacements Replaces = sortIncludes(*FormatStyle, code->getBuffer(), ranges,
|
||||
AssumedFileName, &CursorPosition);
|
||||
|
||||
// To format JSON insert a variable to trick the code into thinking its
|
||||
// JavaScript.
|
||||
if (FormatStyle->isJson() && !FormatStyle->DisableFormat) {
|
||||
auto err =
|
||||
Replaces.add(tooling::Replacement(AssumedFileName, 0, 0, "x = "));
|
||||
if (err)
|
||||
return Err("Bad Json variable insertion");
|
||||
}
|
||||
|
||||
auto ChangedCode =
|
||||
cantFail(tooling::applyAllReplacements(code->getBuffer(), Replaces));
|
||||
|
||||
// Get new affected ranges after sorting `#includes`.
|
||||
ranges = tooling::calculateRangesAfterReplacements(Replaces, ranges);
|
||||
FormattingAttemptStatus Status;
|
||||
Replacements FormatChanges =
|
||||
reformat(*FormatStyle, ChangedCode, ranges, AssumedFileName, &Status);
|
||||
Replaces = Replaces.merge(FormatChanges);
|
||||
|
||||
return Ok(
|
||||
cantFail(tooling::applyAllReplacements(code->getBuffer(), Replaces)));
|
||||
}
|
||||
|
||||
static auto format_range(const std::string str,
|
||||
const std::string assumedFileName,
|
||||
const std::string style, const bool is_line_range,
|
||||
const std::vector<unsigned> ranges) -> Result {
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
|
||||
MemoryBuffer::getMemBuffer(str);
|
||||
|
||||
if (std::error_code EC = CodeOrErr.getError())
|
||||
return Err(EC.message());
|
||||
std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());
|
||||
if (Code->getBufferSize() == 0)
|
||||
return Ok(""); // Empty files are formatted correctly.
|
||||
|
||||
std::vector<tooling::Range> Ranges;
|
||||
|
||||
if (ranges.empty()) {
|
||||
fillRanges(Code.get(), Ranges);
|
||||
return format_range(std::move(Code), assumedFileName, style,
|
||||
std::move(Ranges));
|
||||
}
|
||||
|
||||
IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
|
||||
new llvm::vfs::InMemoryFileSystem);
|
||||
FileManager Files(FileSystemOptions(), InMemoryFileSystem);
|
||||
DiagnosticOptions DiagOpts;
|
||||
DiagnosticsEngine Diagnostics(
|
||||
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), DiagOpts);
|
||||
SourceManager Sources(Diagnostics, Files);
|
||||
FileID ID = createInMemoryFile("<irrelevant>", *Code, Sources, Files,
|
||||
InMemoryFileSystem.get());
|
||||
|
||||
if (is_line_range) {
|
||||
for (auto FromLine = begin(ranges); FromLine < end(ranges); FromLine += 2) {
|
||||
auto ToLine = FromLine + 1;
|
||||
|
||||
SourceLocation Start = Sources.translateLineCol(ID, *FromLine, 1);
|
||||
SourceLocation End = Sources.translateLineCol(ID, *ToLine, UINT_MAX);
|
||||
if (Start.isInvalid() || End.isInvalid())
|
||||
return Err("invalid line number");
|
||||
unsigned Offset = Sources.getFileOffset(Start);
|
||||
unsigned Length = Sources.getFileOffset(End) - Offset;
|
||||
Ranges.push_back(tooling::Range(Offset, Length));
|
||||
}
|
||||
} else {
|
||||
if (ranges.size() > 2 && ranges.size() % 2 != 0)
|
||||
return Err("number of -offset and -length arguments must match");
|
||||
|
||||
if (ranges.size() == 1) {
|
||||
auto offset = begin(ranges);
|
||||
if (*offset >= Code->getBufferSize()) {
|
||||
std::stringstream err;
|
||||
err << "offset " << *offset << " is outside the file";
|
||||
return Err(err.str());
|
||||
}
|
||||
SourceLocation Start =
|
||||
Sources.getLocForStartOfFile(ID).getLocWithOffset(*offset);
|
||||
SourceLocation End = Sources.getLocForEndOfFile(ID);
|
||||
|
||||
unsigned Offset = Sources.getFileOffset(Start);
|
||||
unsigned Length = Sources.getFileOffset(End) - Offset;
|
||||
|
||||
Ranges.push_back(tooling::Range(Offset, Length));
|
||||
} else {
|
||||
for (auto offset = begin(ranges); offset < end(ranges); offset += 2) {
|
||||
auto length = offset + 1;
|
||||
|
||||
if (*offset >= Code->getBufferSize()) {
|
||||
std::stringstream err;
|
||||
err << "offset " << *offset << " is outside the file";
|
||||
return Err(err.str());
|
||||
}
|
||||
|
||||
unsigned end = *offset + *length;
|
||||
if (end > Code->getBufferSize()) {
|
||||
std::stringstream err;
|
||||
err << "invalid length " << *length << ", offset + length (" << end
|
||||
<< ") is outside the file.";
|
||||
return Err(err.str());
|
||||
}
|
||||
|
||||
SourceLocation Start =
|
||||
Sources.getLocForStartOfFile(ID).getLocWithOffset(*offset);
|
||||
SourceLocation End = Start.getLocWithOffset(*length);
|
||||
|
||||
unsigned Offset = Sources.getFileOffset(Start);
|
||||
unsigned Length = Sources.getFileOffset(End) - Offset;
|
||||
|
||||
Ranges.push_back(tooling::Range(Offset, Length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return format_range(std::move(Code), assumedFileName, style,
|
||||
std::move(Ranges));
|
||||
}
|
||||
|
||||
static auto format(const std::string str, const std::string assumedFileName,
|
||||
const std::string style) -> Result {
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
|
||||
MemoryBuffer::getMemBuffer(str);
|
||||
|
||||
if (std::error_code EC = CodeOrErr.getError())
|
||||
return Err(EC.message());
|
||||
std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());
|
||||
if (Code->getBufferSize() == 0)
|
||||
return Ok(""); // Empty files are formatted correctly.
|
||||
|
||||
std::vector<tooling::Range> Ranges;
|
||||
fillRanges(Code.get(), Ranges);
|
||||
|
||||
return format_range(std::move(Code), assumedFileName, style,
|
||||
std::move(Ranges));
|
||||
}
|
||||
|
||||
} // namespace format
|
||||
} // namespace clang
|
||||
|
||||
auto version() -> std::string {
|
||||
return clang::getClangToolFullVersion("clang-format");
|
||||
}
|
||||
|
||||
auto format(const std::string str, const std::string assumedFileName,
|
||||
const std::string style) -> Result {
|
||||
return clang::format::format(str, assumedFileName, style);
|
||||
}
|
||||
|
||||
auto format_byte(const std::string str, const std::string assumedFileName,
|
||||
const std::string style, const std::vector<unsigned> ranges)
|
||||
-> Result {
|
||||
return clang::format::format_range(str, assumedFileName, style, false,
|
||||
std::move(ranges));
|
||||
}
|
||||
|
||||
auto format_line(const std::string str, const std::string assumedFileName,
|
||||
const std::string style, const std::vector<unsigned> ranges)
|
||||
-> Result {
|
||||
return clang::format::format_range(str, assumedFileName, style, true,
|
||||
std::move(ranges));
|
||||
}
|
||||
|
||||
auto set_fallback_style(const std::string style) -> void {
|
||||
FallbackStyle = style;
|
||||
}
|
||||
|
||||
auto set_sort_includes(const bool sort) -> void { SortIncludes = sort; }
|
||||
|
||||
auto dump_config(const std::string style, const std::string FileName,
|
||||
const std::string code) -> Result {
|
||||
llvm::Expected<clang::format::FormatStyle> FormatStyle =
|
||||
clang::format::getStyle(style, FileName, FallbackStyle, code);
|
||||
if (!FormatStyle)
|
||||
return Err(llvm::toString(FormatStyle.takeError()));
|
||||
std::string Config = clang::format::configurationAsText(*FormatStyle);
|
||||
return Ok(Config);
|
||||
}
|
||||
24
frontend/src/common/prettier/plugins/clang/lib.h
Normal file
24
frontend/src/common/prettier/plugins/clang/lib.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef CLANG_FORMAT_WASM_LIB_H_
|
||||
#define CLANG_FORMAT_WASM_LIB_H_
|
||||
#include <sstream>
|
||||
|
||||
struct Result {
|
||||
bool error;
|
||||
std::string content;
|
||||
};
|
||||
|
||||
auto version() -> std::string;
|
||||
auto format(const std::string str, const std::string assumedFileName, const std::string style) -> Result;
|
||||
auto format_byte(const std::string str,
|
||||
const std::string assumedFileName,
|
||||
const std::string style,
|
||||
const std::vector<unsigned> ranges) -> Result;
|
||||
auto format_line(const std::string str,
|
||||
const std::string assumedFileName,
|
||||
const std::string style,
|
||||
const std::vector<unsigned> ranges) -> Result;
|
||||
auto set_fallback_style(const std::string style) -> void;
|
||||
auto set_sort_includes(const bool sort) -> void;
|
||||
auto dump_config(const std::string style, const std::string FileName, const std::string code) -> Result;
|
||||
|
||||
#endif
|
||||
146
frontend/src/common/prettier/plugins/clang/template.js
Normal file
146
frontend/src/common/prettier/plugins/clang/template.js
Normal file
@@ -0,0 +1,146 @@
|
||||
/* @ts-self-types="./clang-format.d.ts" */
|
||||
async function load(module) {
|
||||
if (typeof Response === "function" && module instanceof Response) {
|
||||
if ("compileStreaming" in WebAssembly) {
|
||||
try {
|
||||
return await WebAssembly.compileStreaming(module);
|
||||
} catch (e) {
|
||||
if (module.headers.get("Content-Type") !== "application/wasm") {
|
||||
console.warn(
|
||||
"`WebAssembly.compileStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",
|
||||
e,
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return module.arrayBuffer();
|
||||
}
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
let wasm;
|
||||
export default async function initAsync(input) {
|
||||
if (wasm !== undefined) {
|
||||
return wasm;
|
||||
}
|
||||
|
||||
if (typeof input === "undefined") {
|
||||
input = new URL("clang-format.wasm", import.meta.url);
|
||||
}
|
||||
|
||||
if (
|
||||
typeof input === "string" ||
|
||||
(typeof Request === "function" && input instanceof Request) ||
|
||||
(typeof URL === "function" && input instanceof URL)
|
||||
) {
|
||||
input = fetch(input);
|
||||
}
|
||||
|
||||
wasm = await load(await input).then((wasm) => Module({ wasm }));
|
||||
assert_init = () => {};
|
||||
}
|
||||
|
||||
function assert_init() {
|
||||
throw new Error("uninit");
|
||||
}
|
||||
|
||||
export function version() {
|
||||
assert_init();
|
||||
return wasm.version();
|
||||
}
|
||||
|
||||
export function set_fallback_style(style) {
|
||||
assert_init();
|
||||
wasm.set_fallback_style(style);
|
||||
}
|
||||
|
||||
export function set_sort_includes(sort) {
|
||||
assert_init();
|
||||
wasm.set_sort_includes(sort);
|
||||
}
|
||||
|
||||
function unwrap(result) {
|
||||
const { error, content } = result;
|
||||
if (error) {
|
||||
throw Error(content);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
export function format(content, filename = "<stdin>", style = "LLVM") {
|
||||
assert_init();
|
||||
const result = wasm.format(content, filename, style);
|
||||
return unwrap(result);
|
||||
}
|
||||
|
||||
export function format_line_range(
|
||||
content,
|
||||
range,
|
||||
filename = "<stdin>",
|
||||
style = "LLVM",
|
||||
) {
|
||||
assert_init();
|
||||
const rangeList = new wasm.RangeList();
|
||||
for (const [fromLine, toLine] of range) {
|
||||
if (fromLine < 1) {
|
||||
throw Error("start line should be at least 1");
|
||||
}
|
||||
if (fromLine > toLine) {
|
||||
throw Error("start line should not exceed end line");
|
||||
}
|
||||
rangeList.push_back(fromLine);
|
||||
rangeList.push_back(toLine);
|
||||
}
|
||||
|
||||
const result = wasm.format_line(content, filename, style, rangeList);
|
||||
rangeList.delete();
|
||||
return unwrap(result);
|
||||
}
|
||||
|
||||
export function format_byte_range(
|
||||
content,
|
||||
range,
|
||||
filename = "<stdin>",
|
||||
style = "LLVM",
|
||||
) {
|
||||
assert_init();
|
||||
const rangeList = new wasm.RangeList();
|
||||
|
||||
if (range.length === 1 && range[0].length === 1) {
|
||||
rangeList.push_back(range[0][0]);
|
||||
} else {
|
||||
for (const [offset, length] of range) {
|
||||
if (offset < 0) {
|
||||
throw Error("start offset should be at least 0");
|
||||
}
|
||||
if (length < 0) {
|
||||
throw Error("length should be at least 0");
|
||||
}
|
||||
rangeList.push_back(offset);
|
||||
rangeList.push_back(length);
|
||||
}
|
||||
}
|
||||
|
||||
const result = wasm.format_byte(content, filename, style, rangeList);
|
||||
rangeList.delete();
|
||||
return unwrap(result);
|
||||
}
|
||||
|
||||
export function dump_config({
|
||||
style = "file",
|
||||
filename = "<stdin>",
|
||||
code = "",
|
||||
} = {}) {
|
||||
assert_init();
|
||||
const result = wasm.dump_config(style, filename, code);
|
||||
return unwrap(result);
|
||||
}
|
||||
|
||||
export {
|
||||
format_byte_range as formatByteRange,
|
||||
format_line_range as formatLineRange,
|
||||
};
|
||||
42
frontend/src/common/prettier/plugins/go/build-tinygo.bat
Normal file
42
frontend/src/common/prettier/plugins/go/build-tinygo.bat
Normal file
@@ -0,0 +1,42 @@
|
||||
@echo off
|
||||
rem Build script for Go Prettier Plugin WASM using TinyGo
|
||||
rem This script compiles the Go code to WebAssembly for browser environment
|
||||
|
||||
echo Building Go Prettier Plugin WASM with TinyGo...
|
||||
|
||||
rem Check if TinyGo is available
|
||||
tinygo version >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
echo TinyGo not found! Please install TinyGo first.
|
||||
echo Visit: https://tinygo.org/getting-started/install/
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
rem Display TinyGo version
|
||||
echo Using TinyGo version:
|
||||
tinygo version
|
||||
|
||||
rem Build the WASM file using TinyGo
|
||||
echo Compiling main.go to go.wasm with TinyGo...
|
||||
tinygo build -o go-format.wasm -target wasm main.go
|
||||
if errorlevel 1 (
|
||||
echo Build failed!
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo Build successful!
|
||||
|
||||
rem Show file size (Windows version)
|
||||
for %%A in (go.wasm) do echo WASM file size: %%~zA bytes
|
||||
|
||||
rem Copy to public directory for browser access
|
||||
if exist "..\..\..\..\..\public" (
|
||||
copy go.wasm ..\..\..\..\..\public\go.wasm > nul
|
||||
echo Copied to public directory
|
||||
del go.wasm
|
||||
echo Cleaned up local WASM file
|
||||
)
|
||||
|
||||
echo Go Prettier Plugin WASM (TinyGo) is ready!
|
||||
38
frontend/src/common/prettier/plugins/go/build-tinygo.sh
Normal file
38
frontend/src/common/prettier/plugins/go/build-tinygo.sh
Normal file
@@ -0,0 +1,38 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Build script for Go Prettier Plugin WASM using TinyGo
|
||||
# This script compiles the Go code to WebAssembly for browser environment
|
||||
|
||||
echo "Building Go Prettier Plugin WASM with TinyGo..."
|
||||
|
||||
# Check if TinyGo is available
|
||||
if ! command -v tinygo &> /dev/null; then
|
||||
echo "TinyGo not found! Please install TinyGo first."
|
||||
echo "Visit: https://tinygo.org/getting-started/install/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Display TinyGo version
|
||||
echo "Using TinyGo version: $(tinygo version)"
|
||||
|
||||
# Build the WASM file using TinyGo
|
||||
echo "Compiling main.go to go.wasm with TinyGo..."
|
||||
tinygo build -o go-format.wasm -target wasm main.go
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Build failed!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Build successful!"
|
||||
echo "WASM file size: $(du -h go-format.wasm | cut -f1)"
|
||||
|
||||
# Copy to public directory for browser access
|
||||
if [ -d "../../../../../public" ]; then
|
||||
cp go-format.wasm ../../../../../public/go-format.wasm
|
||||
echo "Copied to public directory"
|
||||
rm go-format.wasm
|
||||
echo "Cleaned up local WASM file"
|
||||
fi
|
||||
|
||||
echo "Go Prettier Plugin WASM (TinyGo) is ready!"
|
||||
43
frontend/src/common/prettier/plugins/go/build.bat
Normal file
43
frontend/src/common/prettier/plugins/go/build.bat
Normal file
@@ -0,0 +1,43 @@
|
||||
@echo off
|
||||
rem Build script for Go Prettier Plugin WASM using native Go
|
||||
rem This script compiles the Go code to WebAssembly for browser environment
|
||||
|
||||
echo Building Go Prettier Plugin WASM with native Go...
|
||||
|
||||
rem Check if Go is available
|
||||
go version >nul 2>&1
|
||||
if %ERRORLEVEL% NEQ 0 (
|
||||
echo Go not found! Please install Go 1.21+ first.
|
||||
echo Visit: https://golang.org/dl/
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
rem Set WASM build environment for browser (js/wasm)
|
||||
set GOOS=js
|
||||
set GOARCH=wasm
|
||||
|
||||
rem Build the WASM file using native Go
|
||||
echo Compiling main.go to go.wasm with Go...
|
||||
go build -o go-format.wasm main.go
|
||||
|
||||
if %ERRORLEVEL% EQU 0 (
|
||||
echo Build successful!
|
||||
|
||||
rem Show file size (Windows version)
|
||||
for %%A in (go.wasm) do echo WASM file size: %%~zA bytes
|
||||
|
||||
rem Copy to public directory for browser access
|
||||
if exist "..\..\..\..\..\public" (
|
||||
copy go.wasm ..\..\..\..\..\public\go.wasm > nul
|
||||
echo Copied to public directory
|
||||
del go.wasm
|
||||
echo Cleaned up local WASM file
|
||||
)
|
||||
|
||||
echo Go Prettier Plugin WASM is ready!
|
||||
) else (
|
||||
echo Build failed!
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
42
frontend/src/common/prettier/plugins/go/build.sh
Normal file
42
frontend/src/common/prettier/plugins/go/build.sh
Normal file
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Build script for Go Prettier Plugin WASM using native Go
|
||||
# This script compiles the Go code to WebAssembly for browser environment
|
||||
|
||||
echo "Building Go Prettier Plugin WASM with native Go..."
|
||||
|
||||
# Check if Go is available
|
||||
if ! command -v go &> /dev/null; then
|
||||
echo "Go not found! Please install Go 1.21+ first."
|
||||
echo "Visit: https://golang.org/dl/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Display Go version
|
||||
echo "Using Go version: $(go version)"
|
||||
|
||||
# Set WASM build environment for browser (js/wasm)
|
||||
export GOOS=js
|
||||
export GOARCH=wasm
|
||||
|
||||
# Build the WASM file using native Go
|
||||
echo "Compiling main.go to go.wasm with Go..."
|
||||
go build -o go-format.wasm main.go
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Build successful!"
|
||||
echo "WASM file size: $(du -h go-format.wasm | cut -f1)"
|
||||
|
||||
# Copy to public directory for browser access
|
||||
if [ -d "../../../../../public" ]; then
|
||||
cp go-format.wasm ../../../../../public/go-format.wasm
|
||||
echo "Copied to public directory"
|
||||
rm go-format.wasm
|
||||
echo "Cleaned up local WASM file"
|
||||
fi
|
||||
|
||||
echo "Go Prettier Plugin WASM is ready!"
|
||||
else
|
||||
echo "Build failed!"
|
||||
exit 1
|
||||
fi
|
||||
10
frontend/src/common/prettier/plugins/go/go.d.ts
vendored
Normal file
10
frontend/src/common/prettier/plugins/go/go.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Parser, Plugin } from "prettier";
|
||||
|
||||
export declare const languages: Plugin["languages"];
|
||||
export declare const parsers: {
|
||||
go: Parser;
|
||||
};
|
||||
export declare const printers: Plugin["printers"];
|
||||
|
||||
declare const plugin: Plugin;
|
||||
export default plugin;
|
||||
142
frontend/src/common/prettier/plugins/go/go.mjs
Normal file
142
frontend/src/common/prettier/plugins/go/go.mjs
Normal file
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* @fileoverview Go Prettier Format Plugin
|
||||
* A Prettier plugin for formatting Go code using WebAssembly.
|
||||
* This plugin leverages Go's native formatting capabilities through WASM.
|
||||
*/
|
||||
import "./wasm_exec.js"
|
||||
/** @type {Promise<void>|null} */
|
||||
let initializePromise;
|
||||
|
||||
/**
|
||||
* Initializes the Go WebAssembly module for formatting Go code.
|
||||
* This function sets up the WASM runtime and makes the formatGo function
|
||||
* available on the global object.
|
||||
*
|
||||
* @async
|
||||
* @function initialize
|
||||
* @returns {Promise<void>} A promise that resolves when the WASM module is ready
|
||||
* @throws {Error} If the WASM file cannot be loaded or instantiated
|
||||
*/
|
||||
function initialize() {
|
||||
if (initializePromise) {
|
||||
return initializePromise;
|
||||
}
|
||||
|
||||
initializePromise = (async () => {
|
||||
|
||||
const go = new TinyGo();
|
||||
|
||||
// Load WASM file from browser
|
||||
const response = await fetch('/go-format.wasm');
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load WASM file: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
const wasmBuffer = await response.arrayBuffer();
|
||||
|
||||
const { instance } = await WebAssembly.instantiate(
|
||||
wasmBuffer,
|
||||
go.importObject
|
||||
);
|
||||
|
||||
// go.run returns a promise that resolves when the go program exits.
|
||||
// Since our program is a long-running service (it exposes a function and waits),
|
||||
// we don't await it.
|
||||
go.run(instance);
|
||||
|
||||
// The `formatGo` function is now available on the global object.
|
||||
})();
|
||||
|
||||
return initializePromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prettier language configuration for Go.
|
||||
* Defines the language settings, file extensions, and parser mappings.
|
||||
*
|
||||
* @type {Array<Object>}
|
||||
* @property {string} name - The display name of the language
|
||||
* @property {string[]} parsers - Array of parser names for this language
|
||||
* @property {string[]} extensions - File extensions associated with this language
|
||||
* @property {string[]} vscodeLanguageIds - VSCode language identifier mappings
|
||||
*/
|
||||
const languages = [
|
||||
{
|
||||
name: "Go",
|
||||
parsers: ["go-format"],
|
||||
extensions: [".go"],
|
||||
vscodeLanguageIds: ["go"],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Prettier parser configuration for Go.
|
||||
* Defines how Go source code should be parsed and processed.
|
||||
*
|
||||
* @type {Object<string, Object>}
|
||||
* @property {Object} go-format - Go language parser configuration
|
||||
* @property {Function} go-format.parse - Parser function that returns the input text as-is
|
||||
* @property {string} go-format.astFormat - AST format identifier for the printer
|
||||
* @property {Function} go-format.locStart - Function to get the start location of a node
|
||||
* @property {Function} go-format.locEnd - Function to get the end location of a node
|
||||
*/
|
||||
const parsers = {
|
||||
"go-format": {
|
||||
/**
|
||||
* Parse Go source code. For this plugin, we pass through the text as-is
|
||||
* since the actual formatting is handled by the Go WASM module.
|
||||
*
|
||||
* @param {string} text - The Go source code to parse
|
||||
* @returns {string} The input text unchanged
|
||||
*/
|
||||
parse: (text) => text,
|
||||
astFormat: "go-format",
|
||||
// These are required for Prettier to work
|
||||
/**
|
||||
* Get the start location of a node in the source code.
|
||||
*
|
||||
* @param {string} node - The node (in this case, the source text)
|
||||
* @returns {number} Always returns 0 as we treat the entire text as one node
|
||||
*/
|
||||
locStart: (node) => 0,
|
||||
/**
|
||||
* Get the end location of a node in the source code.
|
||||
*
|
||||
* @param {string} node - The node (in this case, the source text)
|
||||
* @returns {number} The length of the text
|
||||
*/
|
||||
locEnd: (node) => node.length,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Prettier printer configuration for Go.
|
||||
* Defines how the parsed Go AST should be formatted back to text.
|
||||
*
|
||||
* @type {Object<string, Object>}
|
||||
* @property {Object} go-format - Go formatting printer configuration
|
||||
* @property {Function} go-format.print - Async function that formats Go code
|
||||
*/
|
||||
const printers = {
|
||||
"go-format": {
|
||||
/**
|
||||
* Format Go source code using the WebAssembly Go formatter.
|
||||
* This function initializes the WASM module if needed and calls the
|
||||
* global formatGo function exposed by the Go program.
|
||||
*
|
||||
* @async
|
||||
* @param {Object} path - Prettier's path object containing the source code
|
||||
* @param {Function} path.getValue - Function to get the current node value
|
||||
* @returns {Promise<string>} The formatted Go source code
|
||||
* @throws {Error} If the WASM module fails to initialize or format the code
|
||||
*/
|
||||
print: async (path) => {
|
||||
// The WASM module must be initialized before we can format.
|
||||
await initialize();
|
||||
const text = path.getValue();
|
||||
// The `formatGo` function is exposed on the global object by our Go program.
|
||||
return globalThis.formatGo(text);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default { languages, parsers, printers, initialize };
|
||||
66
frontend/src/common/prettier/plugins/go/main.go
Normal file
66
frontend/src/common/prettier/plugins/go/main.go
Normal file
@@ -0,0 +1,66 @@
|
||||
//go:build js && wasm
|
||||
|
||||
// Package main implements a WebAssembly module that provides Go code formatting
|
||||
// functionality for the Prettier plugin. This package exposes the formatGo function
|
||||
// to JavaScript, enabling web-based Go code formatting using Go's built-in format package.
|
||||
//
|
||||
// The module is designed to be compiled to WebAssembly using native Go (GOOS=js GOARCH=wasm)
|
||||
// and loaded in browser environments as part of the Go Prettier plugin.
|
||||
package main
|
||||
|
||||
import (
|
||||
"go/format"
|
||||
"syscall/js"
|
||||
)
|
||||
|
||||
// formatGo is a JavaScript-callable function that formats Go source code.
|
||||
// It wraps the standard library's go/format.Source function to be accessible
|
||||
// from JavaScript environments through WebAssembly.
|
||||
//
|
||||
// Parameters:
|
||||
// - this: The JavaScript 'this' context (unused)
|
||||
// - i: JavaScript arguments array where i[0] should contain the Go source code as a string
|
||||
//
|
||||
// Returns:
|
||||
// - js.Value: The formatted Go source code as a JavaScript string value
|
||||
// - If formatting fails due to syntax errors, returns the original code unchanged
|
||||
// - If no arguments are provided, returns js.Null() and logs an error
|
||||
//
|
||||
// The function handles syntax errors gracefully by returning the original code
|
||||
// and logging error details to the JavaScript console for debugging purposes.
|
||||
func formatGo(this js.Value, i []js.Value) interface{} {
|
||||
if len(i) == 0 {
|
||||
js.Global().Get("console").Call("error", "formatGo: missing code argument")
|
||||
return js.Null()
|
||||
}
|
||||
code := i[0].String()
|
||||
formatted, err := format.Source([]byte(code))
|
||||
if err != nil {
|
||||
// In case of a syntax error in the Go code, go/format returns an error.
|
||||
// Prettier expects the original text to be returned in case of an error.
|
||||
// We also log the error to the console for debugging purposes.
|
||||
js.Global().Get("console").Call("error", "Error formatting Go code:", err.Error())
|
||||
return js.ValueOf(code)
|
||||
}
|
||||
return js.ValueOf(string(formatted))
|
||||
}
|
||||
|
||||
// main initializes the WebAssembly module and exposes the formatGo function
|
||||
// to the JavaScript global scope. The function sets up a blocking channel
|
||||
// to prevent the WASM module from exiting, allowing it to serve as a
|
||||
// long-running service for formatting operations.
|
||||
//
|
||||
// The exposed formatGo function can be called from JavaScript as:
|
||||
//
|
||||
// global.formatGo(sourceCode)
|
||||
func main() {
|
||||
// Create a channel to keep the Go program running.
|
||||
// This is necessary because the WASM module would exit otherwise.
|
||||
c := make(chan struct{}, 0)
|
||||
|
||||
// Expose the formatGo function to the JavaScript global scope.
|
||||
js.Global().Set("formatGo", js.FuncOf(formatGo))
|
||||
|
||||
// Block forever
|
||||
<-c
|
||||
}
|
||||
553
frontend/src/common/prettier/plugins/go/wasm_exec.js
Normal file
553
frontend/src/common/prettier/plugins/go/wasm_exec.js
Normal file
@@ -0,0 +1,553 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
//
|
||||
// This file has been modified for use by the TinyGo compiler.
|
||||
|
||||
(() => {
|
||||
// Map multiple JavaScript environments to a single common API,
|
||||
// preferring web standards over Node.js API.
|
||||
//
|
||||
// Environments considered:
|
||||
// - Browsers
|
||||
// - Node.js
|
||||
// - Electron
|
||||
// - Parcel
|
||||
|
||||
if (typeof global !== "undefined") {
|
||||
// global already exists
|
||||
} else if (typeof window !== "undefined") {
|
||||
window.global = window;
|
||||
} else if (typeof self !== "undefined") {
|
||||
self.global = self;
|
||||
} else {
|
||||
throw new Error("cannot export Go (neither global, window nor self is defined)");
|
||||
}
|
||||
|
||||
if (!global.require && typeof require !== "undefined") {
|
||||
global.require = require;
|
||||
}
|
||||
|
||||
if (!global.fs && global.require) {
|
||||
global.fs = require("node:fs");
|
||||
}
|
||||
|
||||
const enosys = () => {
|
||||
const err = new Error("not implemented");
|
||||
err.code = "ENOSYS";
|
||||
return err;
|
||||
};
|
||||
|
||||
if (!global.fs) {
|
||||
let outputBuf = "";
|
||||
global.fs = {
|
||||
constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
|
||||
writeSync(fd, buf) {
|
||||
outputBuf += decoder.decode(buf);
|
||||
const nl = outputBuf.lastIndexOf("\n");
|
||||
if (nl != -1) {
|
||||
console.log(outputBuf.substr(0, nl));
|
||||
outputBuf = outputBuf.substr(nl + 1);
|
||||
}
|
||||
return buf.length;
|
||||
},
|
||||
write(fd, buf, offset, length, position, callback) {
|
||||
if (offset !== 0 || length !== buf.length || position !== null) {
|
||||
callback(enosys());
|
||||
return;
|
||||
}
|
||||
const n = this.writeSync(fd, buf);
|
||||
callback(null, n);
|
||||
},
|
||||
chmod(path, mode, callback) { callback(enosys()); },
|
||||
chown(path, uid, gid, callback) { callback(enosys()); },
|
||||
close(fd, callback) { callback(enosys()); },
|
||||
fchmod(fd, mode, callback) { callback(enosys()); },
|
||||
fchown(fd, uid, gid, callback) { callback(enosys()); },
|
||||
fstat(fd, callback) { callback(enosys()); },
|
||||
fsync(fd, callback) { callback(null); },
|
||||
ftruncate(fd, length, callback) { callback(enosys()); },
|
||||
lchown(path, uid, gid, callback) { callback(enosys()); },
|
||||
link(path, link, callback) { callback(enosys()); },
|
||||
lstat(path, callback) { callback(enosys()); },
|
||||
mkdir(path, perm, callback) { callback(enosys()); },
|
||||
open(path, flags, mode, callback) { callback(enosys()); },
|
||||
read(fd, buffer, offset, length, position, callback) { callback(enosys()); },
|
||||
readdir(path, callback) { callback(enosys()); },
|
||||
readlink(path, callback) { callback(enosys()); },
|
||||
rename(from, to, callback) { callback(enosys()); },
|
||||
rmdir(path, callback) { callback(enosys()); },
|
||||
stat(path, callback) { callback(enosys()); },
|
||||
symlink(path, link, callback) { callback(enosys()); },
|
||||
truncate(path, length, callback) { callback(enosys()); },
|
||||
unlink(path, callback) { callback(enosys()); },
|
||||
utimes(path, atime, mtime, callback) { callback(enosys()); },
|
||||
};
|
||||
}
|
||||
|
||||
if (!global.process) {
|
||||
global.process = {
|
||||
getuid() { return -1; },
|
||||
getgid() { return -1; },
|
||||
geteuid() { return -1; },
|
||||
getegid() { return -1; },
|
||||
getgroups() { throw enosys(); },
|
||||
pid: -1,
|
||||
ppid: -1,
|
||||
umask() { throw enosys(); },
|
||||
cwd() { throw enosys(); },
|
||||
chdir() { throw enosys(); },
|
||||
}
|
||||
}
|
||||
|
||||
if (!global.crypto) {
|
||||
const nodeCrypto = require("node:crypto");
|
||||
global.crypto = {
|
||||
getRandomValues(b) {
|
||||
nodeCrypto.randomFillSync(b);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (!global.performance) {
|
||||
global.performance = {
|
||||
now() {
|
||||
const [sec, nsec] = process.hrtime();
|
||||
return sec * 1000 + nsec / 1000000;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (!global.TextEncoder) {
|
||||
global.TextEncoder = require("node:util").TextEncoder;
|
||||
}
|
||||
|
||||
if (!global.TextDecoder) {
|
||||
global.TextDecoder = require("node:util").TextDecoder;
|
||||
}
|
||||
|
||||
// End of polyfills for common API.
|
||||
|
||||
const encoder = new TextEncoder("utf-8");
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
let reinterpretBuf = new DataView(new ArrayBuffer(8));
|
||||
var logLine = [];
|
||||
const wasmExit = {}; // thrown to exit via proc_exit (not an error)
|
||||
|
||||
global.TinyGo = class {
|
||||
constructor() {
|
||||
this._callbackTimeouts = new Map();
|
||||
this._nextCallbackTimeoutID = 1;
|
||||
|
||||
const mem = () => {
|
||||
// The buffer may change when requesting more memory.
|
||||
return new DataView(this._inst.exports.memory.buffer);
|
||||
}
|
||||
|
||||
const unboxValue = (v_ref) => {
|
||||
reinterpretBuf.setBigInt64(0, v_ref, true);
|
||||
const f = reinterpretBuf.getFloat64(0, true);
|
||||
if (f === 0) {
|
||||
return undefined;
|
||||
}
|
||||
if (!isNaN(f)) {
|
||||
return f;
|
||||
}
|
||||
|
||||
const id = v_ref & 0xffffffffn;
|
||||
return this._values[id];
|
||||
}
|
||||
|
||||
|
||||
const loadValue = (addr) => {
|
||||
let v_ref = mem().getBigUint64(addr, true);
|
||||
return unboxValue(v_ref);
|
||||
}
|
||||
|
||||
const boxValue = (v) => {
|
||||
const nanHead = 0x7FF80000n;
|
||||
|
||||
if (typeof v === "number") {
|
||||
if (isNaN(v)) {
|
||||
return nanHead << 32n;
|
||||
}
|
||||
if (v === 0) {
|
||||
return (nanHead << 32n) | 1n;
|
||||
}
|
||||
reinterpretBuf.setFloat64(0, v, true);
|
||||
return reinterpretBuf.getBigInt64(0, true);
|
||||
}
|
||||
|
||||
switch (v) {
|
||||
case undefined:
|
||||
return 0n;
|
||||
case null:
|
||||
return (nanHead << 32n) | 2n;
|
||||
case true:
|
||||
return (nanHead << 32n) | 3n;
|
||||
case false:
|
||||
return (nanHead << 32n) | 4n;
|
||||
}
|
||||
|
||||
let id = this._ids.get(v);
|
||||
if (id === undefined) {
|
||||
id = this._idPool.pop();
|
||||
if (id === undefined) {
|
||||
id = BigInt(this._values.length);
|
||||
}
|
||||
this._values[id] = v;
|
||||
this._goRefCounts[id] = 0;
|
||||
this._ids.set(v, id);
|
||||
}
|
||||
this._goRefCounts[id]++;
|
||||
let typeFlag = 1n;
|
||||
switch (typeof v) {
|
||||
case "string":
|
||||
typeFlag = 2n;
|
||||
break;
|
||||
case "symbol":
|
||||
typeFlag = 3n;
|
||||
break;
|
||||
case "function":
|
||||
typeFlag = 4n;
|
||||
break;
|
||||
}
|
||||
return id | ((nanHead | typeFlag) << 32n);
|
||||
}
|
||||
|
||||
const storeValue = (addr, v) => {
|
||||
let v_ref = boxValue(v);
|
||||
mem().setBigUint64(addr, v_ref, true);
|
||||
}
|
||||
|
||||
const loadSlice = (array, len, cap) => {
|
||||
return new Uint8Array(this._inst.exports.memory.buffer, array, len);
|
||||
}
|
||||
|
||||
const loadSliceOfValues = (array, len, cap) => {
|
||||
const a = new Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
a[i] = loadValue(array + i * 8);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
const loadString = (ptr, len) => {
|
||||
return decoder.decode(new DataView(this._inst.exports.memory.buffer, ptr, len));
|
||||
}
|
||||
|
||||
const timeOrigin = Date.now() - performance.now();
|
||||
this.importObject = {
|
||||
wasi_snapshot_preview1: {
|
||||
// https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#fd_write
|
||||
fd_write: function(fd, iovs_ptr, iovs_len, nwritten_ptr) {
|
||||
let nwritten = 0;
|
||||
if (fd == 1) {
|
||||
for (let iovs_i=0; iovs_i<iovs_len;iovs_i++) {
|
||||
let iov_ptr = iovs_ptr+iovs_i*8; // assuming wasm32
|
||||
let ptr = mem().getUint32(iov_ptr + 0, true);
|
||||
let len = mem().getUint32(iov_ptr + 4, true);
|
||||
nwritten += len;
|
||||
for (let i=0; i<len; i++) {
|
||||
let c = mem().getUint8(ptr+i);
|
||||
if (c == 13) { // CR
|
||||
// ignore
|
||||
} else if (c == 10) { // LF
|
||||
// write line
|
||||
let line = decoder.decode(new Uint8Array(logLine));
|
||||
logLine = [];
|
||||
console.log(line);
|
||||
} else {
|
||||
logLine.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.error('invalid file descriptor:', fd);
|
||||
}
|
||||
mem().setUint32(nwritten_ptr, nwritten, true);
|
||||
return 0;
|
||||
},
|
||||
fd_close: () => 0, // dummy
|
||||
fd_fdstat_get: () => 0, // dummy
|
||||
fd_seek: () => 0, // dummy
|
||||
proc_exit: (code) => {
|
||||
this.exited = true;
|
||||
this.exitCode = code;
|
||||
this._resolveExitPromise();
|
||||
throw wasmExit;
|
||||
},
|
||||
random_get: (bufPtr, bufLen) => {
|
||||
crypto.getRandomValues(loadSlice(bufPtr, bufLen));
|
||||
return 0;
|
||||
},
|
||||
},
|
||||
gojs: {
|
||||
// func ticks() int64
|
||||
"runtime.ticks": () => {
|
||||
return BigInt((timeOrigin + performance.now()) * 1e6);
|
||||
},
|
||||
|
||||
// func sleepTicks(timeout int64)
|
||||
"runtime.sleepTicks": (timeout) => {
|
||||
// Do not sleep, only reactivate scheduler after the given timeout.
|
||||
setTimeout(() => {
|
||||
if (this.exited) return;
|
||||
try {
|
||||
this._inst.exports.go_scheduler();
|
||||
} catch (e) {
|
||||
if (e !== wasmExit) throw e;
|
||||
}
|
||||
}, Number(timeout)/1e6);
|
||||
},
|
||||
|
||||
// func finalizeRef(v ref)
|
||||
"syscall/js.finalizeRef": (v_ref) => {
|
||||
// Note: TinyGo does not support finalizers so this is only called
|
||||
// for one specific case, by js.go:jsString. and can/might leak memory.
|
||||
const id = v_ref & 0xffffffffn;
|
||||
if (this._goRefCounts?.[id] !== undefined) {
|
||||
this._goRefCounts[id]--;
|
||||
if (this._goRefCounts[id] === 0) {
|
||||
const v = this._values[id];
|
||||
this._values[id] = null;
|
||||
this._ids.delete(v);
|
||||
this._idPool.push(id);
|
||||
}
|
||||
} else {
|
||||
console.error("syscall/js.finalizeRef: unknown id", id);
|
||||
}
|
||||
},
|
||||
|
||||
// func stringVal(value string) ref
|
||||
"syscall/js.stringVal": (value_ptr, value_len) => {
|
||||
value_ptr >>>= 0;
|
||||
const s = loadString(value_ptr, value_len);
|
||||
return boxValue(s);
|
||||
},
|
||||
|
||||
// func valueGet(v ref, p string) ref
|
||||
"syscall/js.valueGet": (v_ref, p_ptr, p_len) => {
|
||||
let prop = loadString(p_ptr, p_len);
|
||||
let v = unboxValue(v_ref);
|
||||
let result = Reflect.get(v, prop);
|
||||
return boxValue(result);
|
||||
},
|
||||
|
||||
// func valueSet(v ref, p string, x ref)
|
||||
"syscall/js.valueSet": (v_ref, p_ptr, p_len, x_ref) => {
|
||||
const v = unboxValue(v_ref);
|
||||
const p = loadString(p_ptr, p_len);
|
||||
const x = unboxValue(x_ref);
|
||||
Reflect.set(v, p, x);
|
||||
},
|
||||
|
||||
// func valueDelete(v ref, p string)
|
||||
"syscall/js.valueDelete": (v_ref, p_ptr, p_len) => {
|
||||
const v = unboxValue(v_ref);
|
||||
const p = loadString(p_ptr, p_len);
|
||||
Reflect.deleteProperty(v, p);
|
||||
},
|
||||
|
||||
// func valueIndex(v ref, i int) ref
|
||||
"syscall/js.valueIndex": (v_ref, i) => {
|
||||
return boxValue(Reflect.get(unboxValue(v_ref), i));
|
||||
},
|
||||
|
||||
// valueSetIndex(v ref, i int, x ref)
|
||||
"syscall/js.valueSetIndex": (v_ref, i, x_ref) => {
|
||||
Reflect.set(unboxValue(v_ref), i, unboxValue(x_ref));
|
||||
},
|
||||
|
||||
// func valueCall(v ref, m string, args []ref) (ref, bool)
|
||||
"syscall/js.valueCall": (ret_addr, v_ref, m_ptr, m_len, args_ptr, args_len, args_cap) => {
|
||||
const v = unboxValue(v_ref);
|
||||
const name = loadString(m_ptr, m_len);
|
||||
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
|
||||
try {
|
||||
const m = Reflect.get(v, name);
|
||||
storeValue(ret_addr, Reflect.apply(m, v, args));
|
||||
mem().setUint8(ret_addr + 8, 1);
|
||||
} catch (err) {
|
||||
storeValue(ret_addr, err);
|
||||
mem().setUint8(ret_addr + 8, 0);
|
||||
}
|
||||
},
|
||||
|
||||
// func valueInvoke(v ref, args []ref) (ref, bool)
|
||||
"syscall/js.valueInvoke": (ret_addr, v_ref, args_ptr, args_len, args_cap) => {
|
||||
try {
|
||||
const v = unboxValue(v_ref);
|
||||
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
|
||||
storeValue(ret_addr, Reflect.apply(v, undefined, args));
|
||||
mem().setUint8(ret_addr + 8, 1);
|
||||
} catch (err) {
|
||||
storeValue(ret_addr, err);
|
||||
mem().setUint8(ret_addr + 8, 0);
|
||||
}
|
||||
},
|
||||
|
||||
// func valueNew(v ref, args []ref) (ref, bool)
|
||||
"syscall/js.valueNew": (ret_addr, v_ref, args_ptr, args_len, args_cap) => {
|
||||
const v = unboxValue(v_ref);
|
||||
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
|
||||
try {
|
||||
storeValue(ret_addr, Reflect.construct(v, args));
|
||||
mem().setUint8(ret_addr + 8, 1);
|
||||
} catch (err) {
|
||||
storeValue(ret_addr, err);
|
||||
mem().setUint8(ret_addr+ 8, 0);
|
||||
}
|
||||
},
|
||||
|
||||
// func valueLength(v ref) int
|
||||
"syscall/js.valueLength": (v_ref) => {
|
||||
return unboxValue(v_ref).length;
|
||||
},
|
||||
|
||||
// valuePrepareString(v ref) (ref, int)
|
||||
"syscall/js.valuePrepareString": (ret_addr, v_ref) => {
|
||||
const s = String(unboxValue(v_ref));
|
||||
const str = encoder.encode(s);
|
||||
storeValue(ret_addr, str);
|
||||
mem().setInt32(ret_addr + 8, str.length, true);
|
||||
},
|
||||
|
||||
// valueLoadString(v ref, b []byte)
|
||||
"syscall/js.valueLoadString": (v_ref, slice_ptr, slice_len, slice_cap) => {
|
||||
const str = unboxValue(v_ref);
|
||||
loadSlice(slice_ptr, slice_len, slice_cap).set(str);
|
||||
},
|
||||
|
||||
// func valueInstanceOf(v ref, t ref) bool
|
||||
"syscall/js.valueInstanceOf": (v_ref, t_ref) => {
|
||||
return unboxValue(v_ref) instanceof unboxValue(t_ref);
|
||||
},
|
||||
|
||||
// func copyBytesToGo(dst []byte, src ref) (int, bool)
|
||||
"syscall/js.copyBytesToGo": (ret_addr, dest_addr, dest_len, dest_cap, src_ref) => {
|
||||
let num_bytes_copied_addr = ret_addr;
|
||||
let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable
|
||||
|
||||
const dst = loadSlice(dest_addr, dest_len);
|
||||
const src = unboxValue(src_ref);
|
||||
if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) {
|
||||
mem().setUint8(returned_status_addr, 0); // Return "not ok" status
|
||||
return;
|
||||
}
|
||||
const toCopy = src.subarray(0, dst.length);
|
||||
dst.set(toCopy);
|
||||
mem().setUint32(num_bytes_copied_addr, toCopy.length, true);
|
||||
mem().setUint8(returned_status_addr, 1); // Return "ok" status
|
||||
},
|
||||
|
||||
// copyBytesToJS(dst ref, src []byte) (int, bool)
|
||||
// Originally copied from upstream Go project, then modified:
|
||||
// https://github.com/golang/go/blob/3f995c3f3b43033013013e6c7ccc93a9b1411ca9/misc/wasm/wasm_exec.js#L404-L416
|
||||
"syscall/js.copyBytesToJS": (ret_addr, dst_ref, src_addr, src_len, src_cap) => {
|
||||
let num_bytes_copied_addr = ret_addr;
|
||||
let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable
|
||||
|
||||
const dst = unboxValue(dst_ref);
|
||||
const src = loadSlice(src_addr, src_len);
|
||||
if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) {
|
||||
mem().setUint8(returned_status_addr, 0); // Return "not ok" status
|
||||
return;
|
||||
}
|
||||
const toCopy = src.subarray(0, dst.length);
|
||||
dst.set(toCopy);
|
||||
mem().setUint32(num_bytes_copied_addr, toCopy.length, true);
|
||||
mem().setUint8(returned_status_addr, 1); // Return "ok" status
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// Go 1.20 uses 'env'. Go 1.21 uses 'gojs'.
|
||||
// For compatibility, we use both as long as Go 1.20 is supported.
|
||||
this.importObject.env = this.importObject.gojs;
|
||||
}
|
||||
|
||||
async run(instance) {
|
||||
this._inst = instance;
|
||||
this._values = [ // JS values that Go currently has references to, indexed by reference id
|
||||
NaN,
|
||||
0,
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
global,
|
||||
this,
|
||||
];
|
||||
this._goRefCounts = []; // number of references that Go has to a JS value, indexed by reference id
|
||||
this._ids = new Map(); // mapping from JS values to reference ids
|
||||
this._idPool = []; // unused ids that have been garbage collected
|
||||
this.exited = false; // whether the Go program has exited
|
||||
this.exitCode = 0;
|
||||
|
||||
if (this._inst.exports._start) {
|
||||
let exitPromise = new Promise((resolve, reject) => {
|
||||
this._resolveExitPromise = resolve;
|
||||
});
|
||||
|
||||
// Run program, but catch the wasmExit exception that's thrown
|
||||
// to return back here.
|
||||
try {
|
||||
this._inst.exports._start();
|
||||
} catch (e) {
|
||||
if (e !== wasmExit) throw e;
|
||||
}
|
||||
|
||||
await exitPromise;
|
||||
return this.exitCode;
|
||||
} else {
|
||||
this._inst.exports._initialize();
|
||||
}
|
||||
}
|
||||
|
||||
_resume() {
|
||||
if (this.exited) {
|
||||
throw new Error("Go program has already exited");
|
||||
}
|
||||
try {
|
||||
this._inst.exports.resume();
|
||||
} catch (e) {
|
||||
if (e !== wasmExit) throw e;
|
||||
}
|
||||
if (this.exited) {
|
||||
this._resolveExitPromise();
|
||||
}
|
||||
}
|
||||
|
||||
_makeFuncWrapper(id) {
|
||||
const go = this;
|
||||
return function () {
|
||||
const event = { id: id, this: this, args: arguments };
|
||||
go._pendingEvent = event;
|
||||
go._resume();
|
||||
return event.result;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
global.require &&
|
||||
global.require.main === module &&
|
||||
global.process &&
|
||||
global.process.versions &&
|
||||
!global.process.versions.electron
|
||||
) {
|
||||
if (process.argv.length != 3) {
|
||||
console.error("usage: go_js_wasm_exec [wasm binary] [arguments]");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const go = new Go();
|
||||
WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then(async (result) => {
|
||||
let exitCode = await go.run(result.instance);
|
||||
process.exit(exitCode);
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
})();
|
||||
67
frontend/src/common/prettier/plugins/groovy/index.ts
Normal file
67
frontend/src/common/prettier/plugins/groovy/index.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Prettier Plugin for Groovy/Jenkins file formatting
|
||||
*
|
||||
* This plugin provides support for formatting Groovy and Jenkins files using the groovy-beautify library.
|
||||
* It supports .groovy files and Jenkins-related files like Jenkinsfile.
|
||||
*/
|
||||
import type { Plugin, Parser, Printer } from 'prettier';
|
||||
import groovyBeautify from 'groovy-beautify';
|
||||
|
||||
const parserName = 'groovy';
|
||||
|
||||
// 语言配置
|
||||
const languages = [
|
||||
{
|
||||
name: 'Groovy',
|
||||
aliases: ['groovy'],
|
||||
parsers: [parserName],
|
||||
filenames: ['jenkinsfile', 'Jenkinsfile'],
|
||||
extensions: ['.jenkinsfile', '.Jenkinsfile', '.groovy'],
|
||||
aceMode: 'groovy',
|
||||
tmScope: 'source.groovy',
|
||||
linguistLanguageId: 142,
|
||||
vscodeLanguageIds: ['groovy']
|
||||
},
|
||||
];
|
||||
|
||||
// 解析器配置
|
||||
const groovyParser: Parser<string> = {
|
||||
astFormat: parserName,
|
||||
parse: (text: string) => text,
|
||||
locStart: () => 0,
|
||||
locEnd: (node: string) => node.length,
|
||||
};
|
||||
|
||||
// 打印器配置
|
||||
const groovyPrinter: Printer<string> = {
|
||||
print: (path, options) => {
|
||||
try {
|
||||
return groovyBeautify(path.node, {
|
||||
width: options.printWidth || 80,
|
||||
}).trim();
|
||||
} catch (error) {
|
||||
return path.node;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const options = {
|
||||
|
||||
};
|
||||
|
||||
// 插件对象
|
||||
const groovyPlugin: Plugin = {
|
||||
languages,
|
||||
parsers: {
|
||||
[parserName]: groovyParser,
|
||||
},
|
||||
printers: {
|
||||
[parserName]: groovyPrinter,
|
||||
},
|
||||
options,
|
||||
};
|
||||
|
||||
export default groovyPlugin;
|
||||
export { languages };
|
||||
export const parsers = groovyPlugin.parsers;
|
||||
export const printers = groovyPlugin.printers;
|
||||
17
frontend/src/common/prettier/plugins/java/comments.d.ts
vendored
Normal file
17
frontend/src/common/prettier/plugins/java/comments.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
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;
|
||||
};
|
||||
199
frontend/src/common/prettier/plugins/java/comments.js
Normal file
199
frontend/src/common/prettier/plugins/java/comments.js
Normal file
@@ -0,0 +1,199 @@
|
||||
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
Normal file
563
frontend/src/common/prettier/plugins/java/index.d.ts
vendored
Normal file
@@ -0,0 +1,563 @@
|
||||
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;
|
||||
29
frontend/src/common/prettier/plugins/java/index.js
Normal file
29
frontend/src/common/prettier/plugins/java/index.js
Normal file
@@ -0,0 +1,29 @@
|
||||
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"
|
||||
}
|
||||
};
|
||||
43
frontend/src/common/prettier/plugins/java/options.d.ts
vendored
Normal file
43
frontend/src/common/prettier/plugins/java/options.d.ts
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
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;
|
||||
284
frontend/src/common/prettier/plugins/java/options.js
Normal file
284
frontend/src/common/prettier/plugins/java/options.js
Normal file
@@ -0,0 +1,284 @@
|
||||
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."
|
||||
}
|
||||
};
|
||||
9
frontend/src/common/prettier/plugins/java/parser.d.ts
vendored
Normal file
9
frontend/src/common/prettier/plugins/java/parser.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
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;
|
||||
24
frontend/src/common/prettier/plugins/java/parser.js
Normal file
24
frontend/src/common/prettier/plugins/java/parser.js
Normal file
@@ -0,0 +1,24 @@
|
||||
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;
|
||||
}
|
||||
};
|
||||
18
frontend/src/common/prettier/plugins/java/printer.d.ts
vendored
Normal file
18
frontend/src/common/prettier/plugins/java/printer.d.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
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;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user