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
变量中得到 属性 字段的组合,您或多或少需要在该启用语句中进行解析。并且每个进一步可能的上下文值选项都必须通过整个 commands
和 menus
定义,如下所示:
...
{
"command": "extension.myTreeView.delete",
"when": "view == extension.myTreeView && viewItem == archive || viewItem == archiveWithFiles || viewItem == ...",
"group": "inline@2"
},
...
一般来说,custom context variables,比如上面示例命令配置中的hasFilter
,可以在enablement
和when
子句中使用,可以用于 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 其中之一作为输出的基本类型,即 true
或 false
以确定是否应启用该项目。
虽然 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"
}
我目前正在开发一个自定义 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
变量中得到 属性 字段的组合,您或多或少需要在该启用语句中进行解析。并且每个进一步可能的上下文值选项都必须通过整个 commands
和 menus
定义,如下所示:
...
{
"command": "extension.myTreeView.delete",
"when": "view == extension.myTreeView && viewItem == archive || viewItem == archiveWithFiles || viewItem == ...",
"group": "inline@2"
},
...
一般来说,custom context variables,比如上面示例命令配置中的hasFilter
,可以在enablement
和when
子句中使用,可以用于 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 其中之一作为输出的基本类型,即 true
或 false
以确定是否应启用该项目。
虽然 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"
}