Skip to content

Softcode in Your Editor

If you write softcode in files rather than straight into the game, the SharpMUSH Language Server gives your editor the same understanding of softcode the game has. It works with any editor that supports the Language Server Protocol (LSP), including VS Code, Neovim and Emacs. The portal’s built-in softcode editor uses the same analysis.

The offline stdio server described below only reads your code. It never connects to a game, needs no database, and changes nothing. Remote mode, at the end, connects to a game instead.

  • Error checking as you type, with the exact place and a message.
  • Completion for function names (with their arguments), command names (with their switches), substitutions such as %#, %! and %N, and q- and v-registers.
  • Hover help: a function’s arguments, a command’s switches and locks, and what a substitution means.
  • Argument hints while you type a function call, marking which arguments are optional, and the names of arguments shown inline.
  • Go to definition for attributes set with &attribute or @set, and find all uses of an attribute, function or object.
  • Quick fixes, such as closing a parenthesis or correcting a misspelled function name.
  • An outline of the attributes, function calls and commands in a file, and a search across all your files.
  • Renaming an attribute everywhere it is used.
  • Formatting: trailing spaces removed, spacing around operators and after commands made consistent.
  • Highlighting that knows the difference between built-in and custom functions, object references, substitutions, registers and commands.

It works on files ending in .mush or .mu (commands, one per line), .mushfn or .fun (a function) and .mushcmd (a command list).

The language server is part of the SharpMUSH source code. Follow Develop SharpMUSH to get the code and the .NET SDK, then build it:

Terminal window
dotnet build SharpMUSH.LanguageServer/SharpMUSH.LanguageServer.csproj

Your editor starts it for you. It talks to the editor over standard input and output, so you never run it by hand except to test it:

Terminal window
dotnet run --project SharpMUSH.LanguageServer/SharpMUSH.LanguageServer.csproj

It writes a log file each day, named lsp-server followed by the date, in ~/.local/share/SharpMUSH/ on Linux, ~/Library/Application Support/SharpMUSH/ on macOS, or %LOCALAPPDATA%\SharpMUSH\ on Windows. Look there first if your editor shows no errors or completions.

VS Code needs a small extension that starts the server. The SharpMUSH source includes a working example in SharpMUSH.LanguageServer/vscode-extension-example. The parts that matter:

  1. Declare the language in the extension’s package.json, so .mush and .mu files are recognised:

    {
    "name": "sharpmush",
    "displayName": "SharpMUSH Language Support",
    "engines": { "vscode": "^1.75.0" },
    "activationEvents": ["onLanguage:mush"],
    "main": "./out/extension.js",
    "contributes": {
    "languages": [{
    "id": "mush",
    "extensions": [".mush", ".mu", ".mushfn", ".fun", ".mushcmd"],
    "aliases": ["MUSH", "mush"]
    }]
    },
    "dependencies": { "vscode-languageclient": "^8.1.0" }
    }
  2. Start the server from the extension, pointing at the built SharpMUSH.LanguageServer.dll (here copied or linked into a server folder in the extension):

    import * as path from 'path';
    import { workspace, ExtensionContext } from 'vscode';
    import { LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient/node';
    let client: LanguageClient;
    export function activate(context: ExtensionContext) {
    const serverPath = context.asAbsolutePath(path.join('server', 'SharpMUSH.LanguageServer.dll'));
    const serverOptions: ServerOptions = {
    run: { command: 'dotnet', args: [serverPath] },
    debug: { command: 'dotnet', args: [serverPath] }
    };
    const clientOptions: LanguageClientOptions = {
    documentSelector: [{ scheme: 'file', language: 'mush' }],
    synchronize: { fileEvents: workspace.createFileSystemWatcher('**/*.{mush,mu,mushfn,fun,mushcmd}') }
    };
    client = new LanguageClient('sharpmush', 'SharpMUSH Language Server', serverOptions, clientOptions);
    client.start();
    }
    export function deactivate() {
    return client?.stop();
    }
  3. Build and install it. In the extension’s folder, with Node.js installed:

    Terminal window
    npm install
    npm run compile
    npm run package

    This makes a .vsix file. In VS Code, open the Extensions view, choose Install from VSIX… from its … menu, and pick the file.

With nvim-lspconfig, define the server once and turn it on for .mush and .mu files:

local lspconfig = require('lspconfig')
local configs = require('lspconfig.configs')
if not configs.sharpmush then
configs.sharpmush = {
default_config = {
cmd = { 'dotnet', 'run', '--project', '/path/to/SharpMUSH.LanguageServer/SharpMUSH.LanguageServer.csproj' },
filetypes = { 'mush' },
root_dir = function(fname)
return lspconfig.util.find_git_ancestor(fname) or vim.fn.getcwd()
end,
settings = {},
},
}
end
lspconfig.sharpmush.setup{}
vim.filetype.add({
extension = {
mush = 'mush',
mu = 'mush',
},
})

With lsp-mode:

(require 'lsp-mode)
(add-to-list 'lsp-language-id-configuration '(mush-mode . "mush"))
(lsp-register-client
(make-lsp-client :new-connection (lsp-stdio-connection
'("dotnet" "run" "--project"
"/path/to/SharpMUSH.LanguageServer/SharpMUSH.LanguageServer.csproj"))
:major-modes '(mush-mode)
:server-id 'sharpmush-ls))
(add-to-list 'auto-mode-alist '("\\.mush\\'" . mush-mode))
(add-to-list 'auto-mode-alist '("\\.mu\\'" . mush-mode))

Any LSP client can use it: start dotnet with the built SharpMUSH.LanguageServer.dll (or dotnet run --project …) as a standard input/output server for .mush, .mu, .mushfn, .fun and .mushcmd files. It supports these LSP requests: document open, change, close and save; diagnostics; semantic tokens (whole file and range); completion; hover; definition; references; code actions; signature help; document symbols; rename; formatting; workspace symbols; and inlay hints.

The optional remote LSP plugin offers diagnostics, completion, hover and signature help using metadata filtered for your authenticated character. An ordinary editor launches SharpMUSH.LspBridge over stdio; the bridge connects to the game’s authenticated WebSocket endpoint. Remote analysis supports .mush and .mu (commands per line), .mushfn and .fun (function), and .mushcmd (command list). Positions use UTF-16.

Remote LSP does not offer the broader offline feature list below: formatting, rename, definitions and references are outside its initial scope. It does not synchronize attributes, execute code or read the server filesystem. Connections do not resume documents automatically. Overload can explicitly close a connection when synchronization diagnostics cannot be admitted. After a restart or explicit close, reconnect, initialize a fresh session and reopen documents. An analysis deadline instead produces an analysis-timeout warning, preserves the session/document and retries on the next edit.

See Remote MCP and LSP Analysis for turning the remote LSP on, its endpoint and protocol, and its limits. MCP serves AI analysis clients; LSP serves editors.

Build the bridge with the same .NET 11 SDK as the server:

Terminal window
dotnet build SharpMUSH.LspBridge/SharpMUSH.LspBridge.csproj

Publish SharpMUSH.LspBridge for your client environment if distributing it separately. Configure your editor to launch the bridge rather than the offline server:

Terminal window
dotnet /path/to/SharpMUSH.LspBridge.dll --endpoint wss://game.example/lsp

Provide the character’s name and password through the child environment variables SHARPMUSH_LSP_CHARACTER and SHARPMUSH_LSP_PASSWORD, preferably injected from editor SecretStorage. Do not put passwords in settings JSON, URLs, arguments, logs or committed files. The bridge accepts only WSS endpoints without userinfo/query/fragment and verifies TLS normally; there is no insecure certificate bypass. It uses Content-Length framed UTF-8 LSP on stdio, translating to the WS protocol. Logging is stderr only. Bridge/connection loss exits; client restart opens a fresh authenticated session and must initialize/reopen documents. The bridge needs the .NET 11 runtime. Browser WebSocket clients cannot supply arbitrary Basic headers; no browser-ticket/query-token mechanism is provided.

The VS Code desktop example has explicit offline/remote modes, all five extensions, endpoint-bound SecretStorage-driven remote launch and a strict tsconfig. Packaging it needs Node 22 or newer. The portal’s built-in editor does not use the remote LSP.