ned_ls
A simple language server for OMNeT++ simulation framework, supporting ned and ini files.
About
ned_ls is an independent project and is not affiliated with OMNeT++ or OpenSim Ltd.
I started it in November 2025 to make my own OMNeT++ simulation work more pleasant,
and it grew feature by feature from there. This GitHub repository is the upstream
home of the project.
Status: I use it daily with OMNeT++ and INET. The core
features (goto definition, diagnostics, completion, rename) are stable for my
workflows, but expect rough edges in less common NED/INI constructs.
Short Notes:
- This is my first larger Rust project and my first language server. The original
goal was only goto definition for module names, and parts of the architecture
still reflect that.
- Moving to Salsa made the server incremental
and allowed most of the later features. Some older building blocks (like the
Tree-sitter-based parsing) would be worth replacing at some point.
- I wrote the vast majority of this code myself and carefully reviewed the rest. Because I was short on time, I used AI to generate tests that mostly just cover the parsing and utility functions. I am planning a manual rewrite soon to improve this setup and increase coverage.
Features
NED
- Go to definition for types, submodules, parameters and gates
- Find reference and rename (types, with checks against invalid or colliding names)
- Hover shows the documentation comment of the referenced type
- Completion for keywords, types, submodules, parameters, gates, connections, and context-aware
package / import statements (the context is not always perfectly detected because I am not covering all edge cases yet)
- Formatting → very basic
- Diagnostics
- syntax errors
- package declarations that do not match the folder structure
- unresolved types, imports,
extends and like targets
- inheritance rules (cyclic
extends)
- interface conformance (missing gates/parameters, direction or vector mismatches → some missing edge cases)
- connections (unknown gate, wrong direction, missing vector index, basic $i/$o misuse detection)
- duplicate submodules and parameter used before they are declared
INI
- Completion for configuration options, parameter keys of the selected network, and extends
- Go to definition from INI keys and network names to the NED source → very helpful
- Diagnostics (intentionally strict, so expect occasional false positives for things that are later resolved)
- unknown networks
- assignments to parameters/gates that do not exist
- wrong value types for built-in options
- duplicates keys
- circular
extends between sections
To index related projects, you can create a Ned_ls.toml file in the root of your workspace with the following content:
imported_projects = [
"/<full-path-to-project>/inet/",
"/<full-path-to-project>/AutoTSN/",
"~/AutoTSN/",
"../AutoTSN/",
]
Editors
All Editors supporting the Language Server Protocol
Sublime-Text
![INFO]
Assuming the language server is downloaded and built using cargo build --release
1. Install the Syntax Package
TODO: Remove manual steps once the package is on Package Control.
Until the package is published, you must install the syntax highlighting manually:
- In Sublime Text, go to
Preferences > Browse Packages... to open your Packages directory.
- Open the
User folder.
- Copy the contents of the
editors/sublime folder from the ned_ls folder into this User folder.
2. Install the language server
- Install the LSP package:
Package Control: Install Package → LSP.
- Open
Preferences > Package Settings > LSP > Settings and add:
{
"clients": {
"ned_ls": {
"enabled": true,
"command": ["ned_ls"], // or full path if binary is not globally available
"selector": "source.ned | source.ini.omnetpp"
}
}
}
3. Goto Definition
I have not used Sublime Text a lot and there are reasons for it. For a smoother experience, I recommend creating a new mapping for LSP goto definitions:
Go to Preferences > Browse Packages... and open the User folder.
Inside that folder, create the following file based on your system:
Linux: Default (Linux).sublime-mousemap
Windows: Default (Windows).sublime-mousemap
Mac: Default (OSX).sublime-mousemap
Paste the following into the file and save:
[
{
"button": "button1",
"count": 1,
"modifiers": ["ctrl"],
"press_command": "drag_select",
"command": "lsp_symbol_definition"
}
]
Neovim
Quick installation, thanks to the native LSP-Client.
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities.workspace.didChangeWatchedFiles.dynamicRegistration = true -- enable file watching
vim.api.nvim_create_autocmd("FileType", {
pattern = {"ned", "msg"},
callback = function()
-- vim.lsp.set_log_level("info")
vim.lsp.start({
name = "ned_ls",
cmd = { "<path-to-ls>/ned_ls" },
-- capabilities = require("cmp_nvim_lsp").default_capabilities(), might be used in the future for more complex completion support
root_dir = vim.loop.cwd(),
filetypes = { "ned", "msg" },
})
end,
})
VIM
Since VIM does not offer a native LSP-Client implementation like Neovim, we need to install it manually:
Plug 'prabirshrestha/vim-lsp'
Now we can tell vim to start the language server on .ned files. TODO: update if msg and ini files are supported
if executable('ned_ls')
augroup NedLSP
autocmd!
autocmd User lsp_setup call lsp#register_server({
\ 'name': 'ned_ls',
\ 'cmd': {server_info->['ned_ls']},
\ 'whitelist': ['ned', 'msg'],
\ })
augroup END
endif
This requires that ned_ls is on your $PATH.
Example Configuration using Vundle
```vim
filetype plugin on
syntax on
set number
call vundle#begin()
" let Vundle manage Vundle, required
Plugin 'VundleVim/Vundle.vim'
Plugin 'prabirshrestha/vim-lsp'
call vundle#end()
augroup FiletypeNED
autocmd!
autocmd BufNewFile,BufRead *.ned setlocal filetype=ned
augroup END
augroup FiletypeMsg
autocmd!
autocmd BufNewFile,BufRead *.msg setlocal filetype=msg
augroup END
let g:lsp_work_done_progress_enabled=0
let g:lsp_log_verbose = 0
" let g:lsp_log_verbose = 1
" let g:lsp_log_file = expand('~/vim-lsp.log')
if executable('ned_ls')
augroup NedLSP
autocmd!
autocmd User lsp_setup call lsp#register_server({
\ 'name': 'ned_ls',
\ 'cmd': {server_info->['ned_ls', '--stdio']},
\ 'whitelist': ['ned', 'msg'],
\ })
augroup END
endif
function! s:on_lsp_buffer_enabled() abort
setlocal omnifunc=lsp#complete
setlocal signcolumn=yes
if exists('+tagfunc') | setlocal tagfunc=lsp#tagfunc | endif
nmap gd (lsp-definition)
nmap gs (lsp-document-symbol-search)
nmap gS (lsp-workspace-symbol-search)
nmap gr (lsp-references)
nmap gi (lsp-implementation)
nmap gt (lsp-type-definition)
nmap rn (lsp-rename)
nmap [g (lsp-previous-diagnostic)
nmap ]g (lsp-next-diagnostic)
nmap K (lsp-hover)
nnoremap lsp#scroll(+4)
nnoremap lsp#scroll(-4)
let g:lsp_format_sync_timeout = 1000
autocmd! BufWritePre *.rs,*.go call execute('LspDocumentFormatSync')
" refer to doc to add more commands
endfunction
augroup lsp_install
au!
autocmd User lsp_buffer_enabled call s:on_lsp_buffer_enabled()
augroup END
```
Completion can be added triggered via <C-x><C-o> if you have set the omnifunc as shown above. For a more advanced completion experience have a look at asyncomplete or other available completion plugins for vim.
One important note:
Since VIM does not support dynamic file watching registration, you might need to restart VIM after deleting files in order to update the index correctly.
This might change in the future if I implement an alternative file watching mechanism on the server side (Currently this is not prioritized).
VS Code
Download the ned-language-server.vsix from the action artifacts.
Then the extension can be installed via the command line:
code --install-extension <path-to>/ned-language-server
Or by going to the extensions tab in VS Code and on options click "Install from VSIX...". On v0.2.0 the extension should be available through the marketplace as well.
For macOS you must build the language server yourself and
set the path to the ned_ls binary in the settings tab of the extension.
[!Note]
- Installing the extension using the command palette seems to not work as expected.
goto_definition works via F12 or Ctrl+Click on a symbol. On Linux you can go back using Strg+Alt+-.
Syntax Highlighting via the omnetpp-vscode-extension is also possible in parallel.
Zed
- Install the official tree-sitter support for ned and msg files from omnetpp: omnetpp-zed via the extension tab
- Now use the Install Dev Extension and select the zed folder of the ned_ls repository
Locating the binary
The extension looks for the server in this order:
- An explicit path from your
settings.json (see below).
ned_ls on your PATH.
So if ned_ls is on your PATH — for instance via cargo install --path ., which puts it in ~/.cargo/bin — no configuration is needed at all. You only need to set a
path if the binary lives somewhere unusual, or if you want to run a specific build:
{
"lsp": {
"ned_ls": {
"binary": {
"path": "/absolute/path/to/target/release/ned_ls",
"arguments": []
}
}
}
}
[!Note]
Zed resolves the PATH from your login shell environment. If you launch Zed
from a GUI launcher and your PATH is only extended in an interactive-shell
rc file (.bashrc instead of .profile/.zshenv), the lookup can fail.
Either move the PATH export to the login-shell file or set binary.path.
License
Licensed under either of
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in ned_ls by you shall be dual licensed as above, without any
additional terms or conditions.