VSCode插件开发实战:LSP文本同步第8讲

2026-06-16阅读 0热度 0
其他

vscode插件快餐教程(8) - LSP文本同步

从这一节开始,我们来聊一聊如何通过LSP协议做文本同步。说白了,就是让语言服务器知道编辑器里文件内容的变化——打开、修改、关闭这些操作,都需要实时同步过去。

文件打开

先从最简单的入手:监听文件打开事件。这背后的LSP协议里,对应的参数是DidChangeTextDocumentParams结构。先看一张协议结构的截图:

LSP_

微软的SDK已经在这个协议基础上做了封装,接口更友好。我们来看封装后的API:

_API

当前,TextDocument提供了4个属性:

  • uri: 文件的URI
  • version: 文件的版本号
  • languageId: 编程语言
  • lineCount: 有多少行

另外还有3个函数:getText()获取全文,positionAtoffsetAt用于Position和offset之间的转换。

光说不练假把式,直接看代码示例:

documents.onDidOpen((event: TextDocumentChangeEvent) => {
    logger.debug(`on open:${event.document.uri}`);
    logger.debug(`file version:${event.document.version}`);
    logger.debug(`file content:${event.document.getText()}`);
    logger.debug(`language id:${event.document.languageId}`);
    logger.debug(`line count:${event.document.lineCount}`);
});

跑起来的效果呢?给大家贴一段真实日志:

[2019-06-04T18:11:31.999] [DEBUG] lsp_demo - on open:file:///Users/ziyingliuziying/test.vb
[2019-06-04T18:11:31.999] [DEBUG] lsp_demo - file version:1
[2019-06-04T18:11:31.999] [DEBUG] lsp_demo - file content:dim a as integer;TextView1Ja vascriptButton3Test2
[2019-06-04T18:11:31.999] [DEBUG] lsp_demo - language id:vb
[2019-06-04T18:11:32.000] [DEBUG] lsp_demo - line count:6

监听文件变化

监听文件变化与监听打开文件,写法几乎一模一样。代码长这样:

documents.onDidChangeContent((e: TextDocumentChangeEvent) => {
    logger.debug('document change received.');
    logger.debug(`document version:${e.document.version}`);
    logger.debug(`text:${e.document.getText()}`);
    logger.debug(`language id:${e.document.languageId}`);
    logger.debug(`line count:${e.document.lineCount}`);
});

看看运行时日志,能清楚看到每次修改后版本号递增、内容变化:

[2019-06-04T18:30:34.329] [DEBUG] lsp_demo - document change received.
[2019-06-04T18:30:34.329] [DEBUG] lsp_demo - document version:1
[2019-06-04T18:30:34.329] [DEBUG] lsp_demo - text:dim a as integer;TextView1Ja vascriptButton3Test2
[2019-06-04T18:30:34.329] [DEBUG] lsp_demo - language id:vb
[2019-06-04T18:30:34.329] [DEBUG] lsp_demo - line count:6
[2019-06-04T18:30:39.457] [DEBUG] lsp_demo - document change received.
[2019-06-04T18:30:39.457] [DEBUG] lsp_demo - document version:2
[2019-06-04T18:30:39.457] [DEBUG] lsp_demo - text:
[2019-06-04T18:30:39.457] [DEBUG] lsp_demo - language id:vb
[2019-06-04T18:30:39.458] [DEBUG] lsp_demo - line count:2
[2019-06-04T18:30:41.576] [DEBUG] lsp_demo - document change received.
[2019-06-04T18:30:41.576] [DEBUG] lsp_demo - document version:3
[2019-06-04T18:30:41.577] [DEBUG] lsp_demo - text:b
[2019-06-04T18:30:41.577] [DEBUG] lsp_demo - language id:vb
[2019-06-04T18:30:41.577] [DEBUG] lsp_demo - line count:1
[2019-06-04T18:30:41.949] [DEBUG] lsp_demo - document change received.
[2019-06-04T18:30:41.949] [DEBUG] lsp_demo - document version:4
[2019-06-04T18:30:41.949] [DEBUG] lsp_demo - text:u
[2019-06-04T18:30:41.949] [DEBUG] lsp_demo - language id:vb
[2019-06-04T18:30:41.949] [DEBUG] lsp_demo - line count:1
[2019-06-04T18:30:42.447] [DEBUG] lsp_demo - document change received.
[2019-06-04T18:30:42.447] [DEBUG] lsp_demo - document version:5
[2019-06-04T18:30:42.447] [DEBUG] lsp_demo - text:Button5
[2019-06-04T18:30:42.447] [DEBUG] lsp_demo - language id:vb
[2019-06-04T18:30:42.447] [DEBUG] lsp_demo - line count:1

文本监听模式

上面使用的监听方式是增量监听,对应的模式是TextDocumentSyncKind.Incremental。具体初始化代码里这样配置:

connection.onInitialize((params: InitializeParams) => {
    return {
        capabilities: {
            textDocumentSync: {
                openClose: true,
                change: TextDocumentSyncKind.Incremental
            },
            completionProvider: {
                resolveProvider: true
            }
        }
    };
});

增量模式好理解:每次只把变化了的部分传过去,网络开销小。但如果需要完整状态,也可以改成全量模式。区别只在于把Incremental换成Full

connection.onInitialize((params: InitializeParams) => {
    return {
        capabilities: {
            textDocumentSync: {
                openClose: true,
                change: TextDocumentSyncKind.Full
            },
            completionProvider: {
                resolveProvider: true
            }
        }
    };
});

全量模式下,每次变化后都会把整个文件内容原封不动传过来。看日志就清楚了:

[2019-06-04T19:52:12.305] [DEBUG] lsp_demo - document change received.
[2019-06-04T19:52:12.305] [DEBUG] lsp_demo - document version:1
[2019-06-04T19:52:12.305] [DEBUG] lsp_demo - text:dim a as integer;TextView1Ja vascriptButton3Test2Button5
[2019-06-04T19:52:12.305] [DEBUG] lsp_demo - language id:vb
[2019-06-04T19:52:12.305] [DEBUG] lsp_demo - line count:7
[2019-06-04T19:52:19.442] [DEBUG] lsp_demo - document change received.
[2019-06-04T19:52:19.442] [DEBUG] lsp_demo - document version:2
[2019-06-04T19:52:19.442] [DEBUG] lsp_demo - text:dim a as integer;TextView1Ja vascriptButton3Test2Button5T
[2019-06-04T19:52:19.443] [DEBUG] lsp_demo - language id:vb
[2019-06-04T19:52:19.443] [DEBUG] lsp_demo - line count:7
[2019-06-04T19:52:19.443] [DEBUG] lsp_demo - onCompletion
[2019-06-04T19:52:19.787] [DEBUG] lsp_demo - document change received.
[2019-06-04T19:52:19.787] [DEBUG] lsp_demo - document version:5
[2019-06-04T19:52:19.787] [DEBUG] lsp_demo - text:dim a as integer;TextView1Ja vascriptButton3Test2Button5Test
[2019-06-04T19:52:19.787] [DEBUG] lsp_demo - language id:vb
[2019-06-04T19:52:19.787] [DEBUG] lsp_demo - line count:7
[2019-06-04T19:52:19.788] [DEBUG] lsp_demo - onCompletion
[2019-06-04T19:52:21.877] [DEBUG] lsp_demo - document change received.
[2019-06-04T19:52:21.877] [DEBUG] lsp_demo - document version:6
[2019-06-04T19:52:21.877] [DEBUG] lsp_demo - text:dim a as integer;TextView1Ja vascriptButton3Test2Button5Test
[2019-06-04T19:52:21.877] [DEBUG] lsp_demo - language id:vb
[2019-06-04T19:52:21.877] [DEBUG] lsp_demo - line count:8

除了增量和全量,还有一种TextDocumentSyncKind.None模式,选了它就连文本都不同步了——基本用不上,除非你的语言服务器完全不需要文件内容,只靠URI做标记。但大多数场景下,还是增量和全量二选一,至于选哪个,看你服务器对完整内容的依赖程度和网络带宽的考量。

免责声明

本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。

相关阅读

更多
欢迎回来 登录或注册后,可保存提示词和历史记录
登录后可同步收藏、历史记录和常用模板
注册即表示同意服务条款与隐私政策