VSCode enable/disable 基于 node/item 属性的自定义 TreeView 命令

VSCode enable/disable commands in custom TreeView based on properties of node/item

我目前正在开发一个自定义 VSCode 扩展,它从远程 API 获取数据并在自定义树视图中显示数据。树视图本身允许与 API 交互,即根据一些预定义的条件过滤结果,例如搜索词或响应是否附加了一些文件,向其添加新节点或删除或更新现有节点动态地在树中。

出于简单的原因,我创建了一个自己的 Github project 来专注于树的基本设置和配置,它或多或少地在一些假的 API 方法上运行,但提供类似的功能,典型的 CRUD API提供。

GIF 已调整大小以适应 2MB 的限制,因此会产生难看的伪像

虽然在 TreeView 上显示由自定义 TreeDataProvider 定义的命令的操作图标,但在 TreeItem 上通过定义如下内容相当简单:

...
{
  "contributes":
      "commands": [
          {
              "command": "extension.myTreeView.filter",
              "title": "Filter",
              "icon": "$(filter)",
              "when": "view == extension.myTreeView"
          },
          {
              "command": "extension.myTreeView.resetFilter",
              "title": "Reset Filter",
              "shortTitle": "Reset",
              "icon": "$(close)",
              "enablement": "view == extension.myTreeView && extension.myTreeView.hasFilter",
              "when": "view == extension.myTreeView && extension.myTreeView.hasFilter"
          },
          {
              "command": "extension.myTreeView.delete",
              "title": "Delete Item",
              "shortTitle": "Delete",
              "icon": "$(trash)",
              "when": "view == extension.myTreeView && viewItem == archive"
          }
          ...
      ],
      "menus": {
          "view/title": [
              {
                  "command": "extension.myTreeView.filter",
                  "when": "view == extension.myTreeView"
                  "group": "navigation@1"
              },
              {
                  "command": "extension.myTreeView.resetFilter",
                  "when": "view == extension.myTreeView",
                  "group": "navigation@2"
              },
              ...   
          ],
          "view/item/context": [
              ...
              {
                  "command": "extension.myTreeView.delete",
                  "when": "view == extension.myTreeView && viewItem == archive",
                  "group": "inline@3"
              }
          ]
      }
},
...

package.json 文件中,我不知何故难以为针对特定项目本身的命令添加启用条件。 IE。该树允许下载包含在(或附加到)由节点表示的项目中的一些文件。这些节点可能有也可能没有附加文件,因此启用下载操作仅对至少有文件要下载的节点有意义。虽然通常下载该内容或呈现触发下载操作的按钮不是问题,但该命令的启用选项是我有点无能为力的地方。

我通过为此类树节点定义不同种类的 contextValue 来临时“解决”了这个问题,一种用于有文件下载的存档,一种没有文件下载。虽然这通常似乎可以解决该问题,但它 a) 感觉不合适并且 b) 导致下游问题。想象一下,可以锁定存档以防止不属于存档被锁定组的用户进行实际下载。这只是一个虚构的案例,但应该说明您最终可能会在该 contextValue 变量中得到 属性 字段的组合,您或多或少需要在该启用语句中进行解析。并且每个进一步可能的上下文值选项都必须通过整个 commandsmenus 定义,如下所示:

...
{
    "command": "extension.myTreeView.delete",
    "when": "view == extension.myTreeView && viewItem == archive || viewItem == archiveWithFiles || viewItem == ...",
    "group": "inline@2"
},
...

一般来说,custom context variables,比如上面示例命令配置中的hasFilter,可以在enablementwhen子句中使用,可以用于 show/hide 或 enable/disable 某些命令,但这些命令似乎只适用于整棵树本身,而不适用于特定的树项。 IE。示例应用程序根据是否定义过滤器使用以下代码 enable/disable resetFilter 命令:

    ...

    public async updateFilter(filter: Filter): Promise<void> {
        this.filter = filter;

        await commands.executeCommand("setContext", TestTreeDataProvider.CONTEXT_HAS_FILTER, true);
        this.items = [];
        this._onDidChangeTreeData.fire();
    }

    private async resetFilter(): Promise<void> {
        this.filter = undefined;
        await commands.executeCommand("setContext", TestTreeDataProvider.CONTEXT_HAS_FILTER, false);
        this.items = [];
        this._onDidChangeTreeData.fire();
    }

这是可行的,因为这或多或少是对树本身的全局操作,并且具有单一的 属性 来源。但是这样的上下文值可能只有分配给它们的基本类型或对象,其中只有该对象的键(a.k.a。属性 名称)在 in 子句中检查其可用性。我可能会为每个生成的树项创建自定义上下文变量,但随后我需要某种形式的寻址机制,因为每个上下文值名称必须不同,否则它们将被下一个节点的上下文值覆盖。由于树可以容纳大量节点,因此我觉得为每个节点添加上下文值也不是很高效。最后,这里似乎缺少的是一种将函数定义为上下文值的方法,该函数允许当前节点(TreeItem 或节点所指的实际对象)作为输入,并且需要 return 其中之一作为输出的基本类型,即 truefalse 以确定是否应启用该项目。

虽然 this SO question here 听起来很相似,但它实际上要求禁用整个树项节点,而我只是希望可以在该树项上执行的命令基于某些树 enable/disabled项目的当前 属性 值。可以通过 FileDecorationProvider 完成基于其上下文的 TreeItem 的某些样式。这个问题也是 VSCode 内部错误的结果,该错误似乎不会强制重新呈现对启用守卫所做的任何更改。

我觉得在这个问题上一定有更简单的解决方案,这就是我在这里问的原因。简而言之,如何根据当前树项目的上下文(由该节点持有的属性或在该节点上执行的功能)在 Visual Studio 代码中 enabled/disabled?

您现在将类型描述作为 TreeItem 的上下文值。

为什么不将允许的操作也放在上下文值中:

archive_Del
archive_Read_Update

然后在 when 子句中使用它

{
    "command": "extension.myTreeView.delete",
    "when": "view == extension.myTreeView && viewItem =~ /^.*_Del.*$/",
    "group": "inline@2"
}

{
    "command": "extension.myTreeView.read",
    "when": "view == extension.myTreeView && viewItem =~ /^.*_Read.*$/",
    "group": "inline@2"
}