Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions .vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export default defineConfig({
lastUpdated: true,
cleanUrls: true,
base: "/",
srcExclude: ["Acode/**"],
head: [["link", { rel: "icon", href: "/acode.png" }]],

locales: {
Expand Down Expand Up @@ -102,7 +103,7 @@ export default defineConfig({
collapsed: true,
items: [
{
text: "Ace",
text: "CodeMirror & Legacy Ace",
link: "/docs/global-apis/ace",
},
{
Expand Down Expand Up @@ -196,9 +197,17 @@ export default defineConfig({
link: "/docs/utilities/projects",
},
{
text: "ACE Modes",
text: "Commands",
link: "/docs/utilities/commands",
},
{
text: "Editor Languages",
link: "/docs/utilities/ace-modes",
},
{
text: "Editor Themes",
link: "/docs/utilities/editor-themes",
},
{
text: "Encoding",
link: "/docs/utilities/encoding",
Expand Down Expand Up @@ -303,6 +312,14 @@ export default defineConfig({
text: "Terminal",
link: "/docs/advanced-apis/terminal",
},
{
text: "LSP",
link: "/docs/advanced-apis/lsp",
},
{
text: "File Handlers",
link: "/docs/advanced-apis/file-handlers",
},
{
text: "Action Stack",
link: "/docs/advanced-apis/action-stack",
Expand Down
35 changes: 35 additions & 0 deletions docs/advanced-apis/file-handlers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# File Handlers API

Use this API to register custom open handlers for file extensions.

## `acode.registerFileHandler(id, options)`

Registers a handler.

```js
acode.registerFileHandler("com.example.svg-viewer", {
extensions: ["svgx", ".svgalt"],
handleFile: async (fileInfo) => {
console.log(fileInfo.name, fileInfo.uri);
},
});
```

`options` fields:

- `extensions` (required): extension array. Dots are allowed and normalized.
- `handleFile` (required): async function receiving file info.

## `acode.unregisterFileHandler(id)`

Removes a handler.

```js
acode.unregisterFileHandler("com.example.svg-viewer");
```

## Notes

- Handler ids must be unique.
- Extensions are matched case-insensitively.
- `"*"` can be used to match any extension.
146 changes: 146 additions & 0 deletions docs/advanced-apis/lsp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# LSP API

Use this API to register/manage language servers used by Acode's CodeMirror LSP client.

## Import

```js
const lsp = acode.require("lsp");
```

## Important Transport Reality

CodeMirror's LSP client in Acode communicates via **WebSocket** transport.

- `transport.kind: "websocket"` is the recommended and practical mode.
- `transport.kind: "stdio"` is **not a direct stdio pipe** to the editor.
- In this codebase, `stdio` mode still expects a **WebSocket bridge URL**.

For local stdio language servers, use an **AXS bridge** (`launcher.bridge`) and keep transport as websocket.

::: warning
`stdio` with only `transport.command` is not enough in Acode.
You still need a WebSocket bridge endpoint (AXS or equivalent) for the client connection.
:::

## Recommended Setup (Local Server via AXS Bridge)

```js
lsp.registerServer(
{
id: "typescript-custom",
label: "TypeScript (Custom)",
languages: ["javascript", "javascriptreact", "typescript", "typescriptreact", "tsx", "jsx"],
transport: {
kind: "websocket",
// url can be omitted when using launcher.bridge auto-port mode
},
launcher: {
bridge: {
kind: "axs",
command: "typescript-language-server",
args: ["--stdio"],
},
checkCommand: "which typescript-language-server",
install: {
command:
"apk add --no-cache nodejs npm && npm install -g typescript-language-server typescript",
},
},
enabled: true,
initializationOptions: {
provideFormatter: true,
},
},
{ replace: true },
);
```

## Remote/External WebSocket Server

```js
lsp.registerServer({
id: "remote-lsp",
label: "Remote LSP",
languages: ["json"],
transport: {
kind: "websocket",
url: "ws://127.0.0.1:2087/",
options: {
binary: true,
timeout: 5000,
},
},
enabled: true,
});
```

## API Reference

### `registerServer(definition, options?)`

- `options.replace?: boolean` (default `false`)
- `false`: existing server with same id is kept
- `true`: existing server is replaced

Definition fields commonly used:

- `id` (required): normalized to lowercase
- `label` (optional)
- `languages` (required): non-empty array, normalized to lowercase
- `enabled` (optional, default `true`)
- `transport` (required)
- `kind`: `"websocket"` or `"stdio"` (see transport note above)
- `url?: string`
- `command?: string` (required by registry validation when using `stdio`)
- `args?: string[]`
- `options?: { binary?, timeout?, reconnect?, maxReconnectAttempts? }`
- `launcher` (optional)
- `startCommand?: string | string[]`
- `command?: string`
- `args?: string[]`
- `checkCommand?: string`
- `install?: { command?: string }`
- `bridge?: { kind: "axs", command: string, args?: string[], port?: number, session?: string }`
- `initializationOptions?: object`
- `clientConfig?: object`
- `startupTimeout?: number`
- `capabilityOverrides?: object`
- `rootUri?: (uri, context) => string | null`
- `resolveLanguageId?: (context) => string | null`
- `useWorkspaceFolders?: boolean`

### Other Registry Methods

- `unregisterServer(id)`
- `updateServer(id, updater)`
- `getServer(id)`
- `listServers()`
- `getServersForLanguage(languageId, options?)`

```js
const jsServers = lsp.getServersForLanguage("javascript");

lsp.updateServer("typescript-custom", (current) => ({
...current,
enabled: false,
}));
```

`getServersForLanguage` options:

- `includeDisabled?: boolean` (default `false`)

## Client Manager

- `clientManager.setOptions(options)`
- `clientManager.getActiveClients()`

```js
lsp.clientManager.setOptions({
diagnosticsUiExtension: [],
});

const activeClients = lsp.clientManager.getActiveClients();
console.log(activeClients);
```
4 changes: 2 additions & 2 deletions docs/editor-components/editor-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Both methods are equivalent and accept & return the same parameters.
| cursorPos | `object` | Cursor position | - |
| scrollLeft | `number` | Scroll left position | - |
| scrollTop | `number` | Scroll top position | - |
| folds | [`Array<Fold>`](https://ajaxorg.github.io/ace-api-docs/classes/src_edit_session_fold.Fold.html) | Code folds | - |
| folds | `Array<{ fromLine: number, fromCol: number, toLine: number, toCol: number }>` | Code folds | - |
| type | `string` | Type of content (e.g., 'editor') | `'editor'` |
| tabIcon | `string` | Icon class for the file tab | `'file file_type_default'` |
| content | string \| [HTMLElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement) | Custom content element or HTML string. Strings are sanitized using DOMPurify | - |
Expand Down Expand Up @@ -76,7 +76,7 @@ Both methods are equivalent and accept & return the same parameters.
| SAFMode | `'single' \| 'tree' \| null` | Storage access framework mode |
| loaded | `boolean` | Whether file has completed loading text |
| loading | `boolean` | Whether file is still loading text |
| session | `AceAjax.IEditSession` | EditSession of the file |
| session | `Proxy<EditorState>` | Session state with Ace-compatible helper methods |
| readOnly | `boolean` | Whether file is readonly |
| markChanged | `boolean` | Whether to mark changes when session text changes |

Expand Down
105 changes: 94 additions & 11 deletions docs/getting-started/understanding-plugin.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,100 @@
# Understanding Plugins
# Understanding How Plugins Work

:::warning
**Note:** Work is in progress 🚧
:::
This page is the practical mental model for writing Acode plugins: what Acode does, what your plugin must do, and what happens during load/unload.

We are currently working on this section to provide you with detailed and comprehensive information about how Acode plugins work. Please check back soon for updates!
## The Plugin Contract

## Contribute to the Documentation
From Acode's perspective, your plugin is:

We welcome contributions from the community! If you would like to help improve this documentation, please visit our [GitHub repository](https://github.com/bajrangCoder/acode-plugin-docs) and follow the contribution guidelines.
1. A folder in `PLUGIN_DIR`
2. A `plugin.json`
3. An entry script (usually `main.js`)

:::tip
You can suggest changes, add new content, or improve existing sections. Every bit of help is appreciated! 🤗
:::
From your perspective, your script should register:

For more information, see official [Guide](https://acode.app/plugin-docs).
- `acode.setPluginInit(pluginId, initFn)`
- `acode.setPluginUnmount(pluginId, unmountFn)` (strongly recommended)

If you skip `setPluginInit`, your script may load, but your plugin logic will not run through Acode's lifecycle.

## Lifecycle In One View

1. Acode discovers plugin folders.
2. It decides which plugins to load (enabled, not broken, not already loaded).
3. It loads your entry script.
4. It calls your registered `init` with runtime context.
5. Later, on disable/uninstall/reload, it calls your registered `unmount`.

## What You Get In `init`

Your init function receives:

- `baseUrl`: internal base URL to your plugin files
- `$page`: a plugin page object for UI screens
- `cache`: object with:
- `cacheFileUrl`
- `cacheFile`
- `firstInit`
- `ctx`

Use `firstInit` for one-time setup or migration.

## Recommended `main.js` Shape

```js
import plugin from "../plugin.json";

function init(baseUrl, $page, cache) {
const commands = acode.require("commands");

commands.addCommand({
name: "example.open",
description: "Open Example Panel",
exec: () => {
$page.innerHTML = "<h2>Example Plugin</h2>";
$page.show();
},
});
}

function unmount() {
const commands = acode.require("commands");
commands.removeCommand("example.open");
}

acode.setPluginInit(plugin.id, init);
acode.setPluginUnmount(plugin.id, unmount);
```

## What Happens On Disable / Enable / Uninstall

- Disable:
- Acode calls `acode.unmountPlugin(id)` which triggers your unmount.
- Plugin runtime state is cleared (including plugin cache file).
- Enable:
- Acode loads the plugin again and runs init again.
- Uninstall:
- Plugin files are removed.
- Acode runs unmount cleanup for loaded resources.

Treat `init` as repeatable and `unmount` as mandatory cleanup.

## Failure Behavior You Should Know

If your plugin throws during load/init:

- it is marked as broken for the session flow,
- Acode skips loading it again until user/action retries it.

For programmatic recovery:

```js
acode.clearBrokenPluginMark("com.example.plugin");
```

## Author Guidelines

- Keep `init` fast; do heavy work lazily.
- Register commands through `acode.require("commands")`.
- Always remove listeners, commands, intervals, and UI hooks in `unmount`.
- Avoid storing important state only in memory; use cache/settings when needed.
Loading