A Rider / IntelliJ Platform plugin that provides language support for the BHL (Behaviour Highlevel Language) scripting language by acting as a client for the BHL language server.
It is the Rider counterpart of the
BHL VSCode extension: it launches
bhl lsp over stdio and connects it to .bhl files using JetBrains' built-in
Platform LSP API.
Syntax highlighting, diagnostics, completion, and navigation are all provided by the
language server via LSP semantic tokens.
- Rider 2024.3 (build 243) or newer. The plugin uses the built-in LSP API, which is available in Rider and other paid JetBrains IDEs.
- A working BHL executable/script (
bhl) — either on yourPATHor at a path you configure in settings. See the BHL repository for how to obtain and build it. On startup the plugin invokes it asbhl lsp. - A
bhl.projfile in the opened project. The language server is started with its working directory set to the folder containingbhl.proj(matching the VSCode extension's behaviour).
Build the plugin and install the resulting zip:
./gradlew buildPluginThe distributable is written to build/distributions/BHL-<version>.zip. Install it in
Rider via Settings ▸ Plugins ▸ ⚙ ▸ Install Plugin from Disk….
- Open a project/folder that contains a
bhl.projand one or more.bhlfiles. - Open any
.bhlfile. The plugin locatesbhl.proj, then starts the BHL language server. Its status is shown in the LSP widget in the status bar. - If the project contains more than one
bhl.proj, you are prompted to pick which one to use; the choice is remembered for that session. To pin a project explicitly, use Tools ▸ Select BHL Project File… — it lists thebhl.projfiles it discovered, or, if none are indexed (e.g. a C# solution with BHL scripts in a separate folder), opens a file browser so you can point at abhl.proj(or its directory). Either way it stores the chosen directory as the BHL project directory override (top priority below) and restarts the server against it. Clear that setting to return to automatic discovery.
The working directory for the server is resolved in this order:
- BHL project directory setting — if set, it always wins (useful when the BHL scripts live outside the open solution, e.g. a C# solution with BHL scripts in a separate folder).
- Walk up from the opened
.bhlfile to the nearest ancestor directory containing abhl.proj. This works even if the file is not part of the open project's indexed content. - Project-wide search of the open project's indexed files for
bhl.proj(0 → server doesn't start; 1 → used; many → remembered choice or a picker).
You can attach additional directories to an existing Rider window (File ▸ Open…,
select the folder, choose Attach) — useful when BHL scripts live in a separate folder from
the C# solution you have open, or when you work with more than one independent BHL
project (each with its own bhl.proj) at the same time.
Opening a .bhl file under an attached directory starts a separate BHL LSP server
process for that directory (walk-up resolves each file to its own nearest bhl.proj, and
the server descriptor is keyed by that directory, so two different directories always get
two independent servers rather than sharing one). Each server is scoped to only the files
under its own directory, so opening files from two attached BHL directories at once runs
two isolated language servers without cross-talk.
The BHL project directory override (set via Select BHL Project File…) is a single value, so it's meant for pinning one directory when discovery can't find it automatically — it doesn't apply to a specific attached folder. With multiple directories attached, leave it unset and let walk-up resolve each file to its own directory's server.
Configure the plugin under Settings ▸ Languages & Frameworks ▸ BHL:
| Setting | Default | Description |
|---|---|---|
| Executable path | (empty) | Path to the BHL executable/script. When empty, bhl is resolved on your PATH. |
| Log file | (empty) | Optional path passed to the server as --log-file=PATH. |
| Force rebuild | false |
When enabled, launches the server with BHL_REBUILD=1 and BHL_SILENT=1, which makes the launcher rebuild before starting (for LSP development). Off by default — the rebuild can exceed the IDE's init timeout. |
These map 1:1 to the VSCode extension's bhl.executablePath, bhl.logFile, and
bhl.forceRebuild settings.
The project-directory override used as discovery priority #1 is set via Tools ▸ Select BHL Project File… (it is not an editable settings field).
On opening a .bhl file the plugin builds this command line:
<executablePath or "bhl"> lsp [--log-file=<logFile>]
- Working directory: the directory containing the selected
bhl.proj. - Environment: inherits the IDE environment, plus
BHL_REBUILD=1/BHL_SILENT=1when Force rebuild is enabled. - Transport: stdio.
- On Windows, a
.batexecutable is run throughcmd.exe /c(Java cannot exec.batfiles directly), mirroring the VSCode client'sshell: truehandling.
Highlighting is layered:
- A minimal built-in lexer colors comments (
//,/* */) and strings — the BHL server sends comments to ANTLR's hidden channel, so they are never reported as semantic tokens. - Everything else comes from LSP semantic tokens. Types/classes, functions, variables, and parameters use BHL-specific color keys (with colors shipped for the Default and Darcula scheme families); keywords, strings, numbers, properties, and operators use the IDE's standard colors.
Signature help (the parameter popup while typing call arguments) is bridged by the
plugin itself: Rider's built-in LSP client does not implement textDocument/signatureHelp,
so the plugin queries the server directly. It auto-pops on ( and , and is available on
demand via Ctrl+P (View ▸ Parameter Info).
.bhl files are plain-text PSI (see above), so the IDE's spell checker treats their
contents as prose and would otherwise flag BHL keywords (coro, func, paral, …) and
primitive type names as typos. The plugin ships a bundled dictionary
(resources/dictionaries/bhl.dic) listing them so they're never underlined.
The BHL LSP tool window (bottom) shows a console with both client- and server-side activity, and opens automatically on the first message:
- Client side: file opened,
bhl.projdiscovery result (or why the server won't start), the launch command line + working directory, server state transitions (Initializing → Running → Shutdown…), and documents opened for the server (didOpen). - Server side: the process's stderr and exit code (where startup crashes show up),
plus
window/logMessage/$/logTraceoutput,window/showMessagenotifications, and diagnostics counts. - Rider's platform LSP logs (
com.intellij.platform.lsp.*) are mirrored in too, prefixed[lsp]— server start/stop, init timeouts, the process kill, etc. INFO/WARN/errors show by default; enable DEBUG forcom.intellij.platform.lspin Help ▸ Diagnostic Tools ▸ Debug Log Settings to see the finer records here as well. (This reflects all LSP servers, which in Rider is normally just BHL.)
To see the raw JSON-RPC requests/responses (completion, hover, semantic tokens, …),
enable Trace LSP traffic in the settings — every frame is mirrored into the console,
prefixed --> (client → server) and <-- (server → client), truncated at 10 000 chars.
The toggle is read per frame, so it applies immediately without a server restart. With
tracing off, frames are still written at DEBUG level to the plugin's log category
(#com.bitdotgames.bhl.rider.lsp.BhlLspServerDescriptorWithTracing in Debug Log Settings).
Tracing relies on an internal, unstable platform class
(com.intellij.platform.lsp.impl.connector.LspCommunicationLoggerProvider) that isn't part of
the public plugin SDK and isn't guaranteed to exist on every IDE version. The plugin checks
for it at runtime and simply runs without tracing if it's absent — this never affects the
rest of the plugin (server launch, highlighting, completion, etc.), which don't depend on it.
Alternatively, set a Log file in the settings (passed to the server as --log-file) to
get the server's own view of the traffic. Everything in the console is also mirrored to
idea.log (Help ▸ Show Log in Finder), except [lsp] platform records and traced
frames.
# Run a sandbox Rider with the plugin loaded
./gradlew runIde
# Build the installable plugin zip
./gradlew buildPlugin
# Verify plugin compatibility
./gradlew verifyPluginRequirements for building: JDK 21 and the bundled Gradle wrapper. The IDE to build
against is configured via platformType/platformVersion in gradle.properties.
src/main/
├── kotlin/com/bitdotgames/bhl/rider/
│ ├── BhlLanguage.kt / BhlFileType.kt / BhlIcons.kt # language + .bhl file type
│ ├── lsp/
│ │ ├── BhlLspServerSupportProvider.kt # LSP wiring + command line
│ │ └── BhlProjectFileResolver.kt # bhl.proj discovery
│ ├── settings/
│ │ ├── BhlSettings.kt # persisted settings
│ │ └── BhlSettingsConfigurable.kt # settings UI
│ └── actions/
│ └── SelectBhlProjectFileAction.kt # Tools ▸ Select BHL Project File…
└── resources/META-INF/plugin.xml