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 nameextensionsFile extensions this plugin handles (e.g. [".yaml", ".yml"])2 Optional Hooks
highlightRegex-based syntax highlighting -define patterns for comments, strings, keywords etc.
highlightLineFull custom highlighting -return tokens per line, with access to all lines (for multiline strings, block comments etc.)
onKeyDownIntercept any key press before the editor handles it. Return handled: true to prevent default behavior. Great for auto-close brackets, snippets, custom shortcuts.
onCursorActionFires after each edit (char typed, newline, backspace, delete, tab, paste). Use for auto-indent, auto-close, list continuation etc.
onFormatFires on F3. Reformat the entire document.
onSaveFires on Ctrl+S. Apply edits before saving.
onOpenFires when a file is opened.
onValidateValidate 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:
{
"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
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;