# easycode **Repository Path**: SongpingWang/easycode ## Basic Information - **Project Name**: easycode - **Description**: easycode 简单的IDE - **Primary Language**: C++ - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-04-02 - **Last Updated**: 2026-09-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # easycode #### 介绍 easycode 简单的IDE #### 软件架构 ```text ┌──────────────────────────────────────────────────────────────────────────────┐ │ IDE Application │ │ ┌───────────────────┐ ┌────────────────────────────────────────────────┐ │ │ │ VirtualFileSystem │ │ DocumentManager (管理打开的文件) │ │ │ │ FileSystemWatch │ └─┬──────────────────────────────────────────────┘ │ │ └───────────────────┘ │ 持有 shared_ptr │ └────────────────────────┼─────────────────────────────────────────────────────┘ ▼ ┌──────────────────────────────────────────────────────────────────────────────┐ │ EditorDocument (核心聚合域 / Model) │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌────────────────────┐ │ │ │ TextBuffer │◄──────┤ EditManager │◄──────┤ UndoManager │ │ │ │ (PieceTree) ├──────►│ (多光标/事务) │ │ (Command Stack) │ │ │ └─┬─────────────┬─┘ └─────────────────┘ └────────────────────┘ │ │ │ │ ▲ │ │ │ Snapshot(V) │ EditEvent(V) │ 用户操作 │ │ ▼ ▼ │ │ │ ┌──────────────────┐ ┌────────┴────────┐ ┌────────────────────┐ │ │ │ SyntaxHighlighter│──────►│ LspClient │◄──────┤ DebugSession │ │ │ │ (AST / Tokens) │ 诊断 │ (JSON-RPC) │ │ (DAP 协议) │ │ │ └─┬──────────────┬─┘ └─────────────────┘ └────────────────────┘ │ │ │ │ │ └────┼──────────────┼───────────────────────────────────────────────────────────┘ │ treeReady(V, ranges) (Signal) │ │ │ Pull tokens (Direct Call with ReadLock) │ ▼ ▼ │ ┌──────────────────────────────────────────────────────────────────────────────┐ │ UI Views (可多个视图共享同一文档) │ │ ┌─────────────────────────────────┐ ┌──────────────────────────────────┐ │ │ │ EditorWidget (View 1 - 主屏) │ │ EditorWidget (View 2 - 分屏) │ │ │ │ - Virtual Scroll (Viewport) │ │ - Virtual Scroll │ │ │ │ - pull tokens & diagnostics │ │ - pull tokens & diagnostics │ │ │ │ - paintEvent() │ │ - paintEvent() │ │ │ └───────────────┬─────────────────┘ └───────────────┬──────────────────┘ │ │ │ Input Events │ │ └──────────────────┴─────────────────────────────────────┴─────────────────────┘ ``` ### 关键子系统组装逻辑分析 各个类如何组合,依赖关系。 #### 第一阶段:底层基建组装 (Memory & Text Storage) 1. **内存管理组装**: - StringHierarchy::init() 在 main() 最早调用。 - TypedPool 应该作为 SyntaxHighlighter 的成员变量,每次解析新快照时,先 reset() 内存池,然后重新分配。因为 Token 随 AST 销毁而销毁。 3. **不可变存储组装**: - TextBuffer 内部持有 AppendOnlyBuffer。将 AppendOnlyBuffer 的互斥锁替换为无锁机制(或者至少是粗粒度的共享锁)。 - PieceTree 的读操作全部基于传值的 Snapshot,确保主线程和后台高亮线程拿到的根节点 m_root 是相互独立的 shared_ptr,彻底消除读写锁。 #### 第二阶段:核心编辑链路组装 (Buffer + EditManager) 1. **Caret 唯一性组装**: - 修改 struct Caret { uint64_t id; size_t pos; ... }。 - EditManager 在执行 executeEdit 后,不再通过模糊的 position 找主光标,而是 find_if(id == primaryId)。 3. **事件分发机制 (Event Bus)**: - TextBuffer::onTextEdited 从 std::function 改为 Qt Signal 或者 std::vector,允许 SyntaxHighlighter 和 LspClient 同时监听。 #### 第三阶段:高亮与LSP的异步渲染链路 (The Async Pipeline) 这里是避免全盘崩溃的最关键链路,组装顺序必须严格: 2. **触发阶段**: - 用户输入 -> EditManager -> TextBuffer 产生修改。 - TextBuffer 生成 Snapshot(version=2),并广播 EditEvent。 3. **LSP 分发**: - LspClient 收到 EditEvent,将其放入防抖队列(Debounce Timer 约 500ms)。超时后向 LSP Server 发送 textDocument/didChange。 4. **高亮触发与打断 (Cancellation)**: - SyntaxHighlighter 收到 EditEvent。 - 如果当前后台线程正在解析 Snapshot(version=1),立即设置一个原子标志位 m_cancelFlag = true。 - Tree-sitter 解析器定期检查该标志位,若为 true 则终止解析,返回空。 - 高亮线程启动新任务:基于 Snapshot(version=2) 进行增量解析。 5. **渲染回调 (Pull Model)**: - 高亮解析完成,写入 lineTokensCache(受读写锁保护)。 - 触发信号 emit treeReady(version, modified_start_row, modified_end_row)。 - EditorWidget 的槽函数接收信号,判断 if (this->viewport_intersects(start, end)),如果可见,调用 update() 触发 paintEvent。 - paintEvent 中,调用 highlighter->getTokensForRange() 获取可见行的 Token 绘制。 #### 第四阶段:UI 性能组装 (Virtual Scrolling) 1. **重构 QAbstractScrollArea**: - 严禁在 EditorWidget::paintEvent 中绘制全量文本。 - 根据 verticalScrollBar()->value() 和 fontMetrics.height() 计算出 startLine 和 endLine。 - **行列缓存**:直接向 Document 查询这几十行的 offset 范围,然后向高亮缓存索要 Token。 - 绘制采用分层:背景层(选区) -> 文本层(语法着色) -> 装饰层(LSP波浪线) -> 光标层(闪烁)。 ------ **落地顺序:** 1. **重构底层 Model:** 先把 EditorDocument 这个中间层建出来,把 TextBuffer 和 EditManager 塞进去。跑通无 UI 的单元测试(验证光标排序、Undo/Redo)。 2. **重构 UI 渲染:** 将 EditorWidget 改写为纯粹的 Viewport 机制,只接写死的假数据(Mock Token),把虚拟滚动的 FPS 压榨到 60 帧以上。 3. **联调高亮线程:** 引入 Tree-sitter,实现前面设计的“高亮取消、版本对比、按需拉取(Pull)”机制。 4. **外围生态接入:** 最后挂载 LspClient 和 DebugSession,因为它们的生命周期和状态管理最复杂,必须在基础编辑流水线稳如磐石后才能加入。 架构图中明确划分了层级: - **Model 层**:`TextBuffer`、`PieceTree`、`Snapshot` —— 负责文本存储与字节级查询。 - **Language Service 层**:`SyntaxHighlighter` —— 负责调用 Tree-sitter 进行 AST 解析,产出 Token。 - **View 层**:`EditorWidget` —— 负责接收 Token 缓存、绘制文本与高亮。 如下代码是一个复刻现代 IDE 文本编辑区域 参考对象:Visual Studio 2022、CLion 的最佳实践。 现在需要完善并组装各个类与组件之间的功能代码,我们需要再次评估代码的整体架构,为了避免篇幅过长,我提供各个组件的头文件代码如下, 在优化前请先分析代码的架构,避免优化陷入代码细节中而忘记整体架构导致改动架构而全盘奔溃无法收敛。此次仅仅思考架构优化与组装,请先分析代码组织架构,整体架构图,文本编辑语法分析渲染架构。(本次无需实现代码) 我的分析如下请参考优化。