janojano
@jano-editor/plugin-types

Build a Plugin

A jano plugin is a single TypeScript file that exports a LanguagePlugin object. No build system required -esbuild bundles it for you.

1 Required

namePlugin display name
extensionsFile extensions this plugin handles (e.g. [".yaml", ".yml"])

2 Optional Hooks

highlight

Regex-based syntax highlighting -define patterns for comments, strings, keywords etc.

highlightLine

Full custom highlighting -return tokens per line, with access to all lines (for multiline strings, block comments etc.)

onKeyDown

Intercept any key press before the editor handles it. Return handled: true to prevent default behavior. Great for auto-close brackets, snippets, custom shortcuts.

onCursorAction

Fires after each edit (char typed, newline, backspace, delete, tab, paste). Use for auto-indent, auto-close, list continuation etc.

onFormat

Fires on F3. Reformat the entire document.

onSave

Fires on Ctrl+S. Apply edits before saving.

onOpen

Fires when a file is opened.

onValidate

Validate the document content. Return diagnostics (errors, warnings) shown inline.

3 File Structure

Your GitHub repo needs these files:

my-plugin/
├── src/
│   └── index.ts        # plugin code
├── plugin.json         # manifest
├── package.json        # npm config + build script
└── README.md           # shown in plugin store

4 Plugin Manifest

The plugin.json tells jano what your plugin does:

plugin.json
{
  "name": "my-language",
  "version": "1.0.0",
  "api": 1,
  "description": "My language support for jano",
  "extensions": [".ext"],
  "entry": "index.js",
  "author": "your-name",
  "license": "MIT"
}

5 Minimal Example

src/index.ts
import type { LanguagePlugin } from "@jano-editor/plugin-types";

const plugin: LanguagePlugin = {
  name: "My Language",
  extensions: [".ext"],

  highlight: {
    keywords: ["if", "else", "return", "function"],
    patterns: {
      comment: /\/\/.*$/gm,
      string: /"(?:[^"\\]|\\.)*"|'[^']*'/g,
      number: /\b\d+\.?\d*\b/g,
    },
  },

  onCursorAction(ctx) {
    if (ctx.action?.type !== "newline") return null;
    const prev = ctx.lines[ctx.action.cursor.position.line - 1] || "";
    const indent = prev.match(/^(\s*)/)?.[1] || "";
    if (!indent) return null;
    const line = ctx.action.cursor.position.line;
    return {
      edits: [{ range: { start: { line, col: 0 }, end: { line, col: 0 } }, text: indent }],
      cursors: [{ position: { line, col: indent.length }, anchor: null }],
    };
  },
};

export default plugin;
→See a real plugin as reference: jano-editor/plugin-yaml

💡 Tips

→All hooks are optional -start with just highlight and add more as needed
→onKeyDown gets the full key info (name, ctrl, alt, shift) and can prevent default editor behavior
→onCursorAction receives deletedText for backspace/delete -useful for auto-close bracket pairs
→highlightLine overrides regex-based highlight when both are defined
→Use replaceAll in EditResult to replace the entire document (e.g. for formatting)
→The plugin never sees editor internals -it only works with lines, cursors, and edits