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:
| 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 two places when it starts. Each plugin is a subfolder that holds a plugin.json.
| Folder | What is there |
|---|---|
plugins next to the Runesmith executable | The plugins that ship with Runesmith, such as C#. |
| Your plugins folder | The plugins you install: from the plugin hub, and the ones you put there yourself, which Runesmith marks as local. |
Your plugins folder is:
| System | Folder |
|---|---|
| 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:
| 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. |
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.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"]
}
| 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.
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.0, 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. 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, 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.