使VS Code解析并在VSC的大纲区域中显示新语言的结构

7
我正在尝试通过将语言定义添加到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

我遵循了stackoverflow上的所有提示,都指向了VSC和/或语言服务器协议(LSP)的官方文档。然而,没有一个有用,一点也没用-.- 是的,我可以使用CodeMap扩展,但我不想依赖它,因为VSC实际上能够理解新的语言。对于众所周知的语言,没有必要创建专用的树形视图元素或其他东西,因此一定有办法使VSC解析语言结构。
VSC中的“大纲视图”为空。我发现,安装的(例如)markdown或json的语言支持如果删除VSC的扩展文件夹中的“xxx-language-features”文件夹,则不会生成任何内容到“大纲”中。因此,似乎我也需要一个语言功能扩展。
我阅读了https://code.visualstudio.com/api/language-extensions/language-configuration-guidehttps://microsoft.github.io/language-server-protocol/和其他许多与VSC相关的内容,包括来自VSC Github仓库的LSP示例,但是没有任何有用的信息。我还尝试使用"yo code"创建新语言,但是依然无济于事。微软提供的LSP示例只适用于纯文本文件...为什么要为纯文本创建语言服务器?我需要一个针对某种语言的示例。查看扩展内部编译后的文件也没有帮助,因为它们都是被压缩的。目前没有完整的“如何”解决此问题的指南 - 因此,任何帮助都将不胜感激!我该如何告诉VSC将文档结构解析到“大纲视图”中?
3个回答

8
您有几种解决方法,可以选择以下方式之一:
  1. 创建一个专门为IDE设计的扩展程序,它可以完成所有的工作(解析文件、理解所有令牌的含义并提供符号列表等)
  2. 使用符合Microsoft规定的语言服务器实现语言服务器协议来创建Language Server客户端扩展。在这种情况下,您的VSCode扩展实质上是一个“外壳”扩展程序,启动语言服务器并与之通信。由vscode-languageclient包的LanguageClient类提供了关于如何处理服务器返回信息的默认实现。
选项2具有巨大的优势:它可以解决N*M问题:所有支持语言服务器协议的编辑器都可以与您的语言服务器进行通信,因此,只需花费最少的精力就可以轻松为多个编辑器(VSCode、Visual Studio、Atom、Vim、emacs等,使用第三方插件的有限方式来支持IntelliJ)提供语言支持。
在您的问题中,我不清楚您是否计划编写一个扩展程序来完成所有的工作,还是计划开发一个语言服务器和相应的语言客户端扩展程序。
答案取决于您的语言(或DSL)的复杂性。如果它具有相对复杂的语法,并且您已经为其编写了编译器,那么创建一个语言服务器是合理的,因为大多数解析和逻辑已经在编译器中实现了。如果您的语言就像您给出的示例一样简单,并且没有语法,则可能过于繁琐。
如果您选择“语言服务器”路线,则不同语言中都有一些库可以简化服务器的实现,它们负责LSP的JSON-RPC部分,而您只需重写由Language Server Protocol定义的方法即可(例如任何JVM语言的LSP4J或NodeJSvscode-languageserver(尽管它的名称很明显,但该包并不特定于VSCode))。
在这种情况下,只需在您的语言服务器中实现textDocument/documentSymbol请求,而扩展程序中的其他内容则由vscode-languageclient包的LanguageClient类处理,因此您可以轻松地填充VSCode的大纲视图,以显示符号的层次结构!
  • 如果您的语言客户端支持分层文档符号:在语言服务器的 initialize 方法中,您将收到带有 textDocument.documentSymbol.hierarchicalDocumentSymbolSupport == true 的 InitializeParams(VSCode 支持此功能),然后您返回 DocumentSymbol[](它具有 children 属性,从而为客户端提供了一棵符号树)。
  • 如果您的语言服务器客户端不支持分层文档符号(例如 Visual Studio),则返回 SymbolInformation[],这是一个扁平化的符号列表。

需要注意的一件重要事情:VSCode 在显示符号树(在大纲视图中)时会考虑 DocumentSymbolrange 属性。您子级符号的 range 必须包含在其父级的 range 中,否则即使您的符号树结构和 selectionRange 正确,VSCode 也不会在大纲视图中显示任何符号。 range 是定义范围(符号的整个定义,例如 TypeScript 类的关键字 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 }
};

通过这样做,您将获得一个漂亮的大纲结构: 在此输入图片描述


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

}

我想Gamma11的回答在某种程度上是接近的。但由于官方文档缺乏关于在哪里和如何使用它的信息,它不幸地无法真正帮助我们。
由于文档除了基本结构外没有提供太多信息,因此获得有关VSC编码的知识似乎非常困难。 如果有人知道一个网站,其中包含完整的类/接口/函数示例或描述,请留言评论。 在将近20年的编码生涯中,我从未见过这样一个巨大项目缺乏文档,其中主要部分由 ... 表示,或者根本没有关于一个对象的任何信息 -.-

1
大纲视图由文档符号请求填充。面包屑导航和常规文档符号弹出窗口(转到→转到文件中的符号...)也是如此。
通过使用DocumentSymbol.children而不是返回平面列表来实现层次结构。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接