-
Notifications
You must be signed in to change notification settings - Fork 7
User Guide
The extension adds syntax highlighting, semantic (error) highlighting, and local execution for HL7 Clinical Quality Language (CQL) to Visual Studio Code
To install Visual Studio Code, visit https://code.visualstudio.com and follow the download instructions appropriate for your platform.
The cqframework.cql extension has been published to the VS Code Marketplace, so the installation is simple. Just search for "Clinical Quality Language" in the marketplace and install the extension:

The extension will be activated once you open any file with the .cql extension in the editor.
Note that the extension will download the CQL Language Server, which may take a while. During the download, the status is displayed in the lower-right corner of the VSCode environment.
In addition, this extension requires Java to be installed. It'll prompt you to install Java if required.
Download and unzip vscodeTest.zip to a local drive. In VSCode, use 'Open Folder', open the /vscodeTest directory. This should also open a panel on the left hand side called 'Explorer'. Select the /input/cql/HelloWorld.cql file, which should display on the right hand side. Select the CQL file on right, right mouse click, 'Execute CQL' option. This should cause a new HelloWorld.txt file to be generated, which should contain "HelloWorld=Your CQL extension works if you see this text in the output HelloWorld.TXT file". If you see that output being generated your CQL extension has been installed correctly and is up and working.
If not: please ensure you have the latest version of VSCode and Java. Please check your PATH environment variables. Should these not correctly refer to the \Java\javapath directory the CQL extension won't work.
The plugin provides syntax highlighting for CQL:

To see the result type of any expression, hover over the expression with the mouse:

Errors in the CQL are highlighted using a red underline. To see the error message, hover over the underlined CQL and the error message will display:

Warnings in the CQL (i.e. messages that indicate something may be wrong or unexpected with the CQL but doesn't necessarily mean an error) are highlighted using a yellow underline. Again, to see the warning message, hover over the underlined CQL and the warning message will display:

The plugin provides LSP-based code completion for CQL. Start typing in a .cql file and the editor suggests CQL keywords, library names, defined expressions, function signatures, and snippets.
The completion popup appears automatically as you type, or press Ctrl+Space to trigger it manually.
By default VS Code also offers word-based suggestions (independent of LSP completion). Users can enable/disable word based suggestions with the user/workspace settings.json file:
- Open the Command Palette → Preferences: Open User Settings (JSON).
- Add to the
settings.jsonfile:
"[cql]": {
"editor.wordBasedSuggestions": "off"
}The plugin supports execution of CQL. The user can run either all test cases or have the ability to select one or more individual test cases, or run mulitple libraries at the same time.
Note: CQL commands require an active connection to the CQL Language Server (CQL-LS). On initial start-up, commands will not appear in the context menus or command palette until the CQL-LS connection is established.
The CQL-LS status is displayed in the lower right-hand corner of the VSCode status bar. Hovering over it shows a tooltip with the current status and component version information (translator, engine, clinical reasoning, and language server versions) — useful for diagnosing version compatibility issues.

With an open CQL file, right-click anywhere in the CQL editor and select Execute CQL or Execute CQL - Select Test Cases from the context-menu:
Note: The plugin relies on the name of the library, so the filename and CQL library declaration name must match. In the above example, the file is named
Simple.cqland the library name isSimple.

The Execute CQL - Select Test Cases option will open a quick-pick that wil allow the user to select one or more test cases.

Note: If a MeasureReport is defined for the test case, the cqfm-testCaseDescription will be used for quickpick detail.
Running multiple libraries is accessible from the VSCode Command Palette.

-
Open the Command Palette: Use the keyboard shortcut
Ctrl+Shift+P(Windows/Linux) orCmd+Shift+P(Mac). You can also open it from the top menu by selectingView > Command Palette. -
Search for the CQL Execute commands: Type
CQLin the command palette input box. -
Select the command: Choose the
CQL: ExecuteorCQL: Execute CQL - Select Test Casesoption that appears in the dropdown list.

User can select one or more libraries. The libraries will processed sequentially and will execute all test cases.
During execution, progress will shown in a notifcation window.

The extension supports 2 styles of result files, the classic flat text file, and individual results.
The flat text file includes all test case results in a single file. The individual results saves each test result in a separate file.

Executing the CQL targeting the flat file format will open a new editor on a file with the library name and a .txt extension, logging the results of the evaluation. Every top-level expression in the library is evaluated and the results are added to the evaluation log:
In addition, any messages generated by the execution are output to the log.
Note that not all messages are errors, the evaluation may result in information, warning, and other messages that don't necessarily mean the evaluation failed. Check for the keyword
ERROR:at the beginning of the message to make sure an error has occurred, rather than just a warning or informational message produced by the evaluation.

The Individual File results are saved as JSON.
Executing the CQL targeting the Individual format a new editor window for each test case will not be opened. The test runner indicator will show in the lower left corner of the editor to show when the tests are complete. Look at the input/tests/results/<LIBRARY NAME> folder, or the CQL Explorer panel to see the new results.
Key features of the JSON format
- parameters used for the test case are captured
- all execution errors are captured
- version details for the supporting packages are captured
By default all test cases found in a measure specific test folder will be executed. There is an option to exclude specific test cases.
To exclude test cases, create a config.json file in the input\tests folder.

The measure test results file will include information about any excluded test cases.

CQL is translated into a machine-friendly format called Expression Logical Model or ELM. Most engines that run CQL are processing ELM to perform the actual evaluation. The plugin provides several ways to view the ELM output for a given CQL file.
Right-click anywhere in the CQL editor and select 1 of the 4 options
View ELM - ASTView ELM - AST (Split)View ELM - JSONView ELM - XML

Right-click and select View ELM - AST to open the ELM as a text-based tree with box-drawing characters. This format is especially useful for understanding the structure of nested expressions.

Right-click and select View ELM - AST (Split) to open a linked split editor: CQL source on the left, AST on the right. Clicking in either pane highlights the corresponding line in the other. The AST refreshes automatically when the CQL file is saved.

This feature is useful mainly as a tool for helping understand what an engine is doing or when debugging an unexpected result or error.
The plugin will open a new editor with the ELM output in XML format:

The plugin will open a new editor with the ELM output in JSON format:

The CQL Explorer view appears in the Explorer sidebar when a CQL project is opened in the workspace. It provides a hierarchical tree view of CQL libraries, their test cases, and execution results.

The explorer automatically discovers CQL projects in the workspace by looking for ig.ini or input/cql/ directories. Multi-project workspaces are supported, with each project shown under its own root node.

Each .cql file appears as a library node. Expand a library to see its test cases and results. Test cases load on demand when a library node is first expanded. The tree stays synchronized with the file system — libraries, test cases, and resources appear and disappear automatically as files are created or deleted.

The CQL Explorer toolbar (top of the tree view) provides the following commands:

| Command | Description |
|---|---|
| Edit Configuration | Open the project configuration editor |
| Execute CQL - Filtered Libraries | Run all visible libraries (respecting the active filter) |
| Execute CQL - Select Libraries | Open a quick-pick to select and execute specific libraries |
| Expand All / Collapse All | Expand or collapse all library nodes |
| Filter by Library Name | Filter the displayed libraries by name |
| More Actions | Open more actions menu |
| Refresh | Rebuild the entire tree from disk |
More Actions Menu
| Command | Description |
|---|---|
| Hide Empty / Show All | Toggle whether libraries with no test cases are shown |
| Sort A→Z / Z→A | Toggle sort order (CMS measure numbers are sorted numerically) |

Right-click a library node to access these commands:
| Command | Description |
|---|---|
| Execute All Test Cases | Run all test cases for this library |
| View ELM - AST | View the compiled ELM as a text-based AST tree |
| View ELM - JSON | View the compiled ELM as JSON |
| View ELM - XML | View the compiled ELM as XML |
Command Bar

| Command | Description |
|---|---|
| Debug Test Case | Select a test case, using quick-pick, and start a debug session |
| Execute Test Cases | Run all test cases for this library (respecting the active filter) |
| Filter Test Cases | Filter test cases by name or description |
| Select and Execute Test Cases | Choose specific test cases via quick-pick |
Command Bar

| Command | Description |
|---|---|
| Debug Test Case | Start a debug session with this test case's data |
| Execute Test Case | Run this test case |
| Open All Resources | Open all FHIR resource files for this test case in separate editor tabs |
Context Menu

Right-click a single test case node:
| Command | Description |
|---|---|
| Debug Test Case | Start a debug session with this test case's data |
| Execute Test Case | Run this test case |
| Open All Resources | Open all FHIR resource files for this test case in separate editor tabs |
| Clone Test Case | Duplicate the test case (see Cloning a Test Case below) |
| Delete | Remove the test case directory |
Context Menu

Right-click a FHIR resource file within a test case:
| Command | Description |
|---|---|
| Copy / Cut | Copy or cut the file to the resource clipboard |
| Delete | Remove the resource file |
| Rename | Rename the resource file |
The Clone command duplicates an existing test case directory. All UUIDs in filenames and JSON resource content (patient ID, resource IDs, references) are regenerated automatically so the clone is immediately usable without manual editing.
The in-memory resource clipboard lets you copy, cut, and paste FHIR resource files between test cases. Paste - Update References rewrites patient UUIDs and resource IDs in the pasted files to avoid conflicts with existing resources in the target test case.
Cut/Copy is an action on a Test Case Resource.
- user right-clicks on a resource to cut/copy the resource to the clipboard.
Paste/Paste - Update References is an action on the Test Case.
- user right-clicks on a test case to paste the resource from the clipboard.
- paste options are available after a resource is cut/copied to the clipboard.

The CQL Explorer lives inside VS Code's built-in Explorer sidebar (the file tree), not on its own activity bar icon. If it is not visible, try these steps in order:
-
Unhide it from the sidebar — right-click anywhere in the Explorer sidebar header and make sure CQL Explorer is checked. If it was previously hidden via "Hide", this re-enables it.
-
Focus it via the Command Palette — open the Command Palette (
Ctrl+Shift+P/Cmd+Shift+P), typeView: Open View, then select CQL Explorer from the list. -
Scroll down in the Explorer sidebar — the panel may be collapsed below the fold. Look for a "CQL Explorer" section heading and click it to expand.
-
Confirm a CQL project is open — the explorer only populates when the workspace contains a project with an
ig.inifile or aninput/cql/directory. Open the root folder of your CQL project (File > Open Folder) and the tree should appear.
The plugin provides full LSP-based code navigation for CQL files.
Press F12 or Ctrl+Click on any expression reference, function call, value set name, or code system reference to navigate to its definition. This works across included libraries — clicking a reference from an included library jumps to that library's definition.
Right-click a symbol and select Find All References (or press Shift+F12) to see all locations where a definition is used across the workspace. Results appear in the References panel grouped by file.
Press Ctrl+Shift+O to jump to any library, define expression, function, or parameter in the current file. The outline groups symbols by category.
Right-click a function or expression and navigate to Peek > Peek Call Hierarchy to see which expressions call it and which expressions it calls. This is useful for understanding data flow through a library.
The plugin provides access to both the extension client log and the language server log for troubleshooting. Access the commands from the Command Palette
| Command | Description |
|---|---|
CQL: Open Server Logs |
Opens the language server log file (cql-ls.log) |
CQL: Open Extension Logs |
Opens the extension (client) log file |
CQL: Open Logs |
Opens both logs side by side in two editor columns |
The plugin provides a webview-based configuration editor for managing input/tests/config.jsonc settings. Open it from the CQL Explorer toolbar (pencil icon) or the Command Palette (CQL: Edit Configuration).
The editor provides form controls for:
-
Result Format — Choose between Flat (
.txt) and Individual (per-test-case.json) -
Store flate results in library-named folder — Selected and the flat file reulsts are saved in indvidual library folder. Not selected and all flat file results are in the
input/tests/resultsfolder, which is the original approach to saving the results. - Test Case Exclusions — Add, edit, or remove excluded test cases per library with an optional reason
- Global Parameters — CQL parameter overrides applied to all libraries
- Library Parameters — Parameters scoped to a specific library (optionally versioned)
- Test Case Overrides — Per-patient (per-UUID) parameter values within a library-scoped block

The config.jsonc file is also validated by a bundled JSON Schema, providing IntelliSense (auto-complete and validation) when editing the file directly.
The translation and execution capabilities in the plugin expect CQL files to be in the following directories, by convention:
<PROJECT>/input/cql
<PROJECT>/input/tests
<PROJECT>/input/tests/<cql-library-name>
<PROJECT>/input/tests/<cql-library-name>/<patient-id>
<PROJECT>/input/tests/<cql-library-name>/<patient-id>/<resource-type-name>/<resource files> // flexible structure
<PROJECT>/input/vocabulary/codesystem
<PROJECT>/input/vocabulary/valuesetWithin the tests folder, there is a folder for each CQL library, by name (note that the name of the file must match the name of the library in order for the evaluator to properly execute the CQL).
Note also that the evaluator is a separate subsystem from the translator, so it will read whatever is current on disk, so be sure to save before executing.
Within the library folder, there is a folder for each "test case", in the form of a Patient (the execution only supports patient context execution at this point). The folder must have the same id as the patient (that's how the evaluator knows what the patient id is).
Within each test case folder are the resources for that specific test case. The resource files can be provided either directly in this folder, or they can be organized into folders by resource type name. Whether they are in the test folder or in subfolders, resources can be provided as bundles (included nested bundles), or as separate files, and in either XML or JSON format. If a Patient is provided, the id element of the Patient resource must match the name of the test case folder. Resource file names are expected to follow the convention <id>.(json|xml) (when in a resource-specific folder) or <ResourceType>-<id>.(json|xml), for example Patient-123.json.
NOTE: As of the 0.7.3 release of the VSCode plugin, test case resources can no longer be provided as bundles, each resource must be in a separate file in order for it to be recognized by the plugin. This change was made to accommodate future capabilities for supporting test case authoring and updating, making it easier for the plugin to identify where resource contents should be updated.
Refer to the Adding a Test Case topic below for more information.
NOTE: The plugin will actually search any subfolders of the
input/testsfolder, looking for the first match of a folder with the library name being tested. This allows for flexible organization of tests to match intent, for example, there may belibrarytests organized under a library folder, andmeasuretests organized under a measure folder. However, when this feature is used, the plugin will use the first folder it finds that matches the library name, regardless of how nested the folder structure is.
The plugin can make use of an options file to support setting compiler and run-time options for the plugin. These options are read from a file named cql-options.json in the input/cql folder. Here is an example cql-options file with typical defaults for common options:
{
"options":[
"EnableAnnotations",
"EnableLocators",
"DisableListDemotion",
"DisableListPromotion"
],
"formats":[
"XML",
"JSON"
],
"validateUnits":true,
"verifyOnly":false,
"errorLevel":"Info",
"signatureLevel":"None",
"analyzeDataRequirements":false,
"collapseDataRequirements":true
}For a complete description of the available options, refer to the Usage topic in the CQL-to-ELM translator documentation.
To support collaboration among multiple authors, Visual Studio Code has built-in support for using Github. To use Github, you will need a Github account, it's free and can be associated with your favorite identity provider if you want. Once you have a github account, go to the Source Control tab in Visual Studio Code and select "Clone a repository":

In the popup that displays select Clone from github:

Type in the Github organization and repository you want to clone (e.g. cqframework/cqf-ccc), then select the repository in the dropdown:

Choose a folder locally where you want to clone the repository and click Select a Repository Destination:

VSCode will prompt you whether you want to open the newly cloned repository:

VSCode will then prompt for whether you trust the authors of the repository. Select Yes, I trust the authors. You can optionally select the checkbox to Trust the authors of all files in the parent folder, that way other repositories you clone from this same organization will already be trusted.

You are now taken to the main VSCode folder explorer interface. On the left-hand side you will see a listing of the files and folders in the repository:

In the explorer view, navigate to the input/cql folder to see all the CQL files in the repository. Click on the CQL file you want to open:

Make some changes to the CQL library (in this case we've added a comment to the expression). Then save the changes and the editor will indicate where the changed lines are in the file, and the explorer will indicate that the file is modified:

These changes are only in your local clone of the repository at this point, so they are visible only to you.
To commit these changes back to the github repository, select the Source Control icon on the left side of VSCode:

Provide a message describing the changes, and select the + icon next to the list of changes to Stage All Changes:

Once the changes you want to commit back to the repository are staged, pull down the arrow on the Commit button and select Commit and Push:

This will commit the changes directly back to the main branch of the repository.
NOTE: This process requires write access to the repository. Contact the repository administrator to ensure you have privileges to write.
NOTE: Many repositories require the use of a Pull Request to commit changes to the main branch of the repository. There is an extension (github.vscode-pull-request-github) that enables the use of pull requests in VSCode.
To pull changes from the github repository (i.e. get the latest changes that other users have committed), click the Source Control icon on the left side of VSCode, and open the Source Control menu by clicking the ellipses. On the Source Control menu, select Pull:

The plugin can use locally available value sets in the form of FHIR ValueSet resources. The plugin looks in the input/vocabulary/valueset folder (and any sub-folders recursively) and loads any files it finds and tries to interpret them as FHIR ValueSet resources. This means that the value set files can be FHIR Bundles or FHIR ValueSet resources in both XML and JSON format.
For example, given the following declaration in a CQL library:
valueset "Active Condition": 'http://fhir.org/guides/cqf/common/ValueSet/active-condition'The plugin will look in the input/vocabulary folder for a FHIR ValueSet resource with this url:
{
"resourceType" : "ValueSet",
"id" : "active-condition",
...
"url" : "http://fhir.org/guides/cqf/common/ValueSet/active-condition",
"version" : "4.0.1",
"name" : "CQFActiveCondition",
"title" : "CQF Active Condition",
...Note that the
iddoesn't matter, only theurlelement is used to find the value set.
If the value set is a simple extensional value set (i.e. it is defined only in terms of a list of specific codes from a code system) then the plugin will compute the expansion. When this happens, a WARNING is issued by the evaluator indicating that the plugin performed the expansion and it may not be correct (because it does not use the code system to compute the expansion so it may include codes that should not be included based on the expansion rules that a full terminology server would follow).
...
"status" : "active",
"experimental" : false,
"date" : "2019-07-21",
"publisher" : "Alphora",
...
"compose" : {
"include" : [
{
"system" : "http://terminology.hl7.org/CodeSystem/condition-clinical",
"concept" : [
{
"code" : "active",
"display" : "Active"
},
{
"code" : "recurrence",
"display" : "Recurrence"
},
{
"code" : "relapse",
"display" : "Relapse"
}
]
}
]
}
}Best practice is to use ValueSet resources which have an expansion element already computed, rather than relying on the plugin to perform the computation. ValueSets which are more complex than simple extensional definitions will not be expanded by the plugin and must have an expansion element present in order to be used.
The structure of CQL projects is an extension to the FHIR IG publisher project structure, it is best practice to use an external sub-folder to contain value sets that are copied from other locations and not actually defined as part of the content IG. Future enhancements to the plugin will be able to make use of value sets distributed in dependent IGs, but for now, all terminology dependencies need to be copied locally in order for the plugin to make use of them.
Once you have added any required terminology, use the same process above for committing those changes back to the repository.
The plugin is also capable of making use of locally available FHIR resources for test data. The plugin looks in the input/tests folder (and any sub-folders, recursively) for a folder with the same name as the library being tested. For example, when running the ColorectalCancerElements library, the plugin will look in the input/cql/library/ColorectalCancerElements folder.
The evaluation log lists the directory that the plugin is using to find test data:
Executing CQL...
CQL path: <PROJECT>\input\cql
Data path: <PROJECT>\input\tests\library\ColorectalCancerElements
Terminology path: <PROJECT>\cqf-ccc\input\vocabulary\valuesetThe plugin supports defining any number of Test Cases, where each test case is a separate folder in the test folder. In this case, the plugin finds two folders:
- denom-EXM130
- numer-EXM130
Each of these folders contains JSON files with the FHIR resources representing the test case. The name of the test case is the name of the folder and is used as the patient id for the test case. This means that all the FHIR data in the folder must use this patient id:
{
"resourceType": "Patient",
"id": "denom-EXM130",
...
}{
"resourceType": "Encounter",
"id": "denom-EXM130-1",
"subject": {
"reference": "Patient/denom-EXM130"
},
...
}To create a new test case, add a new folder and place the resource files corresponding to the test case in the folder. As with the terminology, the plugin will search for any files in the folder and any sub-folders, recursively, attempting to load any files and interpret them as FHIR resources, so the files can be in XML or JSON format and can be either Bundles or individual resources, and organized in whatever folder structure makes the most sense for your team.
As with changes to the CQL and Vocabulary, once you have made changes and want to commit them back to the repository, follow the same process for committing and pushing changes described above.
Files can be named by Id only or Type and Id.
input\tests\library\PatientAndCoverage\minimal-patient-example\minimal-coverage-example.json
input\tests\library\PatientAndCoverage\minimal-patient-example\minimal-organization-example.json
input\tests\library\PatientAndCoverage\minimal-patient-example\minimal-patient-example.jsoninput\tests\library\PatientAndCoverage\minimal-patient-example\Coverage-mpe.json
input\tests\library\PatientAndCoverage\minimal-patient-example\Organization-mpe.json
input\tests\library\PatientAndCoverage\minimal-patient-example\Patient-mpe.jsonNOTE: Mixing
Id onlyandType and Idfile naming conventions is not allowed. If mixed mode is used the extension won't return results.
Once you have defined your test cases, right click anywhere in the library and select Execute CQL to run the library. The plugin will find the test cases and run the library for each test case, outputting the result of evaluating each expression definition in the library to the evaluation log:
Executing CQL...
CQL path: c:\Users\Bryn\Documents\Src\Github\CQFramework\cqf-ccc\input\cql
Data path: c:\Users\Bryn\Documents\Src\Github\CQFramework\cqf-ccc\input\tests\library\ColorectalCancerElements
Terminology path: c:\Users\Bryn\Documents\Src\Github\CQFramework\cqf-ccc\input\vocabulary\valueset
Patient=Patient(id=denom-EXM130)
AsOf=2023-09-28T11:59:30.162
...
Has Appropriate Colorectal Cancer Screening=false
Has History of Appropriate Colorectal Cancer Screening=false
Patient=Patient(id=numer-EXM130)
AsOf=2023-09-28T11:59:30.624
...
Has Appropriate Colorectal Cancer Screening=true
Has History of Appropriate Colorectal Cancer Screening=falseNOTE: The plugin reads what is on disk, so make sure to save any changes in the editor before running your tests
For step-by-step manual acceptance testing of all extension features — including server startup, syntax highlighting, diagnostics, hover, formatting, ELM output, CQL execution, the call graph, and the CQL debugger — see the UAT Script.
The most common error is Error: spawn /undefined/bin/java ENOENT, which indicates that JAVA_HOME is not set or accessible to the extension.
| Manager | Setup Instructions |
|---|---|
| SDKMAN! | Usually manages JAVA_HOME automatically. Ensure you have run sdk use java <version>. |
| jenv | Add jenv add ~/.jenv/plugins/java/set-java-home.zsh (or .bash) to your shell profile. |
| asdf | Ensure the Java plugin is set to export the environment variable: echo "java_home_export = yes" >> ~/.asdfrc. |
| mise | Ensure java_home = true is set in your .mise.toml or your shell is correctly evaluating mise activate. |
CQL libraries often declare parameters (e.g., Measurement Period) that must be supplied at evaluation time. The extension will support a three-tier parameter configuration system that lets you set parameter values globally, override them per library, and override them again per individual test case.
Parameters are defined in input/tests/config.json — the same file already used for Test Case Exclusion. The extension provides full IntelliSense (auto-complete and validation) for this file via a bundled JSON Schema.
| Tier | Key | Description |
|---|---|---|
global |
— | Applies to every library and every test case |
libraries |
library name | Overrides global for a specific library |
testCases |
library name → patient UUID | Overrides global and library for one specific test case |
When the same parameter name appears at multiple tiers, the highest-priority value wins.
In this example:
- All libraries receive
Measurement Period = Interval[@2024-01-01, @2024-12-31] -
MyQualityMeasureadditionally receivesProduct Line = HMO - The specific test case
a1b2c3d4-...overridesProduct LinewithMedicaid
Each parameter entry requires three fields:
| Field | Description |
|---|---|
name |
Must match the CQL parameter declaration exactly (case-sensitive) |
type |
One of the supported CQL types (see below) |
value |
String representation of the value |
type value |
CQL type | Notes |
|---|---|---|
String |
String |
Passed as-is |
Integer |
Integer |
Parsed to a whole number |
Decimal |
Decimal |
Parsed to a decimal number |
Boolean |
Boolean |
true or false (case-sensitive) |
DateTime |
DateTime |
CQL literal, e.g. @2024-01-01T00:00:00.000Z
|
Date |
Date |
CQL literal, e.g. @2024-01-01
|
Time |
Time |
CQL literal, e.g. @T12:00:00
|
Quantity |
Quantity |
CQL literal, e.g. 5 'mg'
|
Interval<DateTime> |
Interval<DateTime> |
CQL literal, e.g. Interval[@2024-01-01T00:00:00.000Z, @2025-01-01T00:00:00.000Z)
|
Interval<Date> |
Interval<Date> |
CQL literal, e.g. Interval[@2024-01-01, @2025-01-01)
|
Note: All types are parsed and converted to their native CQL runtime types by the language server before being passed to the engine. If a value cannot be parsed (e.g.
"abc"for anInteger, or a malformed interval literal), a warning is logged and the string value is passed through unchanged.
Each test case produces a TestCaseResult-<patientId>.json file in input/tests/results/<LibraryName>/. The file records which parameters were supplied via config.json and which fell back to the default value declared in the CQL library:
{
"parameters": [
{ "name": "Measurement Period", "type": "Interval<DateTime>", "value": "Interval[@2026-01-01T00:00:00.000Z, @2027-01-01T00:00:00.000Z)", "source": "config-global" },
{ "name": "Product Line", "type": "String", "value": "HMO", "source": "config-library" },
{ "name": "Some Default Param", "value": "42", "source": "default" }
],
"results": [...],
"errors": []
}parameters is a single unified list covering all parameters for the evaluation. Use the source field to understand where each value came from:
source |
Meaning |
|---|---|
"config-global" |
Supplied in the top-level global array of config.json
|
"config-library" |
Supplied in libraries.<LibraryName> of config.json
|
"config-test-case" |
Supplied in testCases.<LibraryName>.<patientUUID> (highest priority) |
"default" |
Not in config.json — fell back to the CQL-declared default expression |
Config-supplied entries appear first; CQL-default entries are appended. Default entries have no type field (the CQL type is not returned by the engine).
If a parameter appears in defaultParameters, you may want to add it to config.json to make the value explicit and version-controlled.
Test case keys in the testCases section are the patient UUID directory names found under input/tests/<LibraryName>/. For example, if your test case folder is:
input/tests/MyQualityMeasure/a1b2c3d4-e5f6-7890-abcd-ef1234567890/
then the key is a1b2c3d4-e5f6-7890-abcd-ef1234567890.
CQL execution can produce results in two formats: individual (default) and flat. The format is selected by the cql.execute.resultFormat VS Code setting (workspace or user scope), and can also be overridden per-project in config.json.
Each test case produces a separate JSON file written to:
input/tests/results/<LibraryName>/TestCaseResult-<patientId>.json
This format is well suited for programmatic consumption and for tracking per-test-case results over time, since each file is independently diff-able and can be committed to source control.
When executing a single test case, the result file opens automatically in the editor. When executing multiple test cases at once, a notification appears after all files are written.
Example result file:
{
"executedAt": "2026-04-09T14:22:00.000Z",
"libraryName": "MyQualityMeasure",
"testCaseName": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"testCaseDescription": "Patient meets denominator criteria",
"parameters": [
{ "name": "Measurement Period", "type": "Interval<DateTime>", "value": "Interval[@2024-01-01, @2024-12-31]", "source": "config-global" }
],
"results": [
{ "name": "Initial Population", "value": "true" },
{ "name": "Denominator", "value": "true" },
{ "name": "Numerator", "value": "false" }
],
"errors": []
}| Field | Description |
|---|---|
executedAt |
ISO 8601 timestamp of when execution started |
libraryName |
Name of the CQL library |
testCaseName |
Patient UUID (folder name), or null when no test data was found |
testCaseDescription |
The cqfm-testCaseDescription from the MeasureReport, if present |
parameters |
All parameters for this evaluation — see Test Case Parameters for the source field values |
results |
One entry per top-level define expression (excluding Error) |
errors |
Any Error-named expression values from the evaluation |
All test cases for a library are written to a single text file:
input/tests/results/<LibraryName>.txt
The file opens automatically in the editor after each run. This format matches the output style from earlier versions of the extension and is useful for quick, human-readable inspection.
Set the format globally in VS Code settings:
{
"cql.execute.resultFormat": "individual"
}Override it per project in input/tests/config.json:
{
"testCasesToExclude": [],
"resultFormat": "flat"
}The config.json setting takes precedence over the VS Code setting. When running multiple libraries via the Command Palette (CQL: Execute - Select Libraries), the individual format is always used regardless of either setting.
When the language server compiles a CQL file it must resolve every include statement to an
actual .cql file. This section explains how that resolution works and how you can configure
it per project.
The language server searches for included libraries in priority order:
-
Local
.cqlfiles — files on disk in your project (highest priority) -
npm packages — libraries distributed via FHIR IG npm packages in
~/.fhir/packages -
Bundled FHIRHelpers — built-in copies of
FHIRHelpersat selected versions (lowest priority)
For local file search, the language server performs a breadth-first search (BFS) starting
from the directory of the requesting library. It searches shallower directories before deeper
ones, so a file in input/cql/ beats a file in input/cql/subfolder/.
Tier 1 — the directory of the requesting library itself.
Tier 2 — the project's input/cql/ root (always enabled; skipped if tier 1 already is
input/cql/). This means a library in input/cql/measures/ can include a shared library
stored at input/cql/FHIRHelpers-4.0.1.cql without any configuration.
Configure how the language server matches versions in filenames using libraryResolution
in input/tests/config.jsonc:
| Mode | Behavior |
|---|---|
patch-flexible (default) |
Same major.minor required; accepts any patch version >= the requested patch. FHIRHelpers-4.0.1.cql satisfies include FHIRHelpers version '4.0.0'. |
strict |
Exact match only. FHIRHelpers-4.0.1.cql does not satisfy version '4.0.0'. |
{
"libraryResolution": "strict"
}The recommended way to share libraries across projects in a multi-project workspace is
namespace-qualified includes. The namespace is the package ID from the project's ig.ini:
include com.smiledigitalhealth.common.helper100 version '1.0.000'The language server reads each workspace project's ig.ini at startup, extracts the
packageId (e.g. com.smiledigitalhealth.common) and canonical URL, and registers them as
CQL namespaces. When a namespace-qualified include arrives, the language server routes it
directly to the matching project's input/cql/ without searching other tiers.
Requirement: A project must have an
ig.inifile to participate in cross-project resolution. Projects withoutig.iniare invisible to other projects.
By default, unqualified includes (e.g. include FHIRHelpers) are only searched within the
requesting project (tiers 1 and 2). Cross-project search for unqualified includes is
disabled by default to avoid accidental resolution from a sibling project when the
intended source is an npm package.
To enable it:
{
"unqualifiedCrossProjectSearch": true
}When enabled, the search expands to tier 3: other workspace projects' input/cql/ directories
(projects with ig.ini only). Control the search order and exclusions:
{
"unqualifiedCrossProjectSearch": true,
"projectSearchOrder": ["shared-libs", "common-terminology"],
"projectSearchExclude": ["reference-only"]
}-
projectSearchOrder— workspace folder names to search first; unlisted projects are searched alphabetically after listed ones. -
projectSearchExclude— workspace folder names to skip entirely (e.g. reference-only projects that should not contribute libraries).
All library resolution keys in input/tests/config.jsonc:
{
// Test case exclusions and parameters (existing keys) ...
// Library resolution (new in v0.9.4)
"libraryResolution": "patch-flexible", // "strict" | "patch-flexible" (default)
"unqualifiedCrossProjectSearch": false, // true to enable tier 3 search (default: false)
"projectSearchOrder": ["shared-libs"], // workspace folder names; unlisted → alphabetical
"projectSearchExclude": ["ref-only"] // excluded from tier 3 cross-project search
}The plugin supports interactive step-through debugging of CQL libraries using the VS Code Debug Adapter Protocol (DAP). Breakpoints can be set on any expression definition, and the Variables panel shows the result of each expression as it is evaluated.
The quickest way to start debugging is via the Debug CQL File button (▷ with a bug icon) that appears in the editor title bar whenever a .cql file is open. Clicking it launches the debugger using the active file.
Alternatively, press F5 or use Run > Start Debugging to launch via a launch.json configuration (see below).
Right-click a test case node and select Debug, or right-click the Test Cases heading node and select Debug Test Case (then pick a test case from the quick-pick). The debugger starts with that test case's FHIR resources and parameters.
When a .cql file is open in the editor and the language server is ready, right-click anywhere in the editor to access the Debug Test Case command alongside Execute CQL and the ELM viewers.
Selecting it opens a quick-pick listing the available test cases for the active library — pick one to start a debug session with that test case's FHIR resources and parameters.
This approach does not require a launch.json — the extension manages the debug adapter lifecycle internally. The debugger uses:
- The library from the active editor tab
- The selected test case's patient data as evaluation context
- Parameters from
config.jsonc(global → library → test-case priority)
For workspaces with many test cases, right-clicking in the editor is typically faster than navigating to a test case through the CQL Explorer.
Click in the gutter (left margin of the editor) on any line containing an expression definition to set a breakpoint. The debugger will pause before evaluating that expression and display its result in the Variables panel.
Note: Breakpoints on non-expression lines (e.g.,
librarydeclarations,usingstatements) are shown as unverified and are not hit during evaluation.
The standard VS Code step controls are available:
- Continue (F5) — resume evaluation to the next breakpoint
- Step Over (F10) / Step Into (F11) / Step Out (Shift+F11) — all advance to the next breakpoint (CQL's functional evaluation model does not support sub-expression stepping)
For users who prefer VS Code's native debug workflow, the CQL debugger can also be launched via F5 with a .vscode/launch.json configuration. Create (or let VS Code generate) a configuration with at minimum libraryUri and libraryName:
{
"version": "0.2.0",
"configurations": [
{
"type": "cql",
"request": "launch",
"name": "Evaluate CQL",
"libraryUri": "${fileDirname}",
"libraryName": "${fileBasenameNoExtension}",
"testCaseName": "patient-uuid",
"testCaseUri": "${workspaceFolder}/input/tests/LibraryName/patient-uuid"
}
]
}Important: A
launch.jsonrequirestestCaseNameandtestCaseUrito provide patient context — without them, expressions evaluate tonull. The right-click Debug Test Case command and CQL Explorer context menu set these fields automatically and are the recommended approach for most users.
When the debugger is paused at a breakpoint the Variables panel shows three scopes:
| Scope | Content |
|---|---|
| Arguments | Parameter values passed to the current function (only shown when paused at a define function) |
| Expressions | Results of all define expressions evaluated so far in this session |
| Context | The evaluation context, e.g. Patient = <patient-id>
|
The debugger supports two step granularities. Toggle between them via the status bar item or the CQL: Toggle Step Granularity command:

Step controls advance to the next CQL source line that contains an expression definition. Sub-expressions within a define body are not individually steppable — the entire line is treated as one unit.
This mode is the most practical for routine debugging: it follows the logical structure of your CQL library without exposing the internal ELM representation.
Step controls advance to every unvisited ELM AST node, including sub-expressions within a define body. This is the highest granularity available — every node in the compiled expression tree is a potential pause point.
Use AST level when you need to trace exactly which sub-expression produced an unexpected value, or when diagnosing issues that require understanding what the engine is doing at the ELM level.
During a debug session, the CQL AST view appears in the debug sidebar. It shows the ELM AST tree synchronized with the current debug frame. The current AST node is highlighted, and clicking any AST node reveals its corresponding CQL source location.

- No live data editing. Patient test data is loaded from disk at session start. To evaluate with different data, edit the FHIR resource files on disk and restart the debug session.
- No conditional breakpoints. Breakpoint conditions entered in VS Code's breakpoint editor are not evaluated — all breakpoints pause unconditionally.
The CQL debugger evaluates expressions against patient test data. At least one test case folder (a patient directory under input/tests/<LibraryName>/) must exist for the debug session to produce results. If no test data is found, expressions will evaluate to null.
See CQL Project Structure and Adding Test Data for details on how to set up test cases.
{ "testCasesToExclude": [], "parameters": [ { "name": "Measurement Period", "type": "Interval<DateTime>", "value": "Interval[@2024-01-01, @2024-12-31]" }, { "library": "MyQualityMeasure", "parameters": [ { "name": "Product Line", "type": "String", "value": "HMO" } ], "testCases": { "a1b2c3d4-e5f6-7890-abcd-ef1234567890": [ { "name": "Product Line", "type": "String", "value": "Medicaid" } ] } } ] }