Escape hatch completions now use a two-part edit strategy for VS Code compatibility:
- Main textEdit: Inserts newline and indented content at cursor position
(empty range so VS Code won't filter based on key text)
- additionalTextEdits: Replaces 'key: ' with 'key:' to remove trailing space
This prevents VS Code from filtering out escape hatches while still
producing the correct final YAML structure.
* Use labelDetails for completion item qualifiers
Use labelDetails.description instead of detail for qualifier text like
'full syntax' and 'list'. This renders the text inline after the label
in the completion menu, making variants immediately distinguishable
without hovering.
* Fix formatting
Provides runner label completions (ubuntu-latest, macos-latest, etc.)
when using the runs-on mapping syntax with the labels property:
jobs:
build:
runs-on:
labels: |
jobs:
build:
runs-on:
labels:
- |
Previously, completions only worked for the simple runs-on syntax:
jobs:
build:
runs-on: |
The fix registers the same value provider for both 'runs-on' and
'runs-on-labels' definition keys in the schema.
When completing an empty value position (e.g., `runs-on: |`), add special
completions that let users switch to alternative structural forms:
- "(switch to list)" - restructures to `key:\n - `
- "(switch to mapping)" - restructures to `key:\n `
These help users escape "dead end" situations where the current form has
no valid completions but alternative forms are available in the schema.
- Move qualifiers (list, full syntax) from label to detail field
- Remove filterText since labels are now clean
- Update distinctValues to preserve variants with different details
- Standard LSP pattern: detail shown after label in completion UI
Follow-up to #265
When completing an empty value (e.g., `permissions: |`), mapping keys were
incorrectly shown alongside scalar values. This made completions confusing.
Before:
- `permissions: |` showed read-all, write-all, AND actions, contents, etc.
- `on: |` showed check_run AND check_run (full syntax), etc.
After:
- `permissions: |` shows only read-all and write-all
- `on: |` shows only event names like push, check_run
- `concurrency: |` shows no completions (user types their own group name)
Users who want the mapping form choose (full syntax) completions at the
parent level.
Recent autocomplete improvements (typing activation, completion chaining, schema
variant surfacing) now guide users to discover the full schema naturally. This
change removes the legacy behavior that showed invalid options and silently
transformed YAML upon insertion.
Key changes:
- Filter one-of completion options based on the token's actual structural type
- When user commits to scalar (non-empty string), only show scalar options
- When user commits to mapping/sequence, only show those options
- Skip null-only scalars in Key mode to prevent clobbering string constants
- Scalar event completions (e.g., check_run at 'on: |') now insert inline
This ensures that when a user explicitly chooses a simplified form, they only
see values valid for that form, creating a cleaner and more predictable
autocomplete experience.
* Expand one-of keys to multiple completion items
Some workflow fields accept multiple YAML structures (scalar, sequence, or
mapping), but completions previously only showed a single option—leaving users
unaware of the full schema flexibility. This change surfaces structural options
and inserts the correct YAML scaffolding so users land in the right place to
keep typing.
Example: runs-on
Completing runs-on now shows three options:
- runs-on → Ready for a string like ubuntu-latest
- runs-on (list) → Ready to add runner labels
- runs-on (full syntax) → Ready for labels:, group:, etc.
Notes:
- Qualifiers (list) and (full syntax) only appear when multiple structural types exist
- Scalar completions use the plain key name
- Qualified variants use filterText matching the base key
* Sort expanded one-of completions: scalar, list, full syntax
When completing a one-of typed property in key mode (e.g., 'check_run: ty|'),
insert newline and indentation to produce valid YAML structure instead of
inserting just the key name which creates invalid YAML.
Migrate expressions, workflow-parser, and languageservice packages to use
proper ESM imports with .js extensions that work with node16 moduleResolution.
Changes:
- Update tsconfig.build.json in each package to use module: node16 and
moduleResolution: node16
- Add .js extensions to all relative import paths (Option B approach)
- Fix yaml internal type imports in workflow-parser by defining local types
- Add skipLibCheck to handle @types/node compatibility issues
- Add TypeScript 5.8.3 override in root package.json
- Add ESM migration plan documentation
The languageserver package is deferred due to test hang issues that need
further investigation.
Related #154 - Upgrade moduleResolution from node to node16 or nodenext
Related #110 - Published ESM code has imports without file extensions
Related #64 - expressions: ERR_MODULE_NOT_FOUND attempting to run example
Related #146 - Can not import @actions/workflow-parser
Test results:
- expressions: 1068 tests passed
- workflow-parser: 292 tests passed
- languageservice: 452 tests passed
* docs: update ESM migration plan with findings
- Update languageserver blocker: vscode-languageserver v8.0.2 lacks ESM
exports (not a test hang issue)
- Document that Option B (manual .js extensions) was chosen over Option A
due to ts-jest compatibility issues
- Add workaround for yaml package internal types (LinePos, NodeBase)
- Update migration status table with accurate reason for deferral
- Add skipLibCheck note for @types/node compatibility