This is an automated email from the ASF dual-hosted git repository.

shanedell pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/daffodil-vscode.git


The following commit(s) were added to refs/heads/main by this push:
     new 5a4d2fd  - Dynamically evaluate a command's enablement conditions for 
displaying   it in the Daffodil Explorer Pane - Unify all commands from all 
sources to have (mostly) the same   enablement/when conditions - Fix keywords 
used in enablement/when conditions to be common and   succinct
5a4d2fd is described below

commit 5a4d2fdc25bd81c938b6b1a6560820b63e248205
Author: Michael Hoke <michael.h...@nteligen.com>
AuthorDate: Fri Aug 22 11:58:18 2025 -0400

    - Dynamically evaluate a command's enablement conditions for displaying
      it in the Daffodil Explorer Pane
    - Unify all commands from all sources to have (mostly) the same
      enablement/when conditions
    - Fix keywords used in enablement/when conditions to be common and
      succinct
---
 build/package/LICENSE            |  24 +++++
 package.json                     |  99 +++++++++---------
 src/tests/suite/daffodil.test.ts |   6 +-
 src/views/commands.ts            | 215 ++++++++++++++++++++++++++++++++++-----
 yarn.lock                        |   5 +
 5 files changed, 271 insertions(+), 78 deletions(-)

diff --git a/build/package/LICENSE b/build/package/LICENSE
index 1eb2180..d27b478 100644
--- a/build/package/LICENSE
+++ b/build/package/LICENSE
@@ -705,6 +705,30 @@ conditions of the following licenses.
 
     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
SOFTWARE.
 
+- 'jsep' in extension/dist/ext/extension.js
+  This product bundles the 'jsep' from the above files.
+  These files are available under the MIT License
+    Copyright (c) 2013 Stephen Oney, https://ericsmekens.github.io/jsep/
+
+    Permission is hereby granted, free of charge, to any person obtaining
+    a copy of this software and associated documentation files (the
+    "Software"), to deal in the Software without restriction, including
+    without limitation the rights to use, copy, modify, merge, publish,
+    distribute, sublicense, and/or sell copies of the Software, and to
+    permit persons to whom the Software is furnished to do so, subject to
+    the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+    LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
 - 'jsonc-parser' in extension/dist/ext/extension.js
   This product bundles 'jsonc-parser' from the above files.
   This package is available under the MIT License:
diff --git a/package.json b/package.json
index 657a513..fb13b51 100644
--- a/package.json
+++ b/package.json
@@ -59,6 +59,7 @@
     "await-notify": "1.0.1",
     "hexy": "0.3.5",
     "iso-639-1": "^3.1.0",
+    "jsep": "^1.4.0",
     "jsonc-parser": "3.2.1",
     "semver": "7.5.4",
     "unzip-stream": "0.3.4",
@@ -105,24 +106,14 @@
   },
   "main": "./dist/ext/extension.js",
   "activationEvents": [
-    "onLanguage:dfdl",
     "onDebugResolve:dfdl",
     "onDebugDynamicConfigurations:dfdl",
     "onCommand:extension.dfdl-debug.getSchemaName",
     "onCommand:extension.dfdl-debug.getDataName",
-    "onCommand:extension.dfdl-debug.runEditorContents",
-    "onCommand:extension.dfdl-debug.debugEditorContents",
-    "onCommand:extension.dfdl-debug.appendTDML",
-    "onCommand:extension.dfdl-debug.executeTDML",
-    "onCommand:extension.dfdl-debug.createTDML",
     "onCommand:extension.dfdl-debug.getTDMLName",
     "onCommand:extension.dfdl-debug.getTDMLPath",
     "onCommand:extension.dfdl-debug.getValidatedTDMLPath",
-    "onCommand:extension.dfdl-debug.getValidatedTDMLCopyPath",
-    "onCommand:launch.config",
-    "onCommand:extension.data.edit",
-    "onCommand:extension.dfdl-debug.debugLastEditorContents",
-    "onView:commandsView"
+    "onCommand:extension.dfdl-debug.getValidatedTDMLCopyPath"
   ],
   "workspaceTrust": {
     "request": "never"
@@ -212,93 +203,91 @@
       "editor/title": [
         {
           "command": "launch.config",
+          "when": "true",
           "group": "navigation@1"
         },
         {
           "command": "infoset.display",
-          "when": "resourceLangId == dfdl",
+          "when": "inDebugMode && editorLangId == 'dfdl'",
           "group": "navigation@2"
         },
         {
           "command": "infoset.diff",
-          "when": "resourceLangId == dfdl",
+          "when": "inDebugMode && editorLangId == 'dfdl'",
           "group": "navigation@3"
         },
         {
           "command": "tdml-editor.openInTextEditor",
-          "when": "activeCustomEditorId == 'tdml-editor.editor' && 
activeEditorIsNotPreview == false",
+          "when": "activeEditor == 'tdml-editor.editor'",
           "group": "navigation@1"
         },
         {
           "command": "tdml-editor.openPreview",
-          "when": "(resourceExtname == '.tdml' || resourceExtname == 
'.tdml.xsd') && activeEditor == 'workbench.editors.files.textFileEditor'",
+          "when": "editorLangId == 'tdml' && editorTextFocus",
           "group": "navigation@1"
         },
         {
           "command": "tdml-editor.openInTdmlEditor",
-          "when": "(resourceExtname == '.tdml' || resourceExtname == 
'.tdml.xsd') && activeEditor == 'workbench.editors.files.textFileEditor'",
+          "when": "editorLangId == 'tdml' && editorTextFocus",
           "group": "navigation@1"
         }
       ],
       "webview/context": [
         {
           "command": "tdml-editor.deleteTest",
-          "when": "activeCustomEditorId == 'tdml-editor.editor'"
+          "when": "false"
         }
       ],
       "editor/title/run": [
         {
           "command": "extension.dfdl-debug.runEditorContents",
-          "when": "resourceLangId == dfdl"
+          "when": "!inDebugMode && editorLangId == 'dfdl'"
         },
         {
           "command": "extension.dfdl-debug.debugEditorContents",
-          "when": "resourceLangId == dfdl"
+          "when": "!inDebugMode && editorLangId == 'dfdl'"
         },
         {
           "command": "extension.dfdl-debug.debugLastEditorContents",
-          "when": "resourceLangId == dfdl"
+          "when": "!inDebugMode && editorLangId == 'dfdl'"
         },
         {
           "command": "extension.dfdl-debug.appendTDML",
-          "when": "resourceLangId == tdml"
+          "when": "!inDebugMode && ((editorLangId == 'tdml' && 
editorTextFocus) || activeEditor == 'tdml-editor.editor')"
         },
         {
           "command": "extension.dfdl-debug.executeTDML",
-          "when": "resourceLangId == tdml"
-        },
-        {
-          "command": "extension.dfdl-debug.createTDML",
-          "when": "resourceLangId == dfdl"
+          "when": "!inDebugMode && ((editorLangId == 'tdml' && 
editorTextFocus) || activeEditor == 'tdml-editor.editor')"
         }
       ],
       "commandPalette": [
         {
           "command": "extension.dfdl-debug.debugEditorContents",
-          "when": "resourceLangId == dfdl"
+          "when": "!inDebugMode && editorLangId == 'dfdl'"
         },
         {
           "command": "extension.dfdl-debug.runEditorContents",
-          "when": "resourceLangId == dfdl"
+          "when": "!inDebugMode && editorLangId == 'dfdl'"
         },
         {
           "command": "extension.dfdl-debug.appendTDML",
-          "when": "resourceLangId == tdml"
+          "when": "!inDebugMode && ((editorLangId == 'tdml' && 
editorTextFocus) || activeEditor == 'tdml-editor.editor')"
         },
         {
           "command": "extension.dfdl-debug.executeTDML",
-          "when": "resourceLangId == tdml"
+          "when": "!inDebugMode && ((editorLangId == 'tdml' && 
editorTextFocus) || activeEditor == 'tdml-editor.editor')"
         },
         {
           "command": "extension.dfdl-debug.createTDML",
-          "when": "resourceLangId == dfdl"
+          "when": "true"
         },
         {
-          "command": "extension.data.edit"
+          "command": "extension.data.edit",
+          "when": "true"
         },
         {
           "command": "extension.dfdl-debug.debugLastEditorContents",
-          "when": "resourceLangId == dfdl"
+          "when": "!inDebugMode && editorLangId == 'dfdl'"
         },
         {
           "command": "tdml-editor.deleteTest",
@@ -306,19 +295,19 @@
         },
         {
           "command": "tdml-editor.addNewTest",
-          "when": "activeCustomEditorId == 'tdml-editor.editor'"
+          "when": "false"
         },
         {
           "command": "tdml-editor.openInTextEditor",
-          "when": "activeCustomEditorId == 'tdml-editor.editor'"
+          "when": "activeEditor == 'tdml-editor.editor'"
         },
         {
           "command": "tdml-editor.openPreview",
-          "when": "false"
+          "when": "editorLangId == 'tdml' && editorTextFocus"
         },
         {
           "command": "tdml-editor.openInTdmlEditor",
-          "when": "(resourceExtname == '.tdml' || resourceExtname == 
'.tdml.xsd') && activeCustomEditorId != 'tdml-editor.editor'"
+          "when": "editorLangId == 'tdml' && editorTextFocus"
         }
       ],
       "debug/variables/context": [
@@ -343,34 +332,34 @@
         "command": "extension.dfdl-debug.debugEditorContents",
         "title": "Debug File",
         "category": "Daffodil Debug",
-        "enablement": "!inDebugMode",
+        "enablement": "!inDebugMode && editorLangId == 'dfdl'",
         "icon": "$(debug-alt)"
       },
       {
         "command": "extension.dfdl-debug.runEditorContents",
         "title": "Run File",
         "category": "Daffodil Debug",
-        "enablement": "!inDebugMode",
+        "enablement": "!inDebugMode && editorLangId == 'dfdl'",
         "icon": "$(play)"
       },
       {
         "command": "extension.dfdl-debug.debugLastEditorContents",
         "title": "Debug Last File",
         "category": "Daffodil Debug",
-        "enablement": "!inDebugMode",
+        "enablement": "!inDebugMode && editorLangId == 'dfdl'",
         "icon": "$(debug-alt)"
       },
       {
         "command": "extension.dfdl-debug.appendTDML",
         "title": "Append TDML",
         "category": "Daffodil Debug",
-        "enablement": "!inDebugMode"
+        "enablement": "!inDebugMode && ((editorLangId == 'tdml' && 
editorTextFocus) || activeEditor == 'tdml-editor.editor')"
       },
       {
         "command": "extension.dfdl-debug.executeTDML",
         "title": "Execute TDML",
         "category": "Daffodil Debug",
-        "enablement": "!inDebugMode"
+        "enablement": "!inDebugMode && ((editorLangId == 'tdml' && 
editorTextFocus) || activeEditor == 'tdml-editor.editor')"
       },
       {
         "command": "extension.dfdl-debug.createTDML",
@@ -388,14 +377,14 @@
         "command": "infoset.display",
         "title": "Display the infoset view",
         "category": "Daffodil Debug",
-        "enablement": "inDebugMode",
+        "enablement": "inDebugMode && editorLangId == 'dfdl'",
         "icon": "$(file-code)"
       },
       {
         "command": "infoset.diff",
         "title": "View infoset diff",
         "category": "Daffodil Debug",
-        "enablement": "inDebugMode",
+        "enablement": "inDebugMode && editorLangId == 'dfdl'",
         "icon": "$(diff)"
       },
       {
@@ -408,45 +397,53 @@
         "command": "launch.config",
         "title": "Configure launch.json",
         "category": "Daffodil Debug",
+        "enablement": "true",
         "icon": "$(debug-configure)"
       },
       {
         "command": "extension.data.edit",
         "title": "Data Editor",
-        "category": "Daffodil Debug"
+        "category": "Daffodil Debug",
+        "enablement": "true"
       },
       {
         "command": "position.goto",
         "title": "Go to position",
-        "category": "Daffodil Debug"
+        "category": "Daffodil Debug",
+        "enablement": "true"
       },
       {
         "command": "tdml-editor.deleteTest",
         "title": "Delete Resource",
-        "category": "TDML Editor"
+        "category": "TDML Editor",
+        "enablement": "false"
       },
       {
         "command": "tdml-editor.addNewTest",
         "title": "Add New Test",
-        "category": "TDML Editor"
+        "category": "TDML Editor",
+        "enablement": "false"
       },
       {
         "command": "tdml-editor.openInTextEditor",
         "title": "Open in Text Editor",
         "category": "TDML Editor",
-        "icon": "$(notebook-open-as-text)"
+        "icon": "$(notebook-open-as-text)",
+        "enablement": "activeEditor == 'tdml-editor.editor'"
       },
       {
         "command": "tdml-editor.openPreview",
         "title": "Open Preview",
         "category": "TDML Editor",
-        "icon": "$(open-preview)"
+        "icon": "$(open-preview)",
+        "enablement": "editorLangId == 'tdml' && editorTextFocus"
       },
       {
         "command": "tdml-editor.openInTdmlEditor",
         "title": "Open in TDML Editor",
         "category": "TDML Editor",
-        "icon": "$(notebook-render-output)"
+        "icon": "$(notebook-render-output)",
+        "enablement": "editorLangId == 'tdml' && editorTextFocus"
       }
     ],
     "keybindings": [
diff --git a/src/tests/suite/daffodil.test.ts b/src/tests/suite/daffodil.test.ts
index a217156..09019cc 100644
--- a/src/tests/suite/daffodil.test.ts
+++ b/src/tests/suite/daffodil.test.ts
@@ -173,10 +173,12 @@ suite('Daffodfil', () => {
     })
   })
 
-  suite('non-debug specifc commands', () => {
+  suite('non-debug specific commands', () => {
     const nonDebugSpecificCmds = [
       'extension.dfdl-debug.debugEditorContents',
       'extension.dfdl-debug.runEditorContents',
+      'extension.dfdl-debug.debugLastEditorContents',
+      'extension.dfdl-debug.executeTDML',
     ]
 
     // This breaks when the omega-edit tests run for some reason
@@ -201,7 +203,7 @@ suite('Daffodfil', () => {
     })
   })
 
-  suite('debug specifc commands', () => {
+  suite('debug specific commands', () => {
     const debugSpecificCmds = [
       'extension.dfdl-debug.toggleFormatting',
       'infoset.display',
diff --git a/src/views/commands.ts b/src/views/commands.ts
index 6a73bb8..062ab09 100644
--- a/src/views/commands.ts
+++ b/src/views/commands.ts
@@ -16,6 +16,8 @@
  */
 
 import * as vscode from 'vscode'
+import { outputChannel } from '../adapter/activateDaffodilDebug'
+import jsep from 'jsep'
 
 const viewName = 'commandsView'
 const packageCommands = require('../../package.json').contributes.commands
@@ -41,7 +43,8 @@ class CommandItem extends vscode.TreeItem {
 export class CommandsProvider implements vscode.TreeDataProvider<CommandItem> {
   private commands: Array<CommandItem>
   constructor() {
-    this.commands = getCommands('!inDebugMode')
+    this.commands = this.getCommands()
+    this.refresh()
   }
 
   private _onDidChangeTreeData: vscode.EventEmitter<
@@ -80,40 +83,202 @@ export class CommandsProvider implements 
vscode.TreeDataProvider<CommandItem> {
 
     // Create listeners to update the commands based on if a debug session is 
happening
     vscode.debug.onDidStartDebugSession(() => {
-      this.commands = getCommands('inDebugMode')
+      this.commands = this.getCommands()
       this.refresh()
     })
     vscode.debug.onDidTerminateDebugSession(() => {
-      this.commands = getCommands('!inDebugMode')
+      this.commands = this.getCommands()
       this.refresh()
     })
+    context.subscriptions.push(
+      vscode.window.tabGroups.onDidChangeTabs(() => {
+        this.commands = this.getCommands()
+        this.refresh()
+      }),
+
+      vscode.window.onDidChangeActiveTextEditor(() => {
+        this.commands = this.getCommands()
+        this.refresh()
+      })
+    )
 
     context.subscriptions.push(tree)
   }
-}
 
-// Function to parse all the commands from the package.json, that currently 
enabled,
-// to an array of CommandItems
-function getCommands(enablement: String): Array<CommandItem> {
-  const commands = Array<CommandItem>()
+  // Get the type of the currently active custom editor. If we're not using a 
custom editor, return an empty string
+  private getActiveCustomEditor(): string {
+    // TabGroups are able to represent many different types of editors, 
including Text Editors, Notebook Editors, and WebView Editors
+    const tabInput = vscode.window.tabGroups.activeTabGroup.activeTab?.input
+    if (tabInput && tabInput instanceof vscode.TabInputCustom)
+      return tabInput.viewType
+    return ''
+  }
 
-  packageCommands
-    .filter(
-      (c) =>
-        !c.command.startsWith(viewName) &&
-        (enablement === c.enablement || c.enablement === undefined)
-    )
-    .forEach((command) => {
-      commands.push(
-        new CommandItem(
-          command.title,
-          command.command,
-          command.category,
-          command.enablement ?? '',
-          vscode.TreeItemCollapsibleState.None
-        )
+  // This context will be passed into the AST parser. It should contain 
information on the different keywords expected
+  // in our boolean expressions mapped to their resolved values.
+  private getEnablementContext(): Record<string, any> {
+    return {
+      inDebugMode: () => vscode.debug.activeDebugSession,
+      editorLangId: () => this.getActiveLangId(),
+      activeEditor: () => this.getActiveCustomEditor(),
+      editorTextFocus: () => !(vscode.window.activeTextEditor === undefined),
+    }
+  }
+
+  private getActiveLangId(): string {
+    return vscode.window.activeTextEditor?.document.languageId || ''
+  }
+
+  // Evaluate an Abstract Syntax Tree - useful for evaluating boolean 
expressions that are contained within a string
+  // Be sure to wrap the string in a jsep constructor (eg. jsep(str))
+  private evaluateAst(
+    node:
+      | jsep.Expression
+      | jsep.baseTypes
+      | (jsep.Expression | jsep.baseTypes)[],
+    context: Record<string, any>
+  ): any {
+    if (Array.isArray(node)) {
+      outputChannel.appendLine('Unexpected array node')
+      return
+    }
+
+    if (!node) {
+      outputChannel.appendLine('Unexpected undefined node')
+      return
+    }
+
+    if (
+      typeof node === 'string' ||
+      typeof node === 'number' ||
+      typeof node === 'boolean'
+    ) {
+      return node
+    }
+
+    if (typeof node !== 'object' || !('type' in node)) {
+      outputChannel.appendLine(`Invalid AST node: ${JSON.stringify(node)}`)
+      return
+    }
+
+    switch (node.type) {
+      case 'Literal':
+        return node.value
+      case 'Identifier': {
+        if (!node.name || typeof node.name !== 'string') {
+          outputChannel.appendLine(
+            `Invalid Identifier: ${node.name}, type of ${typeof node.name}`
+          )
+          return
+        }
+
+        if (!(node.name in context)) {
+          outputChannel.appendLine(`Unknown identifier: ${node.name}`)
+        }
+
+        const val = context[node.name]
+        return typeof val === 'function' ? val() : val
+      }
+      case 'CallExpression': {
+        const callee = node.callee as jsep.Identifier
+
+        if (callee.type !== 'Identifier') {
+          outputChannel.appendLine(
+            `Unsupported function call: ${node.name}. Only top-level 
identifiers are allowed`
+          )
+          return
+        }
+
+        const fn = context[callee.name]
+        if (typeof fn !== 'function') {
+          outputChannel.appendLine(
+            `Indentifier ${callee.name} is not a function`
+          )
+          return
+        }
+
+        // None of the functions currently need arguments
+        // To support arguments, start with the following lines:
+        // const args = node.arguments.map(arg => evaluateEnablement2(arg, 
context))
+        // fn(...args)
+        return fn()
+      }
+      case 'UnaryExpression': {
+        const arg = this.evaluateAst(node.argument, context)
+        switch (node.operator) {
+          case '!':
+            return !arg
+          default:
+            outputChannel.appendLine(
+              `Unsupported unary operator: ${node.operator}`
+            )
+            return
+        }
+      }
+      case 'LogicalExpression': {
+        const left = this.evaluateAst(node.left, context)
+        const right = this.evaluateAst(node.right, context)
+        switch (node.operator) {
+          case '&&':
+            return left && right
+          case '||':
+            return left || right
+          default:
+            outputChannel.appendLine(
+              `Unsupported logical operator: ${node.operator}`
+            )
+            return
+        }
+      }
+      case 'BinaryExpression': {
+        const left = this.evaluateAst(node.left, context)
+        const right = this.evaluateAst(node.right, context)
+        switch (node.operator) {
+          case '==':
+            return left == right
+          case '!=':
+            return left != right
+          case '&&':
+            return left && right
+          case '||':
+            return left || right
+          default:
+            outputChannel.appendLine(
+              `Unsupported binary operator: ${node.operator}`
+            )
+            return
+        }
+      }
+      default:
+        outputChannel.appendLine(`Unsupported node type: ${(node as 
any).type}`)
+        return
+    }
+  }
+
+  // Function to parse all the commands from the package.json, that currently 
enabled,
+  // to an array of CommandItems
+  private getCommands(): Array<CommandItem> {
+    const commands = Array<CommandItem>()
+
+    packageCommands
+      .filter(
+        (c) =>
+          !c.command.startsWith(viewName) &&
+          c.enablement &&
+          this.evaluateAst(jsep(c.enablement), this.getEnablementContext())
       )
-    })
+      .forEach((command) => {
+        commands.push(
+          new CommandItem(
+            command.title,
+            command.command,
+            command.category ?? '',
+            command.enablement ?? '',
+            vscode.TreeItemCollapsibleState.None
+          )
+        )
+      })
 
-  return commands
+    return commands
+  }
 }
diff --git a/yarn.lock b/yarn.lock
index d2a77d9..ee3843a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3054,6 +3054,11 @@ js-yaml@^4.1.0:
   dependencies:
     argparse "^2.0.1"
 
+jsep@^1.4.0:
+  version "1.4.0"
+  resolved 
"https://registry.yarnpkg.com/jsep/-/jsep-1.4.0.tgz#19feccbfa51d8a79f72480b4b8e40ce2e17152f0";
+  integrity 
sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==
+
 json-buffer@3.0.1:
   version "3.0.1"
   resolved 
"https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13";

Reply via email to