Plugins
A plugin adds features to Runesmith: commands, tool windows, languages, language servers, completion and hover, build providers and settings. Runesmith's official plugins, such as Git and C# support, load 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:
| Project | Assembly | What it holds |
|---|---|---|
| Contracts | <Root>.Contracts, such as Acme.Todo.Contracts | The 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.Todo | Everything 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 three places when it starts. Each plugin is a subfolder that holds a plugin.json, named after its id.
| Folder | What is there |
|---|---|
plugins next to the Runesmith executable | The plugins that ship with Runesmith: Git and GitHub. |
| The hub's plugins folder | The plugins you install from the plugin hub. Runesmith manages this folder. |
| Your plugins folder | The plugins you put there yourself, such as with dotnet build -t:InstallPlugin. Runesmith marks them as local. |
| System | Your plugins folder | The hub's plugins folder |
|---|---|---|
| Windows | %APPDATA%\Runesmith\plugins | %LOCALAPPDATA%\Runesmith\hub\plugins |
| Linux | ~/.config/runesmith/plugins (or $XDG_CONFIG_HOME/runesmith/plugins) | ~/.local/share/runesmith/hub/plugins (or $XDG_DATA_HOME/runesmith/hub/plugins) |
| macOS | ~/Library/Application Support/Runesmith/plugins | ~/Library/Application Support/Runesmith/hub/plugins |
When the RUNESMITH_HOME environment variable is set, your plugins folder is $RUNESMITH_HOME/plugins and the hub's is
$RUNESMITH_HOME/data/hub/plugins instead.
The two are apart, so a plugin you build and install yourself never changes a copy from the hub, even one with the same id; which of the
two runs then is up to the setting plugins.allowLocalOverrides, see Plugins from the hub. Leave the hub's
folder to Runesmith: it checks the files of hub plugins at each start and turns off one whose files changed.
An installed plugin's folder holds the manifest the build writes, its assemblies and its icons:
| Path | Content |
|---|---|
plugin.json | The 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.png | The plugin's icon. The build copies icon.png from the plugin's root to both, unless the RunesmithPluginIcon64 and RunesmithPluginIcon128 properties name other files. |
web/ | The pages web views show, copied from the web folder in the plugin's root when there is one. |
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:
{
"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.1",
"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"]
}
| Field | Required | Meaning |
|---|---|---|
schemaVersion | Yes | The manifest format, 1. |
id | Yes | publisher.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. |
name | Yes | The name shown to the user, 2 to 40 characters. |
version | Yes | The plugin's semantic version, such as 1.2.0. |
summary | Yes | One sentence about what the plugin does, at most 120 characters. |
authors | Yes | Who made it. |
license | Yes | An SPDX license expression, such as MIT or Apache-2.0. |
repository | Yes | The plugin's GitHub repository. |
homepage | No | An https address for the plugin's own site. |
runesmithApi | Yes | The plugin API versions the plugin works with, as a range. |
projects | Yes | The paths of the contracts and implementation projects, relative to the manifest. |
dependencies | No | The plugins it uses, each with an id and a version range. See Dependencies. |
capabilities | No | What it does beyond working inside Runesmith, each with a reason. See Capabilities. |
networkHosts | With network | The hosts it talks to, or * for hosts the user chooses. |
platforms | No | windows, linux and macos; all of them when left out. |
icon | Yes | A PNG or WebP icon, 256 to 1024 pixels square. |
categories | Yes | One 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. |
keywords | No | Up 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.
| Capability | The plugin |
|---|---|
network | Connects to the internet or other computers. |
process | Starts other programs, or uses ILauncher to open links and folders. |
filesystem | Reads or writes files outside the open folder and its own storage. |
environment | Reads environment variables, the registry or system settings. |
credentials | Stores or reads passwords and tokens with ISecretStore. |
native | Runs native code. |
dynamic-code | Loads 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. Web views open
https pages only on the hosts in networkHosts, and only with network; see Web views.
Your plugin's data
| Data | Where it goes |
|---|---|
| Files | IPluginStorage.GetFolder() gives the plugin a folder named after its id. |
| Settings | A plugin changes only the settings it contributes and those whose keys start with its id and a dot. |
| Secrets | A 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.1, and
RunesmithApi.OldestSupported the oldest version plugins can be built for, 0.1.0. Runesmith loads a plugin when:
RunesmithApi.Versionis inside the plugin'srunesmithApirange, 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, and ^0.1.1 in those from 0.1.1. Before 1.0,
additions to the API come in patch versions, so a plugin that uses one declares the version that added it; new plugins start at ^0.1.1.
The API overview lists what each version added. 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.
Related
- Create a plugin with the
runesmith-plugintemplate. - API overview: what a plugin can export and import.
- C# support, an official plugin with its source in RunesmithHub/plugin-csharp.
- 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.
- Gitea and Forgejo: signing in to gitea.com, Codeberg and other Gitea and Forgejo servers, cloning, links and pull requests.