mirror of
https://github.com/esm7/obsidian-vimrc-support.git
synced 2026-07-22 05:00:25 +00:00
Version 0.6.0
This commit is contained in:
parent
9937ecf51b
commit
50e18decbd
5 changed files with 182 additions and 13 deletions
61
JsSnippets.md
Normal file
61
JsSnippets.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# Useful JS Snippets for `jscommand` and `jsfile`
|
||||
|
||||
In this document I will collect some of my and user-contributed ideas for how to utilize JS commands in the Vimrc plugin.
|
||||
|
||||
If you have interesting snippets, please contribute by opening a pull request!
|
||||
|
||||
|
||||
## Jump to Next/Prev Markdown Header
|
||||
|
||||
To map `]]` and `[[` to next/prev markdown header, I use the following.
|
||||
|
||||
In a file I call `mdHelpers.js`, put this:
|
||||
|
||||
```js
|
||||
// Taken from https://stackoverflow.com/questions/273789/is-there-a-version-of-javascripts-string-indexof-that-allows-for-regular-expr
|
||||
|
||||
function regexIndexOf(string, regex, startpos) {
|
||||
var indexOf = string.substring(startpos || 0).search(regex);
|
||||
return (indexOf >= 0) ? (indexOf + (startpos || 0)) : indexOf;
|
||||
}
|
||||
|
||||
function regexLastIndexOf(string, regex, startpos) {
|
||||
regex = (regex.global) ? regex : new RegExp(regex.source, "g" + (regex.ignoreCase ? "i" : "") + (regex.multiLine ? "m" : ""));
|
||||
if(typeof (startpos) == "undefined") {
|
||||
startpos = string.length;
|
||||
} else if(startpos < 0) {
|
||||
startpos = 0;
|
||||
}
|
||||
var stringToWorkWith = string.substring(0, startpos + 1);
|
||||
var lastIndexOf = -1;
|
||||
var nextStop = 0;
|
||||
while((result = regex.exec(stringToWorkWith)) != null) {
|
||||
lastIndexOf = result.index;
|
||||
regex.lastIndex = ++nextStop;
|
||||
}
|
||||
return lastIndexOf;
|
||||
}
|
||||
|
||||
function jumpHeading(isForward) {
|
||||
const editor = view.editor;
|
||||
let posToSearchFrom = editor.getCursor();
|
||||
posToSearchFrom.line += isForward ? 1 : -1;
|
||||
const cursorOffset = editor.posToOffset(posToSearchFrom);
|
||||
const lookupToUse = isForward ? regexIndexOf : regexLastIndexOf;
|
||||
let headingOffset = lookupToUse(editor.getValue(), /^#(#*) /gm, cursorOffset);
|
||||
// If not found from the cursor position, try again from the document beginning (or reverse beginning)
|
||||
if (headingOffset === -1)
|
||||
headingOffset = lookupToUse(editor.getValue(), /^#(#*) /gm);
|
||||
const newPos = editor.offsetToPos(headingOffset);
|
||||
editor.setCursor(newPos);
|
||||
}
|
||||
```
|
||||
|
||||
Then in your `.obsidian.vimrc` file add the following:
|
||||
|
||||
```
|
||||
exmap nextHeading jsfile mdHelpers.js {jumpHeading(true)}
|
||||
exmap prevHeading jsfile mdHelpers.js {jumpHeading(false)}
|
||||
nmap ]] :nextHeading
|
||||
nmap [[ :prevHeading
|
||||
```
|
||||
59
README.md
59
README.md
|
|
@ -51,6 +51,7 @@ In addition to that:
|
|||
- `cmcommand` - execute arbitrary CodeMirror commands, see details below.
|
||||
- `surround` - surround your selected text in visual mode or word in normal mode with text.
|
||||
- `pasteinto` - paste your current clipboard into your selected text in visual mode or word in normal mode. Useful for creating hyperlinks.
|
||||
- `jscommand` and `jsfile` - extend Vim mode using JavaScript snippets.
|
||||
|
||||
Commands that fail don't generate any visible error for now.
|
||||
|
||||
|
|
@ -138,8 +139,6 @@ The full list of CodeMirror commands is available [here](https://codemirror.net/
|
|||
|
||||
## Surround Text with `surround`
|
||||
|
||||
**Note:** this is currently unsupported for the new (CM6-based) editor.
|
||||
|
||||
The plugin defines a custom Ex command named `surround` to surround either your currently selected text in visual mode or the word your cursor is over in normal mode with text.
|
||||
This is particularly useful for creating wikilinks in Obsidian `[[WikiLink]]`.
|
||||
|
||||
|
|
@ -160,8 +159,6 @@ map [[ :wiki
|
|||
|
||||
## Inserting Links/Hyperlinks with `pasteinto`
|
||||
|
||||
**Note:** this is currently unsupported for the new (CM6-based) editor.
|
||||
|
||||
The plugin defines a custom Ex command named `pasteinto` to paste text into your currently selected text in visual mode, or the word your cursor is over in normal mode.
|
||||
This is particularly useful for creating links/hyperlinks `[selected-text](paste)`.
|
||||
|
||||
|
|
@ -197,8 +194,62 @@ When you enter insert mode, you will type in your actual current system layout/l
|
|||
|
||||
Relative line numbers work very nicely with [this](https://github.com/nadavspi/obsidian-relative-line-numbers) Obsidian plugin (thank you @piotryordanov for bringing it to my attention!)
|
||||
|
||||
## Extending Vim Commands with JavaScript Snippets
|
||||
|
||||
The plugin allows to define Vim commands that map to JavaScript snippets, which adds exciting new possibilities to what you can achieve with Vim key bindings. **But -- this comes with a price of a security risk, and is therefore disabled by default.**
|
||||
|
||||
> :warning: **Security Warning**
|
||||
>
|
||||
> Before using this feature, you **must be sure** that you understand its potential security implications.
|
||||
>
|
||||
> Running JavaScript snippets with Vim commands stored in your vault means that anyone who gains access to
|
||||
> your notes can run arbitrary code inside your Obsidian app.
|
||||
|
||||
If you understand the risks and choose to use this feature, turn on "Support JS Commands" in the plugin settings and continue reading.
|
||||
|
||||
### JavaScript Command Usage
|
||||
|
||||
There are two ways to define JS-based commands.
|
||||
|
||||
**The `jscommand` Ex command** defines a JS function that has an `editor: Editor` and `view: MarkdownView` arguments (see the [Obsidian API](https://github.com/obsidianmd/obsidian-api/blob/master/obsidian.d.ts) if you're not sure what these are).
|
||||
You define only the syntax of the function, in a single line wrapped by curly braces, e.g.:
|
||||
|
||||
```
|
||||
jscommand { console.log(editor.getCursor()); }
|
||||
```
|
||||
|
||||
This will immediately log your current cursor position to the developer console.
|
||||
If you want, you can make this an Ex command using `exmap`:
|
||||
|
||||
```
|
||||
exmap logCursor jscommand { console.log(editor.getCursor()); }
|
||||
nmap <C-q> :logCursor
|
||||
```
|
||||
|
||||
Another version of the same functionality is **the `jsfile` Ex command**, which executes code from a file you give as a parameter, then appends another optional piece of code to it (e.g. in case you want to store several helper methods in a file and launch different ones as part of different commands).
|
||||
|
||||
As above, the code running as part of `jsfile` has `editor: Editor` and `view: MarkdownView` arguments.
|
||||
|
||||
Here's an example from my own `.obsidian.vimrc` that maps `]]` and `[[` to jump to the next/previous Markdown header:
|
||||
|
||||
```
|
||||
exmap nextHeading jsfile mdHelpers.js {jumpHeading(true)}
|
||||
exmap prevHeading jsfile mdHelpers.js {jumpHeading(false)}
|
||||
nmap ]] :nextHeading
|
||||
nmap [[ :prevHeading
|
||||
```
|
||||
|
||||
See [here](JsSnippets.md) for the full example, and please contribute your own!
|
||||
|
||||
|
||||
## Changelog
|
||||
|
||||
### 0.6.0
|
||||
|
||||
- The `surround` and `pasteinto` commands now work with the new (CM6-based) editor.
|
||||
- Made the existence of the Vimrc file not required for other plugin features to work (https://github.com/esm7/obsidian-vimrc-support/issues/89)
|
||||
- New exciting, but dangerous, `jscommand` and `jsfile` commands, that allow extending the plugin with JavaScript snippets.
|
||||
|
||||
### 0.5.2
|
||||
|
||||
- Fixed wrong detection of the editor (legacy vs new) on some occasions, leading to the plugin not really working in these situations.
|
||||
|
|
|
|||
71
main.ts
71
main.ts
|
|
@ -8,7 +8,8 @@ interface Settings {
|
|||
displayChord: boolean,
|
||||
displayVimMode: boolean,
|
||||
fixedNormalModeLayout: boolean,
|
||||
capturedKeyboardMap: Record<string, string>
|
||||
capturedKeyboardMap: Record<string, string>,
|
||||
supportJsCommands?: boolean
|
||||
}
|
||||
|
||||
const DEFAULT_SETTINGS: Settings = {
|
||||
|
|
@ -16,7 +17,8 @@ const DEFAULT_SETTINGS: Settings = {
|
|||
displayChord: false,
|
||||
displayVimMode: false,
|
||||
fixedNormalModeLayout: false,
|
||||
capturedKeyboardMap: {}
|
||||
capturedKeyboardMap: {},
|
||||
supportJsCommands: false
|
||||
}
|
||||
|
||||
const enum vimStatus {
|
||||
|
|
@ -111,9 +113,13 @@ export default class VimrcPlugin extends Plugin {
|
|||
if (!this.initialized)
|
||||
await this.initialize();
|
||||
const VIMRC_FILE_NAME = this.settings.vimrcFileName;
|
||||
this.app.vault.adapter.read(VIMRC_FILE_NAME).
|
||||
then((lines) => this.readVimInit(lines)).
|
||||
catch(error => { console.log('Error loading vimrc file', VIMRC_FILE_NAME, 'from the vault root', error) });
|
||||
let vimrcContent = '';
|
||||
try {
|
||||
vimrcContent = await this.app.vault.adapter.read(VIMRC_FILE_NAME);
|
||||
} catch (e) {
|
||||
console.log('Error loading vimrc file', VIMRC_FILE_NAME, 'from the vault root', e.message)
|
||||
}
|
||||
this.readVimInit(vimrcContent);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -174,6 +180,8 @@ export default class VimrcPlugin extends Plugin {
|
|||
this.defineObCommand(this.codeMirrorVimObject);
|
||||
this.defineCmCommand(this.codeMirrorVimObject);
|
||||
this.defineSurround(this.codeMirrorVimObject);
|
||||
this.defineJsCommand(this.codeMirrorVimObject);
|
||||
this.defineJsFile(this.codeMirrorVimObject);
|
||||
|
||||
// Record the position of selections
|
||||
CodeMirror.on(cmEditor, "cursorActivity", async (cm: any) => {
|
||||
|
|
@ -344,8 +352,6 @@ export default class VimrcPlugin extends Plugin {
|
|||
defineSurround(vimObject: any) {
|
||||
// Function to surround selected text or highlighted word.
|
||||
var surroundFunc = (params: string[]) => {
|
||||
if (this.editorMode === 'cm6')
|
||||
throw new Error("surround is not yet supported in the new editor. To be added soon.");
|
||||
var editor = this.getActiveView().editor;
|
||||
if (!params.length) {
|
||||
throw new Error("surround requires exactly 2 parameters: prefix and postfix text.");
|
||||
|
|
@ -512,6 +518,46 @@ export default class VimrcPlugin extends Plugin {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
defineJsCommand(vimObject: any) {
|
||||
vimObject.defineEx('jscommand', '', (cm: any, params: any) => {
|
||||
if (!this.settings.supportJsCommands)
|
||||
throw new Error("JS commands are turned off; enable them via the Vimrc plugin configuration if you're sure you know what you're doing");
|
||||
const jsCode = params.argString.trim() as string;
|
||||
if (jsCode[0] != '{' || jsCode[jsCode.length - 1] != '}')
|
||||
throw new Error("Expected an argument which is JS code surrounded by curly brackets: {...}");
|
||||
const command = Function('editor', 'view', jsCode);
|
||||
const view = this.getActiveView();
|
||||
command(view.editor, view);
|
||||
});
|
||||
}
|
||||
|
||||
defineJsFile(vimObject: any) {
|
||||
vimObject.defineEx('jsfile', '', async (cm: any, params: any) => {
|
||||
if (!this.settings.supportJsCommands)
|
||||
throw new Error("JS commands are turned off; enable them via the Vimrc plugin configuration if you're sure you know what you're doing");
|
||||
if (params?.args?.length < 1)
|
||||
throw new Error("Expected format: fileName {extraCode}");
|
||||
let extraCode = '';
|
||||
const fileName = params.args[0];
|
||||
if (params.args.length > 1) {
|
||||
params.args.shift();
|
||||
extraCode = params.args.join(' ').trim() as string;
|
||||
if (extraCode[0] != '{' || extraCode[extraCode.length - 1] != '}')
|
||||
throw new Error("Expected an extra code argument which is JS code surrounded by curly brackets: {...}");
|
||||
}
|
||||
let content = '';
|
||||
try {
|
||||
content = await this.app.vault.adapter.read(fileName);
|
||||
} catch (e) {
|
||||
throw new Error(`Cannot read file ${params.args[0]} from vault root: ${e.message}`);
|
||||
}
|
||||
const command = Function('editor', 'view', content + extraCode);
|
||||
const view = this.getActiveView();
|
||||
command(view.editor, view);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SettingsTab extends PluginSettingTab {
|
||||
|
|
@ -582,5 +628,16 @@ class SettingsTab extends PluginSettingTab {
|
|||
this.plugin.saveSettings();
|
||||
});
|
||||
})
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Support JS commands (beware!)')
|
||||
.setDesc("Support the 'jscommand' and 'jsfile' commands, which allow defining Ex commands using Javascript. WARNING! Review the README to understand why this may be dangerous before enabling.")
|
||||
.addToggle(toggle => {
|
||||
toggle.setValue(this.plugin.settings.supportJsCommands ?? DEFAULT_SETTINGS.supportJsCommands);
|
||||
toggle.onChange(value => {
|
||||
this.plugin.settings.supportJsCommands = value;
|
||||
this.plugin.saveSettings();
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "obsidian-vimrc-support",
|
||||
"name": "Vimrc Support",
|
||||
"version": "0.5.2",
|
||||
"version": "0.6.0",
|
||||
"description": "Auto-load a startup file with Obsidian Vim commands.",
|
||||
"author": "esm",
|
||||
"authorUrl": "",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "obsidian-vimrc-support",
|
||||
"version": "0.5.2",
|
||||
"version": "0.6.0",
|
||||
"description": "Auto-load a startup file with Obsidian Vim commands.",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
Loading…
Reference in a new issue