增量 Session

packages/ttsc/driver/session.go 是 driver 层的增量类型检查原语:它让一个已加载的 Program 常驻,每次文件编辑只重解析改动的那个文件(复用未改的 AST 与钉定的 checker,经 UpdateProgram),而不是每次请求重编译整个项目。

本页讲 Session 的设计、它和常驻转换路径(utility serve)的关键区别、以及"为什么 type-check 能复用而 transform 不能"。

Session 是什么

type Session struct {
  cwd     string
  overlay *OverlayFS
  prog    *Program
}

NewSessionsession.go:29)在一个 OverlayFS 之上加载项目并把结果 Program 常驻。OverlayFSdriver/overlay.go)是内存编辑覆盖在真实文件系统之上的 VFS:Set(path, content) 写入内存版本,让 tsgo 看到内存编辑而非磁盘内容。

用法:每个项目构造一个 Session(cwd 绝对),通过 Apply 喂文件编辑,通过 SourceText 读常驻 Program 的源。Apply 在编辑文件的 import/reference 图未变时复用现有 Program。

Apply:增量更新

func (s *Session) Apply(absPath, content string) bool {
  s.overlay.Set(absPath, content)
  name := absPath
  if file := s.prog.SourceFile(absPath); file != nil {
    name = file.FileName()
  }
  changed := shimtspath.ToPath(name, s.cwd, s.overlay.caseSensitive)
  newHost := DefaultHost(s.cwd, s.overlay)
  newProg, reused := s.prog.TSProgram.UpdateProgram(changed, newHost, nil)
  if newProg != nil {
    s.prog.TSProgram = newProg
    s.prog.Host = newHost
  }
  return reused
}

Applysession.go:46):

  1. 把新内容写进 overlay;
  2. 把编辑文件的路径规范化成 tsgo 的 Path
  3. 用新 host 调 TSProgram.UpdateProgram——tsgo 在 import/reference 图未变时复用现有 Program(返回 reused=true),变了则重建(reused=false);
  4. 返回是否复用。

句柄会变:当编辑重塑 import 图时 s.prog.TSProgram 被换掉,所以调用方应在 Apply 之后读 Program()session.go:65),而不是跨编辑缓存它。

Session vs 常驻转换(关键区别)

这是 session.go 包注释(session.go:7)记录的一个重要设计判断,引用了 issue samchon/ttsc#255:

Session 是 driver 层的增量类型检查原语。常驻转换路径(utility-host serve)刻意用它:链接插件阶段会原地改 source AST,所以 transform 不能复用一个热的、干净的 Program,必须每次编辑重建一个新的。Session 提供的是 type-check 复用,不是 transform 复用。

换句话说:

路径复用什么为什么
Session.Apply干净 Program 的 AST + checker类型检查不改 AST,可安全增量更新
utility serveutility/serve.gooverlay 状态,但每次 update 重建 Program链接插件原地改 AST,热 Program 已被污染

utility/serve.go::buildServeCacheutility/serve.go:132)每次 update 都重跑整个项目 transform(ApplyLinkedPlugins + 逐文件 EmitSourceFile),把转换后文本按 key 缓存。失败的编辑会被回滚(handleServeLine:115),让不编译的文件不污染后续重建。

TtscService 的两层后端

JS 侧的 TtscServicesrc/TtscService.ts)是这条常驻转换路径的门面。它通过 utility-host serve 协议(newline-delimited JSON 请求)与 Go 常驻进程通信:

  • {"file":"<path>"} → 返回该文件转换后的 TypeScript(serveResponse)。
  • {"update":"<path>","content":"<text>"} → 应用新内容并重转换(serveUpdateResponse)。

RunServeutility/serve.go:58)用 bufio.NewReader.ReadString('\n') 而非 bufio.Scanner,因为 update 请求把整个文件内容放在一行上,Scanner 的行长上限会变成常驻宿主的人为死亡点(serve.go:70 注释)。

注意:driver 的 Session 与 utility 的 serve 是两套常驻机制。Session 给增量类型检查(如未来的 watch server 复用),serve 给 transform 复用(TtscService、Metro worker)。跨独立 worker 进程共享一个宿主(Metro worker pool)仍在 #255 跟踪中。

不变量

  • cwd 必须绝对,tsconfig 可相对(NewSession 签名)。
  • 编辑后读 Program 必须用 s.Program(),因句柄可能已被 UpdateProgram 换掉。
  • Close() 释放常驻 Program 的 checker 租约(session.go:80)。

失败模式

失败行为
加载项目失败NewSession 返回诊断 + 错误
编辑引入类型错误(serve 路径)update 回滚,Updated: false,诊断写 stderr,旧转换保留
请求 JSON 非法(serve 路径)返回空 serveResponse,不崩溃

维护者提示

  • 别把 serve 改成复用热 Program——链接插件原地改 AST 的前提决定了它必须重建。这是 #255 的明确结论。
  • 改 Session 时记住 UpdateProgramreused 语义是性能信号,不是正确性信号;正确性由 overlay + 重建保证。

接下来