实现保存另存为

This commit is contained in:
锦恢 2024-10-22 21:28:02 +08:00
parent 67b008fa31
commit f9a62bfe09
10 changed files with 161 additions and 13 deletions

View File

@ -18,7 +18,7 @@
<script> <script>
window.readVcdFile = async () => { window.readVcdFile = async () => {
let inputVcdFile = 'test.vcd'; let inputVcdFile = 'test.vcd';
let inputViewFile = 'test.vcd.view'; let inputViewFile = 'test.view';
const response = await fetch(inputVcdFile); const response = await fetch(inputVcdFile);
const blob = await response.blob(); const blob = await response.blob();

Binary file not shown.

BIN
public/test.view Normal file

Binary file not shown.

View File

@ -8,7 +8,7 @@ for file in os.listdir('dist'):
with open('./dist/index.html', 'r', encoding='utf-8') as fp: with open('./dist/index.html', 'r', encoding='utf-8') as fp:
html = fp.read() html = fp.read()
html = html.replace("''", "'<workerRoot>'") html = html.replace("''", "'<root>'")
with open('./dist/index.html', 'w', encoding='utf-8') as fp: with open('./dist/index.html', 'w', encoding='utf-8') as fp:
fp.write(html) fp.write(html)

View File

@ -30,6 +30,7 @@ import RightNav from '@/components/right-nav.vue';
import Pivot from '@/components/pivot'; import Pivot from '@/components/pivot';
import { recoverFromInputFile, recoverSession } from './hook/recover'; import { recoverFromInputFile, recoverSession } from './hook/recover';
import { setDefaultCss } from './hook/css'; import { setDefaultCss } from './hook/css';
import { handleVscodeMessage } from './api/message';
const { t } = useI18n(); const { t } = useI18n();
@ -114,14 +115,19 @@ onMounted(async () => {
globalLookup.topModules.push(mod); globalLookup.topModules.push(mod);
} }
} }
loading.close();
worker.terminate(); worker.terminate();
// recoverConfig // recoverConfig
recoverSession(VcdInfo.topModules); recoverSession(VcdInfo.topModules);
loading.close();
}); });
}); });
// vscode
window.addEventListener('message', event => {
handleVscodeMessage(event);
});
</script> </script>
<style> <style>

View File

@ -26,7 +26,7 @@ export async function saveView(originVcdFile, originVcdViewFile, payload) {
} else { } else {
vscode.postMessage({ vscode.postMessage({
command: 'save-view', command: 'save-view',
data: { originVcdFile, originVcdViewFile, payload } data: { originVcdFile, originVcdViewFile, payload: structuredClone(payload) }
}); });
} }
} }
@ -47,11 +47,28 @@ export async function saveViewAs(originVcdFile, originVcdViewFile, payload) {
} else { } else {
vscode.postMessage({ vscode.postMessage({
command: 'save-view-as', command: 'save-view-as',
data: { originVcdFile, originVcdViewFile, payload } data: { originVcdFile, originVcdViewFile, payload: structuredClone(payload) }
}); });
} }
} }
export async function loadView(originVcdFile) {
if (mode === 'debug') {
const res = await axios.post('http://localhost:3000/load-view', { originVcdFile });
if (res.data && res.data.recoverJson && res.data.viewPath) {
globalLookup.originVcdViewFile = res.data.viewPath;
return res.data.recoverJson;
}
} else {
vscode.postMessage({
command: 'load-view',
data: { originVcdFile }
});
}
return undefined;
}
/** /**
* *
* @param {number} delay * @param {number} delay

48
src/api/message.js Normal file
View File

@ -0,0 +1,48 @@
import { globalLookup } from "@/hook/global";
import { recoverFromJson, recoverSession } from "@/hook/recover";
import { ElLoading } from "element-plus";
import i18n from "@/i18n/index";
const { t } = i18n.global;
/**
*
* @param {MessageEvent} event
*/
export function handleVscodeMessage(event) {
const command = event.data.command;
switch (command) {
case 'save-view-as':
saveViewAs(event.data);
break;
case 'load-view':
loadView(event.data);
break;
default:
break;
}
}
function loadView(data) {
const { recoverJson, viewPath } = data;
if (recoverJson === undefined || viewPath === undefined) {
return;
}
const loading = new ElLoading.service({
lock: true,
text: t('loading'),
background: 'rgba(0, 0, 0, 0.7)'
});
globalLookup.originVcdViewFile = viewPath;
recoverFromJson(recoverJson);
recoverSession(globalLookup.topModules);
loading.close();
}
function saveViewAs(data) {
const { viewPath } = data;
if (viewPath === undefined) {
return;
}
globalLookup.originVcdViewFile = viewPath;
}

View File

@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<div class="status"></div> <div class="status"></div>
<div class="item"> <div class="item" @click="manualLoadView()">
<span>{{ t('filemenu.load-view') }}</span> <span>{{ t('filemenu.load-view') }}</span>
<span><code>Ctrl K</code></span> <span><code>Ctrl K</code></span>
</div> </div>
@ -9,6 +9,10 @@
</template> </template>
<script setup> <script setup>
import { loadView } from '@/api';
import { globalLookup } from '@/hook/global';
import { recoverFromJson, recoverSession } from '@/hook/recover';
import { ElLoading } from 'element-plus';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
@ -16,4 +20,26 @@ const { t } = useI18n();
defineComponent({ name: 'load-view' }); defineComponent({ name: 'load-view' });
async function manualLoadView() {
const loading = new ElLoading.service({
lock: true,
text: t('loading'),
background: 'rgba(0, 0, 0, 0.7)'
});
const recoverJson = await loadView(globalLookup.originVcdFile);
if (recoverJson) {
recoverFromJson(recoverJson);
recoverSession(globalLookup.topModules);
}
loading.close();
}
document.addEventListener('keydown', async event => {
if (event.ctrlKey && event.key === 'k') {
event.preventDefault();
manualLoadView();
}
});
</script> </script>

View File

@ -66,6 +66,11 @@ async function attemptRecover(inputFile, inputViewFile) {
globalLookup.originVcdViewFile = inputViewFile; globalLookup.originVcdViewFile = inputViewFile;
return recoverResult; return recoverResult;
} }
// 不存在则创建一个和 inputFile 同名的 view 文件
if (inputFile.endsWith('.vcd')) {
inputFile = inputFile.slice(0, -4);
}
globalLookup.originVcdViewFile = inputFile + '.view'; globalLookup.originVcdViewFile = inputFile + '.view';
return await findRecoverFile(globalLookup.originVcdViewFile); return await findRecoverFile(globalLookup.originVcdViewFile);
} }
@ -82,13 +87,16 @@ export async function recoverFromInputFile(inputFile, inputViewFile) {
recoverFromJson(recoverJson); recoverFromJson(recoverJson);
} }
function recoverFromJson(recoverJson) { export function recoverFromJson(recoverJson) {
if (recoverJson === undefined) { if (recoverJson === undefined) {
return; return;
} }
// 加载 waves // 加载 waves
const waves = recoverJson.waves; const waves = recoverJson.waves;
if (!recoverConfig.waves) {
recoverConfig.waves = new Map();
}
if (waves instanceof Array && waves.length > 0) { if (waves instanceof Array && waves.length > 0) {
for (const wave of waves) { for (const wave of waves) {
const name = wave.name; const name = wave.name;
@ -401,7 +409,8 @@ export function makeSaveViewPayload(config) {
// 窗口状态 // 窗口状态
if (all || defaultFalseWrapper(config, 'state')) { if (all || defaultFalseWrapper(config, 'state')) {
const state = globalLookup.pstate; // proxy 对象在一定情况下无法序列化
const state = JSON.parse(JSON.stringify(globalLookup.pstate));
Object.assign(payload, { state }); Object.assign(payload, { state });
} }

View File

@ -3,6 +3,7 @@
* 设计参考文档https://nc-ai-lab.feishu.cn/wiki/Fy3ZwtbYbiatmxkhOp2cyHSFnlw * 设计参考文档https://nc-ai-lab.feishu.cn/wiki/Fy3ZwtbYbiatmxkhOp2cyHSFnlw
*/ */
import { groupColorDispatcher } from "@/components/sidebar/manage-group";
import { globalLookup } from "./global"; import { globalLookup } from "./global";
import { getSmartCurrentSignalValue } from "./utils"; import { getSmartCurrentSignalValue } from "./utils";
@ -65,11 +66,28 @@ export const WaveContainerView = {
delete globalLookup.currentSignalValues[signal.link]; delete globalLookup.currentSignalValues[signal.link];
// 从 currentWiresRenderView 中删除 // 从 currentWiresRenderView 中删除
const i = findViewIndexByLink(signal.link); // 此处需要考虑到处于 group 中的 link 的情况
const tailElements = renderView.slice(i + 1); const { index, childIndex } = findViewIndexTuple(signal.link);
renderView.length = i;
tailElements.forEach(view => renderView.push(view));
function deleteElement(container, index) {
const tailElements = container.slice(index + 1);
container.length = index;
tailElements.forEach(el => container.push(el));
}
if (childIndex >= 0) {
// 删除 group 内元素
const view = renderView[index];
deleteElement(view.children, childIndex);
// 如果 group 空了,则也删除
if (view.children.length === 0) {
deleteElement(renderView, index);
groupColorDispatcher.put(view.groupInfo.color);
}
} else if (index >= 0) {
// 删除总体的元素
deleteElement(renderView, index);
}
}, },
/** /**
@ -82,12 +100,36 @@ export const WaveContainerView = {
} }
}; };
/**
* @description 找到位于嵌套结构中的索引
* @param {string} link
* @returns {{ index: number, childIndex: number }} index: 位于 renderView 的索引位于 group children 中的索引
*/
function findViewIndexTuple(link) {
const renderView = globalLookup.currentWiresRenderView;
for (let i = 0; i < renderView.length; ++ i) {
const view = renderView[i];
if (view.signalInfo.link === link) {
return { index: i, childIndex: -1 };
}
for (let j = 0; j < view.children.length; ++ j) {
const child = view.children[j];
if (child.signalInfo.link === link) {
return { index: i, childIndex: j };
}
}
}
return { index: -1, childIndex: -1 };
}
export function findViewIndexByLink(link) { export function findViewIndexByLink(link) {
const renderView = globalLookup.currentWiresRenderView; const renderView = globalLookup.currentWiresRenderView;
let i = 0; let i = 0;
while (renderView[i].signalInfo.link !== link && i < renderView.length) { while (i < renderView.length && renderView[i].signalInfo.link !== link) {
++ i; ++ i;
} }