mirror of
https://github.com/heyman/heynote.git
synced 2025-08-14 16:58:36 +02:00
Revamp key bindings
This is a work in progress revamp of the key binding system. It implements a system, built on top of CodeMirror's key binding system, for defining key bindings. The system uses a dumb "KeyShortcut" -> "Command" mapping with a set of default keys (which will be different if Heynote's Emacs mode is used) that can be overridden by user key bindings. The key bindings are *displayed* in the Settings, and it's possible to set user defined key bindings in Heynote's config file, but it's not yet possible to define custom key bindings in the UI. Previously we Heynote on a bunch of default key bindings from CodeMirror (some of which was not "block aware"). This is no longer the case, and because of this, it's quite likely that there are key bindings that was previously working that is now missing (if so, these can easily be added later).
This commit is contained in:
@ -24,6 +24,16 @@ const schema = {
|
||||
properties: {
|
||||
"keymap": { "enum": ["default", "emacs"], default:"default" },
|
||||
"emacsMetaKey": { "enum": [null, "alt", "meta"], default: null },
|
||||
"keyBindings": {
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"type": "string"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
|
||||
"showLineNumberGutter": {type: "boolean", default:true},
|
||||
"showFoldGutter": {type: "boolean", default:true},
|
||||
"autoUpdate": {type: "boolean", default: true},
|
||||
@ -61,6 +71,7 @@ const defaults = {
|
||||
settings: {
|
||||
keymap: "default",
|
||||
emacsMetaKey: isMac ? "meta" : "alt",
|
||||
keyBindings: {},
|
||||
showLineNumberGutter: true,
|
||||
showFoldGutter: true,
|
||||
autoUpdate: true,
|
||||
|
85
src/components/settings/KeyBindRow.vue
Normal file
85
src/components/settings/KeyBindRow.vue
Normal file
@ -0,0 +1,85 @@
|
||||
<script>
|
||||
export default {
|
||||
props: [
|
||||
"keys",
|
||||
"command",
|
||||
"isDefault",
|
||||
"isOverridden",
|
||||
],
|
||||
|
||||
computed: {
|
||||
formattedKeys() {
|
||||
return this.keys.replaceAll(
|
||||
"Mod",
|
||||
window.heynote.platform.isMac ? "⌘" : "Ctrl",
|
||||
)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<tr :class="{overridden:isOverridden}">
|
||||
<td class="source">
|
||||
{{ isDefault ? "Heynote" : "User" }}
|
||||
</td>
|
||||
<td class="key">
|
||||
<template v-if="keys">
|
||||
{{ formattedKeys }}
|
||||
</template>
|
||||
</td>
|
||||
<td class="command">
|
||||
<span v-if="!command" class="unbound">Unbound</span>
|
||||
<span class="command-name">{{ command }}</span>
|
||||
</td>
|
||||
<td class="actions">
|
||||
<template v-if="!isDefault">
|
||||
<button class="delete">Delete</button>
|
||||
</template>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
tr
|
||||
&.overridden
|
||||
text-decoration: line-through
|
||||
color: rgba(0,0,0, 0.4)
|
||||
+dark-mode
|
||||
color: rgba(255,255,255, 0.4)
|
||||
td
|
||||
&.key
|
||||
//letter-spacing: 1px
|
||||
&.command
|
||||
.command-name
|
||||
font-family: monospace
|
||||
margin-right: 10px
|
||||
.unbound
|
||||
font-style: italic
|
||||
color: #999
|
||||
button
|
||||
padding: 0 10px
|
||||
height: 22px
|
||||
font-size: 12px
|
||||
background: none
|
||||
border: none
|
||||
border-radius: 2px
|
||||
margin-right: 2px
|
||||
cursor: pointer
|
||||
background: #ddd
|
||||
&:hover
|
||||
background: #ccc
|
||||
+dark-mode
|
||||
background: #555
|
||||
&:hover
|
||||
background: #666
|
||||
//&.delete
|
||||
// background: #e95050
|
||||
// &:hover
|
||||
// background: #ce4848
|
||||
// +dark-mode
|
||||
// &.delete
|
||||
// background: #ae1e1e
|
||||
// &:hover
|
||||
// background: #bf2222
|
||||
</style>
|
126
src/components/settings/KeyboardBindings.vue
Normal file
126
src/components/settings/KeyboardBindings.vue
Normal file
@ -0,0 +1,126 @@
|
||||
<script>
|
||||
import { mapState} from 'pinia'
|
||||
|
||||
import { DEFAULT_KEYMAP, EMACS_KEYMAP } from "@/src/editor/keymap"
|
||||
import { useSettingsStore } from "@/src/stores/settings-store"
|
||||
import KeyBindRow from "./KeyBindRow.vue"
|
||||
|
||||
export default {
|
||||
props: [
|
||||
"userKeys"
|
||||
],
|
||||
components: {
|
||||
KeyBindRow,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(useSettingsStore, [
|
||||
"settings",
|
||||
]),
|
||||
|
||||
keymapOld() {
|
||||
const userKeys = [
|
||||
{key: "Mod-Enter", command: null},
|
||||
]
|
||||
|
||||
// merge default keymap with user keymap
|
||||
const defaultKeys = Object.fromEntries(DEFAULT_KEYMAP.map(km => [km.key, km.command]))
|
||||
let mergedKeys = {...defaultKeys, ...Object.fromEntries(userKeys.map(km => [km.key, km.command]))}
|
||||
|
||||
|
||||
|
||||
//console.log("defaultKeys:", defaultKeys)
|
||||
|
||||
return Object.entries(mergedKeys).map(([key, command]) => {
|
||||
return {
|
||||
key: key,
|
||||
command: command,
|
||||
isDefault: defaultKeys[key] !== undefined && defaultKeys[key] === command,
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
keymap() {
|
||||
//const userKeys = {
|
||||
// "Mod-Enter": null,
|
||||
// "Mod-Shift-A": "test",
|
||||
//}
|
||||
|
||||
const keymap = this.settings.keymap === "emacs" ? EMACS_KEYMAP : DEFAULT_KEYMAP
|
||||
|
||||
return [
|
||||
...Object.entries(this.userKeys).map(([key, command]) => {
|
||||
return {key, command}
|
||||
}),
|
||||
|
||||
...keymap.map((km) => {
|
||||
return {
|
||||
key: km.key,
|
||||
command: km.command,
|
||||
isDefault: true,
|
||||
isOverridden: km.key in this.userKeys,
|
||||
}
|
||||
}),
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container">
|
||||
<h2>Keyboard Bindings</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Source</th>
|
||||
<th>Key</th>
|
||||
<th>Command</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
<KeyBindRow
|
||||
v-for="key in keymap"
|
||||
:key="key.key"
|
||||
:keys="key.key"
|
||||
:command="key.command"
|
||||
:isOverridden="key.isOverridden"
|
||||
:isDefault="key.isDefault"
|
||||
/>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
h2
|
||||
font-weight: 600
|
||||
margin-bottom: 20px
|
||||
font-size: 14px
|
||||
|
||||
table
|
||||
width: 100%
|
||||
background: #f1f1f1
|
||||
border: 2px solid #f1f1f1
|
||||
+dark-mode
|
||||
background: #3c3c3c
|
||||
border: 2px solid #3c3c3c
|
||||
::v-deep(tr)
|
||||
&:nth-child(2n)
|
||||
background: #fff
|
||||
+dark-mode
|
||||
background: #333
|
||||
th
|
||||
text-align: left
|
||||
font-weight: 600
|
||||
th, td
|
||||
padding: 8px
|
||||
|
||||
</style>
|
@ -1,9 +1,11 @@
|
||||
<script>
|
||||
import { toRaw} from 'vue';
|
||||
import { LANGUAGES } from '../../editor/languages.js'
|
||||
|
||||
import KeyboardHotkey from "./KeyboardHotkey.vue"
|
||||
import TabListItem from "./TabListItem.vue"
|
||||
import TabContent from "./TabContent.vue"
|
||||
import KeyboardBindings from './KeyboardBindings.vue'
|
||||
|
||||
const defaultFontFamily = window.heynote.defaultFontFamily
|
||||
const defaultFontSize = window.heynote.defaultFontSize
|
||||
@ -20,15 +22,18 @@
|
||||
KeyboardHotkey,
|
||||
TabListItem,
|
||||
TabContent,
|
||||
KeyboardBindings,
|
||||
},
|
||||
|
||||
data() {
|
||||
console.log("settings:", this.initialSettings)
|
||||
return {
|
||||
keymaps: [
|
||||
{ name: "Default", value: "default" },
|
||||
{ name: "Emacs", value: "emacs" },
|
||||
],
|
||||
keymap: this.initialSettings.keymap,
|
||||
keyBindings: this.initialSettings.keyBindings,
|
||||
metaKey: this.initialSettings.emacsMetaKey,
|
||||
isMac: window.heynote.platform.isMac,
|
||||
showLineNumberGutter: this.initialSettings.showLineNumberGutter,
|
||||
@ -93,6 +98,7 @@
|
||||
showLineNumberGutter: this.showLineNumberGutter,
|
||||
showFoldGutter: this.showFoldGutter,
|
||||
keymap: this.keymap,
|
||||
keyBindings: toRaw(this.keyBindings),
|
||||
emacsMetaKey: window.heynote.platform.isMac ? this.metaKey : "alt",
|
||||
allowBetaVersions: this.allowBetaVersions,
|
||||
enableGlobalHotkey: this.enableGlobalHotkey,
|
||||
@ -159,6 +165,12 @@
|
||||
:activeTab="activeTab"
|
||||
@click="activeTab = 'appearance'"
|
||||
/>
|
||||
<TabListItem
|
||||
name="Key Bindings"
|
||||
tab="keyboard-bindings"
|
||||
:activeTab="activeTab"
|
||||
@click="activeTab = 'keyboard-bindings'"
|
||||
/>
|
||||
<TabListItem
|
||||
:name="isWebApp ? 'Version' : 'Updates'"
|
||||
tab="updates"
|
||||
@ -169,23 +181,6 @@
|
||||
</nav>
|
||||
<div class="settings-content">
|
||||
<TabContent tab="general" :activeTab="activeTab">
|
||||
<div class="row">
|
||||
<div class="entry">
|
||||
<h2>Keymap</h2>
|
||||
<select ref="keymapSelector" v-model="keymap" @change="updateSettings" class="keymap">
|
||||
<template v-for="km in keymaps" :key="km.value">
|
||||
<option :selected="km.value === keymap" :value="km.value">{{ km.name }}</option>
|
||||
</template>
|
||||
</select>
|
||||
</div>
|
||||
<div class="entry" v-if="keymap === 'emacs' && isMac">
|
||||
<h2>Meta Key</h2>
|
||||
<select v-model="metaKey" @change="updateSettings" class="metaKey">
|
||||
<option :selected="metaKey === 'meta'" value="meta">Command</option>
|
||||
<option :selected="metaKey === 'alt'" value="alt">Option</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" v-if="!isWebApp">
|
||||
<div class="entry">
|
||||
<h2>Global Keyboard Shortcut</h2>
|
||||
@ -354,6 +349,29 @@
|
||||
</div>
|
||||
</TabContent>
|
||||
|
||||
<TabContent tab="keyboard-bindings" :activeTab="activeTab">
|
||||
<div class="row">
|
||||
<div class="entry">
|
||||
<h2>Keymap</h2>
|
||||
<select ref="keymapSelector" v-model="keymap" @change="updateSettings" class="keymap">
|
||||
<template v-for="km in keymaps" :key="km.value">
|
||||
<option :selected="km.value === keymap" :value="km.value">{{ km.name }}</option>
|
||||
</template>
|
||||
</select>
|
||||
</div>
|
||||
<div class="entry" v-if="keymap === 'emacs' && isMac">
|
||||
<h2>Meta Key</h2>
|
||||
<select v-model="metaKey" @change="updateSettings" class="metaKey">
|
||||
<option :selected="metaKey === 'meta'" value="meta">Command</option>
|
||||
<option :selected="metaKey === 'alt'" value="alt">Option</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<KeyboardBindings
|
||||
:userKeys="keyBindings ? keyBindings : {}"
|
||||
/>
|
||||
</TabContent>
|
||||
|
||||
<TabContent tab="updates" :activeTab="activeTab">
|
||||
<div class="row">
|
||||
<div class="entry">
|
||||
@ -421,6 +439,8 @@
|
||||
background: rgba(0, 0, 0, 0.5)
|
||||
|
||||
.dialog
|
||||
--dialog-height: 560px
|
||||
--bottom-bar-height: 48px
|
||||
box-sizing: border-box
|
||||
z-index: 2
|
||||
position: absolute
|
||||
@ -428,7 +448,7 @@
|
||||
top: 50%
|
||||
transform: translate(-50%, -50%)
|
||||
width: 700px
|
||||
height: 560px
|
||||
height: var(--dialog-height)
|
||||
max-width: 100%
|
||||
max-height: 100%
|
||||
display: flex
|
||||
@ -448,6 +468,7 @@
|
||||
.dialog-content
|
||||
flex-grow: 1
|
||||
display: flex
|
||||
height: calc(var(--dialog-height) - var(--bottom-bar-height))
|
||||
.sidebar
|
||||
box-sizing: border-box
|
||||
width: 140px
|
||||
@ -521,6 +542,8 @@
|
||||
background: #222
|
||||
color: #aaa
|
||||
.bottom-bar
|
||||
box-sizing: border-box
|
||||
height: var(--bottom-bar-height)
|
||||
border-radius: 0 0 5px 5px
|
||||
background: #eee
|
||||
text-align: right
|
||||
|
@ -18,6 +18,7 @@
|
||||
li
|
||||
padding: 9px 20px
|
||||
font-size: 13px
|
||||
line-height: 1.3
|
||||
user-select: none
|
||||
cursor: pointer
|
||||
&:hover
|
||||
|
12
src/editor/close-brackets.js
Normal file
12
src/editor/close-brackets.js
Normal file
@ -0,0 +1,12 @@
|
||||
import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete'
|
||||
import { Prec } from "@codemirror/state"
|
||||
import { keymap } from "@codemirror/view"
|
||||
|
||||
export function getCloseBracketsExtensions() {
|
||||
return [
|
||||
closeBrackets(),
|
||||
Prec.highest(keymap.of([
|
||||
...closeBracketsKeymap,
|
||||
])),
|
||||
]
|
||||
}
|
122
src/editor/commands.js
Normal file
122
src/editor/commands.js
Normal file
@ -0,0 +1,122 @@
|
||||
import * as codeMirrorCommands from "@codemirror/commands"
|
||||
import {
|
||||
undo, redo,
|
||||
indentMore, indentLess,
|
||||
deleteCharBackward, deleteCharForward,
|
||||
deleteGroupBackward, deleteGroupForward,
|
||||
deleteLineBoundaryBackward, deleteLineBoundaryForward,
|
||||
deleteToLineEnd, deleteToLineStart,
|
||||
simplifySelection,
|
||||
splitLine,
|
||||
} from "@codemirror/commands"
|
||||
import { foldCode, unfoldCode } from "@codemirror/language"
|
||||
import { selectNextOccurrence } from "@codemirror/search"
|
||||
|
||||
import {
|
||||
addNewBlockAfterCurrent, addNewBlockBeforeCurrent, addNewBlockAfterLast, addNewBlockBeforeFirst, insertNewBlockAtCursor,
|
||||
gotoPreviousBlock, gotoNextBlock, selectNextBlock, selectPreviousBlock,
|
||||
gotoPreviousParagraph, gotoNextParagraph, selectNextParagraph, selectPreviousParagraph,
|
||||
moveLineUp, moveLineDown,
|
||||
selectAll,
|
||||
deleteBlock, deleteBlockSetCursorPreviousBlock,
|
||||
newCursorBelow, newCursorAbove,
|
||||
} from "./block/commands.js"
|
||||
import { deleteLine } from "./block/delete-line.js"
|
||||
import { formatBlockContent } from "./block/format-code.js"
|
||||
import { transposeChars } from "./block/transpose-chars.js"
|
||||
|
||||
import { cutCommand, copyCommand, pasteCommand } from "./copy-paste.js"
|
||||
|
||||
import { emacsMoveCommand, toggleEmacsMarkMode, emacsCancel } from "./emacs-mode.js"
|
||||
|
||||
|
||||
const cursorPreviousBlock = emacsMoveCommand(gotoPreviousBlock, selectPreviousBlock)
|
||||
const cursorNextBlock = emacsMoveCommand(gotoNextBlock, selectNextBlock)
|
||||
const cursorPreviousParagraph = emacsMoveCommand(gotoPreviousParagraph, selectPreviousParagraph)
|
||||
const cursorNextParagraph = emacsMoveCommand(gotoNextParagraph, selectNextParagraph)
|
||||
|
||||
|
||||
const openLanguageSelector = (editor) => () => {
|
||||
editor.openLanguageSelector()
|
||||
return true
|
||||
}
|
||||
const openBufferSelector = (editor) => () => {
|
||||
editor.openBufferSelector()
|
||||
return true
|
||||
}
|
||||
const openMoveToBuffer = (editor) => () => {
|
||||
editor.openMoveToBufferSelector()
|
||||
return true
|
||||
}
|
||||
const openCreateNewBuffer = (editor) => () => {
|
||||
editor.openCreateBuffer("new")
|
||||
return true
|
||||
}
|
||||
|
||||
const HEYNOTE_COMMANDS = {
|
||||
//undo,
|
||||
//redo,
|
||||
|
||||
addNewBlockAfterCurrent, addNewBlockBeforeCurrent, addNewBlockAfterLast, addNewBlockBeforeFirst, insertNewBlockAtCursor,
|
||||
cursorPreviousBlock, cursorNextBlock,
|
||||
cursorPreviousParagraph, cursorNextParagraph,
|
||||
deleteBlock, deleteBlockSetCursorPreviousBlock,
|
||||
|
||||
toggleEmacsMarkMode,
|
||||
emacsCancel,
|
||||
|
||||
openLanguageSelector,
|
||||
openBufferSelector,
|
||||
openMoveToBuffer,
|
||||
openCreateNewBuffer,
|
||||
|
||||
cut: cutCommand,
|
||||
copy: copyCommand,
|
||||
}
|
||||
|
||||
// emacs-mode:ify all cursor/select commands from CodeMirror
|
||||
for (let commandSuffix of [
|
||||
"CharLeft", "CharRight",
|
||||
"CharBackward", "CharForward",
|
||||
"LineUp", "LineDown",
|
||||
"LineStart", "LineEnd",
|
||||
"GroupLeft", "GroupRight",
|
||||
"GroupForward", "GroupBackward",
|
||||
"PageUp", "PageDown",
|
||||
"SyntaxLeft", "SyntaxRight",
|
||||
"SubwordBackward", "SubwordForward",
|
||||
"LineBoundaryBackward", "LineBoundaryForward",
|
||||
]) {
|
||||
HEYNOTE_COMMANDS[`cursor${commandSuffix}`] = emacsMoveCommand(codeMirrorCommands[`cursor${commandSuffix}`], codeMirrorCommands[`select${commandSuffix}`])
|
||||
HEYNOTE_COMMANDS[`select${commandSuffix}`] = (editor) => codeMirrorCommands[`select${commandSuffix}`]
|
||||
}
|
||||
|
||||
const NON_EDITOR_CONTEXT_COMMANDS = {
|
||||
selectAll,
|
||||
moveLineUp, moveLineDown,
|
||||
deleteLine,
|
||||
formatBlockContent,
|
||||
newCursorAbove, newCursorBelow,
|
||||
selectPreviousParagraph, selectNextParagraph,
|
||||
selectPreviousBlock, selectNextBlock,
|
||||
paste: pasteCommand,
|
||||
|
||||
// directly from CodeMirror
|
||||
undo, redo,
|
||||
indentMore, indentLess,
|
||||
foldCode, unfoldCode,
|
||||
selectNextOccurrence,
|
||||
deleteCharBackward, deleteCharForward,
|
||||
deleteGroupBackward, deleteGroupForward,
|
||||
deleteLineBoundaryBackward, deleteLineBoundaryForward,
|
||||
deleteToLineEnd, deleteToLineStart,
|
||||
simplifySelection,
|
||||
splitLine,
|
||||
transposeChars,
|
||||
}
|
||||
|
||||
for (const [key, cmCommand] of Object.entries(NON_EDITOR_CONTEXT_COMMANDS)) {
|
||||
HEYNOTE_COMMANDS[key] = (editor) => cmCommand
|
||||
}
|
||||
|
||||
export { HEYNOTE_COMMANDS }
|
@ -2,7 +2,6 @@ import { EditorState, EditorSelection } from "@codemirror/state"
|
||||
import { EditorView } from "@codemirror/view"
|
||||
|
||||
import { LANGUAGES } from './languages.js';
|
||||
import { setEmacsMarkMode } from "./emacs.js"
|
||||
|
||||
|
||||
const languageTokensMatcher = LANGUAGES.map(l => l.token).join("|")
|
||||
@ -61,7 +60,7 @@ export const heynoteCopyCut = (editor) => {
|
||||
}
|
||||
|
||||
// if we're in Emacs mode, we want to exit mark mode in case we're in it
|
||||
setEmacsMarkMode(false)
|
||||
editor.emacsMarkMode = false
|
||||
|
||||
// if Editor.deselectOnCopy is set (e.g. we're in Emacs mode), we want to remove the selection after we've copied the text
|
||||
if (editor.deselectOnCopy && event.type == "copy") {
|
||||
@ -95,7 +94,7 @@ const copyCut = (view, cut, editor) => {
|
||||
}
|
||||
|
||||
// if we're in Emacs mode, we want to exit mark mode in case we're in it
|
||||
setEmacsMarkMode(false)
|
||||
editor.emacsMarkMode = false
|
||||
|
||||
// if Editor.deselectOnCopy is set (e.g. we're in Emacs mode), we want to remove the selection after we've copied the text
|
||||
if (editor.deselectOnCopy && !cut) {
|
||||
@ -107,6 +106,7 @@ const copyCut = (view, cut, editor) => {
|
||||
selection: newSelection,
|
||||
}))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Annotation, EditorState, Compartment, Facet, EditorSelection, Transaction } from "@codemirror/state"
|
||||
import { Annotation, EditorState, Compartment, Facet, EditorSelection, Transaction, Prec } from "@codemirror/state"
|
||||
import { EditorView, keymap, drawSelection, ViewPlugin, lineNumbers } from "@codemirror/view"
|
||||
import { indentUnit, forceParsing, foldGutter, ensureSyntaxTree } from "@codemirror/language"
|
||||
import { markdown } from "@codemirror/lang-markdown"
|
||||
@ -11,12 +11,12 @@ import { heynoteBase } from "./theme/base.js"
|
||||
import { getFontTheme } from "./theme/font-theme.js";
|
||||
import { customSetup } from "./setup.js"
|
||||
import { heynoteLang } from "./lang-heynote/heynote.js"
|
||||
import { getCloseBracketsExtensions } from "./close-brackets.js"
|
||||
import { noteBlockExtension, blockLineNumbers, blockState, getActiveNoteBlock, triggerCursorChange } from "./block/block.js"
|
||||
import { heynoteEvent, SET_CONTENT, DELETE_BLOCK, APPEND_BLOCK, SET_FONT } from "./annotation.js";
|
||||
import { changeCurrentBlockLanguage, triggerCurrenciesLoaded, getBlockDelimiter, deleteBlock, selectAll } from "./block/commands.js"
|
||||
import { formatBlockContent } from "./block/format-code.js"
|
||||
import { heynoteKeymap } from "./keymap.js"
|
||||
import { emacsKeymap } from "./emacs.js"
|
||||
import { heynoteKeymap, DEFAULT_KEYMAP, EMACS_KEYMAP } from "./keymap.js"
|
||||
import { heynoteCopyCut } from "./copy-paste"
|
||||
import { languageDetection } from "./language-detection/autodetect.js"
|
||||
import { autoSaveContent } from "./save.js"
|
||||
@ -28,12 +28,12 @@ import { useHeynoteStore } from "../stores/heynote-store.js";
|
||||
import { useErrorStore } from "../stores/error-store.js";
|
||||
|
||||
|
||||
function getKeymapExtensions(editor, keymap) {
|
||||
if (keymap === "emacs") {
|
||||
return emacsKeymap(editor)
|
||||
} else {
|
||||
return heynoteKeymap(editor)
|
||||
}
|
||||
function getKeymapExtensions(editor, keymap, keyBindings) {
|
||||
return heynoteKeymap(
|
||||
editor,
|
||||
keymap === "emacs" ? EMACS_KEYMAP : DEFAULT_KEYMAP,
|
||||
keyBindings,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -53,6 +53,7 @@ export class HeynoteEditor {
|
||||
fontSize,
|
||||
defaultBlockToken,
|
||||
defaultBlockAutoDetect,
|
||||
keyBindings,
|
||||
}) {
|
||||
this.element = element
|
||||
this.path = path
|
||||
@ -71,20 +72,20 @@ export class HeynoteEditor {
|
||||
this.notesStore = useHeynoteStore()
|
||||
this.errorStore = useErrorStore()
|
||||
this.name = ""
|
||||
this.emacsMarkMode = false
|
||||
|
||||
|
||||
const state = EditorState.create({
|
||||
doc: "",
|
||||
extensions: [
|
||||
this.keymapCompartment.of(getKeymapExtensions(this, keymap)),
|
||||
this.keymapCompartment.of(getKeymapExtensions(this, keymap, keyBindings)),
|
||||
heynoteCopyCut(this),
|
||||
|
||||
//minimalSetup,
|
||||
this.lineNumberCompartment.of(showLineNumberGutter ? [lineNumbers(), blockLineNumbers] : []),
|
||||
customSetup,
|
||||
this.foldGutterCompartment.of(showFoldGutter ? [foldGutter()] : []),
|
||||
|
||||
this.closeBracketsCompartment.of(bracketClosing ? [closeBrackets()] : []),
|
||||
this.closeBracketsCompartment.of(bracketClosing ? [getCloseBracketsExtensions()] : []),
|
||||
|
||||
this.readOnlyCompartment.of([]),
|
||||
|
||||
@ -273,11 +274,11 @@ export class HeynoteEditor {
|
||||
})
|
||||
}
|
||||
|
||||
setKeymap(keymap, emacsMetaKey) {
|
||||
setKeymap(keymap, emacsMetaKey, keyBindings) {
|
||||
this.deselectOnCopy = keymap === "emacs"
|
||||
this.emacsMetaKey = emacsMetaKey
|
||||
this.view.dispatch({
|
||||
effects: this.keymapCompartment.reconfigure(getKeymapExtensions(this, keymap)),
|
||||
effects: this.keymapCompartment.reconfigure(getKeymapExtensions(this, keymap, keyBindings)),
|
||||
})
|
||||
}
|
||||
|
||||
@ -371,7 +372,7 @@ export class HeynoteEditor {
|
||||
|
||||
setBracketClosing(value) {
|
||||
this.view.dispatch({
|
||||
effects: this.closeBracketsCompartment.reconfigure(value ? [closeBrackets()] : []),
|
||||
effects: this.closeBracketsCompartment.reconfigure(value ? [getCloseBracketsExtensions()] : []),
|
||||
})
|
||||
}
|
||||
|
||||
|
37
src/editor/emacs-mode.js
Normal file
37
src/editor/emacs-mode.js
Normal file
@ -0,0 +1,37 @@
|
||||
import {
|
||||
simplifySelection,
|
||||
} from "@codemirror/commands"
|
||||
|
||||
/**
|
||||
* Takes a command that moves the cursor and a command that marks the selection, and returns a new command that
|
||||
* will run the mark command if we're in Emacs mark mode, or the move command otherwise.
|
||||
*/
|
||||
export function emacsMoveCommand(defaultCmd, markModeCmd) {
|
||||
return (editor) => {
|
||||
if (editor.emacsMarkMode) {
|
||||
return (view) => {
|
||||
markModeCmd(view)
|
||||
// we need to return true here instead of returning what the default command returns, since the default
|
||||
// codemirror select commands will return false if the selection doesn't change, which in turn will
|
||||
// make the default *move* command run which will kill the selection if we're in mark mode
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
return (view) => defaultCmd(view)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function toggleEmacsMarkMode(editor) {
|
||||
return (view) => {
|
||||
editor.emacsMarkMode = !editor.emacsMarkMode
|
||||
}
|
||||
}
|
||||
|
||||
export function emacsCancel(editor) {
|
||||
return (view) => {
|
||||
simplifySelection(view)
|
||||
editor.emacsMarkMode = false
|
||||
}
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
import { Direction} from "@codemirror/view"
|
||||
import { EditorSelection, EditorState, Prec } from "@codemirror/state"
|
||||
import {
|
||||
undo, redo,
|
||||
cursorGroupLeft, cursorGroupRight, selectGroupLeft, selectGroupRight,
|
||||
simplifySelection,
|
||||
deleteCharForward, deleteCharBackward, deleteToLineEnd,
|
||||
splitLine,
|
||||
transposeChars,
|
||||
cursorPageDown,
|
||||
cursorCharLeft, selectCharLeft,
|
||||
cursorCharRight, selectCharRight,
|
||||
cursorLineUp, selectLineUp,
|
||||
cursorLineDown, selectLineDown,
|
||||
cursorLineStart, selectLineStart,
|
||||
cursorLineEnd, selectLineEnd,
|
||||
} from "@codemirror/commands"
|
||||
import { heynoteKeymap, keymapFromSpec } from "./keymap.js"
|
||||
|
||||
import {
|
||||
gotoPreviousBlock, gotoNextBlock,
|
||||
selectNextBlock, selectPreviousBlock,
|
||||
gotoPreviousParagraph, gotoNextParagraph,
|
||||
selectNextParagraph, selectPreviousParagraph,
|
||||
selectAll,
|
||||
} from "./block/commands.js"
|
||||
import { pasteCommand, copyCommand, cutCommand } from "./copy-paste.js"
|
||||
|
||||
|
||||
// if set to true, all keybindings for moving around is changed to their corresponding select commands
|
||||
let emacsMarkMode = false
|
||||
|
||||
export function setEmacsMarkMode(value) {
|
||||
emacsMarkMode = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a command that will conditionally execute either the default command or the mark mode command
|
||||
*
|
||||
* @param defaultCmd Default command to execute
|
||||
* @param {*} markModeCmd Command to execute if mark mode is active
|
||||
*/
|
||||
function emacsMoveCommand(defaultCmd, markModeCmd) {
|
||||
return (view) => emacsMarkMode ? markModeCmd(view) : defaultCmd(view)
|
||||
}
|
||||
|
||||
/**
|
||||
* C-g command that exits mark mode and simplifies selection
|
||||
*/
|
||||
function emacsCancel(view) {
|
||||
simplifySelection(view)
|
||||
setEmacsMarkMode(false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit mark mode before executing selectAll command
|
||||
*/
|
||||
function emacsSelectAll(view) {
|
||||
setEmacsMarkMode(false)
|
||||
return selectAll(view)
|
||||
}
|
||||
|
||||
|
||||
function emacsMetaKeyCommand(key, editor, command) {
|
||||
const handler = (view, event) => {
|
||||
if (editor.emacsMetaKey === "meta" && event.metaKey || editor.emacsMetaKey === "alt" && event.altKey) {
|
||||
event.preventDefault()
|
||||
return command(view)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return [
|
||||
{key, run:handler, preventDefault:false},
|
||||
{key:key.replace("Meta", "Alt"), run:handler, preventDefault:false},
|
||||
]
|
||||
}
|
||||
|
||||
export function emacsKeymap(editor) {
|
||||
return [
|
||||
heynoteKeymap(editor),
|
||||
Prec.highest(keymapFromSpec([
|
||||
["Ctrl-Shift--", undo],
|
||||
["Ctrl-.", redo],
|
||||
["Ctrl-g", emacsCancel],
|
||||
["ArrowLeft", emacsMoveCommand(cursorCharLeft, selectCharLeft)],
|
||||
["ArrowRight", emacsMoveCommand(cursorCharRight, selectCharRight)],
|
||||
["ArrowUp", emacsMoveCommand(cursorLineUp, selectLineUp)],
|
||||
["ArrowDown", emacsMoveCommand(cursorLineDown, selectLineDown)],
|
||||
{key: "Ctrl-ArrowLeft", run: emacsMoveCommand(cursorGroupLeft, selectGroupLeft), shift: selectGroupLeft},
|
||||
{key: "Ctrl-ArrowRight", run: emacsMoveCommand(cursorGroupRight, selectGroupRight), shift: selectGroupRight},
|
||||
|
||||
["Ctrl-d", deleteCharForward],
|
||||
["Ctrl-h", deleteCharBackward],
|
||||
["Ctrl-k", deleteToLineEnd],
|
||||
["Ctrl-o", splitLine],
|
||||
["Ctrl-t", transposeChars],
|
||||
["Ctrl-v", cursorPageDown],
|
||||
|
||||
["Ctrl-y", pasteCommand],
|
||||
["Ctrl-w", cutCommand(editor)],
|
||||
...emacsMetaKeyCommand("Meta-w", editor, copyCommand(editor)),
|
||||
|
||||
{ key: "Ctrl-b", run: emacsMoveCommand(cursorCharLeft, selectCharLeft), shift: selectCharLeft },
|
||||
{ key: "Ctrl-f", run: emacsMoveCommand(cursorCharRight, selectCharRight), shift: selectCharRight },
|
||||
{ key: "Ctrl-a", run: emacsMoveCommand(cursorLineStart, selectLineStart), shift: selectLineStart },
|
||||
{ key: "Ctrl-e", run: emacsMoveCommand(cursorLineEnd, selectLineEnd), shift: selectLineEnd },
|
||||
])),
|
||||
|
||||
Prec.highest(keymapFromSpec([
|
||||
["Ctrl-Space", (view) => { emacsMarkMode = !emacsMarkMode }],
|
||||
["Mod-a", emacsSelectAll],
|
||||
{key:"Mod-ArrowUp", run:emacsMoveCommand(gotoPreviousBlock, selectPreviousBlock), shift:selectPreviousBlock},
|
||||
{key:"Mod-ArrowDown", run:emacsMoveCommand(gotoNextBlock, selectNextBlock), shift:selectNextBlock},
|
||||
{key:"Ctrl-ArrowUp", run:emacsMoveCommand(gotoPreviousParagraph, selectPreviousParagraph), shift:selectPreviousParagraph},
|
||||
{key:"Ctrl-ArrowDown", run:emacsMoveCommand(gotoNextParagraph, selectNextParagraph), shift:selectNextParagraph},
|
||||
])),
|
||||
]
|
||||
}
|
@ -1,75 +1,162 @@
|
||||
import { keymap } from "@codemirror/view"
|
||||
//import { EditorSelection, EditorState } from "@codemirror/state"
|
||||
import {
|
||||
indentLess, indentMore, redo,
|
||||
} from "@codemirror/commands"
|
||||
import { Prec } from "@codemirror/state"
|
||||
|
||||
import {
|
||||
insertNewBlockAtCursor,
|
||||
addNewBlockBeforeCurrent, addNewBlockAfterCurrent,
|
||||
addNewBlockBeforeFirst, addNewBlockAfterLast,
|
||||
moveLineUp, moveLineDown,
|
||||
selectAll,
|
||||
gotoPreviousBlock, gotoNextBlock,
|
||||
selectNextBlock, selectPreviousBlock,
|
||||
gotoPreviousParagraph, gotoNextParagraph,
|
||||
selectNextParagraph, selectPreviousParagraph,
|
||||
newCursorBelow, newCursorAbove,
|
||||
deleteBlock,
|
||||
} from "./block/commands.js"
|
||||
import { pasteCommand, copyCommand, cutCommand } from "./copy-paste.js"
|
||||
|
||||
import { formatBlockContent } from "./block/format-code.js"
|
||||
import { deleteLine } from "./block/delete-line.js"
|
||||
import { HEYNOTE_COMMANDS } from "./commands.js"
|
||||
|
||||
|
||||
export function keymapFromSpec(specs) {
|
||||
function keymapFromSpec(specs, editor) {
|
||||
return keymap.of(specs.map((spec) => {
|
||||
if (spec.run) {
|
||||
if ("preventDefault" in spec) {
|
||||
return spec
|
||||
} else {
|
||||
return {...spec, preventDefault: true}
|
||||
}
|
||||
} else {
|
||||
const [key, run] = spec
|
||||
return {
|
||||
key,
|
||||
run,
|
||||
preventDefault: true,
|
||||
}
|
||||
let key = spec.key
|
||||
if (key.indexOf("EmacsMeta") != -1) {
|
||||
key = key.replace("EmacsMeta", editor.emacsMetaKey === "alt" ? "Alt" : "Meta")
|
||||
}
|
||||
return {
|
||||
key: key,
|
||||
//preventDefault: true,
|
||||
preventDefault: false,
|
||||
run: (view) => {
|
||||
//console.log("run()", spec.key, spec.command)
|
||||
const command = HEYNOTE_COMMANDS[spec.command]
|
||||
if (!command) {
|
||||
console.error(`Command not found: ${spec.command} (${spec.key})`)
|
||||
return false
|
||||
}
|
||||
return command(editor)(view)
|
||||
},
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
export function heynoteKeymap(editor) {
|
||||
return keymapFromSpec([
|
||||
["Mod-c", copyCommand(editor)],
|
||||
["Mod-v", pasteCommand],
|
||||
["Mod-x", cutCommand(editor)],
|
||||
["Tab", indentMore],
|
||||
["Shift-Tab", indentLess],
|
||||
["Alt-Shift-Enter", addNewBlockBeforeFirst(editor)],
|
||||
["Mod-Shift-Enter", addNewBlockAfterLast(editor)],
|
||||
["Alt-Enter", addNewBlockBeforeCurrent(editor)],
|
||||
["Mod-Enter", addNewBlockAfterCurrent(editor)],
|
||||
["Mod-Alt-Enter", insertNewBlockAtCursor(editor)],
|
||||
["Mod-a", selectAll],
|
||||
["Alt-ArrowUp", moveLineUp],
|
||||
["Alt-ArrowDown", moveLineDown],
|
||||
["Mod-l", () => editor.openLanguageSelector()],
|
||||
["Mod-p", () => editor.openBufferSelector()],
|
||||
["Mod-s", () => editor.openMoveToBufferSelector()],
|
||||
["Mod-n", () => editor.openCreateBuffer("new")],
|
||||
["Mod-Shift-d", deleteBlock(editor)],
|
||||
["Alt-Shift-f", formatBlockContent],
|
||||
["Mod-Alt-ArrowDown", newCursorBelow],
|
||||
["Mod-Alt-ArrowUp", newCursorAbove],
|
||||
["Mod-Shift-k", deleteLine],
|
||||
["Mod-Shift-z", redo],
|
||||
{key:"Mod-ArrowUp", run:gotoPreviousBlock, shift:selectPreviousBlock},
|
||||
{key:"Mod-ArrowDown", run:gotoNextBlock, shift:selectNextBlock},
|
||||
{key:"Ctrl-ArrowUp", run:gotoPreviousParagraph, shift:selectPreviousParagraph},
|
||||
{key:"Ctrl-ArrowDown", run:gotoNextParagraph, shift:selectNextParagraph},
|
||||
])
|
||||
const cmd = (key, command) => ({key, command})
|
||||
const cmdShift = (key, command, shiftCommand) => {
|
||||
return [
|
||||
cmd(key, command),
|
||||
cmd(`Shift-${key}`, shiftCommand),
|
||||
]
|
||||
}
|
||||
|
||||
const isMac = window.heynote.platform.isMac
|
||||
const isLinux = window.heynote.platform.isLinux
|
||||
const isWindows = window.heynote.platform.isWindows
|
||||
|
||||
export const DEFAULT_KEYMAP = [
|
||||
cmd("Mod-a", "selectAll"),
|
||||
cmd("Mod-Enter", "addNewBlockAfterCurrent"),
|
||||
cmd("Mod-Shift-Enter", "addNewBlockAfterLast"),
|
||||
cmd("Alt-Enter", "addNewBlockBeforeCurrent"),
|
||||
cmd("Alt-Shift-Enter", "addNewBlockBeforeFirst"),
|
||||
cmd("Mod-Alt-Enter", "insertNewBlockAtCursor"),
|
||||
...cmdShift("ArrowLeft", "cursorCharLeft", "selectCharLeft"),
|
||||
...cmdShift("ArrowRight", "cursorCharRight", "selectCharRight"),
|
||||
...cmdShift("ArrowUp", "cursorLineUp", "selectLineUp"),
|
||||
...cmdShift("ArrowDown", "cursorLineDown", "selectLineDown"),
|
||||
...cmdShift("Ctrl-ArrowLeft", "cursorGroupLeft", "selectGroupLeft"),
|
||||
...cmdShift("Ctrl-ArrowRight", "cursorGroupRight", "selectGroupRight"),
|
||||
...cmdShift("Alt-ArrowLeft", "cursorGroupLeft", "selectGroupLeft"),
|
||||
...cmdShift("Alt-ArrowRight", "cursorGroupRight", "selectGroupRight"),
|
||||
...cmdShift("Mod-ArrowUp", "cursorPreviousBlock", "selectPreviousBlock"),
|
||||
...cmdShift("Mod-ArrowDown", "cursorNextBlock", "selectNextBlock"),
|
||||
...cmdShift("Ctrl-ArrowUp", "cursorPreviousParagraph", "selectPreviousParagraph"),
|
||||
...cmdShift("Ctrl-ArrowDown", "cursorNextParagraph", "selectNextParagraph"),
|
||||
...cmdShift("PageUp", "cursorPageUp", "selectPageUp"),
|
||||
...cmdShift("PageDown", "cursorPageDown", "selectPageDown"),
|
||||
...cmdShift("Home", "cursorLineBoundaryBackward", "selectLineBoundaryBackward"),
|
||||
...cmdShift("End", "cursorLineBoundaryForward", "selectLineBoundaryForward"),
|
||||
cmd("Backspace", "deleteCharBackward"),
|
||||
cmd("Delete", "deleteCharForward"),
|
||||
cmd("Escape", "simplifySelection"),
|
||||
cmd("Ctrl-Backspace", "deleteGroupBackward"),
|
||||
cmd("Ctrl-Delete", "deleteGroupForward"),
|
||||
...(isMac ? [
|
||||
cmd("Alt-Backspace", "deleteGroupBackward"),
|
||||
cmd("Alt-Delete", "deleteGroupForward"),
|
||||
cmd("Mod-Backspace", "deleteLineBoundaryBackward"),
|
||||
cmd("Mod-Delete", "deleteLineBoundaryForward"),
|
||||
] : []),
|
||||
|
||||
cmd("Alt-ArrowUp", "moveLineUp"),
|
||||
cmd("Alt-ArrowDown", "moveLineDown"),
|
||||
cmd("Mod-Shift-k", "deleteLine"),
|
||||
cmd("Mod-Alt-ArrowDown", "newCursorBelow"),
|
||||
cmd("Mod-Alt-ArrowUp", "newCursorAbove"),
|
||||
cmd("Mod-Shift-d", "deleteBlock"),
|
||||
cmd("Mod-d", "selectNextOccurrence"),
|
||||
cmd(isMac ? "Cmd-Alt-[" : "Ctrl-Shift-[", "foldCode"),
|
||||
cmd(isMac ? "Cmd-Alt-]" : "Ctrl-Shift-]", "unfoldCode"),
|
||||
|
||||
cmd("Mod-c", "copy"),
|
||||
cmd("Mod-v", "paste"),
|
||||
cmd("Mod-x", "cut"),
|
||||
cmd("Mod-z", "undo"),
|
||||
cmd("Mod-Shift-z", "redo"),
|
||||
...(isWindows || isLinux ? [
|
||||
cmd("Mod-y", "redo"),
|
||||
] : []),
|
||||
|
||||
cmd("Tab", "indentMore"),
|
||||
cmd("Shift-Tab", "indentLess"),
|
||||
//cmd("Alt-ArrowLeft", "cursorSubwordBackward"),
|
||||
//cmd("Alt-ArrowRight", "cursorSubwordForward"),
|
||||
cmd("Ctrl-Space", "toggleEmacsMarkMode"),
|
||||
cmd("Ctrl-g", "emacsCancel"),
|
||||
|
||||
cmd("Mod-l", "openLanguageSelector"),
|
||||
cmd("Mod-p", "openBufferSelector"),
|
||||
cmd("Mod-s", "openMoveToBuffer"),
|
||||
cmd("Mod-n", "openCreateNewBuffer"),
|
||||
|
||||
cmd("Alt-Shift-f", "formatBlockContent"),
|
||||
|
||||
// search
|
||||
//cmd("Mod-f", "openSearchPanel"),
|
||||
//cmd("F3", "findNext"),
|
||||
//cmd("Mod-g", "findNext"),
|
||||
//cmd("Shift-F3", "findPrevious"),
|
||||
//cmd("Shift-Mod-g", "findPrevious"),
|
||||
//cmd("Mod-Alt-g", "gotoLine"),
|
||||
//cmd("Mod-d", "selectNextOccurrence"),
|
||||
/*
|
||||
- Mod-f: [`openSearchPanel`](https://codemirror.net/6/docs/ref/#search.openSearchPanel)
|
||||
- F3, Mod-g: [`findNext`](https://codemirror.net/6/docs/ref/#search.findNext)
|
||||
- Shift-F3, Shift-Mod-g: [`findPrevious`](https://codemirror.net/6/docs/ref/#search.findPrevious)
|
||||
- Mod-Alt-g: [`gotoLine`](https://codemirror.net/6/docs/ref/#search.gotoLine)
|
||||
- Mod-d: [`selectNextOccurrence`](https://codemirror.net/6/docs/ref/#search.selectNextOccurrence)
|
||||
*/
|
||||
]
|
||||
|
||||
export const EMACS_KEYMAP = [
|
||||
cmd("Ctrl-w", "cut"),
|
||||
cmd("Ctrl-y", "paste"),
|
||||
cmd("EmacsMeta-w", "copy"),
|
||||
cmd("Ctrl-Space", "toggleEmacsMarkMode"),
|
||||
cmd("Ctrl-g", "emacsCancel"),
|
||||
cmd("Escape", "emacsCancel"),
|
||||
cmd("Ctrl-o", "splitLine"),
|
||||
cmd("Ctrl-d", "deleteCharForward"),
|
||||
cmd("Ctrl-h", "deleteCharBackward"),
|
||||
cmd("Ctrl-k", "deleteToLineEnd"),
|
||||
cmd("Ctrl-t", "transposeChars"),
|
||||
cmd("Ctrl-Shift--", "undo"),
|
||||
cmd("Ctrl-.", "redo"),
|
||||
...cmdShift("Ctrl-v", "cursorPageDown", "selectPageDown"),
|
||||
...cmdShift("Ctrl-b", "cursorCharLeft", "selectCharLeft"),
|
||||
...cmdShift("Ctrl-f", "cursorCharRight", "selectCharRight"),
|
||||
...cmdShift("Ctrl-a", "cursorLineStart", "selectLineStart"),
|
||||
...cmdShift("Ctrl-e", "cursorLineEnd", "selectLineEnd"),
|
||||
...DEFAULT_KEYMAP,
|
||||
]
|
||||
|
||||
|
||||
|
||||
export function heynoteKeymap(editor, keymap, userKeymap) {
|
||||
// merge the default keymap with the custom keymap
|
||||
const defaultKeys = Object.fromEntries(keymap.map(km => [km.key, km.command]))
|
||||
//let mergedKeys = Object.entries({...defaultKeys, ...Object.fromEntries(userKeymap.map(km => [km.key, km.command]))}).map(([key, command]) => cmd(key, command))
|
||||
let mergedKeys = Object.entries({...defaultKeys, ...userKeymap}).map(([key, command]) => cmd(key, command))
|
||||
//console.log("userKeys:", userKeymap)
|
||||
//console.log("mergedKeys:", mergedKeys)
|
||||
|
||||
return [
|
||||
Prec.high(keymapFromSpec(mergedKeys, editor)),
|
||||
]
|
||||
}
|
||||
|
@ -68,11 +68,12 @@ const customSetup = /*@__PURE__*/(() => [
|
||||
EditorView.lineWrapping,
|
||||
scrollPastEnd(),
|
||||
keymap.of([
|
||||
...closeBracketsKeymap,
|
||||
...defaultKeymap,
|
||||
//...closeBracketsKeymap,
|
||||
//...defaultKeymap,
|
||||
...searchKeymap,
|
||||
...historyKeymap,
|
||||
...foldKeymap,
|
||||
//...historyKeymap,
|
||||
//...foldKeymap,
|
||||
|
||||
//...completionKeymap,
|
||||
//...lintKeymap
|
||||
])
|
||||
|
@ -38,6 +38,7 @@ export const useEditorCacheStore = defineStore("editorCache", {
|
||||
fontSize: settingsStore.settings.fontSize,
|
||||
defaultBlockToken: settingsStore.settings.defaultBlockLanguage,
|
||||
defaultBlockAutoDetect: settingsStore.settings.defaultBlockLanguageAutoDetect,
|
||||
keyBindings: settingsStore.settings.keyBindings,
|
||||
})
|
||||
} catch (e) {
|
||||
errorStore.addError("Error! " + e.message)
|
||||
@ -122,7 +123,8 @@ export const useEditorCacheStore = defineStore("editorCache", {
|
||||
switch (key) {
|
||||
case "keymap":
|
||||
case "emacsMetaKey":
|
||||
editor.setKeymap(newSettings.keymap, newSettings.emacsMetaKey)
|
||||
case "keyBindings":
|
||||
editor.setKeymap(newSettings.keymap, newSettings.emacsMetaKey, newSettings.keyBindings)
|
||||
break
|
||||
case "showLineNumberGutter":
|
||||
editor.setLineNumberGutter(newSettings.showLineNumberGutter)
|
||||
|
@ -13,6 +13,7 @@ test.beforeEach(async ({ page, browserName }) => {
|
||||
test.skip()
|
||||
}
|
||||
await page.locator("css=.status-block.settings").click()
|
||||
await page.locator("css=li.tab-keyboard-bindings").click()
|
||||
//await page.locator("css=li.tab-editing").click()
|
||||
await page.locator("css=select.keymap").selectOption("emacs")
|
||||
if (heynotePage.isMac) {
|
||||
|
Reference in New Issue
Block a user