- 开发工具
- CLI
- 代码质量
【免费下载链接】cangjie_tools
仓颉编程语言命令行工具,包括仓颉包管理工具、仓颉格式化工具、仓颉多语言桥接工具及仓颉语言服务。
本文以 cangjie-language-server 的用户指南 user_guide.md 为主体,完整介绍 Cangjie Language Server(LSPServer)的功能范围、SDK 集成方式、全部启动参数的含义与底层实现,并结合 启动入口、日志模块、崩溃上报模块 等源码,帮助你理解每个参数在语言服务内部的真实作用,从而正确部署和调仓颉语言服务。
功能概览
Cangjie Language Server 是一个基于 LSP(Language Server Protocol)的仓颉语言服务后端,运行在 DevEco Studio 等编辑器客户端之后,通过标准输入输出与客户端通信,为仓颉代码提供智能编辑能力。用户指南中明确列出的核心能力包括:
- 定义跳转(Go to Definition):根据类名、函数名、变量名等仓颉代码符号跳转到其定义位置;
- 查找引用(Find References):定位某个符号在整个工程中所有被使用的地方;
- 代码补全(Completion):根据输入内容实时提供仓颉代码联想与补全,并支持自动包导入。
从源码结构看,实际注册的能力比用户指南的三句话更丰富。在 ArkLanguageServer.cpp 的GetServerCapabilities函数中,LSPServer 在initialize阶段向客户端声明了以下能力:
| 能力字段 | 说明 |
|---|---|
definitionProvider/referencesProvider | 定义跳转、查找引用 |
completionProvider | 补全,触发字符为.和` |
signatureHelpProvider | 签名帮助,触发字符为(和, |
hoverProvider | 悬浮提示 |
documentHighlightProvider/semaHighlightProvider | 文档高亮与语义高亮 |
renameProvider | 重命名,支持prepareProvider |
workspaceSymbolProvider/documentSymbolProvider | 工程级/文档级符号检索 |
typeHierarchyProvider/callHierarchyProvider | 类型层级、调用层级 |
documentLinkProvider、breakpointsProvider | 文档链接、断点 |
crossLanguageDefinition、findFileReferences、findFileRefactor | 跨语言定义、文件引用查找与重构 |
semanticTokensProvider | 语义 Token(支持 full delta 增量) |
codeActionProvider+executeCommandProvider | 仅在识别到 DevEco 客户端时声明 |
其中codeLensProvider只在非 DevEco 客户端下声明,而codeActionProvider与executeCommandProvider(APPLY_EDIT_COMMAND)只在 DevEco 客户端下声明,这说明 LSPServer 会对不同客户端做能力裁剪,后文"客户端识别机制"一节会解释其判定依据。
服务器自报信息为serverInfo.name = "Cangjie language server"、version = "1.0",见 GetServerInfo。
运行环境与前置依赖
与仓颉 SDK 配合使用
使用 Cangjie Language Server 必须结合仓颉 SDK 一起使用:确保 SDK 的tools/bin目录下存在仓颉语言服务的LSPServer二进制;此外还需要一个 LSP 客户端,官方支持的是DevEco Studio。LSPServer 本身是纯服务端进程,不具备 UI,所有编辑动作都由客户端发起、服务端应答。
进程启动时的环境变量解析
LSPServer 启动时会从进程环境中读取三个关键变量,实现位于 main.cpp 的 StringifyEnvironmentPointer:
| 环境变量 | Linux / macOS | Windows | macOS 备注 |
|---|---|---|---|
CANGJIE_HOME | 仓颉 SDK 根路径,取绝对路径后存入Environment::cangjieHome | 同左(键名统一转大写后匹配) | — |
CANGJIE_PATH | 仓颉工具链路径,存入Environment::cangjiePath | 同左 | — |
LD_LIBRARY_PATH | 运行时库搜索路径,存入Environment::runtimePath | PATH | DYLD_FALLBACK_LIBRARY_PATH |
从源码结构看,Environment结构体定义在 Options.h,包含cangjieHome、cangjiePath、runtimePath三个字段,随后作为参数传入ArkLanguageServer构造函数,用于后续编译项目上下文时定位 SDK 中的运行时与系统库。因此,如果 SDK 没有正确设置CANGJIE_HOME,语言服务将无法构建有效的编译环境,这是部署时最常见的环境问题来源。
DevEco 客户端的识别与单文件模式
LSPServer 并不假设客户端一定是 DevEco。在解析initialize请求参数时,Protocol.cpp 的逻辑是:
- 若
initializationOptions中携带非空的cangjieRootUri字段,则用其覆盖rootUri,并调用MessageHeaderEndOfLine::SetIsDeveco(true)标记当前为 DevEco 客户端; - 若
rootUri为空且initializationOptions中提供了singleFilePath,则以该文件的父目录作为工程根,进入"单文件模式"。
MessageHeaderEndOfLine::GetIsDeveco()这个全局开关随后会影响补全、诊断等多个能力分支(例如 LSPCompilerInstance.cpp 与 CompletionImpl.cpp 中的行为差异),也可以从源码结构推断:LSPServer 对 DevEco 做了专门的请求头协议适配,因此"是否 DevEco"是内部行为分叉的核心依据。
启动参数详解
LSPServer 的参数解析由 Options 类 完成,其Parse函数支持三种写法:短参数(-V)后直接跟值或视为无值开关;长参数(--enable-log=true)可用等号带值,或以下一个不以-开头的参数作为值。用户指南列出的 5 个启动参数逐一说明如下,并补充了源码中实际支持但指南未列出的参数。
--test:测试模式
用于在测试中启动 LSPServer。从源码看,--test是贯穿多个模块的全局开关,IsOptionSet("test")的判断散布在 ArkASTWorker.cpp、ArkServer.cpp、CompilerCangjieProject.cpp 等处。最典型的差异在补全任务调度上:
- 非测试模式下,补全任务通过"运行中任务 + 等待队列"的互斥机制(
isCompleteRunning/waitingCompletionTask)串行执行,避免并发解析同一文件; - 测试模式下(RunWithASTCache),任务改为
thread.join()同步执行,保证每次请求立即返回结果,便于自动化测试稳定断言。
配套测试设施包括:gtest框架入口 test/main.cpp,以及test/message/下按功能(goToDefinition、completion、diagnostic、rename等)组织的.test/.base请求-期望消息对,例如 goToDefinition 测试消息;测试用例清单见 testCase.list。按 开发者指南 的说法,执行python3 build.py build -t release --test会在output/bin下同时生成LSPServer与gtest_LSPServer_test两个二进制,再用python3 build.py test运行用例。
--disableAutoImport:禁用补全自动包导入
仓颉补全默认带有"自动包导入"能力:当补全项来自当前文件尚未import的包时,服务端会在补全结果中附带导入信息,客户端选中后自动补全import语句。该逻辑位于 CompletionImpl.cpp:
auto ifImportInfo = GetIfImportInfo(input, pos, prefix); KeywordCompleter::Complete(result, ifImportInfo); if (Options::GetInstance().IsOptionSet("disableAutoImport")) { return; // 跳过 AutoImportPackageComplete,不再追加自动导入补全项 } AutoImportPackageComplete(input, result, prefix);也就是说该参数是一个"早退开关":常规补全与关键字补全仍然生效,只是不再追加跨包的自动导入候选。当自动导入与用户预期冲突(例如频繁弹出错误包名的导入项)时,可在启动参数中加入--disableAutoImport关闭该行为。
--enable-log=<value>:开启日志生成
默认关闭日志生成;传入true才开启。对应实现是 main.cpp 的 ConfigByOptions:
auto enableLog = opts.GetLongOption("enable-log"); if (opts.IsOptionSet("enable-log") && enableLog.has_value() && enableLog.value() == "true") { ark::Logger::SetLogEnable(true); }注意判定条件是字符串严格等于"true",传其他值(如1、yes)不会开启日志。日志模块的关键实现约束见 Logger.cpp 与 Logger.h:
- 全局开关
enableLog初始为false,LogMessage在开关关闭时直接返回,不会打开任何文件; - 日志文件默认写在日志路径下的
log.txt,单文件超过LOG_FILE_MAX = 10MB时改名为cangjie_lsp_YYYY.MM.DD-HH-MM-SS_log.txt归档(见 HandleNewLog),最多保留LOG_FILE_MAX_COUNT = 5个归档文件; - 日志行格式为"时间 + 毫秒 + [类型] 消息",类型分
Error / Warning / Info / Log四级,代码中通过Trace::Log / Wlog / Elog宏(Logger.h)输出,并自动带上线程 ID。
--log-path=<value>:设置日志路径
调用Logger::SetPath(Logger.cpp)。它对路径做了严格校验,可以据此理解参数取值限制:
- 路径长度上限为
LOG_PATH_MAX = 1024,超长直接置空、放弃写日志; - 必须是一个真实存在的目录(
stat+S_ISDIR检查),否则pathBuf置空; - 若传空值,回退到进程当前工作目录(
getcwd)。
也就是说,目录不存在时不会自动创建日志目录,日志会被静默丢弃——使用时应先在 shell 中准备好目录。
-V:开启崩溃日志
对应 ConfigByOptions 中的CrashReporter::RegisterHandlers()。该注册动作按平台分叉(CrashReporter.cpp):
- Linux(RegisterHandlers):对
SIGABRT、SIGSEGV安装信号处理函数,对SIGPIPE单独安装只记录一条错误日志的处理函数。发生SIGSEGV等崩溃时,SignalHandler会在日志路径下创建.log/子目录,写入crash.dump(含最多 50 帧的回溯栈),并通过MessageInfoHandler/KernelLogHandler把最近的消息队列与内核日志分别落盘到messageInfo.txt、kernelLog.txt,随后进程退出; - Windows(VectoredExceptionHandler):通过向量化异常处理器捕获访问违规、栈溢出、整数溢出等结构化异常,结合
dbghelp符号解析输出带函数名的调用栈到.log/crash.dump; - macOS:从源码结构看,
RegisterHandlers是空实现(CrashReporter.cpp),即-V在该平台上暂不产生崩溃转储。
崩溃日志与运行日志共用--log-path指定的基础目录,因此排障时建议--enable-log=true --log-path=<dir> -V同时使用,崩溃现场与运行轨迹可互为佐证。
指南未列出、但源码支持的启动参数
阅读 ConfigByOptions 还能发现两个用户指南没有列出的参数:
| 参数 | 源码行为 |
|---|---|
--disable-incremental-optimization | 调用CompilerCangjieProject::SetIncrementalOptimize(false),关闭编译包的增量优化 |
--cache-path=<value> | 开启索引数据库缓存:在<cache-path>/.cache/index/下建立index.db,并在valid.txt中记录当前CANGJIE_VERSION |
--cache-path的完整流程见 main.cpp:启动时若valid.txt缺失或其中版本号与当前CANGJIE_VERSION不一致,说明缓存的index.db已过期,LSPServer 会删除旧库、重写版本文件后重新建库;之后通过OpenIndexDatabase打开数据库供符号索引使用(相关实现位于 index/ 目录,索引结构定义参考 generate/index.fbs)。换言之,该参数以 SDK 版本为失效判据,自动处理"升级 SDK 后旧索引不可用"的场景。
参数速查表
| 参数 | 默认 | 作用 | 源码依据 |
|---|---|---|---|
--test | 关 | 测试模式:同步执行任务、适配 gtest 用例 | ArkASTWorker.cpp |
--disableAutoImport | 关 | 禁用补全时的自动包导入 | CompletionImpl.cpp |
--enable-log=<value> | false | 传true才开启运行日志(log.txt,10MB 轮转,最多 5 个归档) | Logger.cpp |
--log-path=<value> | 空 | 日志目录,须为已存在目录且长度 ≤ 1024 | Logger.cpp |
-V | 关 | 注册崩溃处理器,输出.log/crash.dump(Linux/Windows 生效,macOS 空实现) | CrashReporter.cpp |
--disable-incremental-optimization | 增量优化开启 | 关闭编译增量优化 | main.cpp |
--cache-path=<value> | 不用 DB | 启用index.db索引缓存,按 SDK 版本自动失效重建 | main.cpp |
传输层与进程生命周期
理解启动参数后,再看 LSPServer 与客户端之间如何通信、如何退出,有助于判断异常退出的日志含义。
stdio 传输。main函数通过传输工厂注册并取出stdio传输(main.cpp),随后SetIO(stdin, stdout)把 LSP 消息绑定到标准输入输出。Windows 上会先_setmode将 stdin/stdout 置为二进制模式,避免 CRLF 转换破坏 JSON-RPC 报文。从源码结构看,传输层采用注册表设计(TransportRegistrar+TransportFactory),为日后替换 WebSocket 等其他传输留了扩展点,当前发行形态只有 stdio。
消息循环。ArkLanguageServer::Run()只做一件事——transp.Loop(*MsgHandler)(ArkLanguageServer.cpp),所有 LSP 请求(initialize、textDocument/completion、shutdown等)在循环内分发给MsgHandler处理。
优雅退出。退出码语义定义在LSPRet中:
NORMAL_EXIT:正常结束,记录LSP finished;ABNORMAL_EXIT:没有先发shutdown请求就发了exit通知,进程会向 stderr 打印warning: exit mode is abnormal, it's necessary to send shutdown request before exit notify.(main.cpp)。看到该 warning 时应检查客户端是否正确走shutdown → exit两步握手;ERR_IO且已请求过 shutdown:说明 stdout 已断开(客户端进程先退出),服务端会启动一个 10 秒倒计时线程,给清理动作留窗口后以std::_Exit(0)结束。
initialize阶段本身也会做工程编译:OnInitialize里先PerformCompiler(以rootUri和initializationOptions构建CompilerCangjieProject编译上下文),失败即返回SERVER_NOT_INITIALIZED错误(ArkLanguageServer.cpp),这解释了为什么 SDK 环境问题会在 initialize 响应中直接暴露。
部署与验证建议
结合用户指南与仓库内证据,给出一套可操作的部署检查清单:
- 确认二进制:SDK
tools/bin下存在LSPServer;该二进制可用 cangjie-language-server 工程源码构建,构建流程(build.py build -t release,交叉编译 Windows 用--target windows-x86_64)详见 developer_guide_zh.md; - 确认环境变量:
CANGJIE_HOME指向 SDK 根目录,必要时配置CANGJIE_PATH与运行时库搜索路径(Linux 为LD_LIBRARY_PATH); - 客户端对齐:DevEco Studio 发起
initialize时携带initializationOptions.cangjieRootUri;其他客户端可借助singleFilePath以单文件模式启动; - 排障启动:加
--enable-log=true --log-path=/tmp/cjlsp观察运行日志(log.txt及轮转归档),加-V准备崩溃现场(.log/crash.dump); - 回归验证:以
--test模式配合gtest_LSPServer_test运行仓库自带的功能测试,消息级断言数据位于 test/message/,源码级单测位于 test/unitTests/。
小结
Cangjie Language Server 以LSPServer二进制的形式嵌入仓颉 SDK,由 DevEco Studio 等客户端驱动。五个启动参数分别对应"测试同步化(--test)、补全自动导入开关(--disableAutoImport)、运行日志(--enable-log+--log-path)、崩溃上报(-V)"四类运行时行为,而--cache-path、--disable-incremental-optimization两个源码中支持的参数则分别服务于索引缓存与编译优化策略。掌握参数背后的源码路径与约束(如日志目录必须预先存在、enable-log只认字符串true、索引库按 SDK 版本自动失效),即可在实际部署中准确定位配置问题。
- 开发工具
- CLI
- 代码质量
【免费下载链接】cangjie_tools
仓颉编程语言命令行工具,包括仓颉包管理工具、仓颉格式化工具、仓颉多语言桥接工具及仓颉语言服务。
相关推荐
Ark/Velero 服务器端启动指南:`ark server` 命令参数详解与运行原理源码解析
Ark/Velero 服务器端启动指南: ark server 命令参数详解与运行原理源码解析 本指南以 v0.8.1 文档 ark_server.md htt
逆向工程MCP 服务AI 技能PaddleNLP 在燧原 S60(GCU)上运行 Llama2-13B 推理:环境搭建、参数调优与源码级原理剖析
PaddleNLP 在燧原 S60(GCU)上运行 Llama2 13B 推理:环境搭建、参数调优与源码级原理剖析 本文面向希望在燧原(Enflame)S60
人工智能大模型预训练微调LoRARLHF强化学习分布式训练模型推理服务推理引擎模型量化模型压缩本地部署NLPCPython 3.15 命令行与运行环境全解析:从启动参数到 PYTHON* 环境变量
CPython 3.15 命令行与运行环境全解析:从启动参数到 PYTHON 环境变量 关联权威文档: Doc/using/cmdline.rst https:
编程语言语言运行时解释器标准库
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考