diff --git a/plug-api/app_event.ts b/plug-api/app_event.ts index 7a105b91..80e3057e 100644 --- a/plug-api/app_event.ts +++ b/plug-api/app_event.ts @@ -7,6 +7,7 @@ export type AppEvent = | "minieditor:complete" | "page:load" | "editor:init" + | "editor:modeswitch" | "plugs:loaded"; export type QueryProviderEvent = { diff --git a/plug-api/lib/yaml_page.ts b/plug-api/lib/yaml_page.ts index 39f4aec8..35204e93 100644 --- a/plug-api/lib/yaml_page.ts +++ b/plug-api/lib/yaml_page.ts @@ -2,13 +2,13 @@ import { findNodeOfType, traverseTree } from "$sb/lib/tree.ts"; import { markdown, space } from "$sb/silverbullet-syscall/mod.ts"; import * as YAML from "yaml"; -export async function readYamlPage( +export async function readCodeBlockPage( pageName: string, - allowedLanguages = ["yaml"], -): Promise { + allowedLanguages?: string[], +): Promise { const text = await space.readPage(pageName); const tree = await markdown.parseMarkdown(text); - let data: any = {}; + let codeText: string | undefined; traverseTree(tree, (t): boolean => { // Find a fenced code block @@ -16,10 +16,13 @@ export async function readYamlPage( return false; } const codeInfoNode = findNodeOfType(t, "CodeInfo"); - if (!codeInfoNode) { + if (allowedLanguages && !codeInfoNode) { return false; } - if (!allowedLanguages.includes(codeInfoNode.children![0].text!)) { + if ( + allowedLanguages && + !allowedLanguages.includes(codeInfoNode!.children![0].text!) + ) { return false; } const codeTextNode = findNodeOfType(t, "CodeText"); @@ -27,17 +30,27 @@ export async function readYamlPage( // Honestly, this shouldn't happen return false; } - const codeText = codeTextNode.children![0].text!; - try { - data = YAML.parse(codeText); - } catch (e: any) { - console.error("YAML Page parser error", e); - throw new Error(`YAML Error: ${e.message}`); - } + codeText = codeTextNode.children![0].text!; return true; }); - return data; + return codeText; +} + +export async function readYamlPage( + pageName: string, + allowedLanguages = ["yaml"], +): Promise { + const codeText = await readCodeBlockPage(pageName, allowedLanguages); + if (codeText === undefined) { + return undefined; + } + try { + return YAML.parse(codeText); + } catch (e: any) { + console.error("YAML Page parser error", e); + throw new Error(`YAML Error: ${e.message}`); + } } export async function writeYamlPage( diff --git a/plug-api/silverbullet-syscall/editor.ts b/plug-api/silverbullet-syscall/editor.ts index 8aa96055..129ed876 100644 --- a/plug-api/silverbullet-syscall/editor.ts +++ b/plug-api/silverbullet-syscall/editor.ts @@ -123,3 +123,8 @@ export function getUiOption(key: string): Promise { export function setUiOption(key: string, value: any): Promise { return syscall("editor.setUiOption", key, value); } + +// Vim specific +export function vimEx(exCommand: string): Promise { + return syscall("editor.vimEx", exCommand); +} diff --git a/plugs/core/core.plug.yaml b/plugs/core/core.plug.yaml index 1063946f..efce3965 100644 --- a/plugs/core/core.plug.yaml +++ b/plugs/core/core.plug.yaml @@ -22,10 +22,6 @@ functions: path: "./editor.ts:setEditorMode" events: - editor:init - toggleVimMode: - path: "./editor.ts:toggleVimMode" - command: - name: "Editor: Toggle Vim Mode" toggleDarkMode: path: "./editor.ts:toggleDarkMode" command: @@ -432,3 +428,15 @@ functions: pageNamespace: pattern: "💭 .+" operation: getFileMeta + + # Vim + toggleVimMode: + path: "./vim.ts:toggleVimMode" + command: + name: "Editor: Toggle Vim Mode" + loadVimRc: + path: "./vim.ts:loadVimRc" + command: + name: "Editor: Vim: Load VIMRC" + events: + - editor:modeswitch diff --git a/plugs/core/editor.ts b/plugs/core/editor.ts index 91098511..dfc2fdb4 100644 --- a/plugs/core/editor.ts +++ b/plugs/core/editor.ts @@ -10,13 +10,6 @@ export async function setEditorMode() { } } -export async function toggleVimMode() { - let vimMode = await clientStore.get("vimMode"); - vimMode = !vimMode; - await editor.setUiOption("vimMode", vimMode); - await clientStore.set("vimMode", vimMode); -} - export async function toggleDarkMode() { let darkMode = await clientStore.get("darkMode"); darkMode = !darkMode; diff --git a/plugs/core/vim.ts b/plugs/core/vim.ts new file mode 100644 index 00000000..075ae9bc --- /dev/null +++ b/plugs/core/vim.ts @@ -0,0 +1,34 @@ +import { readCodeBlockPage } from "../../plug-api/lib/yaml_page.ts"; +import { clientStore, editor } from "$sb/silverbullet-syscall/mod.ts"; + +export async function toggleVimMode() { + let vimMode = await clientStore.get("vimMode"); + vimMode = !vimMode; + await editor.setUiOption("vimMode", vimMode); + await clientStore.set("vimMode", vimMode); +} + +export async function loadVimRc() { + const vimMode = await editor.getUiOption("vimMode"); + if (!vimMode) { + console.log("Not in vim mode"); + return; + } + try { + const vimRc = await readCodeBlockPage("VIMRC"); + if (vimRc) { + console.log("Now running vim ex commands from VIMRC"); + const lines = vimRc.split("\n"); + for (const line of lines) { + try { + console.log("Running vim ex command", line); + await editor.vimEx(line); + } catch (e: any) { + await editor.flashNotification(e.message, "error"); + } + } + } + } catch (e: any) { + // No VIMRC page found + } +} diff --git a/web/editor.tsx b/web/editor.tsx index 8fe3bc8f..3f0ab7a2 100644 --- a/web/editor.tsx +++ b/web/editor.tsx @@ -149,7 +149,6 @@ export class Editor { // Runtime state (that doesn't make sense in viewState) collabState?: CollabState; - // enableVimMode = false; constructor( space: Space, @@ -332,6 +331,7 @@ export class Editor { } }); + // Need to find a nicer way of doing this stuff if (this.builtinSettings.fontFamily) { document.getElementById("sb-root")!.setAttribute( "style", @@ -1067,6 +1067,7 @@ export class Editor { useEffect(() => { this.rebuildEditorState(); + this.dispatchAppEvent("editor:modeswitch"); }, [viewState.uiOptions.vimMode]); useEffect(() => { diff --git a/web/syscalls/editor.ts b/web/syscalls/editor.ts index 393d5497..3fe2a06f 100644 --- a/web/syscalls/editor.ts +++ b/web/syscalls/editor.ts @@ -1,5 +1,5 @@ import { Editor } from "../editor.tsx"; -import { EditorView, Transaction } from "../deps.ts"; +import { EditorView, Transaction, Vim, vimGetCm } from "../deps.ts"; import { SysCallMapping } from "../../plugos/system.ts"; import { FilterOption } from "../../common/types.ts"; @@ -164,6 +164,10 @@ export function editorSyscalls(editor: Editor): SysCallMapping { value, }); }, + "editor.vimEx": (_ctx, exCommand: string) => { + const cm = vimGetCm(editor.editorView!)!; + return Vim.handleEx(cm, exCommand); + }, }; return syscalls; diff --git a/website/CHANGELOG.md b/website/CHANGELOG.md index 72870382..ec267747 100644 --- a/website/CHANGELOG.md +++ b/website/CHANGELOG.md @@ -1,6 +1,10 @@ An attempt at documenting the changes/new features introduced in each release. +--- +## Next +* Syntax highlighting for a bunch of new languages: PgSQL, Rust, CSS, Python, Protobuf, Shell, Swift, toml, XML, JSON, C, C++, Java, C#, Scala, Kotlin, ObjectiveC, ObjectiveC++ and Dart +* [[Vim]] support for VIMRC (see [[Vim]] documentation) --- ## 0.2.9 diff --git a/website/Vim.md b/website/Vim.md new file mode 100644 index 00000000..7c2303a3 --- /dev/null +++ b/website/Vim.md @@ -0,0 +1,12 @@ +SilverBullet has a basic Vim mode. You can toggle it using the {[Editor: Toggle Vim Mode]} command. + +In addition, it supports various ex commands that you can run as you would expect, for instance: `:imap jj `. + +## VIMRC +SilverBullet also has the ability to load a set of these ex command on boot. To use this functionality, create a page in your space named [[VIMRC]] and put a fenced code block in it (similar to how this is done in [[SETTINGS]]) with one ex command per line, for example: + + ``` + imap jj + ``` + +To manually reload your [[VIMRC]] you can use the {[Editor: Vim: Load VIMRC]} command. \ No newline at end of file