首页 > 其他分享 >2.实现语言服务端

2.实现语言服务端

时间:2023-04-08 09:11:06浏览次数:40  
标签:语言 实现 textDocument capabilities item connection let workspace 服务端

笔记软件在2023/4/6 14:01:06推送该笔记

Language Server

这个语言服务器的源码在/server中。比较重要的pacakge.json部分是

"dependencies": {
    "vscode-languageserver": "^6.1.1",
    "vscode-languageserver-textdocument": "^1.0.1"
}

下面是一个服务器的实现,提供了简单的纯文本管理——VS Code会向服务器发送一个文件的全部内容

import {
  createConnection,
  TextDocuments,
  Diagnostic,
  DiagnosticSeverity,
  ProposedFeatures,
  InitializeParams,
  DidChangeConfigurationNotification,
  CompletionItem,
  CompletionItemKind,
  TextDocumentPositionParams,
  TextDocumentSyncKind,
  InitializeResult
} from 'vscode-languageserver';

import { TextDocument } from 'vscode-languageserver-textdocument';

// 创建一个服务器连接。使用Node的IPC作为传输方式。
// 也包含所有的预览、建议等LSP特性
let connection = createConnection(ProposedFeatures.all);

// 创建一个简单的文本管理器。
// 文本管理器只支持全文本同步。
let documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);

let hasConfigurationCapability: boolean = false;
let hasWorkspaceFolderCapability: boolean = false;
let hasDiagnosticRelatedInformationCapability: boolean = false;

connection.onInitialize((params: InitializeParams) => {
    let capabilities = params.capabilities;

    // 客户端是否支持`workspace/configuration`请求?
    // 如果不是的话,降级到使用全局设置
    hasConfigurationCapability = !!(
      capabilities.workspace && !!capabilities.workspace.configuration
    );
    hasWorkspaceFolderCapability = !!(
        capabilities.workspace && !!capabilities.workspace.workspaceFolders
    );
    hasDiagnosticRelatedInformationCapability = !!(
        capabilities.textDocument &&
        capabilities.textDocument.publishDiagnostics &&
        capabilities.textDocument.publishDiagnostics.relatedInformation
    );

    const result: InitializeResult = {
        capabilities: {
            textDocumentSync: TextDocumentSyncKind.Incremental,
            // Tell the client that this server supports code completion.
            completionProvider: {
                resolveProvider: true
            }
        }
    };

    if (hasWorkspaceFolderCapability) {
        result.capabilities.workspace = {
            workspaceFolders: {
                supported: true
            }
        };
    }

    return result;
});

connection.onInitialized(() => {
    if (hasConfigurationCapability) {
        // 为所有配置Register for all configuration changes.
        connection.client.register(
            DidChangeConfigurationNotification.type,
            undefined
        );
    }
    if (hasWorkspaceFolderCapability) {
        connection.workspace.onDidChangeWorkspaceFolders(_event => {
            connection.console.log('Workspace folder change event received.');
        });
    }
});

// 配置示例
interface ExampleSettings {
    maxNumberOfProblems: number;
}

// 当客户端不支持`workspace/configuration`请求时,使用global settings
// 请注意,在这个例子中服务器使用的客户端并不是问题所在,而是这种情况还可能发生在其他客户端身上。
const defaultSettings: ExampleSettings = { maxNumberOfProblems: 1000 };
let globalSettings: ExampleSettings = defaultSettings;

// 对所有打开的文档配置进行缓存
let documentSettings: Map<string, Thenable<ExampleSettings>> = new Map();

connection.onDidChangeConfiguration(change => {
    if (hasConfigurationCapability) {
        // 重置所有已缓存的文档配置
        documentSettings.clear();
    } else {
        globalSettings = <ExampleSettings>(
            (change.settings.languageServerExample || defaultSettings)
        );
    }

    // 重新验证所有打开的文本文档
    documents.all().forEach(validateTextDocument);
});

function getDocumentSettings(resource: string): Thenable<ExampleSettings> {
    if (!hasConfigurationCapability) {
        return Promise.resolve(globalSettings);
    }
    let result = documentSettings.get(resource);
    if (!result) {
        result = connection.workspace.getConfiguration({
            scopeUri: resource,
            section: 'languageServerExample'
        });
        documentSettings.set(resource, result);
    }
    return result;
}

// 只对打开的文档保留设置
documents.onDidClose(e => {
    documentSettings.delete(e.document.uri);
});

// 文档的文本内容发生了改变。
// 这个事件在文档第一次打开或者内容变动时才会触发。
documents.onDidChangeContent(change => {
    validateTextDocument(change.document);
});

async function validateTextDocument(textDocument: TextDocument): Promise<void> {
    // 在这个简单的示例中,每次校验运行时我们都获取一次配置
    let settings = await getDocumentSettings(textDocument.uri);

    // 校验器如果检测到连续超过2个以上的大写字母则会报错
    let text = textDocument.getText();
    let pattern = /\b[A-Z]{2,}\b/g;
    let m: RegExpExecArray | null;

    let problems = 0;
    let diagnostics: Diagnostic[] = [];
    while ((m = pattern.exec(text)) && problems < settings.maxNumberOfProblems) {
        problems++;
        let diagnosic: Diagnostic = {
            severity: DiagnosticSeverity.Warning,
            range: {
                start: textDocument.positionAt(m.index),
                end: textDocument.positionAt(m.index + m[0].length)
            },
            message: `${m[0]} is all uppercase.`,
            source: 'ex'
        };
        if (hasDiagnosticRelatedInformationCapability) {
            diagnosic.relatedInformation = [
                {
                    location: {
                        uri: textDocument.uri,
                        range: Object.assign({}, diagnosic.range)
                    },
                    message: 'Spelling matters'
                },
                {
                    location: {
                        uri: textDocument.uri,
                        range: Object.assign({}, diagnosic.range)
                    },
                    message: 'Particularly for names'
                }
            ];
        }
        diagnostics.push(diagnosic);
    }
    // 将错误处理结果发送给VS Code
    connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
}

connection.onDidChangeWatchedFiles(_change => {
    // 监测VS Code中的文件变动
    connection.console.log('We received an file change event');
});

// 这个处理函数提供了初始补全项列表
connection.onCompletion(
    (_textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => {
    // 传入的变量包含了文本请求代码补全的位置。
    // 在这个示例中我们忽略了这个信息,总是提供相同的补全选项。
    return [
        {
            label: 'TypeScript',
            kind: CompletionItemKind.Text,
            data: 1
        },
        {
            label: 'JavaScript',
            kind: CompletionItemKind.Text,
            data: 2
        }
        ];
    }
);

// 这个函数为补全列表的选中项提供了更多信息
connection.onCompletionResolve(
    (item: CompletionItem): CompletionItem => {
        if (item.data === 1) {
            item.detail = 'TypeScript details';
            item.documentation = 'TypeScript documentation';
        } else if (item.data === 2) {
            item.detail = 'JavaScript details';
            item.documentation = 'JavaScript documentation';
        }
        return item;
    }
);

// 让文档管理器监听文档的打开,变动和关闭事件。
documents.listen(connection);

// 连接后启动监听
connection.listen();

标签:语言,实现,textDocument,capabilities,item,connection,let,workspace,服务端
From: https://www.cnblogs.com/myfriend/p/implement-the-language-service-side-z1vx5pf.html

相关文章

  • Angular + quill实现富文本编辑器
    前言由于需要一个富文本编辑器来编辑一些网页内容,手动编辑后存储到数据库比较麻烦,所以着手实现一个自己的富文本编辑器,来编辑和存储一些html文件.这里使用Angular框架,加Quill库实现.ngx-quill:https://github.com/KillerCodeMonkey/ngx-quillquill官网:https://quil......
  • C语言 原码 反码 补码
    1.任何类型的数据在计算机中都是以二进制补码形式存储2.原码:最高位为符号位,用0表示正数,用1表示负数,其他位是相应数的2进制数反码:除符号位外按位取反,0变为1,1变为0补码:反码再加13.正数的原码反码补码都相同65的原码反码补码都是:0000000000000000000000000100000......
  • 学习C语言第六天
    一.多维数组元素的地址#include<stdio.h>intmain(){intarr[3][4]={{11,22,33,44},{12,13,15,16},{22,66,77,88}};inti;intj;for(i=0;i<3;i++){for(j=0;j<4;j++){printf("add:0x%p,data:%d",&arr[i......
  • #yyds干货盘点#学习笔记(1)Linux和Windows上实现端口映射
    一、Windows下实现端口映射1.查询端口映射情况netshinterfaceportproxyshowv4tov42.查询某一个IP的所有端口映射情况netshinterfaceportproxyshowv4tov4|find"[IP]"例:netshinterfaceportproxyshowv4tov4|find"192.168.1.1"3.增加一个端口映射netshinterfa......
  • 链表的回文判断—Java实现
    对于这个题,主要是老是局限于方法内的变量,未想到借助外部变量辅助:具如下,不可用数除法,会溢出异常:即使是取最大的long也会溢出,常规方法不再赘述,具体以代码如下:1packageProblemSolve;23publicclassSolution5{4privateListNodefrontNode;5publicboolean......
  • Python实现Json文件转为点表示法(Dot-Notation)
    将Json转换为点表示法有很多用途,本文基于Python实现一个简单demo来转换。【原文见我的博客,如有更新请博客园的不一定及时同步改:https://blog.jfz.me/2023/python-json-to-dot-notation.html】{"vehicle":{"car":{"bmw":true,"audi"......
  • 用Abp实现双因素认证(Two-Factor Authentication, 2FA)登录(一):认证模块
    @目录原理用户验证码校验模块双因素认证模块改写登录在之前的博文用Abp实现短信验证码免密登录(一):短信校验模块一文中,我们实现了用户验证码校验模块,今天来拓展这个模块,使Abp用户系统支持双因素认证(Two-FactorAuthentication)功能。双因素认证(Two-FactorAuthentication,简称2FA......
  • R语言EG(Engle-Granger)两步法协整检验、RESET、格兰杰因果检验、VAR模型分析CPI和PPI
    全文链接:http://tecdat.cn/?p=31108最近我们被客户要求撰写关于VAR模型的研究报告,包括一些图形和统计输出。作为衡量通货膨胀的基本指标,消费者价格指数CPI和生产者价格指数PPI的作用关系与传导机制一直是宏观经济研究的核心问题。对此问题的研究显然具有重要的学术价值与现实意......
  • R语言预测人口死亡率:用李·卡特(Lee-Carter)模型、非线性模型进行平滑估计|附代码数据
    全文链接:http://tecdat.cn/?p=26147最近我们被客户要求撰写关于预测人口死亡率的研究报告,包括一些图形和统计输出。今天早上,我和同事一起分析死亡率。我们在研究人口数据集,可以观察到很多波动性我们得到这样的结果:  由于我们缺少一些数据,因此我们想使用一些广义非线性模......
  • 设计模式_极简实现_适配器模式
    此系列开篇,说一下目的:笔者在实际工作中用到的设计模式十分有限,久而久之很多概念也模糊了,所以想要重新梳理一下;网络上很多讲设计模式的文章,写的很好,但是无法给读者一个最为直观的体会,直接展示上百行代码,足够深入但不浅出;笔者在学习过程中希望真正理解设计模式,即以自己想到的例......