vscode xHarbourLanguage support for Harbour and xHarbour (
Contents
RequirementsYou need a working
A wrapper only needs to compile whatever file it's given, in that file's own
directory — several features below (validation, Language features
Diagnostics / validationTwo independent mechanisms feed the Problems panel:
DocBlock commentsA doc-comment above a function can optionally use JSDoc/PHPDoc-style
This is purely additive, detected automatically — a comment is only
treated as a DocBlock if it contains at least one
See Aliasing features (fork-specific)These settings exist for codebases that lean on preprocessor macros
(
|
| Mode | Foo(x) |
Foo:Exec(x) |
|---|---|---|
either (default) |
allowed | allowed |
suffixOnly |
flagged as Error | allowed |
bareOnly |
allowed | flagged as Error |
This never applies to standard RTL functions (Len:Exec() wouldn't make
sense to require) — only to functions your workspace actually defines.
Because Foo:Exec(...) isn't real xHarbour syntax for calling a plain
function — the compiler sees a bare identifier before : and, unable to
tell whether it's Foo the function or an undeclared memvar, emits
Warning W0001 Ambiguous reference — the compiler-backed validator
specifically suppresses that one warning for identifiers immediately
followed by :<a configured suffix>(, while leaving every other ambiguous
reference on the same or other lines untouched.
harbour.aliases.allowBareCalls
xHarbour actually allows a third calling style, inherited from Clipper: no
parentheses at all, used as a value -- x := Foo or IF Foo instead of
x := Foo() / IF Foo(). The compiler can't tell that apart from an
undeclared/misspelled variable either, so it emits the same
Ambiguous reference warning for it. If your codebase intentionally relies
on that style:
"harbour.aliases.allowBareCalls": true // default false
Unlike the :<suffix>( suppression above, the compiler gives no syntactic
hint here — Ambiguous reference looks identical whether the identifier is
a real function or an actual undeclared/misspelled variable. So when this
is on, the validator asks the language server (the same index
harbour.checkUndefinedFunctions uses: workspace functions, standard RTL,
.hbx-discovered and harbour.aliases.customFunctions-declared ones) for
each flagged name, and only drops the warning for the ones that really are
known functions — a genuine undeclared variable used the same bare way
still gets flagged. Off by default anyway, since it's still one more layer
of "trust the extension's index" than the default, warning-preserving
behavior.
harbour.aliases.commandRules
Declares #command/#xcommand-style macro rules once, in settings, instead
of pasting a #command line at the top of every .prg:
"harbour.aliases.commandRules": [
{ "match": "DEFAULT <v> := <x>", "replace": "Default( <v>, <x> )" }
]
is equivalent to having this at the top of every file compiled in that workspace:
#command DEFAULT <v> := <x> => Default( <v>, <x> )
Each rule is written to a generated .ch file next to the source file being
compiled and passed to the compiler with -u+<file> on every
validate/build — so it works even if harbour.compilerExecutable is a
container wrapper that only mounts that one directory. The first word of
match (DEFAULT above) is also registered automatically as a
customKeyword, so you don't need a separate entry for it.
harbour.aliases.customFunctions
For a function that only exists in your own compiler build — a native
function with no .prg source and no .hbx export for the extension to
discover it from — declare it directly, and it gets
full hover, completion and signature-help docs, and stops
harbour.checkUndefinedFunctions from flagging calls to it:
"harbour.aliases.customFunctions": [
{
"name": "Mens",
"params": ["cMsg"],
"documentation": "Shows <cMsg> as an on-screen message.",
"returns": "NIL"
}
]
params and returns are just plain descriptive text shown in hover, not
type-checked — params becomes <cMsg> in the shown signature
(Mens(<cMsg>) --> NIL). Both are optional; a bare { "name": "Mens" }
still stops the "possibly undefined function" hint, it just won't have any
parameter/return info to show on hover.
Unlike commandRules, this doesn't change how the function is called or
generate anything passed to the compiler — it's purely a hint for the
editor, for a function that's already valid to call as-is (Mens("hi")),
just not visible from any source the extension can scan.
Commands
| Command | Title | What it does |
|---|---|---|
harbour.getDbgCode |
Harbour: Get debugger code | Opens the source of the in-process debugger library (dbg_lib.prg) as a new untitled document — save it into your project (or, better, compile it into a library you link against) to enable debugging. |
harbour.setupCodeFormat |
Harbour: setup code style | Opens a webview to configure the document formatter settings interactively, with a live preview. |
Settings reference
Compiler / validation
| Setting | Default | Description |
|---|---|---|
harbour.compilerExecutable |
"harbour" |
Path (or wrapper script) used for validation and build tasks. |
harbour.validating |
true |
Run the compiler-backed validator on open/save. |
harbour.warningLevel |
1 (0–3) |
Compiler -w level used for validation. |
harbour.extraIncludePaths |
[] |
Extra -I paths; supports ${workspaceFolder}. |
harbour.extraOptions |
"" |
Free-form extra compiler flags. |
harbour.workspaceDepth |
2 |
Subfolder depth the language server scans for .prg/.ch/.c/.h files to index for cross-file features (hover, go to definition, checkUndefinedFunctions). 0 = only files you have open. |
harbour.checkUndefinedFunctions |
false |
See Diagnostics. |
harbour.decorator |
true |
Decorate matching if/endif, for/next, while/endwhile, etc. |
Aliases — see above.
| Setting | Default |
|---|---|
harbour.aliases.customKeywords |
[] |
harbour.aliases.callSuffixes |
[] |
harbour.aliases.callSuffixMode |
"either" |
harbour.aliases.allowBareCalls |
false |
harbour.aliases.commandRules |
[] |
harbour.aliases.customFunctions |
[] |
Icons — see File icons.
| Setting | Default |
|---|---|
harbour.iconTheme.baseIconTheme |
"" |
Formatter — set via harbour.setupCodeFormat, or directly:
| Setting | Default |
|---|---|
harbour.formatter.indent.funcBody |
true |
harbour.formatter.indent.variables |
true |
harbour.formatter.indent.logical |
true |
harbour.formatter.indent.cycle |
true |
harbour.formatter.indent.switch |
true |
harbour.formatter.indent.case |
true |
harbour.formatter.replace.not |
"use !" ("ignore" / "use .not." / "use !") |
harbour.formatter.replace.asterisk |
"use //" ("ignore" / "use //" / "use *" / "use &&") |
harbour.formatter.replace.amp |
"use //" ("ignore" / "use //" / "use &&") |
Code formatting
Run Harbour: setup code style to open a live-preview editor for the
harbour.formatter.* settings above — check the boxes/pick the options you
want and the sample on the right updates immediately; changes are written
straight to your settings.
File icons
The extension contributes a VS Code File Icon Theme named "xHarbour
Icons" that shows a custom icon for .prg/.ch/.hbx/.hb files in the
Explorer. Select it with Ctrl+K Ctrl+T (Cmd+K Cmd+T on macOS) → File
Icon Theme, or set it directly:
"workbench.iconTheme": "xharbour-icons"
A File Icon Theme is a VS Code-wide setting, not per-workspace, and only one can be active at a time — for every file type, not just xHarbour's. On its own, "xHarbour Icons" only knows about xHarbour files, so everything else falls back to a plain generic file/folder icon.
To keep the icons from a theme you already use (Catppuccin, Material Icon
Theme, Seti, etc.) for every other file, and only add the xHarbour icon on
top of it, point harbour.iconTheme.baseIconTheme at that theme's id:
"workbench.iconTheme": "xharbour-icons",
"harbour.iconTheme.baseIconTheme": "catppuccin-mocha"
The id isn't the label shown in the theme picker — it's the id the base
theme's own extension declares under contributes.iconThemes in its
package.json. This works by regenerating a local copy of "xHarbour Icons"
from that base theme's own icon definitions each time the extension
activates or the setting changes; nothing from the base theme is bundled or
redistributed with this extension, it's only read from your own
already-installed copy of it. Leave the setting unset (or pointed at a
theme that isn't installed) to fall back to the plain generic icons.
Reload the window (Ctrl+Shift+P → Reload Window) after changing
harbour.iconTheme.baseIconTheme — icon theme content isn't re-read live.
Debugging
The extension ships a harbour-dbg debug adapter that talks to a small
in-process debugger library over a socket (default port 6110).
- Run Harbour: Get debugger code, save the file into your project (or
compile it into a library and link it in), and compile your program
with debug info (
-b). - Add a launch configuration — the command palette's "Add configuration"
offers ready-made snippets for launch, attach-by-path, and
attach-by-picking-a-running-process. Example
launch.json:
{
"type": "harbour-dbg",
"request": "launch",
"name": "Launch current program",
"program": "${workspaceFolder}/myapp",
"workingDir": "${workspaceFolder}/",
"sourcePaths": ["${workspaceFolder}"],
"stopOnEntry": true
}
request can be "launch" or "attach" (by program path or by
process id — "${command:pickProcess}" opens a picker of running
matching processes). See the protocol the debugger and the extension speak
to each other in debugger.md if you need to build a
compatible client.
Build tasks
Two task types are contributed:
Harbour— runsharbour.compilerExecutabledirectly on one file.output:"portable"(.hrb) or"C code"(c-type:compact/normal/verbose/real C Code).HBMK2— runshbmk2(found next toharbour.compilerExecutable) withplatform/compiler/extraArgs/debugSymbols, and an optionalsetupBatch(or per-OSwindows/linux/osxoverrides) to source environment variables before building — handy for MSVC'svcvars*.bator similar toolchain setup scripts.
Example tasks.json entry:
{
"label": "build",
"type": "HBMK2",
"input": "${file}",
"extraArgs": ["-gtcgi", "-w3"],
"group": { "kind": "build", "isDefault": true }
}
Snippets
A small set of statement snippets (for, for each, do while, etc.) is
contributed for the harbour language — see
harbour.code-snippets.
Building the extension from source
npm install # single node_modules for both the
# extension host and the language
# server (src/client, src/server)
npx webpack --mode production # builds dist/extension.js,
# dist/debugger.js, dist/hb_server.js
npx vsce package --no-dependencies # -> vscode-xharbour-lang.vsix
code --install-extension vscode-xharbour-lang.vsix
npm run prelanch (webpack --mode development) is the pre-launch task
for F5 (Run Extension) in this repo's own .vscode/launch.json.
License
This project is GPL-3.0-or-later (see LICENSE) — the
original server package it's built on was GPL-licensed, and a combined
work built on GPL code stays GPL regardless of how much is added or
rewritten on top of it. The original client package was MIT-licensed;
that text is preserved unmodified in
LICENSE-MIT-client-original.txt for
attribution. Full details in NOTICE.md.