Skip to content

User Guide

raleigh-g-thompson edited this page Jul 7, 2026 · 31 revisions

CQFramework Clinical Quality Language VSCode Extension

The extension adds syntax highlighting, semantic (error) highlighting, and local execution for HL7 Clinical Quality Language (CQL) to Visual Studio Code

Installing Visual Studio Code

To install Visual Studio Code, visit https://code.visualstudio.com and follow the download instructions appropriate for your platform.

Installing the CQFramework Clinical Quality Language Extension

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:

Installing 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.

Validating the Installation

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.

Features

The plugin provides syntax highlighting for CQL:

Syntax Highlighting

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

Result Type Hover

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:

Error Message

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:

Warning Message

CQL Code Completion

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.

Enable/Disable Word Based Suggestions

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:

  1. Open the Command Palette → Preferences: Open User Settings (JSON).
  2. Add to the settings.json file:
  "[cql]": {
    "editor.wordBasedSuggestions": "off"
  }

Executing CQL

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.

CQL Language Server Status

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.

CQL-LS Status

Single Library Execution from context menu

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.cql and the library name is Simple.

Running All Test Cases

Executing CQL

Running One or More Test Cases

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

Select Test Cases QuickPick

Note: If a MeasureReport is defined for the test case, the cqfm-testCaseDescription will be used for quickpick detail.

Multiple Library Execution

Running multiple libraries is accessible from the VSCode Command Palette.

CQL Command Palette

  1. Open the Command Palette: Use the keyboard shortcut Ctrl+Shift+P (Windows/Linux) or Cmd+Shift+P (Mac). You can also open it from the top menu by selecting View > Command Palette.

  2. Search for the CQL Execute commands: Type CQL in the command palette input box.

  3. Select the command: Choose the CQL: Execute or CQL: Execute CQL - Select Test Cases option that appears in the dropdown list.

Select Libraries Quick Pick

CQL Execution - Select Libraries

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.

CQL Execution - Multiple Libraries progress

Execution Results

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.

Flat File format

Execution Result

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.

Individual File format

Execution Result

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

Test Case Exclusion

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.

Tests Configuration

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

Tests Results with Exclusions

Viewing ELM

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 - AST
  • View ELM - AST (Split)
  • View ELM - JSON
  • View ELM - XML

Viewing ELM

View ELM - AST

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.

ELM as AST Output

View ELM - AST (Split)

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.

ELM as AST Split Output

This feature is useful mainly as a tool for helping understand what an engine is doing or when debugging an unexpected result or error.

View ELM - XML

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

ELM as XML Output

View ELM - JSON

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

ELM as JSON Output

CQL Explorer

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.

CQL Explorer=

Project Discovery

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.

Multiple Project Support

Library Nodes

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.

CQL Explorer Test Cases and Results

Toolbar Commands

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

CQL Explorer Annotated

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)

Context Menu: Library

CQL Explorer Library right-click context menu

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

Test Cases

Command Bar

CQL Explorer Test Cases 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

Individual Test Case

Command Bar

CQL Explorer Individual Test Case 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

CQL Explorer Individual Test Case 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

Resource File

Context Menu

CQL Explorer Individual Resource 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

Cloning a Test Case

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.

Resource Clipboard

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.

CQL Explorer Test Case Context Menu - Paste enabled

Troubleshooting: CQL Explorer Not Visible

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:

  1. 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.

  2. Focus it via the Command Palette — open the Command Palette (Ctrl+Shift+P / Cmd+Shift+P), type View: Open View, then select CQL Explorer from the list.

  3. 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.

  4. Confirm a CQL project is open — the explorer only populates when the workspace contains a project with an ig.ini file or an input/cql/ directory. Open the root folder of your CQL project (File > Open Folder) and the tree should appear.

Code Navigation

The plugin provides full LSP-based code navigation for CQL files.

Go to Definition

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.

Find All References

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.

Document Symbols / Outline

Press Ctrl+Shift+O to jump to any library, define expression, function, or parameter in the current file. The outline groups symbols by category.

Call Hierarchy

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.

CQL Logs

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

Configuration Editor

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/results folder, 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

Configuration Webview

The config.jsonc file is also validated by a bundled JSON Schema, providing IntelliSense (auto-complete and validation) when editing the file directly.

CQL Project Structure

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/valueset

Within 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/tests folder, 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 be library tests organized under a library folder, and measure tests 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.

CQL Options

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.

Cloning a Repository

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":

Cloning a Repository

In the popup that displays select Clone from github:

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:

Clone CQF-CCC

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

Local Repository Folder

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

Open 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.

Trust Authors

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:

Newly Cloned Repository

Making Changes to a Library

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:

Open CQL File

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:

Modified Library

These changes are only in your local clone of the repository at this point, so they are visible only to you.

Committing Changes to a Library

To commit these changes back to the github repository, select the Source Control icon on the left side of VSCode:

View Changes

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

Stage 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:

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.

Pulling Changes from the Repository

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:

Pull Changes

Using Terminology

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 id doesn't matter, only the url element 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.

Adding Test Data

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\valueset

The 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.

Test Data File Naming

Files can be named by Id only or Type and Id.

Id Only examples

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.json

Type and Id examples

input\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.json

NOTE: Mixing Id only and Type and Id file naming conventions is not allowed. If mixed mode is used the extension won't return results.

Running Test Cases

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=false

NOTE: The plugin reads what is on disk, so make sure to save any changes in the editor before running your tests

Manual UAT

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.

Java Environment Troubleshooting

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.

Test Case Parameters (v0.9.3)

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.

Configuration File

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.

Parameter Tiers (lowest → highest priority)

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.

Example config.jsonc

{
  "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"
          }
        ]
      }
    }
  ]
}

In this example:

  • All libraries receive Measurement Period = Interval[@2024-01-01, @2024-12-31]
  • MyQualityMeasure additionally receives Product Line = HMO
  • The specific test case a1b2c3d4-... overrides Product Line with Medicaid

Parameter Entry Fields

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

Supported Types

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 an Integer, or a malformed interval literal), a warning is logged and the string value is passed through unchanged.

Result Files and Default Parameters

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

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.

Execution Result Formats (v0.9.3)

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.

Individual Format (default)

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

Flat Format

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.

Configuring the Format

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.

Library Resolution

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.

How Library Search Works

The language server searches for included libraries in priority order:

  1. Local .cql files — files on disk in your project (highest priority)
  2. npm packages — libraries distributed via FHIR IG npm packages in ~/.fhir/packages
  3. Bundled FHIRHelpers — built-in copies of FHIRHelpers at 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.

Version Matching

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"
}

Cross-Project Resolution via Namespace-Qualified Includes

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.ini file to participate in cross-project resolution. Projects without ig.ini are invisible to other projects.

Enabling Unqualified Cross-Project Search (Opt-In)

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).

Complete Configuration Example

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
}

Debugging CQL

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.

Starting a Debug Session

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).

Starting from the CQL Explorer

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.

Debugging from the CQL Editor

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.

Setting Breakpoints

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., library declarations, using statements) are shown as unverified and are not hit during evaluation.

Stepping

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)

Debug Launch Configuration

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.json requires testCaseName and testCaseUri to provide patient context — without them, expressions evaluate to null. The right-click Debug Test Case command and CQL Explorer context menu set these fields automatically and are the recommended approach for most users.

Variables Panel

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>

Step Granularity

The debugger supports two step granularities. Toggle between them via the status bar item or the CQL: Toggle Step Granularity command:

Debug Session Step Mode

CQL Level (default)

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.

AST Level

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.

AST Tree View

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.

Debug Session AST Panel

Known Limitations

  • 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.

Test Data Requirements

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.

Clone this wiki locally