優化代碼塊高度

This commit is contained in:
Waiting 2023-03-10 23:30:21 +08:00
parent 0cb31d67fa
commit 2675392b97
2 changed files with 199 additions and 177 deletions

View file

@ -1,66 +1,66 @@
import hljs from 'highlight.js'; import hljs from 'highlight.js';
import { Utils, CodeData } from './Utils' import {Utils, CodeData} from './Utils'
const myUtils = new Utils() const myUtils = new Utils()
class CodeBlockShape { class CodeBlockShape {
/** /**
* CodeData AntV HTML * CodeData AntV HTML
* @param codeData CodeData * @param codeData CodeData
* @returns HTML * @returns HTML
*/ */
public createCodeBlockShape(codeData: CodeData) { public createCodeBlockShape(codeData: CodeData) {
// 結果對象集合 // 結果對象集合
let result = [] let result = []
// 將 CodeData 中的每個 func 的 code 轉換成 AntV 的 HTML 對象 // 將 CodeData 中的每個 func 的 code 轉換成 AntV 的 HTML 對象
let codeDataFuncs = codeData.funcs let codeDataFuncs = codeData.funcs
for (const codeDataFunc of codeDataFuncs) { for (const codeDataFunc of codeDataFuncs) {
// 獲取 代碼塊字串,並分割成行 // 獲取 代碼塊字串,並分割成行
let codeText = codeDataFunc.code let codeText = codeDataFunc.code
let codeTextLines = codeText.split('\n') let codeTextLines = codeText.split('\n')
// 計算代碼塊所需高度 // 獲取 代碼塊所需高度
const blockHeight = codeTextLines.length * 15 + 40; let blockHeight = myUtils.getTextHeight(codeDataFunc.code) + 20;
// 計算代碼塊所需寬度 // 計算代碼塊所需寬度
let blockWidth = 0 let blockWidth = 0
for (let i = 0; i < codeTextLines.length; i++) { for (let i = 0; i < codeTextLines.length; i++) {
blockWidth = Math.max(blockWidth, myUtils.getTextWidth(codeTextLines[i], "13px ui-sans-serif") + 60) blockWidth = Math.max(blockWidth, myUtils.getTextWidth(codeTextLines[i], "13px") + 60)
} }
// 構建 HTML 對象 // 構建 HTML 對象
const htmlShape = { const htmlShape = {
id: myUtils.getCodeBlockShapeId(codeData.className, codeDataFunc.name), id: myUtils.getCodeBlockShapeId(codeData.className, codeDataFunc.name),
x: 800, x: 800,
y: 100, y: 100,
width: blockWidth, width: blockWidth,
height: blockHeight, height: blockHeight,
shape: 'html', shape: 'html',
attrs: { attrs: {
body: { body: {
fill: 'white', fill: 'white',
stroke: '#666', stroke: '#666',
} }
}, },
html() { html() {
const wrap = document.createElement('div') const wrap = document.createElement('div')
wrap.innerHTML = '<pre style="padding-left: 20px">' + hljs.highlight(codeText, { language: 'java' }).value + '</pre>' wrap.innerHTML = '<pre style="padding-left: 20px">' + hljs.highlight(codeText, {language: 'java'}).value + '</pre>'
return wrap return wrap
}, },
} }
// 將 HTML 對象加入到返回結果中 // 將 HTML 對象加入到返回結果中
result.push(htmlShape) result.push(htmlShape)
} }
return result; return result;
} }
} }
export { CodeBlockShape } export {CodeBlockShape}

View file

@ -1,167 +1,189 @@
import hljs from "highlight.js";
type CodeData = { type CodeData = {
className: string, // 類名稱 className: string, // 類名稱
funcs: CodeDataFunc[] // 函數名稱 與 代碼塊字串 集合 funcs: CodeDataFunc[] // 函數名稱 與 代碼塊字串 集合
} }
type CodeDataFunc = { type CodeDataFunc = {
name: string, // 函數名稱 name: string, // 函數名稱
code: string // 代碼塊字串 code: string // 代碼塊字串
calls: CodeDataFuncCall[] // 調用的函數集合 calls: CodeDataFuncCall[] // 調用的函數集合
} }
type CodeDataFuncCall = { type CodeDataFuncCall = {
className: string, // 類名稱 className: string, // 類名稱
functionName: string // 函數名稱 functionName: string // 函數名稱
} }
export const CLASS_SHAPE_ID_TAG: string = "class-"; export const CLASS_SHAPE_ID_TAG: string = "class-";
export const CODE_BLOCK_ID_TAG: string = 'code-block-'; export const CODE_BLOCK_ID_TAG: string = 'code-block-';
class Utils { class Utils {
/** /**
* *
*/ */
tCanvas: any = null; tCanvas: any = null;
public getTextWidth(text: string, font: string = '13px ui-sans-serif') { public getTextWidth(text: string, font: string) {
// re-use canvas object for better performance // re-use canvas object for better performance
const canvas = this.tCanvas || (this.tCanvas = document.createElement('canvas')); const canvas = this.tCanvas || (this.tCanvas = document.createElement('canvas'));
const context = canvas.getContext('2d'); const context = canvas.getContext('2d');
context.font = font; context.font = font + ' ui-sans-serif';
return context.measureText(text).width; return context.measureText(text).width;
} }
/** /**
* CodeData * HTML
* @param codeBlockText * @param codeText
* @returns CodeData */
*/ public getTextHeight(codeText: string) {
public parseCodeData(codeBlockText: string): CodeData {
// 類名、方法名 的 標示符 // 創建一個新容器,將 HTML 代碼加入到容器中
const classTag = '@class' let node = document.createElement('div');
const functionTag = '@function' node.innerHTML = '<pre style="padding-left: 20px">' + hljs.highlight(codeText, {language: 'java'}).value + '</pre>';
const callTag = '@call' document.body.appendChild(node);
let codeLines = codeBlockText.split('\n') // 計算 容器高度
let height: number = parseInt(global.getComputedStyle(node).height.split('px')[0]);
// 取得 註釋塊 的代碼 // 移除容器
let commentLines = [] document.body.removeChild(node);
for (let i = 0; i < codeLines.length; i++) {
let codeLine = codeLines[i]
// 找到註釋開頭,則從當前行開始,加入到註釋塊列表中 return height;
if (codeLine.indexOf('/**') != -1) { }
commentLines.push(codeLine)
}
else if (commentLines.length > 0) {
commentLines.push(codeLine)
}
// 當碰到第一個多行註釋的結尾,則跳出循環 /**
if (codeLine.indexOf('*/') != -1) { * CodeData
break * @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 className = ''
let functionName = '' let functionName = ''
let calls: CodeDataFuncCall[] = [] let calls: CodeDataFuncCall[] = []
commentLines.forEach((comment) => { commentLines.forEach((comment) => {
// 獲取 類名 // 獲取 類名
if (comment.indexOf(classTag) != -1) { if (comment.indexOf(classTag) != -1) {
className = comment.substring(comment.indexOf(classTag) + classTag.length).trim() className = comment.substring(comment.indexOf(classTag) + classTag.length).trim()
} }
// 獲取 方法名 // 獲取 方法名
if (comment.indexOf(functionTag) != -1) { if (comment.indexOf(functionTag) != -1) {
functionName = comment.substring(comment.indexOf(functionTag) + functionTag.length).trim() functionName = comment.substring(comment.indexOf(functionTag) + functionTag.length).trim()
} }
// 獲取 調用的函數 // 獲取 調用的函數
if (comment.indexOf(callTag) != -1) { if (comment.indexOf(callTag) != -1) {
let callStr = comment.substring(comment.indexOf(callTag) + callTag.length).trim() let callStr = comment.substring(comment.indexOf(callTag) + callTag.length).trim()
calls.push({ calls.push({
className: callStr.substring(0, callStr.indexOf('@')).trim(), className: callStr.substring(0, callStr.indexOf('@')).trim(),
functionName: callStr.substring(callStr.indexOf('@') + 1).trim() functionName: callStr.substring(callStr.indexOf('@') + 1).trim()
}) })
} }
}) })
// 封裝結果 // 封裝結果
let result: CodeData = { let result: CodeData = {
className: className, className: className,
funcs: [{ funcs: [{
name: functionName, name: functionName,
code: codeBlockText, code: codeBlockText,
calls: calls calls: calls
}] }]
} }
return result return result
} }
/** /**
* Id * Id
* @param className * @param className
* @param functionName * @param functionName
* @param type Id * @param type Id
* @returns Id * @returns Id
*/ */
public getClassShapeId(className: string, functionName: string, type: 'class' | 'function'): string { public getClassShapeId(className: string, functionName: string, type: 'class' | 'function'): string {
if (type == 'class') { if (type == 'class') {
return CLASS_SHAPE_ID_TAG + className return CLASS_SHAPE_ID_TAG + className
} else if (type == 'function') { } else if (type == 'function') {
return CLASS_SHAPE_ID_TAG + className + '-' + functionName return CLASS_SHAPE_ID_TAG + className + '-' + functionName
} }
return '' return ''
} }
/** /**
* Id * Id
* @param className * @param className
* @param functionName * @param functionName
* @returns Id * @returns Id
*/ */
public getCodeBlockShapeId(className: string, functionName: string): string { public getCodeBlockShapeId(className: string, functionName: string): string {
return CODE_BLOCK_ID_TAG + className + '-' + functionName return CODE_BLOCK_ID_TAG + className + '-' + functionName
} }
/** /**
* ClassName CodeDataFunc * ClassName CodeDataFunc
* @param codeDatas CodeData * @param codeDatas CodeData
* @returns CodeData * @returns CodeData
*/ */
public mergeCodeDatas(codeDatas: CodeData[]): CodeData[] { public mergeCodeDatas(codeDatas: CodeData[]): CodeData[] {
// 對有相同 className 的 CodeData 的 func 進行合併 // 對有相同 className 的 CodeData 的 func 進行合併
let classNameToCodeData = new Map() let classNameToCodeData = new Map()
codeDatas.forEach((codeData: CodeData) => { codeDatas.forEach((codeData: CodeData) => {
// 如果在 Map 中已存在相應的 CodeData則將 func 進行合併 // 如果在 Map 中已存在相應的 CodeData則將 func 進行合併
if (classNameToCodeData.has(codeData.className)) { if (classNameToCodeData.has(codeData.className)) {
let tempCodeData = classNameToCodeData.get(codeData.className) as CodeData let tempCodeData = classNameToCodeData.get(codeData.className) as CodeData
tempCodeData.funcs.push(codeData.funcs[0]) tempCodeData.funcs.push(codeData.funcs[0])
} }
// 如果在 Map 中還沒有相應的 CodeData則將當前 CodeData 加入到 Map 中 // 如果在 Map 中還沒有相應的 CodeData則將當前 CodeData 加入到 Map 中
else { else {
classNameToCodeData.set(codeData.className, codeData) classNameToCodeData.set(codeData.className, codeData)
} }
}) })
// 封裝返回結果 // 封裝返回結果
let result: CodeData[] = [] let result: CodeData[] = []
for (let codeData of Array.from(classNameToCodeData.values())) { for (let codeData of Array.from(classNameToCodeData.values())) {
result.push(codeData) result.push(codeData)
} }
return result return result
} }
} }
export { Utils, CodeData, CodeDataFunc } export {Utils, CodeData, CodeDataFunc}