Skip to main content

Plugins

A plugin adds features to Runesmith: commands, tool windows, languages, language servers, completion and hover, build providers and settings. Runesmith's own C# support is a plugin, loaded the same way as yours. This page covers what a plugin is made of, its manifest, what it may do and which plugins Runesmith accepts.

What a plugin is​

A plugin is two .NET class libraries and a plugin.json manifest:

ProjectAssemblyWhat it holds
Contracts<Root>.Contracts, such as Acme.Todo.ContractsThe plugin's public API: interfaces, records, enums and other types that plugins depending on it use. It may be empty.
Implementation<Root>, such as Acme.TodoEverything else: the parts the plugin exports, its views, its logic and the libraries it carries. No other plugin sees it.

Both reference the Runesmith.Sdk package and export parts with the System.Composition attributes, such as [Export(typeof(ICommandContributor))]. Runesmith finds the exports in the implementation assembly when it starts and creates each part the first time something needs it, so a plugin that is not used costs almost nothing.

Each plugin loads apart from the others, so two plugins can carry different versions of the same library. A plugin sees its own assemblies, the contracts of the plugins it depends on, and the libraries Runesmith shares with every plugin: the SDK, Avalonia, HammerUI and .NET itself. It cannot load another plugin's implementation.

Where plugins live​

Runesmith looks in two places when it starts. Each plugin is a subfolder that holds a plugin.json.

FolderWhat is there
plugins next to the Runesmith executableThe plugins that ship with Runesmith, such as C#.
Your plugins folderThe plugins you install: from the plugin hub, and the ones you put there yourself, which Runesmith marks as local.

Your plugins folder is:

SystemFolder
Windows%APPDATA%\Runesmith\plugins
Linux~/.config/runesmith/plugins (or $XDG_CONFIG_HOME/runesmith/plugins)
macOS~/Library/Application Support/Runesmith/plugins

When the RUNESMITH_HOME environment variable is set, your plugins folder is $RUNESMITH_HOME/plugins instead.

An installed plugin's folder holds the manifest the build writes, its assemblies and its icons:

PathContent
plugin.jsonThe packaged manifest: your manifest's fields, plus the file name and SHA-256 hash of every assembly.
lib/The contracts and implementation assemblies and the libraries the plugin carries.
icon/64.png, icon/128.pngThe plugin's icon.

dotnet build -t:InstallPlugin on the implementation project writes this folder into your plugins folder; see Create a plugin. To remove a plugin, delete its folder.

The manifest​

You write plugin.json at the root of the plugin, next to the two projects:

plugin.json
{
"schemaVersion": 1,
"id": "acme.todo",
"name": "To-do highlighter",
"version": "1.2.0",
"summary": "Highlights TODO comments and lists them in a tool window.",
"authors": ["Acme"],
"license": "MIT",
"repository": "https://github.com/acme/todo-highlighter",
"runesmithApi": "^0.1.0",
"projects": {
"contracts": "Acme.Todo.Contracts/Acme.Todo.Contracts.csproj",
"implementation": "Acme.Todo/Acme.Todo.csproj"
},
"dependencies": [{ "id": "runesmith.git", "range": "^0.1.0" }],
"capabilities": [{ "id": "network", "reason": "Links to-dos to the issues on your issue tracker." }],
"networkHosts": ["*"],
"icon": "icon.png",
"categories": ["Productivity"]
}
FieldRequiredMeaning
schemaVersionYesThe manifest format, 1.
idYespublisher.name, each part lowercase letters, digits and hyphens, starting with a letter, such as acme.todo. Two plugins with the same id cannot both load; the built-in one wins.
nameYesThe name shown to the user, 2 to 40 characters.
versionYesThe plugin's semantic version, such as 1.2.0.
summaryYesOne sentence about what the plugin does, at most 120 characters.
authorsYesWho made it.
licenseYesAn SPDX license expression, such as MIT or Apache-2.0.
repositoryYesThe plugin's GitHub repository.
homepageNoAn https address for the plugin's own site.
runesmithApiYesThe plugin API versions the plugin works with, as a range.
projectsYesThe paths of the contracts and implementation projects, relative to the manifest.
dependenciesNoThe plugins it uses, each with an id and a version range. See Dependencies.
capabilitiesNoWhat it does beyond working inside Runesmith, each with a reason. See Capabilities.
networkHostsWith networkThe hosts it talks to, or * for hosts the user chooses.
platformsNowindows, linux and macos; all of them when left out.
iconYesA PNG or WebP icon, 256 to 1024 pixels square.
categoriesYesOne to three of: Languages, Version control, Themes, Formatters and linters, Debugging, Testing, Build and run, Snippets and templates, Navigation, Productivity, Visualization, Cloud and remote, Data and databases, Documentation, Education, Other.
keywordsNoUp to ten lowercase words for search.

The plugin hub rejects fields that are not in this table, so a misspelled field does not pass unnoticed.

A version range is an exact version such as 1.4.2, a caret range such as ^1.4.2 (at least 1.4.2, below 2.0.0; for ^0.3.1, below 0.4.0), a tilde range such as ~1.4.2 (below 1.5.0), comparators such as >=1.4.0 <1.9.0, or ranges joined with ||.

Older manifests​

A plugin.json without schemaVersion, with id, name, version, apiVersion (such as 0.1) and assembly fields, still loads, as a local plugin. It declares no capabilities and cannot be a dependency of other plugins.

Capabilities​

A capability says what a plugin does beyond working inside Runesmith through the SDK. Declare each one your plugin uses, with a sentence users read before they install it.

CapabilityThe plugin
networkConnects to the internet or other computers.
processStarts other programs, or uses ILauncher to open links and folders.
filesystemReads or writes files outside the open folder and its own storage.
environmentReads environment variables, the registry or system settings.
credentialsStores or reads passwords and tokens with ISecretStore.
nativeRuns native code.
dynamic-codeLoads or generates code while running.

The SDK services that need a capability refuse a plugin that did not declare it, throw an UnauthorizedAccessException and write the reason to the Plugins output channel: ISecretStore needs credentials and ILauncher needs process.

Your plugin's data​

DataWhere it goes
FilesIPluginStorage.GetFolder() gives the plugin a folder named after its id.
SettingsA plugin changes only the settings it contributes and those whose keys start with its id and a dot.
SecretsA plugin's ISecretStore keys start with its id and a slash, such as acme.todo/token.

Runesmith tells plugins apart by the assembly the calling code belongs to, so a plugin cannot act as another.

Dependencies​

A plugin that uses another plugin lists it in dependencies with a version range, and its projects reference that plugin's contracts. It can use only the contracts of the plugins it lists.

Runesmith loads a plugin after the plugins it depends on. When a dependency is missing, turned off, has a version outside the range or could not load, the plugin does not load either, and the reason names the dependency.

API versions​

The plugin API follows semantic versioning. RunesmithApi.Version in the SDK is the version this Runesmith provides, 0.1.0, and RunesmithApi.OldestSupported the oldest version plugins can be built for, 0.1.0. Runesmith loads a plugin when:

  • RunesmithApi.Version is inside the plugin's runesmithApi range, and
  • the lowest version of that range, the API the plugin was built against, is at least RunesmithApi.OldestSupported.

^0.1.0 loads in every Runesmith with an API from 0.1.0 up to, not including, 0.2.0. A plugin that cannot load is listed with the reason, and the rest of Runesmith works as usual.

When a plugin does not load​

The plugin manager lists every plugin Runesmith found, whether it loaded and why not, and the Plugins channel of the Output panel says why a plugin failed. --diagnostics writes the same list to the Diagnostics channel.

The usual reasons are a manifest with a missing or wrong field, a runesmithApi range this Runesmith does not support, an assembly that does not exist, a second plugin with the same id, or a dependency that did not load.

  • Create a plugin with the runesmith-plugin template.
  • API overview: what a plugin can export and import.
  • C# support, the built-in plugin.
  • Git: changes, commits, branches, history, diffs, cloning and pull requests.
  • GitHub: signing in to GitHub, cloning from your account and organizations, links and pull requests.
  • Forgejo: signing in to Codeberg and other Forgejo servers, cloning, links and pull requests.
  • Gitea: signing in to gitea.com and other Gitea servers, cloning, links and pull requests.