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

make VS Code parse and display the structure of a new language to the outline region of VSC

我试图通过将语言定义添加到 VSC 来使 VSC 显示包含 DSL(领域特定语言)的文档的结构。该结构应出现在 VSC "outline view" 中,其中显示已安装语言的所有文档结构(如 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

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

"outline view" 在 VSC 中保持为空。 我发现如果删除文件夹 "xxx-language-features"(xxx 代表语言),安装的语言支持(例如)markdown 或 json 也不会为 "Outline" 生成任何内容VSC 的扩展文件夹。所以看来我也需要语言功能扩展。

我浏览了 https://code.visualstudio.com/api/language-extensions/language-configuration-guide and https://microsoft.github.io/language-server-protocol/ 和许多其他内容,包括来自 VSC Github-Repo 的 LSP 示例,但是 NOTHING 对我有帮助那。我还尝试借助 "yo code" 创建一种新语言。没有什么。 Microsoft 提供的 LSP 示例是针对明文文件的……为明文创建语言服务器有多大用处?!我想举一个关于语言的例子。查看扩展中的编译文件无济于事,因为它们被缩小了。

关于该问题没有完整的 "how to" - 因此,我们将不胜感激! 我如何告诉 VSC 将文档结构解析为 "outline view"?

大纲视图由 Document Symbols Request 填充。面包屑导航也是如此,当然还有常规的文档符号弹出窗口(转到→转到文件中的符号...)。

层次结构是通过使用 DocumentSymbol.children 而不是返回平面列表来实现的。

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

搜索此关键字会提供一些示例,例如. The official docs 然而,除了您可以创建实例的信息外,什么都不提供,而关于如何使用它的重要代码由 ... 表示。哇 - 这就是我所说的文档 -.-

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

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);
        });
    }

}

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

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

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

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

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

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

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

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

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

需要注意的一件重要事情:VSCode 在显示符号树时考虑 DocumentSymbolrange 属性(在大纲视图中)。您的 children 符号的 range 必须包含在它们的 parent 的 range 或 VSCode 中不会在大纲视图中显示任何符号,即使您的符号树结构和 selectionRanges 是正确的。 rangedefinition 范围(符号的整个定义,例如对于打字稿 class,它从关键字 class 开始并结束}) 和 selectionRange 通常只是符号的标记范围(但根据规范,它还可以包括文档注释块和可见性修饰符,这是实现者的选择)。 例如

class MyClass {
  /**
   * Some method documentation
   */
  private function myFunction(param1) {
    console.log(param1);
  }
}

在这种情况下,范围将是

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

选择范围只能是交易品种的名称

selectionRangeOfClass = {
  "start": { "line": 0, "character": 6 },
  "end": { "line": 0, "character": 13 }
};
selectionRangeOfMethod = {
  "start": { "line": 4, "character": 19 },
  "end": { "line": 4, "character": 29 }
};

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

selectionRangeOfClass = {
  "start": { "line": 0, "character": 0 },
  "end": { "line": 0, "character": 13 }
};
selectionRangeOfMethod = {
  "start": { "line": 1, "character": 2 },
  "end": { "line": 4, "character": 29 }
};

这样,您将得到一个漂亮的大纲: