oimp
Version:
A CLI tool for generating OI problem and packages
136 lines (133 loc) • 6.98 kB
JavaScript
import { getNodePaths,findNodeIdByPath} from "./ide-utility.js";
import { fetchStatus } from "./ide-status.js";
import { loadFile } from './ide-jstree.js';
// 与webscoket相关
let ws = null;
let reconnectTimer = null;
let reconnectCount = 0;
export async function initWebSocket() {
// ===== WebSocket 自动刷新文件树和状态面板 =====
ws = new window.WebSocket('ws://' + window.location.host);
ws.onopen = function () {
reconnectCount = 0;
};
ws.onmessage = async function (event) {
try {
const data = JSON.parse(event.data);
if (data.type === 'tree_changed') {
// console.log('WS tree_changed arrived', data);
// 拉取最新树数据
const doRefreshTree = async (treeData) => {
const $tree = $('#file-tree');
const tree = $tree.jstree(true);
// 记录当前展开和选中节点 path
function getOpenedNodePaths(tree) {
const all = tree.get_json('#', { flat: true });
return all.filter(n => n.state && n.state.opened).map(n => n.data && n.data.path).filter(Boolean);
}
function getSelectedNodePaths(tree) {
if (typeof tree.get_selected === 'function') {
return getNodePaths(tree, tree.get_selected());
}
const all = tree.get_json('#', { flat: true });
return all.filter(n => n.state && n.state.selected).map(n => n.data && n.data.path).filter(Boolean);
}
const openedPaths = getOpenedNodePaths(tree);
const selectedPaths = getSelectedNodePaths(tree);
tree.settings.core.data = treeData;
window.isRefreshingTree = true;
$tree.one('refresh.jstree', function () {
const tree2 = $tree.jstree(true);
window.isRestoringTreeSelection = true;
// 恢复展开
openedPaths.forEach(path => {
const id = findNodeIdByPath(tree2, path);
// console.log('恢复展开', path, '->', id);
if (id) tree2.open_node(id, null, false);
});
// 恢复选中
let found = false;
for (const path of selectedPaths) {
const id = findNodeIdByPath(tree2, path);
// console.log('恢复选中', path, '->', id);
if (id) {
tree2.deselect_all();
tree2.select_node(id, false, false);
found = true;
break;
}
}
if (!found) {
// 尝试保持原来的cpp文件选中状态
const currentCppFile = window.currentFile;
if (currentCppFile && currentCppFile.endsWith('.cpp')) {
// 查找当前cpp文件在新树中的节点
const all = tree2.get_json('#', { flat: true });
const currentCppNode = all.find(n => n.data && n.data.type === 'file' && n.data.path === currentCppFile);
if (currentCppNode) {
tree2.deselect_all();
tree2.select_node(currentCppNode.id, false, false);
loadFile(currentCppNode.data.path);
found = true;
}
}
// 如果仍然没有找到,则选择第一个md文件
if (!found) {
const all = tree2.get_json('#', { flat: true });
const firstMd = all.find(n => n.data && n.data.type === 'file' && n.text.toLowerCase().endsWith('.md'));
if (firstMd) {
tree2.deselect_all();
tree2.select_node(firstMd.id, false, false);
loadFile(firstMd.data.path);
// console.log('未能恢复选中,自动选中第一个md文件', firstMd.data.path);
} else {
// console.log('未能恢复选中,也未找到md文件');
}
}
}
window.isRestoringTreeSelection = false;
window.isRefreshingTree = false;
// 如果有新的 pendingTreeData,立即再次刷新
if (window.pendingTreeData) {
const nextData = window.pendingTreeData;
window.pendingTreeData = null;
setTimeout(() => doRefreshTree(nextData), 0);
}
});
tree.refresh(true, true);
};
// 防抖处理
if (window.isRefreshingTree) {
// 正在刷新,存下最新数据
clearTimeout(window.debounceTreeChangedTimer);
window.debounceTreeChangedTimer = setTimeout(async () => {
const res = await fetch('/api/tree');
const treeData = await res.json();
window.pendingTreeData = treeData;
}, 1000);
} else {
clearTimeout(window.debounceTreeChangedTimer);
window.debounceTreeChangedTimer = setTimeout(async () => {
const res = await fetch('/api/tree');
const treeData = await res.json();
window.pendingTreeData = null;
doRefreshTree(treeData);
}, 1000);
}
return;
} else if (data.type === 'status_changed') {
fetchStatus && fetchStatus();
}
} catch (e) {
console.error("error:", e.message, e);
}
};
ws.onclose = function () {
if (reconnectTimer) clearTimeout(reconnectTimer);
reconnectTimer = setTimeout(initWebSocket, Math.min(5000, 1000 + 1000 * reconnectCount++));
};
ws.onerror = function () {
ws.close();
};
}