Language Server (LSP)
The OPC UA Modeler ships a fully standard Language Server Protocol server. VS Code users get it automatically via the VS Code Extension. For all other editors — Neovim, Zed, Emacs, or any LSP-compliant tool — this page covers manual setup.
Starting the server
# stdio mode — for editors that spawn the server as a child process
opcua-modeler lsp --stdio
# socket mode — for multi-client / WebSocket setups (e.g. browser-based editors)
opcua-modeler lsp --socket 7000
Neovim (nvim-lspconfig)
Add the following to your Neovim configuration (Lua):
local lspconfig = require("lspconfig")
local configs = require("lspconfig.configs")
-- Register the custom server if not already known
if not configs.opcua_modeler then
configs.opcua_modeler = {
default_config = {
cmd = { "opcua-modeler", "lsp", "--stdio" },
filetypes = {
"yaml", -- general YAML; restrict via root_dir if needed
},
root_dir = lspconfig.util.root_pattern("*.model.yaml", "*.nodeset.yaml", ".git"),
single_file_support = true,
settings = {},
},
}
end
lspconfig.opcua_modeler.setup({})
Tip: If you only want the OPC UA LSP to activate on
.model.yaml/.nodeset.yamlfiles, add a customfiletypesdetection or usevim.filetype.addto map those extensions to a dedicated filetype (e.g.opcua_yaml) before callingsetup.
Zed
Add the following to your Zed settings.json (or ~/.config/zed/settings.json):
{
"lsp": {
"opcua-modeler": {
"binary": {
"path": "opcua-modeler",
"arguments": ["lsp", "--stdio"]
}
}
},
"languages": {
"YAML": {
"language_servers": ["opcua-modeler", "..."]
}
}
}
Emacs
With eglot (built-in from Emacs 29+)
(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs
'(yaml-mode . ("opcua-modeler" "lsp" "--stdio"))))
;; Auto-start eglot when opening model files
(add-hook 'yaml-mode-hook #'eglot-ensure)
With lsp-mode
(with-eval-after-load 'lsp-mode
(lsp-register-client
(make-lsp-client
:new-connection (lsp-stdio-connection
'("opcua-modeler" "lsp" "--stdio"))
:activation-fn (lsp-activate-on "yaml")
:server-id 'opcua-modeler)))
Socket / multi-client mode
For setups where multiple editors or a browser-based editor connect to a single shared server,
use the --socket flag:
# Start a persistent LSP server on port 7000
opcua-modeler lsp --socket 7000
Clients connect via WebSocket or TCP to localhost:7000. The server maintains isolated
per-connection state (document buffers, debounce timers, capability flags) while sharing
the expensive OPC UA address space cache across all connections.
Diagnostics reference
The server emits diagnostics using structured codes in the form DSL-E-*, DSL-W-*, and DSL-I-*.
| Prefix | Severity | Description |
|---|---|---|
DSL-E-* | Error | Must be fixed — invalid or illegal model construct |
DSL-W-* | Warning | Potentially incorrect — shown by default, suppressed with --quiet |
DSL-I-* | Info | Style / conformance hints — hidden by default, shown with --style |
Semantic checks performed
The following semantic diagnostics are produced by Layer 2:
| Check | Example message |
|---|---|
Invalid typeDefinition reference | Unknown type 'MySensor' — not found in address space |
Invalid subtypeOf reference | 'MyBase' is not an ObjectType |
Broken organizedBy / componentOf browse path | Path 'Root/Objects/Tank' does not exist in node tree |
Invalid promotedToMandatory entry | 'Status' is not an optional member of 'SensorType' |
Duplicate initializers: variable: entries | Duplicate initializer path 'Parameters/MaxValue' |
Duplicate $anchor IDs | Anchor 'pump1' is already defined on line 42 |
| Self-referencing placement | Node cannot reference itself or its own subtree as placement parent |
| YAML syntax error | YAML parse error at line 12: unexpected ':' |
Suppressing specific codes
Use the --silence flag on the CLI, or configure suppressions in your project:
# Suppress a specific warning code
opcua-modeler generate --silence DSL-W-001
# Suppress all warnings matching a prefix
opcua-modeler generate --silence DSL-W
Address space cache
The server loads the OPC UA base address space (and all imported companion specs) once and caches it across all open connections. This means:
- The first open of a model file may take a second while the address space loads
- Subsequent completions and diagnostics are near-instant
The cache is invalidated when the list of namespaces: imports in your file changes.