Skip to content

Add support for double-extension action matching - #3315

Open
yuvalnn wants to merge 3 commits into
codefori:masterfrom
yuvalnn:feature/multi-extension-actions
Open

Add support for double-extension action matching#3315
yuvalnn wants to merge 3 commits into
codefori:masterfrom
yuvalnn:feature/multi-extension-actions

Conversation

@yuvalnn

@yuvalnn yuvalnn commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Fixes #2741

Changes

  • Added support for actions targeting double extensions such as PGM.RPGLE and PGM.CLLE.

How to test this PR

  1. Create actions for both RPGLE and PGM.RPGLE.
  2. Verify that a file such as foo.pgm.rpgle matches the expected action.
  3. Verify that existing single-extension actions still work.

Checklist

  • have tested my change
  • have created one or more test cases
  • updated relevant documentation
  • Remove any/all console.logs I added
  • have added myself to the contributors' list in CONTRIBUTING.md

@sebjulliand sebjulliand self-assigned this Jul 4, 2026
@sebjulliand sebjulliand added the enhancement New feature or request label Jul 4, 2026
@sebjulliand
sebjulliand self-requested a review July 4, 2026 15:19

@sebjulliand sebjulliand left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking into this @yuvalnn !
I left a few suggestions for code clarity.

However, the logic you implemented needs to be changed a bit, regarding the initial need.

With this PR, I'm still able to run an RPGLE action on a .pgm.rpgle file. So, i'll be presented with both "create a module" and "create a program" action on a file that I want to compile only into a program.

The logic should be something like:

-> if the action extension ends with pgm.xxx, then target file path must end with .pgm.xxx to be a valid target.

-> if the action extension is only xxx, then files ending with pgm.xxx must not be valid targets.

-> All of the above is only valid for local files or IFS files. For the other target, strictly checking the extension (or fragment, for objects) is enough

One way to check the multi-part extensions would be to use split(".") on both the action extension and the target file name and use these arrays to check the target validity.

Something along these lines:

  if (["file", "streamfile"].includes(target.uri.scheme)) {
    //Check all extension parts for workspace files or IFS files
    const parts = path.parse(target.uri.path).base.split(".").map(t => t.toUpperCase());
    parts.splice(0, 1);
    for (const e of extensions.map(ext => ext.split('.'))) {
      //every parts must match
      if (e.every((val, index) => val === parts[index])) {
        return true;
      }
    }
  }
  else {
    //Check extension only for 
    return [target.extension.toUpperCase(), target.fragment.toUpperCase()]
      .filter(Tools.distinct)
      .some(e => extensions.includes(e));
  }
  return false;

Hope this helps 😄

Comment thread src/ui/actions.ts
Comment on lines +685 to +690
if (action.type !== scheme) return false;

// action isn't cleared to run on protected targets and some of them are
if (!action.runOnProtected && targets.some(t => t.protected)) return false;

return targets.every((t) => targetMatchesExtensions(t, action.extensions));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can fuse all the checks in one statement; that will avoid intermediate returns.

Suggested change
if (action.type !== scheme) return false;
// action isn't cleared to run on protected targets and some of them are
if (!action.runOnProtected && targets.some(t => t.protected)) return false;
return targets.every((t) => targetMatchesExtensions(t, action.extensions));
return action.type === scheme &&
// action isn't cleared to run on protected targets and some of them are
(action.runOnProtected || !targets.some(t => t.protected)) &&
targets.every((t) => targetMatchesExtensions(t, action.extensions));

Comment thread src/ui/actions.ts

export function targetMatchesExtensions(target: ActionTarget, extensions?: string[]): boolean {
// action has no extension requirements, or is global
if (!extensions || extensions.every(e => !e) || extensions.includes("GLOBAL")) return true;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking if extensions is truthy or empty will be less confusing than doing it with extensions.every(e => !e)

Suggested change
if (!extensions || extensions.every(e => !e) || extensions.includes("GLOBAL")) return true;
if (!extensions?.length || extensions.includes("GLOBAL")) return true;

Comment thread src/ui/actions.ts
// action has no extension requirements, or is global
if (!extensions || extensions.every(e => !e) || extensions.includes("GLOBAL")) return true;

const targetExtParts = [target.extension.toUpperCase(), target.fragment.toUpperCase()];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filter on distinct and filter out empty string here to avoid unwanted iterations in the for loop after.

Suggested change
const targetExtParts = [target.extension.toUpperCase(), target.fragment.toUpperCase()];
const targetExtParts = [target.extension.toUpperCase(), target.fragment.toUpperCase()]
.filter(Boolean)
.filter(Tools.distinct);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enable actions that are specific to double extensions like .pgm.clle or .pgm.rpgle

3 participants