mirror of
https://github.com/waiting0324/obsidian-code-note.git
synced 2026-07-22 07:30:24 +00:00
Initial commit
This commit is contained in:
commit
bb83c9206d
20 changed files with 1193 additions and 0 deletions
10
.editorconfig
Normal file
10
.editorconfig
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
tab_width = 4
|
||||||
3
.eslintignore
Normal file
3
.eslintignore
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
main.js
|
||||||
23
.eslintrc
Normal file
23
.eslintrc
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"env": { "node": true },
|
||||||
|
"plugins": [
|
||||||
|
"@typescript-eslint"
|
||||||
|
],
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"no-unused-vars": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
|
||||||
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
|
"no-prototype-builtins": "off",
|
||||||
|
"@typescript-eslint/no-empty-function": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
.gitignore
vendored
Normal file
22
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
# vscode
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
# Intellij
|
||||||
|
*.iml
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# npm
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Don't include the compiled main.js file in the repo.
|
||||||
|
# They should be uploaded to GitHub releases instead.
|
||||||
|
main.js
|
||||||
|
|
||||||
|
# Exclude sourcemaps
|
||||||
|
*.map
|
||||||
|
|
||||||
|
# obsidian
|
||||||
|
data.json
|
||||||
|
|
||||||
|
# Exclude macOS Finder (System Explorer) View States
|
||||||
|
.DS_Store
|
||||||
1
.npmrc
Normal file
1
.npmrc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
tag-version-prefix=""
|
||||||
96
README.md
Normal file
96
README.md
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
# Obsidian Sample Plugin
|
||||||
|
|
||||||
|
This is a sample plugin for Obsidian (https://obsidian.md).
|
||||||
|
|
||||||
|
This project uses Typescript to provide type checking and documentation.
|
||||||
|
The repo depends on the latest plugin API (obsidian.d.ts) in Typescript Definition format, which contains TSDoc comments describing what it does.
|
||||||
|
|
||||||
|
**Note:** The Obsidian API is still in early alpha and is subject to change at any time!
|
||||||
|
|
||||||
|
This sample plugin demonstrates some of the basic functionality the plugin API can do.
|
||||||
|
- Changes the default font color to red using `styles.css`.
|
||||||
|
- Adds a ribbon icon, which shows a Notice when clicked.
|
||||||
|
- Adds a command "Open Sample Modal" which opens a Modal.
|
||||||
|
- Adds a plugin setting tab to the settings page.
|
||||||
|
- Registers a global click event and output 'click' to the console.
|
||||||
|
- Registers a global interval which logs 'setInterval' to the console.
|
||||||
|
|
||||||
|
## First time developing plugins?
|
||||||
|
|
||||||
|
Quick starting guide for new plugin devs:
|
||||||
|
|
||||||
|
- Check if [someone already developed a plugin for what you want](https://obsidian.md/plugins)! There might be an existing plugin similar enough that you can partner up with.
|
||||||
|
- Make a copy of this repo as a template with the "Use this template" button (login to GitHub if you don't see it).
|
||||||
|
- Clone your repo to a local development folder. For convenience, you can place this folder in your `.obsidian/plugins/your-plugin-name` folder.
|
||||||
|
- Install NodeJS, then run `npm i` in the command line under your repo folder.
|
||||||
|
- Run `npm run dev` to compile your plugin from `main.ts` to `main.js`.
|
||||||
|
- Make changes to `main.ts` (or create new `.ts` files). Those changes should be automatically compiled into `main.js`.
|
||||||
|
- Reload Obsidian to load the new version of your plugin.
|
||||||
|
- Enable plugin in settings window.
|
||||||
|
- For updates to the Obsidian API run `npm update` in the command line under your repo folder.
|
||||||
|
|
||||||
|
## Releasing new releases
|
||||||
|
|
||||||
|
- Update your `manifest.json` with your new version number, such as `1.0.1`, and the minimum Obsidian version required for your latest release.
|
||||||
|
- Update your `versions.json` file with `"new-plugin-version": "minimum-obsidian-version"` so older versions of Obsidian can download an older version of your plugin that's compatible.
|
||||||
|
- Create new GitHub release using your new version number as the "Tag version". Use the exact version number, don't include a prefix `v`. See here for an example: https://github.com/obsidianmd/obsidian-sample-plugin/releases
|
||||||
|
- Upload the files `manifest.json`, `main.js`, `styles.css` as binary attachments. Note: The manifest.json file must be in two places, first the root path of your repository and also in the release.
|
||||||
|
- Publish the release.
|
||||||
|
|
||||||
|
> You can simplify the version bump process by running `npm version patch`, `npm version minor` or `npm version major` after updating `minAppVersion` manually in `manifest.json`.
|
||||||
|
> The command will bump version in `manifest.json` and `package.json`, and add the entry for the new version to `versions.json`
|
||||||
|
|
||||||
|
## Adding your plugin to the community plugin list
|
||||||
|
|
||||||
|
- Check https://github.com/obsidianmd/obsidian-releases/blob/master/plugin-review.md
|
||||||
|
- Publish an initial version.
|
||||||
|
- Make sure you have a `README.md` file in the root of your repo.
|
||||||
|
- Make a pull request at https://github.com/obsidianmd/obsidian-releases to add your plugin.
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
|
||||||
|
- Clone this repo.
|
||||||
|
- `npm i` or `yarn` to install dependencies
|
||||||
|
- `npm run dev` to start compilation in watch mode.
|
||||||
|
|
||||||
|
## Manually installing the plugin
|
||||||
|
|
||||||
|
- Copy over `main.js`, `styles.css`, `manifest.json` to your vault `VaultFolder/.obsidian/plugins/your-plugin-id/`.
|
||||||
|
|
||||||
|
## Improve code quality with eslint (optional)
|
||||||
|
- [ESLint](https://eslint.org/) is a tool that analyzes your code to quickly find problems. You can run ESLint against your plugin to find common bugs and ways to improve your code.
|
||||||
|
- To use eslint with this project, make sure to install eslint from terminal:
|
||||||
|
- `npm install -g eslint`
|
||||||
|
- To use eslint to analyze this project use this command:
|
||||||
|
- `eslint main.ts`
|
||||||
|
- eslint will then create a report with suggestions for code improvement by file and line number.
|
||||||
|
- If your source code is in a folder, such as `src`, you can use eslint with this command to analyze all files in that folder:
|
||||||
|
- `eslint .\src\`
|
||||||
|
|
||||||
|
## Funding URL
|
||||||
|
|
||||||
|
You can include funding URLs where people who use your plugin can financially support it.
|
||||||
|
|
||||||
|
The simple way is to set the `fundingUrl` field to your link in your `manifest.json` file:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"fundingUrl": "https://buymeacoffee.com"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If you have multiple URLs, you can also do:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"fundingUrl": {
|
||||||
|
"Buy Me a Coffee": "https://buymeacoffee.com",
|
||||||
|
"GitHub Sponsor": "https://github.com/sponsors",
|
||||||
|
"Patreon": "https://www.patreon.com/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Documentation
|
||||||
|
|
||||||
|
See https://github.com/obsidianmd/obsidian-api
|
||||||
192
classShape.ts
Normal file
192
classShape.ts
Normal file
|
|
@ -0,0 +1,192 @@
|
||||||
|
import { Graph } from '@antv/x6';
|
||||||
|
import { utils } from './utils'
|
||||||
|
|
||||||
|
const myUtils = new utils()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 註冊 類聲明 中 方法名稱位置 計算函數
|
||||||
|
*/
|
||||||
|
Graph.registerPortLayout(
|
||||||
|
'classFuncPosition',
|
||||||
|
(portsPositionArgs, elemBBox) => {
|
||||||
|
return portsPositionArgs.map((_, index) => {
|
||||||
|
return {
|
||||||
|
position: {
|
||||||
|
x: 0,
|
||||||
|
y: (index + 1) * elemBBox.height,
|
||||||
|
},
|
||||||
|
angle: 0,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
//true,
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 類聲明 圖形定義
|
||||||
|
*/
|
||||||
|
Graph.registerNode(
|
||||||
|
'clazz-shape',
|
||||||
|
{
|
||||||
|
// 最上層 class 名稱
|
||||||
|
inherit: 'rect',
|
||||||
|
markup: [
|
||||||
|
{
|
||||||
|
tagName: 'rect',
|
||||||
|
selector: 'body',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tagName: 'text',
|
||||||
|
selector: 'label',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
attrs: {
|
||||||
|
rect: {
|
||||||
|
strokeWidth: 1,
|
||||||
|
stroke: '#e5885c',
|
||||||
|
fill: '#fd9a6c',
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fill: '#ffffff',
|
||||||
|
fontSize: 14,
|
||||||
|
textAnchor: 'middle',
|
||||||
|
textVerticalAnchor: 'middle'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// 該 class 所屬的方法列表
|
||||||
|
ports: {
|
||||||
|
groups: {
|
||||||
|
list: {
|
||||||
|
position: 'classFuncPosition',
|
||||||
|
markup: [
|
||||||
|
{
|
||||||
|
tagName: 'rect',
|
||||||
|
selector: 'func',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tagName: 'text',
|
||||||
|
selector: 'funcName',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tagName: 'circle',
|
||||||
|
selector: 'toggle'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tagName: 'text',
|
||||||
|
selector: 'toggleText'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
attrs: {
|
||||||
|
func: {
|
||||||
|
strokeWidth: 1,
|
||||||
|
stroke: '#e5885c',
|
||||||
|
fill: '#FFFFFF',
|
||||||
|
},
|
||||||
|
funcName: {
|
||||||
|
fontSize: 13,
|
||||||
|
textAnchor: 'middle',
|
||||||
|
textVerticalAnchor: 'middle'
|
||||||
|
},
|
||||||
|
toggle: {
|
||||||
|
r: 7,
|
||||||
|
stroke: '#b2bec3',
|
||||||
|
strokeWidth: 2,
|
||||||
|
fill: '#fff',
|
||||||
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
toggleText: {
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: 800,
|
||||||
|
fill: '#b2bec3',
|
||||||
|
textAnchor: 'middle',
|
||||||
|
textVerticalAnchor: 'middle',
|
||||||
|
'pointer-events': 'none', // 避免按鈕不靈敏
|
||||||
|
cursor: 'pointer',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
|
||||||
|
class ClassShape {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根據 類名、方法名稱集合 創建 類聲明圖形
|
||||||
|
* @param className 類名
|
||||||
|
* @param funcNames 方法名稱集合
|
||||||
|
* @returns 類聲明圖形
|
||||||
|
*/
|
||||||
|
public createClassShape(className: string, funcNames: string[]) {
|
||||||
|
|
||||||
|
const height = 30
|
||||||
|
|
||||||
|
// 計算 類聲明 圖形寬度
|
||||||
|
let maxWidth = 100
|
||||||
|
maxWidth = Math.max(maxWidth, myUtils.getTextWidth(className, "14px bold") + 60)
|
||||||
|
funcNames.forEach((funcName: string) => {
|
||||||
|
maxWidth = Math.max(maxWidth, myUtils.getTextWidth(funcName, "13px") + 20)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 定義頂部類名稱方塊
|
||||||
|
let classShape = {
|
||||||
|
"id": myUtils.getClassShapeId(className, '', 'class'),
|
||||||
|
"shape": 'clazz-shape',
|
||||||
|
"label": className,
|
||||||
|
"width": maxWidth,
|
||||||
|
"height": height,
|
||||||
|
"fontSize": 14,
|
||||||
|
"position": {
|
||||||
|
"x": 100,
|
||||||
|
"y": 100
|
||||||
|
},
|
||||||
|
"ports": [{}]
|
||||||
|
}
|
||||||
|
classShape.ports = []
|
||||||
|
|
||||||
|
|
||||||
|
// 定義下方函數名稱方塊
|
||||||
|
funcNames.forEach((funcName: string) => {
|
||||||
|
|
||||||
|
let port = {
|
||||||
|
"id": myUtils.getClassShapeId(className, funcName, 'function'),
|
||||||
|
"group": "list",
|
||||||
|
"attrs": {
|
||||||
|
"func": {
|
||||||
|
width: maxWidth,
|
||||||
|
height: height,
|
||||||
|
},
|
||||||
|
"funcName": {
|
||||||
|
ref: 'func',
|
||||||
|
refX: '50%',
|
||||||
|
refY: '50%',
|
||||||
|
text: funcName,
|
||||||
|
},
|
||||||
|
"toggle": {
|
||||||
|
ref: 'func',
|
||||||
|
refDx: 0,
|
||||||
|
refY: 0.5,
|
||||||
|
event: 'toggle:codeBlock',
|
||||||
|
targetCodeBlock: myUtils.getCodeBlockShapeId(className, funcName),
|
||||||
|
},
|
||||||
|
"toggleText": {
|
||||||
|
ref: 'toggle',
|
||||||
|
refX: 0.5,
|
||||||
|
refY: 0.5,
|
||||||
|
text: '+',
|
||||||
|
event: 'toggle:codeBlock',
|
||||||
|
targetCodeBlock: myUtils.getCodeBlockShapeId(className, funcName),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
classShape.ports.push(port)
|
||||||
|
})
|
||||||
|
|
||||||
|
return classShape
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { ClassShape }
|
||||||
66
codeBlockShape.ts
Normal file
66
codeBlockShape.ts
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
import hljs from 'highlight.js';
|
||||||
|
import { utils, CodeData } from './utils'
|
||||||
|
|
||||||
|
const myUtils = new utils()
|
||||||
|
|
||||||
|
class CodeBlockShape {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根據 CodeData 對象 獲取 AntV 的 HTML 對象集合
|
||||||
|
* @param codeData CodeData 對象
|
||||||
|
* @returns HTML 對象集合
|
||||||
|
*/
|
||||||
|
public createCodeBlockShape(codeData: CodeData) {
|
||||||
|
|
||||||
|
// 結果對象集合
|
||||||
|
let result = []
|
||||||
|
|
||||||
|
// 將 CodeData 中的每個 func 的 code 轉換成 AntV 的 HTML 對象
|
||||||
|
let codeDataFuncs = codeData.funcs
|
||||||
|
for (const codeDataFunc of codeDataFuncs) {
|
||||||
|
|
||||||
|
// 獲取 代碼塊字串,並分割成行
|
||||||
|
let codeText = codeDataFunc.code
|
||||||
|
let codeTextLines = codeText.split('\n')
|
||||||
|
|
||||||
|
// 計算代碼塊所需高度
|
||||||
|
const blockHeight = codeTextLines.length * 15 + 40;
|
||||||
|
|
||||||
|
// 計算代碼塊所需寬度
|
||||||
|
let blockWidth = 0
|
||||||
|
for (let i = 0; i < codeTextLines.length; i++) {
|
||||||
|
blockWidth = Math.max(blockWidth, myUtils.getTextWidth(codeTextLines[i], "13px ui-sans-serif") + 60)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 構建 HTML 對象
|
||||||
|
const htmlShape = {
|
||||||
|
id: myUtils.getCodeBlockShapeId(codeData.className, codeDataFunc.name),
|
||||||
|
x: 800,
|
||||||
|
y: 100,
|
||||||
|
width: blockWidth,
|
||||||
|
height: blockHeight,
|
||||||
|
shape: 'html',
|
||||||
|
attrs: {
|
||||||
|
body: {
|
||||||
|
fill: 'white',
|
||||||
|
stroke: '#666',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
html() {
|
||||||
|
const wrap = document.createElement('div')
|
||||||
|
wrap.innerHTML = '<pre style="padding-left: 20px">' + hljs.highlight(codeText, { language: 'java' }).value + '</pre>'
|
||||||
|
return wrap
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 將 HTML 對象加入到返回結果中
|
||||||
|
result.push(htmlShape)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export { CodeBlockShape }
|
||||||
107
edge.ts
Normal file
107
edge.ts
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
import { Shape } from '@antv/x6';
|
||||||
|
import { CodeData, utils } from './utils'
|
||||||
|
|
||||||
|
const myUtils = new utils()
|
||||||
|
|
||||||
|
class Edge {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根據 CodeData 創建 函數名稱 到 代碼塊圖形 的 連線對象集合
|
||||||
|
* @param codeData CodeData 對象
|
||||||
|
* @returns 連線對象集合
|
||||||
|
*/
|
||||||
|
public createFuncToCodeEdges(codeData: CodeData) {
|
||||||
|
|
||||||
|
let result = [];
|
||||||
|
|
||||||
|
// 遍歷所有的 函數對象,替每個 函數 與 代碼塊圖形 創建 連線對象
|
||||||
|
for (const func of codeData.funcs) {
|
||||||
|
|
||||||
|
// 創建 連線
|
||||||
|
const edge = new Shape.Edge({
|
||||||
|
source: {
|
||||||
|
cell: myUtils.getClassShapeId(codeData.className, '', 'class'),
|
||||||
|
port: myUtils.getClassShapeId(codeData.className, func.name, 'function'),
|
||||||
|
anchor: 'right'
|
||||||
|
},
|
||||||
|
target: {
|
||||||
|
cell: myUtils.getCodeBlockShapeId(codeData.className, func.name),
|
||||||
|
anchor: 'left'
|
||||||
|
},
|
||||||
|
router: {
|
||||||
|
name: 'er',
|
||||||
|
args: {
|
||||||
|
direction: 'L'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
attrs: {
|
||||||
|
line: {
|
||||||
|
stroke: "#bdc3c7",
|
||||||
|
targetMarker: {
|
||||||
|
name: 'diamond',
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// 將 連線對象 加入到 返回結果 中
|
||||||
|
result.push(edge)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根據 CodeData 創建 函數調用 連線對象 集合
|
||||||
|
* @param CodeData 對象
|
||||||
|
* @returns 連線對象 集合
|
||||||
|
*/
|
||||||
|
public createFuncCallEdges(codeData: CodeData) {
|
||||||
|
|
||||||
|
let result = [];
|
||||||
|
|
||||||
|
// 遍歷 CodeData 中的每個 函數對象
|
||||||
|
for (const func of codeData.funcs) {
|
||||||
|
|
||||||
|
// 遍歷 函數對象 中的每個 調用函數
|
||||||
|
for (const call of func.calls) {
|
||||||
|
|
||||||
|
// 創建 連線對象
|
||||||
|
const edge = new Shape.Edge({
|
||||||
|
source: {
|
||||||
|
cell: myUtils.getClassShapeId(codeData.className, '', 'class'),
|
||||||
|
port: myUtils.getClassShapeId(codeData.className, func.name, 'function'),
|
||||||
|
anchor: 'left'
|
||||||
|
},
|
||||||
|
target: {
|
||||||
|
cell: myUtils.getClassShapeId(call.className, '', 'class'),
|
||||||
|
port: myUtils.getClassShapeId(call.className, call.functionName, 'function'),
|
||||||
|
anchor: 'left'
|
||||||
|
},
|
||||||
|
router: {
|
||||||
|
name: 'oneSide',
|
||||||
|
args: {
|
||||||
|
side: 'left',
|
||||||
|
padding: 30
|
||||||
|
},
|
||||||
|
},
|
||||||
|
attrs: {
|
||||||
|
line: {
|
||||||
|
stroke: "#2bb37b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// 將 連線對象 加入到 返回結果 中
|
||||||
|
result.push(edge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Edge }
|
||||||
48
esbuild.config.mjs
Normal file
48
esbuild.config.mjs
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
import esbuild from "esbuild";
|
||||||
|
import process from "process";
|
||||||
|
import builtins from "builtin-modules";
|
||||||
|
|
||||||
|
const banner =
|
||||||
|
`/*
|
||||||
|
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
||||||
|
if you want to view the source, please visit the github repository of this plugin
|
||||||
|
*/
|
||||||
|
`;
|
||||||
|
|
||||||
|
const prod = (process.argv[2] === "production");
|
||||||
|
|
||||||
|
const context = await esbuild.context({
|
||||||
|
banner: {
|
||||||
|
js: banner,
|
||||||
|
},
|
||||||
|
entryPoints: ["main.ts"],
|
||||||
|
bundle: true,
|
||||||
|
external: [
|
||||||
|
"obsidian",
|
||||||
|
"electron",
|
||||||
|
"@codemirror/autocomplete",
|
||||||
|
"@codemirror/collab",
|
||||||
|
"@codemirror/commands",
|
||||||
|
"@codemirror/language",
|
||||||
|
"@codemirror/lint",
|
||||||
|
"@codemirror/search",
|
||||||
|
"@codemirror/state",
|
||||||
|
"@codemirror/view",
|
||||||
|
"@lezer/common",
|
||||||
|
"@lezer/highlight",
|
||||||
|
"@lezer/lr",
|
||||||
|
...builtins],
|
||||||
|
format: "cjs",
|
||||||
|
target: "es2018",
|
||||||
|
logLevel: "info",
|
||||||
|
sourcemap: prod ? false : "inline",
|
||||||
|
treeShaking: true,
|
||||||
|
outfile: "main.js",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (prod) {
|
||||||
|
await context.rebuild();
|
||||||
|
process.exit(0);
|
||||||
|
} else {
|
||||||
|
await context.watch();
|
||||||
|
}
|
||||||
204
index.ts
Normal file
204
index.ts
Normal file
|
|
@ -0,0 +1,204 @@
|
||||||
|
import {Cell, Graph, Shape} from '@antv/x6';
|
||||||
|
import {CodeBlockShape} from './codeBlockShape';
|
||||||
|
import {ClassShape} from './classShape';
|
||||||
|
import {CLASS_SHAPE_ID_TAG, CODE_BLOCK_ID_TAG, CodeData, utils} from './utils';
|
||||||
|
import {Edge} from './edge'
|
||||||
|
|
||||||
|
let codeBlocks = [`
|
||||||
|
/**
|
||||||
|
* listen plugin handler event and handle plugin.
|
||||||
|
* @class ShenyuWebHandler
|
||||||
|
* @function onApplicationEvent(final PluginHandlerEvent event)
|
||||||
|
* @param event sort plugin event
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onApplicationEvent(final PluginHandlerEvent event) {
|
||||||
|
PluginHandlerEventEnum stateEnums = event.getPluginStateEnums();
|
||||||
|
PluginData pluginData = (PluginData) event.getSource();
|
||||||
|
switch (stateEnums) {
|
||||||
|
case ENABLED:
|
||||||
|
onPluginEnabled(pluginData);
|
||||||
|
break;
|
||||||
|
case DELETE:
|
||||||
|
case DISABLED:
|
||||||
|
// disable or removed plugin.
|
||||||
|
onPluginRemoved(pluginData);
|
||||||
|
break;
|
||||||
|
case SORTED:
|
||||||
|
// copy a new one, or there will be concurrency problems
|
||||||
|
onSortedPlugins();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unexpected value: " + event.getPluginStateEnums());
|
||||||
|
}
|
||||||
|
onSortedPlugins();
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
/**
|
||||||
|
* handler error.
|
||||||
|
*
|
||||||
|
* @class ShenyuWebHandler
|
||||||
|
* @function handle(@NonNull final ServerWebExchange exchange, @NonNull final Throwable throwable)
|
||||||
|
* @call ShenyuPluginLoader @ getInstance()
|
||||||
|
*
|
||||||
|
* @param exchange the exchange
|
||||||
|
* @param throwable the throwable
|
||||||
|
* @return error result
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public Mono<Void> handle(@NonNull final ServerWebExchange exchange, @NonNull final Throwable throwable) {
|
||||||
|
LOG.error("handle error: {}{}", exchange.getLogPrefix(), formatError(throwable, exchange.getRequest()), throwable);
|
||||||
|
HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||||
|
if (throwable instanceof ResponseStatusException) {
|
||||||
|
httpStatus = ((ResponseStatusException) throwable).getStatus();
|
||||||
|
}
|
||||||
|
exchange.getResponse().setStatusCode(httpStatus);
|
||||||
|
Object error = ShenyuResultWrap.error(exchange, httpStatus.value(), httpStatus.getReasonPhrase(), throwable);
|
||||||
|
return WebFluxResultUtils.result(exchange, error);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
/**
|
||||||
|
* Get plugin loader instance.
|
||||||
|
*
|
||||||
|
* @class ShenyuPluginLoader
|
||||||
|
* @function getInstance()
|
||||||
|
*
|
||||||
|
* @return plugin loader instance
|
||||||
|
*/
|
||||||
|
public static ShenyuPluginLoader getInstance() {
|
||||||
|
if (null == pluginLoader) {
|
||||||
|
synchronized (ShenyuPluginLoader.class) {
|
||||||
|
if (null == pluginLoader) {
|
||||||
|
pluginLoader = new ShenyuPluginLoader();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pluginLoader;
|
||||||
|
}
|
||||||
|
`]
|
||||||
|
|
||||||
|
const myUtils = new utils()
|
||||||
|
const codeBlockShape = new CodeBlockShape()
|
||||||
|
const classShape = new ClassShape()
|
||||||
|
const edge = new Edge()
|
||||||
|
|
||||||
|
// window.onload = function () {
|
||||||
|
export let indexInit = function (codeBlocks: String[]) {
|
||||||
|
|
||||||
|
// 創建畫布
|
||||||
|
const graph = new Graph({
|
||||||
|
container: <HTMLElement>document.getElementById('container'),
|
||||||
|
width: 2200,
|
||||||
|
height: 1200,
|
||||||
|
background: {
|
||||||
|
color: '#fffbe6', // 设置画布背景颜色
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
size: 10, // 网格大小 10px
|
||||||
|
visible: true, // 渲染网格背景
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 將 代碼塊字串 集合 解析成 CodeData 對象集合,並將相同 className 的對象進行合併
|
||||||
|
let codeDatas: CodeData[] = []
|
||||||
|
codeBlocks.forEach((codeBlock: string) => {
|
||||||
|
let codeData: CodeData = myUtils.parseCodeData(codeBlock)
|
||||||
|
codeDatas.push(codeData)
|
||||||
|
})
|
||||||
|
codeDatas = myUtils.mergeCodeDatas(codeDatas)
|
||||||
|
|
||||||
|
|
||||||
|
// 將 CodeData 對象列表 繪製成 類圖形、代碼塊圖形
|
||||||
|
for (const codeData of codeDatas) {
|
||||||
|
|
||||||
|
// 將 類圖形、代碼塊圖形 加入到 畫布 中
|
||||||
|
let funcNames = codeData.funcs.map(item => item.name)
|
||||||
|
graph.addNode(classShape.createClassShape(codeData.className, funcNames))
|
||||||
|
graph.addNodes(codeBlockShape.createCodeBlockShape(codeData))
|
||||||
|
|
||||||
|
// 創建 函數名稱 與 代碼塊圖形 的 連線
|
||||||
|
graph.addEdges(edge.createFuncToCodeEdges(codeData))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 繪製 CodeData 中的 函數調用 連線
|
||||||
|
for (const codeData of codeDatas) {
|
||||||
|
graph.addEdges(edge.createFuncCallEdges(codeData))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 綁定 開啟/關閉 代碼塊圖形 事件
|
||||||
|
graph.on('toggle:codeBlock', ({e, node}) => {
|
||||||
|
|
||||||
|
console.dir(e)
|
||||||
|
console.dir(node)
|
||||||
|
|
||||||
|
// 獲取 代碼塊圖形、開關文字 對象
|
||||||
|
let codeBlock = graph.getCellById(e.currentTarget.getAttribute('target-code-block'))
|
||||||
|
let toggleText = e.currentTarget.parentNode.childNodes[3].children[0]
|
||||||
|
|
||||||
|
// 如果當前 代碼塊圖形 正在顯示,則關閉。反之則開啟
|
||||||
|
if (codeBlock.getProp().visible) {
|
||||||
|
codeBlock.hide()
|
||||||
|
toggleText.textContent = '+'
|
||||||
|
toggleText.setAttribute('dy', '0.3em')
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// 計算 代碼塊 顯示位置
|
||||||
|
const intervalX = 200
|
||||||
|
const posX = e.offsetX + intervalX
|
||||||
|
const posY = e.offsetY - (codeBlock.getProp().size.height / 2)
|
||||||
|
codeBlock.prop("position", {x: posX, y: posY})
|
||||||
|
|
||||||
|
codeBlock.show()
|
||||||
|
toggleText.textContent = '-'
|
||||||
|
toggleText.setAttribute('dy', '0.2em')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 最大的 類圖形 的寬度
|
||||||
|
let maxClassShapeWidth = 0;
|
||||||
|
// 所有的 類圖形 集合
|
||||||
|
let classShapes: Cell[] = [];
|
||||||
|
|
||||||
|
// 遍歷 所有的圖形
|
||||||
|
for (const node of graph.getCells()) {
|
||||||
|
|
||||||
|
// 默認隱藏所有的 代碼塊
|
||||||
|
if (node.id.startsWith(CODE_BLOCK_ID_TAG)) {
|
||||||
|
node.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 計算 類圖形 最大寬度、加入到集合中
|
||||||
|
if (node.id.startsWith(CLASS_SHAPE_ID_TAG)) {
|
||||||
|
maxClassShapeWidth = Math.max(maxClassShapeWidth, node.getProp().size.width)
|
||||||
|
classShapes.push(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 計算 類圖形 的 座標
|
||||||
|
*/
|
||||||
|
// 左側保留寬度
|
||||||
|
const marginLeftWidth = 100
|
||||||
|
// 類圖形的 Y 座標
|
||||||
|
let curY = 200
|
||||||
|
// 多個 類圖形 的 上下間格
|
||||||
|
const intervalY = 150
|
||||||
|
// 所有 類圖形 的 中線 X 座標
|
||||||
|
const midX = marginLeftWidth + (maxClassShapeWidth / 2)
|
||||||
|
for (const classShape of classShapes) {
|
||||||
|
|
||||||
|
// 計算 類圖形 的 左上X 座標,並設置
|
||||||
|
const posX = midX - (classShape.getProp().size.width / 2)
|
||||||
|
classShape.prop("position", {x: posX, y: curY})
|
||||||
|
|
||||||
|
// 下個 類圖形 的 Y 座標 = 當前 類圖形 高度 + 間隔
|
||||||
|
curY = curY + classShape.getProp().size.height + intervalY
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
67
main.ts
Normal file
67
main.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
import {Plugin, Workspace, WorkspaceLeaf} from 'obsidian';
|
||||||
|
import SourceCodeView from "./source-code";
|
||||||
|
|
||||||
|
|
||||||
|
export const SOURCE_CODE_VIEW_TYPE = 'source-code-view'
|
||||||
|
export default class MyPlugin extends Plugin {
|
||||||
|
|
||||||
|
sourceCodeView: SourceCodeView
|
||||||
|
codeBlocks: string[] = []
|
||||||
|
|
||||||
|
async onload() {
|
||||||
|
this.registerView(
|
||||||
|
SOURCE_CODE_VIEW_TYPE,
|
||||||
|
(leaf: WorkspaceLeaf) =>
|
||||||
|
(this.sourceCodeView = new SourceCodeView(this.codeBlocks, leaf))
|
||||||
|
);
|
||||||
|
|
||||||
|
this.addRibbonIcon("dice", "Activate view", () => {
|
||||||
|
this.initCanvas()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initCanvas() {
|
||||||
|
|
||||||
|
// 當 ResourceCodeView 頁面已開啟,則返回
|
||||||
|
if (this.app.workspace.getLeavesOfType(SOURCE_CODE_VIEW_TYPE).length > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找不到激活的文件,則返回
|
||||||
|
const activeFile = this.app.workspace.getActiveFile();
|
||||||
|
if (activeFile == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 處理文件內容
|
||||||
|
this.app.vault.process(activeFile, (data) => {
|
||||||
|
|
||||||
|
// 清理數據
|
||||||
|
this.codeBlocks = []
|
||||||
|
|
||||||
|
// 獲取文件內所有的 Markdown 代碼塊
|
||||||
|
const regex = /```(\w+)\s([\s\S]*?)```/gm;
|
||||||
|
let match;
|
||||||
|
while ((match = regex.exec(data)) !== null) {
|
||||||
|
const lang = match[1]; // 程式語言
|
||||||
|
const codeBlockData = match[2]; // 代碼塊內容
|
||||||
|
this.codeBlocks.push(codeBlockData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 開啟分頁
|
||||||
|
const preview = this.app.workspace.getLeaf('split', 'vertical')
|
||||||
|
const mmPreview = new SourceCodeView(this.codeBlocks, preview);
|
||||||
|
preview.open(mmPreview)
|
||||||
|
|
||||||
|
// 不更改文件內容
|
||||||
|
return data
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onunload() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
11
manifest.json
Normal file
11
manifest.json
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"id": "obsidian-code-note",
|
||||||
|
"name": "Source Code Note Plugin",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"minAppVersion": "0.15.0",
|
||||||
|
"description": "This is a sample plugin for Obsidian. This plugin demonstrates some of the capabilities of the Obsidian API.",
|
||||||
|
"author": "Obsidian",
|
||||||
|
"authorUrl": "https://obsidian.md",
|
||||||
|
"fundingUrl": "https://obsidian.md/pricing",
|
||||||
|
"isDesktopOnly": false
|
||||||
|
}
|
||||||
26
package.json
Normal file
26
package.json
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"name": "obsidian-sample-plugin",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
|
||||||
|
"main": "main.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "node esbuild.config.mjs",
|
||||||
|
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
|
||||||
|
"version": "node version-bump.mjs && git add manifest.json versions.json"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^16.11.6",
|
||||||
|
"@typescript-eslint/eslint-plugin": "5.29.0",
|
||||||
|
"@typescript-eslint/parser": "5.29.0",
|
||||||
|
"builtin-modules": "3.3.0",
|
||||||
|
"esbuild": "0.17.3",
|
||||||
|
"obsidian": "latest",
|
||||||
|
"tslib": "2.4.0",
|
||||||
|
"typescript": "4.7.4",
|
||||||
|
"@antv/x6": "^1.34.5",
|
||||||
|
"highlight.js": "^11.6.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
35
source-code.ts
Normal file
35
source-code.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import {ItemView, View, WorkspaceLeaf} from "obsidian";
|
||||||
|
import {indexInit} from "./index";
|
||||||
|
import {SOURCE_CODE_VIEW_TYPE} from "./main";
|
||||||
|
|
||||||
|
export default class SourceCodeView extends ItemView {
|
||||||
|
|
||||||
|
codeBlocks: string[]
|
||||||
|
|
||||||
|
constructor(codeBlocks: string[], leaf: WorkspaceLeaf) {
|
||||||
|
super(leaf);
|
||||||
|
this.codeBlocks = codeBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDisplayText(): string {
|
||||||
|
return "MySourceCodeView";
|
||||||
|
}
|
||||||
|
|
||||||
|
getViewType(): string {
|
||||||
|
return SOURCE_CODE_VIEW_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
async onOpen() {
|
||||||
|
const container = this.containerEl.children[1];
|
||||||
|
container.empty();
|
||||||
|
const div = document.createElement("div")
|
||||||
|
div.id = 'container';
|
||||||
|
this.containerEl.children[1].appendChild(div);
|
||||||
|
|
||||||
|
indexInit(this.codeBlocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
async onClose() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
74
styles.css
Normal file
74
styles.css
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
pre {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #000000;
|
||||||
|
font-family: ui-sans-serif;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre code.hljs {
|
||||||
|
display: block;
|
||||||
|
overflow-x: auto;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
code.hljs {
|
||||||
|
padding: 3px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs {
|
||||||
|
background: #f3f3f3;
|
||||||
|
color: #444
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-comment {
|
||||||
|
color: #697070
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-punctuation,.hljs-tag {
|
||||||
|
color: #444a
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-tag .hljs-attr,.hljs-tag .hljs-name {
|
||||||
|
color: #444
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-attribute,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-name,.hljs-selector-tag {
|
||||||
|
font-weight: 700
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-deletion,.hljs-number,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-string,.hljs-template-tag,.hljs-type {
|
||||||
|
color: #800
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-section,.hljs-title {
|
||||||
|
color: #800;
|
||||||
|
font-weight: 700
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-link,.hljs-operator,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-symbol,.hljs-template-variable,.hljs-variable {
|
||||||
|
color: #ab5656
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-literal {
|
||||||
|
color: #695
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-code {
|
||||||
|
color: #397300
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-meta {
|
||||||
|
color: #1f7199
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-meta .hljs-string {
|
||||||
|
color: #38a
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-emphasis {
|
||||||
|
font-style: italic
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-strong {
|
||||||
|
font-weight: 700
|
||||||
|
}
|
||||||
24
tsconfig.json
Normal file
24
tsconfig.json
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"inlineSourceMap": true,
|
||||||
|
"inlineSources": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"target": "ES6",
|
||||||
|
"allowJs": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"importHelpers": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"lib": [
|
||||||
|
"DOM",
|
||||||
|
"ES5",
|
||||||
|
"ES6",
|
||||||
|
"ES7"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"**/*.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
167
utils.ts
Normal file
167
utils.ts
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
type CodeData = {
|
||||||
|
className: string, // 類名稱
|
||||||
|
funcs: CodeDataFunc[] // 函數名稱 與 代碼塊字串 集合
|
||||||
|
}
|
||||||
|
|
||||||
|
type CodeDataFunc = {
|
||||||
|
name: string, // 函數名稱
|
||||||
|
code: string // 代碼塊字串
|
||||||
|
calls: CodeDataFuncCall[] // 調用的函數集合
|
||||||
|
}
|
||||||
|
|
||||||
|
type CodeDataFuncCall = {
|
||||||
|
className: string, // 類名稱
|
||||||
|
functionName: string // 函數名稱
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CLASS_SHAPE_ID_TAG: string = "class-";
|
||||||
|
export const CODE_BLOCK_ID_TAG: string = 'code-block-';
|
||||||
|
class utils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 計算字串寬度
|
||||||
|
*/
|
||||||
|
tCanvas: any = null;
|
||||||
|
public getTextWidth(text: string, font: string = '13px ui-sans-serif') {
|
||||||
|
// re-use canvas object for better performance
|
||||||
|
const canvas = this.tCanvas || (this.tCanvas = document.createElement('canvas'));
|
||||||
|
const context = canvas.getContext('2d');
|
||||||
|
context.font = font;
|
||||||
|
return context.measureText(text).width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根據 代碼塊字串 解析出 CodeData 對象
|
||||||
|
* @param codeBlockText 代碼塊字串
|
||||||
|
* @returns CodeData 對象
|
||||||
|
*/
|
||||||
|
public parseCodeData(codeBlockText: string): CodeData {
|
||||||
|
|
||||||
|
// 類名、方法名 的 標示符
|
||||||
|
const classTag = '@class'
|
||||||
|
const functionTag = '@function'
|
||||||
|
const callTag = '@call'
|
||||||
|
|
||||||
|
let codeLines = codeBlockText.split('\n')
|
||||||
|
|
||||||
|
// 取得 註釋塊 的代碼
|
||||||
|
let commentLines = []
|
||||||
|
for (let i = 0; i < codeLines.length; i++) {
|
||||||
|
let codeLine = codeLines[i]
|
||||||
|
|
||||||
|
// 找到註釋開頭,則從當前行開始,加入到註釋塊列表中
|
||||||
|
if (codeLine.indexOf('/**') != -1) {
|
||||||
|
commentLines.push(codeLine)
|
||||||
|
}
|
||||||
|
else if (commentLines.length > 0) {
|
||||||
|
commentLines.push(codeLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 當碰到第一個多行註釋的結尾,則跳出循環
|
||||||
|
if (codeLine.indexOf('*/') != -1) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 從 註釋塊 中獲取 類名、方法名、調用的函數集合
|
||||||
|
let className = ''
|
||||||
|
let functionName = ''
|
||||||
|
let calls: CodeDataFuncCall[] = []
|
||||||
|
|
||||||
|
commentLines.forEach((comment) => {
|
||||||
|
|
||||||
|
// 獲取 類名
|
||||||
|
if (comment.indexOf(classTag) != -1) {
|
||||||
|
className = comment.substring(comment.indexOf(classTag) + classTag.length).trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 獲取 方法名
|
||||||
|
if (comment.indexOf(functionTag) != -1) {
|
||||||
|
functionName = comment.substring(comment.indexOf(functionTag) + functionTag.length).trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 獲取 調用的函數
|
||||||
|
if (comment.indexOf(callTag) != -1) {
|
||||||
|
let callStr = comment.substring(comment.indexOf(callTag) + callTag.length).trim()
|
||||||
|
calls.push({
|
||||||
|
className: callStr.substring(0, callStr.indexOf('@')).trim(),
|
||||||
|
functionName: callStr.substring(callStr.indexOf('@') + 1).trim()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
// 封裝結果
|
||||||
|
let result: CodeData = {
|
||||||
|
className: className,
|
||||||
|
funcs: [{
|
||||||
|
name: functionName,
|
||||||
|
code: codeBlockText,
|
||||||
|
calls: calls
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 獲取 類聲明 的 圖形Id
|
||||||
|
* @param className 類名
|
||||||
|
* @param functionName 方法名
|
||||||
|
* @param type Id 的類型
|
||||||
|
* @returns 圖形Id
|
||||||
|
*/
|
||||||
|
public getClassShapeId(className: string, functionName: string, type: 'class' | 'function'): string {
|
||||||
|
if (type == 'class') {
|
||||||
|
return CLASS_SHAPE_ID_TAG + className
|
||||||
|
} else if (type == 'function') {
|
||||||
|
return CLASS_SHAPE_ID_TAG + className + '-' + functionName
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 獲取 代碼塊 的 圖形Id
|
||||||
|
* @param className 類名
|
||||||
|
* @param functionName 方法名
|
||||||
|
* @returns 圖形Id
|
||||||
|
*/
|
||||||
|
public getCodeBlockShapeId(className: string, functionName: string): string {
|
||||||
|
return CODE_BLOCK_ID_TAG + className + '-' + functionName
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 將擁有相同 ClassName 的 CodeDataFunc 合併到一起
|
||||||
|
* @param codeDatas 要進行合併的 CodeData 集合
|
||||||
|
* @returns 合併後的 CodeData 集合
|
||||||
|
*/
|
||||||
|
public mergeCodeDatas(codeDatas: CodeData[]): CodeData[] {
|
||||||
|
|
||||||
|
// 對有相同 className 的 CodeData 的 func 進行合併
|
||||||
|
let classNameToCodeData = new Map()
|
||||||
|
codeDatas.forEach((codeData: CodeData) => {
|
||||||
|
|
||||||
|
// 如果在 Map 中已存在相應的 CodeData,則將 func 進行合併
|
||||||
|
if (classNameToCodeData.has(codeData.className)) {
|
||||||
|
let tempCodeData = classNameToCodeData.get(codeData.className) as CodeData
|
||||||
|
tempCodeData.funcs.push(codeData.funcs[0])
|
||||||
|
}
|
||||||
|
// 如果在 Map 中還沒有相應的 CodeData,則將當前 CodeData 加入到 Map 中
|
||||||
|
else {
|
||||||
|
classNameToCodeData.set(codeData.className, codeData)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 封裝返回結果
|
||||||
|
let result: CodeData[] = []
|
||||||
|
for (let codeData of Array.from(classNameToCodeData.values())) {
|
||||||
|
result.push(codeData)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export { utils, CodeData, CodeDataFunc }
|
||||||
14
version-bump.mjs
Normal file
14
version-bump.mjs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { readFileSync, writeFileSync } from "fs";
|
||||||
|
|
||||||
|
const targetVersion = process.env.npm_package_version;
|
||||||
|
|
||||||
|
// read minAppVersion from manifest.json and bump version to target version
|
||||||
|
let manifest = JSON.parse(readFileSync("manifest.json", "utf8"));
|
||||||
|
const { minAppVersion } = manifest;
|
||||||
|
manifest.version = targetVersion;
|
||||||
|
writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t"));
|
||||||
|
|
||||||
|
// update versions.json with target version and minAppVersion from manifest.json
|
||||||
|
let versions = JSON.parse(readFileSync("versions.json", "utf8"));
|
||||||
|
versions[targetVersion] = minAppVersion;
|
||||||
|
writeFileSync("versions.json", JSON.stringify(versions, null, "\t"));
|
||||||
3
versions.json
Normal file
3
versions.json
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"1.0.0": "0.15.0"
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue