An IntelliJ Platform plugin that makes IntelliJ IDEA recognize, highlight, and run
Refal-5 Lambda source files (.ref, .refi).
Syntax highlighting (lexer-based):
- directives / keywords:
$ENTRY,$EXTERN,$EXTRN,$EXTERNAL,$EASTEREGG,$ENUM,$EENUM,$SWAP,$ESWAP,$SCOPEID,$DRIVE,$INLINE,$SPEC,$INCLUDE(unknown$WORDs are flagged) - variables
s.X/t.X/e.X - single- and double-quoted strings, with escape highlighting inside them
(
\n \r \t \\ \' \" \< \> \( \)and\xHHshown as valid; anything else as an invalid escape) - numbers (macrodigits)
- line comments (
*in the first column) and block comments (/* … */) - embedded native blocks (
%% … %%) - function definitions (a name immediately before
{) vs function calls (a name immediately after<or[) - brackets
( ),{ },< >,[ ], plus;,:=
Editor integration:
- a configurable Color Settings page (Settings → Editor → Color Scheme → Refal-5 Lambda)
- brace matching for
{ } ( ) < > [ ] - comment toggling (block comments via Code → Comment with Block Comment)
Code intelligence (PSI/parser-based):
- a Structure view (the file-structure popup, Ctrl/Cmd+F12) listing the functions defined in the file
- code folding for function bodies
{ … }, block comments, and%% … %%native blocks - basic code completion (Ctrl+Space) for directive keywords, common built-in functions,
function names defined in the current file, and the
s./t./e.variables in scope - navigation & refactoring: Go to Declaration (Ctrl/Cmd+click a call jumps to its definition —
in the same file, or another project file declared via
$EXTERN), Go to Symbol (Ctrl+Alt+Shift+N / Cmd+Opt+O — jump to any function in the project by fuzzy name), Find Usages (Alt/Opt+F7), and Rename (Shift+F6) — renaming a function updates all of its calls across the project - Duplicate-function inspection: flags a function name defined more than once in a file (a redefinition the compiler would reject), highlighting every clashing definition
- TODO / FIXME markers inside Refal comments light up and appear in the TODO tool window (both dialects)
- Breadcrumbs show the enclosing function while scrolling a long file (both dialects)
- Live templates:
entry,fn,call,extern,prout,proutqexpand with Tab; File → New → Refal File starts from a runnable skeleton - Unreachable-sentence inspection: Refal tries sentences in order, and a lone
e.Xpattern matches anything — sentences after it are dead code. Flagged narrowly: parentheses, extra variables/symbols, or a where-clause make the pattern fallible and are never reported - Unused function inspection (Settings → Editor → Inspections → Refal): grays out functions
nothing calls — conservatively:
$ENTRYfunctions andGo/GOare exported/entry API and are never flagged, and cross-file usage is checked before reporting; also runs in batch via Code → Inspect Code - formatting: Reformat Code (Ctrl+Alt+L / Cmd+Opt+L) with the canonical 2-space Refal style;
continuation lines (leading
=/,) indent one extra level, spacing around=:,;and inside< >( )is normalized, and your line structure is preserved. Column-0*comments are pinned — indenting them would turn them into code, so the formatter never does - quick documentation (Ctrl/Cmd+Q, or hover): built-in functions show a short description; functions defined in the file show their header
- instant error highlighting (no compiler needed) with quick-fixes — Alt+Enter on an
unresolved call offers Create function or Add
$EXTERN: an undefined function call (not a built-in, standard-library, defined, or$EXTERN-declared name) is flagged as Unresolved function, and a variable used on the result side that the pattern never binds is flagged as Unresolved variable. This works as you type and complements the fullrlcdiagnostics below. The idea is borrowed from the Grammar-Kit-based coursework plugin bmstu-iu9/RefalFiveLambdaPlugin (re-implemented here on this plugin's own grammar, kept conservative to avoid false positives).
Run support: a "Refal-5λ" run configuration that compiles a .ref file with the Refal-5λ
compiler (rlc) and, by default, runs the resulting executable — output streams to the Run console.
The compiler is auto-detected on your PATH (and common install dirs), a green run arrow
appears next to the Go/GO entry function, and compiler diagnostics like file.ref:line:col
become clickable links in the Run console (absolute Windows paths included). If rlc is
found, the compiler also runs a fast --grammar-check (syntax-only, no artifacts, no C++
toolchain needed) in the background and shows its errors inline in the editor.
Boilerplate: a New → Refal File entry that seeds a runnable skeleton, plus live templates —
type entry, fn, or prout and press Tab to expand the program entry point, a function, or a
<Prout …> call.
Refalcon targets Refal-5λ, but .ref belongs to the whole family, so the plugin understands
its relatives too:
- Classic Refal-5 works as-is — the λ syntax is a superset for editor purposes.
$ENTRY/$EXTRN/$EXTERNALdirectives,s.X/t.X/e.Xvariables and column-0*comments are native here, and the classic built-ins (Card,Print,Mu,Br,Dg, …) are already in the completion list and the resolver's known set. - Refal-2 files are detected by content, because extensions cannot help: the historic
refal2-0.2.3 distribution uses
.refas well (verified against its sources). Aname startmodule header, bareentry/extrndirectives, or ak/name/call switch the file to a dedicated Refal-2 lexer and parser. Detection is content-aware: strings and comments are ignored (so ak/...inside a string or comment can't misfire), and a$-directive or a brace block — neither of which exists in Refal-2 — marks the file as Refal-5/5λ. When detection would be ambiguous (e.g. a Refal-2 fragment with no module header), a standalone comment* refal-2(or* refal-5/* dialect: refal-2) at the top forces the dialect deterministically. Each dialect also has its own file icon — a green "2" glyph for Refal-2, the blue glyph for Refal-5λ. What works: full highlighting (/123/macrodigits,s1s2s3variable chains,V(D)Xspecifier variables,+line continuations, both call notations<Name …>andk/name/ … .), brace matching for( ),< >andk/ … ., the structure view, comment toggling, find usages, rename, and case-insensitive go-to-definition (extrn print↔<Print …>, exactly as the real sources do) — in-file and across the project's Refal-2 files. Refal-2 functions also appear in Navigate → Symbol….
Honest limits for Refal-2: no completion, formatter, run configuration, or λ-specific
inspections — dialect support is deliberately compiler-independent. Rename and find-usages
match exact case (the word index is case-sensitive) while navigation resolves
case-insensitively. Grounding: the refal2-0.2.3 distribution's test programs and xcv.ref,
plus its manual.
- IntelliJ IDEA 2024.3 or newer (Community or Ultimate); it also loads on 2025.x+.
- JDK 21 to build (the Gradle wrapper auto-provisions Gradle 9.5.1; a JDK 21 toolchain can be auto-downloaded if you don't have one).
- To actually run programs: the Refal-5λ toolchain (
rlc/rlmake) on your machine — see https://github.com/bmstu-iu9/refal-5-lambda.
JetBrains publishes IntelliJ IDEA Community Edition as Apache-2.0 source code (not prebuilt binaries) at github.com/JetBrains/intellij-community. To get a runnable IDE you build it:
- Pick the branch for the version you want — branch names match build numbers, e.g.
243for 2024.3 (masteris the next major release). - Shallow-clone that branch (saves a lot of time):
git clone --depth 1 -b 243 https://github.com/JetBrains/intellij-community.git - Build the installers with
installers.cmd(works on Windows and Unix; a Docker build is also provided), or open the sources in an existing IntelliJ and use Build → Build Project plus the preconfigured IDEA run configuration to launch it.
(The ready-made installers on jetbrains.com are the same product through a separate, non-GitHub channel.)
Get refalcon-<version>.zip — from a GitHub Release, or build it (see below). Then in the IDE:
Settings → Plugins → ⚙ → Install Plugin from Disk…, choose the zip, and restart. Open a .ref
file such as examples/hello.ref to confirm highlighting works.
./gradlew buildPlugin # -> build/distributions/refalcon-<version>.zipThis produces the installable zip (install it as above). To try the plugin in a throwaway sandbox
IDE instead, run ./gradlew runIde.
For installs and updates without the Marketplace (and without a JetBrains account), the project publishes its own custom plugin repository via GitHub Releases + GitHub Pages.
One-time setup (maintainer): in the repo, enable Settings → Pages → Source: GitHub Actions.
Then each git tag vX.Y.Z && git push --tags runs .github/workflows/release.yml, which builds the
plugin, attaches the zip to a GitHub Release, and publishes an updatePlugins.xml to Pages at
https://<owner>.github.io/<repo>/updatePlugins.xml.
One-time setup (user): Settings → Plugins → ⚙ → Manage Plugin Repositories…, add that
updatePlugins.xml URL, then install "Refalcon" from the Marketplace tab. After that the IDE
offers updates automatically whenever you publish a new tag.
Before publishing, set the
<vendor>(and optionally itsurl) insrc/main/resources/META-INF/plugin.xml.
The plugin itself needs nothing extra — highlighting, completion, navigation and the instant
error checks all work out of the box. Installing rlc additionally enables the inline compiler
diagnostics and the Run button.
Windows (easiest): download setup-refal-5-lambda-<version>.exe from the
latest release and run it — it
unpacks the toolchain into your user profile and adds it to your PATH. No C++ compiler is
needed on Windows: by default programs link against prebuilt runtime prefixes (slim/rich)
shipped with the installer (verified from the toolchain's own rlc.bat, which passes
--exesuffix=.exe and an empty C++ command in this mode). Then restart the IDE: a running
IDE keeps the PATH it started with and will not see a freshly installed rlc.
Linux / macOS:
macOS is officially supported (the toolchain's own CI builds on it); building needs a C++
compiler — on macOS install the Xcode Command Line Tools first (xcode-select --install),
on Linux g++ from your package manager.
git clone https://github.com/bmstu-iu9/refal-5-lambda
cd refal-5-lambda && ./bootstrap.sh # builds the toolchain with your C++ compiler
# then add its bin/ to PATH (the official docs also set RL_MODULE_PATH to the same dir)If you clone into your home directory, the plugin auto-detects ~/refal-5-lambda/bin (and
~/simple-refal-distrib/bin) even before you update PATH.
Open a new terminal and type rlc to verify (it prints usage). The plugin auto-detects
rlc on the PATH and in standard install locations; you can always override it per run
configuration via the "Refal compiler (rlc)" field.
Troubleshooting — 'rlc' is not recognized as an internal or external command: on Windows the
toolchain ships rlc.bat (no rlc.exe), installed to %APPDATA%\Refal-5-lambda\bin —
verified by inspecting the official installer. The plugin auto-detects it there even if the
IDE's PATH is stale. If the shell still can't find it: restart the IDE after installing —
and restart JetBrains Toolbox too if you launch through it (IDEs inherit Toolbox's old
PATH) — or put the full path (e.g. %APPDATA%\Refal-5-lambda\bin\rlc.bat) into the run
configuration's compiler field. On a genuine "command not found" exit, the Run console now
appends these exact instructions.
The Refal-5λ compiler turns a .ref file into a native OS executable (no separate interpreter).
This plugin's run configuration drives that:
- Open a
.reffile (or right-click it / press Ctrl+Shift+F10) and choose Run — a "Refal-5λ" configuration is created with the file prefilled. - In the configuration you can set:
- Refal compiler (rlc) — path to the compiler. Leave it empty to auto-detect
(
PATH+ standard install locations); set it only to pin a specific binary. - Build with rlmake — check this for multi-file programs:
rlmakefollows*$FROM Unitcomments, compiles every dependent unit automatically, and links a single executable named after the main file (verified against the real toolchain; on Windowsrlmake.batships alongsiderlc.batand is auto-detected the same way). - Compiler options — extra flags passed to the compiler.
- Refal file — the
.refsource. - Run the compiled executable after a successful compile — when on, the produced binary is executed right after compilation.
- Output executable — the produced binary (defaults to the source base name, run as
./nameon macOS/Linux orname.exeon Windows). - Program arguments — passed to your compiled program.
- Working directory — defaults to the source file's folder.
- Refal compiler (rlc) — path to the compiler. Leave it empty to auto-detect
(
When "run after compile" is on, the two steps are chained through your OS shell
(rlc … && ./program …). Turn it off to only compile. If your build is more involved, point the
compiler field at rlmake or at your own script.
Install the plugin (above), then open the bundled examples: examples/hello.ref (a small, runnable
program) and examples/showcase.ref (which deliberately contains the tricky cases). Use
hello.ref unless a step says otherwise.
- Highlighting —
$ENTRY/$EXTERN(keyword),s.N/e.Name(variables), strings, numbers,*and/* */comments, and function names vs. calls are all colored. - Escapes — in a string,
\n\t\x41show the valid-escape color; openshowcase.refto see\q/\xin the invalid color and$FOOBARflagged as an unknown directive. - Brace matching — put the caret next to
{,(,<, or[; its partner is highlighted. - Folding — click the gutter arrow by a
{to fold a function body; block comments and the%% %%block (inshowcase.ref) fold too. - Structure view — press Ctrl/Cmd+F12: you should see
Go,Fact,Greet. - Completion — type
<then Ctrl+Space: the list offers$-directives, common built-ins (Prout,Mul,Sub,Symb, …), and the file's own function names. - Run — right-click
hello.ref→ Run 'hello'. Withrlcon yourPATHit compiles and runs, printing the greeting andFactorial of 5 = 120in the Run console. (A green arrow also appears in the gutter next toGo.) - Navigation — Ctrl/Cmd+click the
<Fact …>call to jump to theFactdefinition; press Shift+F6 onFactto rename it and watch the call update; Alt/Opt+F7 lists its usages. - Docs & templates — press Ctrl/Cmd+Q on
<Prout …>to see its description; in a new file, typeentrythen Tab to expand the program skeleton (and try New → Refal File on a folder).
src/main/java/com/github/refal5lambda/
RefalTokenKind.java / RefalScanner.java pure, IntelliJ-free, unit-testable scanner
RefalLexer.java LexerBase adapter (carries string state)
RefalTokenTypes.java IElementType per kind + kind→type map
RefalLanguage.java / RefalFileType.java language + .ref/.refi file type
RefalSyntaxHighlighter*.java token → color mapping + factory
RefalColorSettingsPage.java color settings UI
RefalBraceMatcher.java / RefalCommenter.java
RefalStructureViewFactory/Model/Element.java structure view
RefalFoldingBuilder.java code folding
RefalCompletionContributor.java / RefalBuiltins.java completion
psi/ parser, ParserDefinition, PSI nodes (RefalFile, RefalFunction, RefalBlock, RefalDirective)
RefalIcons.java
run/ run configuration (type, factory, settings, producer)
src/main/resources/META-INF/plugin.xml
src/main/resources/icons/refal.svg
src/test/java/.../RefalScannerTest.java pure scanner unit tests (JUnit 4)
src/test/java/.../psi/RefalParsingTest.java PSI parsing test (ParsingTestCase)
src/test/testData/Hello.ref, Hello.txt parsing-test input + expected PSI tree
examples/hello.ref, examples/showcase.ref sample programs (see "Verify the plugin works")
.github/workflows/build.yml, release.yml CI: build/test/verify, and tagged releases
Scalability (measured against the largest real Refal-5λ codebase — the
compiler's own repository, 901 .ref/.refi
files, ~1.3 MB): the hand-written lexer tokenizes that entire corpus in ~17 ms on commodity
hardware, and highlighting is per-file and incremental anyway. Find Usages and Rename are backed
by the IDE word index (only candidate files are parsed). Cross-file Go to Declaration and
Go to Symbol share one cached project-wide symbol map, rebuilt lazily after a PSI change rather
than rescanning the project per query. The plugin has no stub index — on a cold start or right
after an edit, the first cross-file navigation rebuilds the map by walking project Refal files
once; instant at these scales, but a stub-based index would be the next step for codebases of
many thousands of files. The inline compiler check (rlc --grammar-check) is per-file and took
0.25 s on the project's largest file (107 KB); it does not process $INCLUDE, so multi-file
projects get no spurious include errors from it (verified against the real compiler).
- Parsing is intentionally lenient: it never reports syntax errors, so valid code is never
shown as red. Function bodies are modelled as sentences (
pattern = result ;) with parenthesized groups, nested activations (<…>/[…]), and nested blocks; conditions and where-clauses are tolerated rather than fully structured. - The lexer is mostly stateless; only string literals carry state (so escapes can be split out).
- Inline
rlcdiagnostics: the editor text is compiled withrlcin an isolated temp directory (so the project tree is untouched and unsaved edits are checked), and diagnostics — emitted asfile:line:col: ERROR: message, a format verified against the currentrlc— are shown inline. Because the check is isolated, files that$INCLUDEsiblings may show spurious errors. Ifrlcisn't found, nothing is shown. The invocation and parsing live inRefalExternalAnnotatorandRefalDiagnosticParser. A newline always resets the state, so incremental re-highlighting stays cheap and correct. - Function definition vs call is a lexer-level heuristic (preceding
</[⇒ call, following{⇒ definition), not a full parser; unusual layouts may be mis-tagged but still get a sensible color. - The
%% … %%block is one highlighted region; it does not run a real C++ highlighter inside (IDEA Community has no C++ support to delegate to). - The line-comment prefix
*is only a comment in column 0. IntelliJ places line comments at column 0 by default, so Comment with Line Comment matches Refal's rule. - The run configuration's compile-and-run uses simple shell chaining with basic path quoting; for exotic paths/arguments, run compile and execution as separate configurations.
./gradlew testruns the unit tests: fast, IntelliJ-free tests for the scanner (RefalScannerTest) plus aParsingTestCase(RefalParsingTest) that checks the produced PSI tree againstsrc/test/testData/Hello.txt../gradlew verifyPluginruns the JetBrains Plugin Verifier to check binary compatibility and plugin-descriptor validity. It targets the supported floor (2024.3,sinceBuild) plus the currently-recommended releases (recommended()— the latest patches of recent majors): since the plugin leavesuntilBuildopen and so claims forward compatibility, this is what actually exercises that claim against newer IDEs. The recommended set is downloaded at verification time, so it needs network and runs in CI rather than offline.- GitHub Actions (
.github/workflows/build.yml) runscheck buildPluginand the verifier on every push and pull request and uploads the built zip;release.ymlbuilds the plugin and attaches the zip to a GitHub Release when you push avX.Y.Ztag.
Edit build.gradle.kts: intellijIdeaCommunity("2024.3") (or intellijIdeaUltimate("…")) and the
sinceBuild / untilBuild under pluginConfiguration { ideaVersion { … } }.
Token rules were informed by the Refal-5λ language and the community VS Code extension GDVFox/vscode-refal-5-lambda; the compiler is bmstu-iu9/refal-5-lambda. A separate IDEA plugin also exists — bmstu-iu9/RefalFiveLambdaPlugin, a 2020 student coursework (BMSTU, dept. iu9) by Daria Tereshkina and Alexander Konovalov that builds its lexer and parser with JFlex and Grammar-Kit. This project is an independent implementation, and its instant, compiler-free error highlighting (Unresolved function / Unresolved variable) is inspired by that coursework — re-implemented here on this plugin's own grammar rather than copied. All Java here was written from scratch for the IntelliJ Platform. Use and modify it freely; if you publish it, add your own license and plugin id.