Skip to content

User Guide

raleigh-g-thompson edited this page Apr 15, 2026 · 26 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

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 CQL-LS status and version, if available.

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

Execution Result

Executing the CQL 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 plugin also supports vocabulary and data access as part of execution, see the Adding Vocabulary and Adding Test Data topics below for more information on these features.

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. To see the ELM output, in either XML or JSON, for a given CQL file, right-click anywhere in the CQL editor and select View ELM - XML or View ELM - JSON:

Viewing ELM

The plugin will open a new editor with the ELM output using the selected format, example is XML:

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

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.

Future Features

Test Case Parameters (coming v0.9.4)

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

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

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 (coming v0.9.4)

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 (coming v0.9.6)

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

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

The debugger is configured via a .vscode/launch.json file in the workspace. When no launch.json exists, VS Code generates a default one the first time you press F5 or click the debug button.

The required configuration looks like this:

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "cql",
      "request": "launch",
      "name": "Evaluate CQL",
      "libraryUri": "${fileDirname}",
      "cqlFileUri": "${file}",
      "libraryName": "${fileBasenameNoExtension}"
    }
  ]
}
Field Required Description
libraryUri Yes URI to the directory containing the .cql files (use ${fileDirname})
libraryName Yes CQL library name — must match the library declaration in the file (use ${fileBasenameNoExtension})
cqlFileUri No URI to the specific .cql file being debugged (use ${file}; derived from libraryUri/libraryName if omitted)
fhirVersion No FHIR version to use (default: R4)
terminologyUri No URI to the directory containing FHIR ValueSet resources (defaults to input/vocabulary/valueset)
modelUri No URI to the directory containing patient test data (defaults to input/tests)
context No Evaluation context (default: Patient)
contextValue No Patient ID to use as the evaluation context

Migrating an existing launch.json: Configurations created before version 0.7.13 may have "libraryUri": "${file}" (pointing to the file rather than its directory). Update this to "libraryUri": "${fileDirname}" and add "cqlFileUri": "${file}" to ensure the debugger can correctly resolve library dependencies.

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>

Known Limitations

  • Step controls are coarse. Continue (F5), Step Over (F10), Step Into (F11), and Step Out (Shift+F11) all advance to the next breakpoint. CQL's functional evaluation model does not support stepping into individual sub-expressions within a define body.
  • Results are pre-computed. The engine runs all expressions to completion before the replay begins, so the Variables panel always shows the final result of an expression. This matches what you would see from Execute CQL and is correct for the vast majority of cases.
  • 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.
  • Single-library scope. Only expressions defined in the library being debugged appear as breakpointable. Expressions in included libraries are evaluated but cannot be paused on.
  • No conditional breakpoints. All breakpoints pause unconditionally regardless of any condition entered in VS Code's breakpoint editor.

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