使 VS Code 解析并显示新语言的结构到 VSC 的轮廓区域

mei*_*muh 5 visual-studio-code vscode-extensions language-server-protocol

我试图通过将语言定义添加到 VSC 来使 VSC 显示包含 DSL(域特定语言)的文档的结构。该结构应出现在 VSC“大纲视图”中,其中显示已安装语言(如 json、markdown、html 等)的所有文档结构

DSL 非常简单,只有一些大写字母的元素应该出现在大纲中并保持层次结构:

WORD xxx
GRAMMAR xxx
STRUCTURE xxx xxx
     xxx xxx xxx xxx
MEANING xxx xxx xxx 
    SUB_MEANING xxx xxx xxx xxx
        SUB_SUB_MEANING xxx xxx xxx
Run Code Online (Sandbox Code Playgroud)

我遵循了有关 stackoverflow 的所有提示,这些提示都指向了 VSC 和/或语言服务器协议 (LSP) 的官方文档。但是,没有任何帮助,根本没有-.- 是的,我可以使用 CodeMap 扩展,但我不想依赖它,因为 VSC 实际上能够理解新语言。对于众所周知的语言,不需要创建专用的树视图元素或其他东西,因此必须有一种方法让 VSC 解析语言结构。

VSC 中的“大纲视图”保持为空。我发现如果删除 VSC 扩展文件夹中的文件夹“xxx-language-features”(xxx 代表语言),则安装的对(例如)markdown 或 json 的语言支持也不会向“大纲”生成任何内容. 所以似乎我也需要一个语言功能扩展。

我浏览了https://code.visualstudio.com/api/language-extensions/language-configuration-guidehttps://microsoft.github.io/language-server-protocol/以及许多其他内容,包括 LSP-example来自 VSC 的 Github-Repo,但没有任何帮助。我还尝试通过“yo code”创建一种新语言。没有。Microsoft 提供的 LSP 示例用于纯文本文件……为纯文本创建语言服务器有多大用处?!我想举一个关于语言的例子。查看扩展中的编译文件无济于事,因为它们被缩小了。

在该问题上没有完整的“操作方法” - 因此,感谢您的帮助!如何告诉 VSC 将文档结构解析为“大纲视图”?

小智 5

你有几种方法可以解决这个问题,你可以:

  1. 创建一个 IDE 专有扩展来完成所有工作(解析文件、理解所有标记的含义并提供符号列表等)
  2. 使用实现Microsoft 指定的语言服务器协议语言服务器来创建语言服务器客户端扩展。在这种情况下,您的 VSCode 扩展本质上将是启动语言服务器并与之通信的“外壳”扩展。vscode-languageclient包的LanguageClient类已经开箱即用地实现了如何处理服务器返回的信息。

选项 2. 有一个巨大的优势:它修复了 N*M 问题:所有支持语言服务器协议的编辑器都可以与您的语言服务器通信,因此可以轻松地为多个编辑器(VSCode、Visual Studio、Atom、Vim)提供语言支持、emacs 等,IntelliJ 以有限的方式使用 3rd 方插件)以最少的努力。

在你的问题中,我不清楚你是打算编写一个可以完成所有工作的扩展,还是打算开发一个语言服务器和一个语言客户端扩展来配合它。

答案实际上取决于您的语言(或 DSL)的复杂性。如果它有一个有点复杂的语法并且你已经有了一个编译器,那么制作一个语言服务器是有意义的,因为大多数解析和逻辑已经在编译器中实现了。如果你的语言真的像你给出的例子一样简单并且没有语法,那么它可能是矫枉过正的。

如果你走“语言服务器”路线,那么有不同语言的库可以简化服务器的实现,它们负责 LSP 的 JSON-RPC 部分,你只需要覆盖语言服务器定义的方法协议(例如LSP4J任何JVM的语言,或vscode-languageserver为的NodeJS(尽管它的名称,包装是不特定于vSCode))。在这种情况下,让 VSCode 的大纲视图填充你的符号层次结构只需要你在你的语言服务器中实现textDocument/documentSymbol请求,而在扩展中没有其他任何东西,它由vscode-languageclient包的LanguageClient类处理!

  • 如果您的语言客户端支持分层文档符号:在语言服务器initialize方法中,您将收到 InitializeParams with textDocument.documentSymbol.hierarchicalDocumentSymbolSupport == true(VSCode 确实支持此功能),然后您返回DocumentSymbol[](它有一个children属性,从而为您的客户端提供一个符号树)。
  • 如果您的语言服务器客户端不支持分层文档符号(例如 Visual Studio),则返回SymbolInformation[],它是一个平面符号列表。

需要注意的一件重要事情:VSCode在显示符号树(在大纲视图中)时会考虑 的range属性DocumentSymbol。您的子符号range必须包含在其父range符号中,否则 VSCode 将不会在大纲视图中显示任何符号,即使您的符号树结构和selectionRanges 是正确的。该range定义范围(一打字稿类的符号的整个定义,例如,它开始在关键字class和结束与关闭})和selectionRange通常只是符号的标记范围内(但根据该规范,它也可以包括doc 注释块和可见性修饰符,这是实现者的选择)。例如

class MyClass {
  /**
   * Some method documentation
   */
  private function myFunction(param1) {
    console.log(param1);
  }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,范围将是

rangeOfClass = {
  "start": { "line": 0, "character": 0 },
  "end": { "line": 7, "character": 1 }
};
rangeOfMethod = {
  "start": { "line": 4, "character": 2 },
  "end": { "line": 6, "character": 3 }
};

Run Code Online (Sandbox Code Playgroud)

并且 selectionRange 只能是符号的名称

selectionRangeOfClass = {
  "start": { "line": 0, "character": 6 },
  "end": { "line": 0, "character": 13 }
};
selectionRangeOfMethod = {
  "start": { "line": 4, "character": 19 },
  "end": { "line": 4, "character": 29 }
};
Run Code Online (Sandbox Code Playgroud)

或者它可以包括从文档、关键字和修饰符直到符号的末尾

selectionRangeOfClass = {
  "start": { "line": 0, "character": 0 },
  "end": { "line": 0, "character": 13 }
};
selectionRangeOfMethod = {
  "start": { "line": 1, "character": 2 },
  "end": { "line": 4, "character": 29 }
};
Run Code Online (Sandbox Code Playgroud)

有了这个,你会得到一个很好的大纲: 在此处输入图片说明


mei*_*muh 2

我在错误的地方寻找解决方案,在浪费了几天时间后,可能只见树木不见森林。然后,一位同事对这个问题进行了初步研究,几个小时后他想出了下面的解决方案。基本上,DocumentSymbolProvider就是人们所需要的。

搜索该关键字会提供一些示例,例如此处。然而,官方文档除了您可以创建实例的信息之外没有提供任何内容,而有关如何使用它的重要代码由 表示...。哇 - 这就是我所说的医生 -.-

仍有一些事情尚不清楚,但至少我们现在可以使用该基础:

class MLWDocumentSymbolProvider implements vscode.DocumentSymbolProvider {
public provideDocumentSymbols(document: vscode.TextDocument,
        token: vscode.CancellationToken): Thenable<vscode.SymbolInformation[]> {
    return new Promise((resolve, reject) => {

      // that's the important variable. It must be a multidimensional array, one dimension for each level you need to display.
      let symbols = [];

      let icon_main = vscode.SymbolKind.Class;
      let icon_second = vscode.SymbolKind.Field;
      let icon_third = vscode.SymbolKind.String;

      // check each line of the document about your keywords
      for (let i = 0; i < document.lineCount; i++) {
        let line = document.lineAt(i);
        if(line.text.trim().startsWith("WORD")) {
          symbols.push(new vscode.DocumentSymbol("Level 1: WORD", document.lineAt(i+1).text.trim(), icon_main, line.range, line.range ));
        } /* elses for the levels below */
       }

      resolve(symbols);
        });
    }
Run Code Online (Sandbox Code Playgroud)

}

我想 Gamma11 的答案不知何故很接近。由于官方文档中缺乏有关在何处以及如何使用它的信息,不幸的是它无法真正帮助我们。

获得有关 VSC 编码的知识似乎非常困难,因为文档除了基本结构之外没有提供太多信息。如果有人知道有关于类/接口/函数的完整示例或描述的网站,请发表评论。在近 20 年的编码生涯中,我从未见过如此庞大的项目如此缺乏文档,其中主要部分由对象表示...,或者根本没有任何关于对象的信息 -.-