diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6fea7a5..ba97155 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,15 @@
# Changelog
+## 0.7.2 - 2024-02-25
+
+### ✨ Introduce new features
+
+- Support multiple cursors for arrow / shift / ctrl / ctrl-shift + arrow keys
+
+### 📝 Add or update documentation
+
+- Add an example using ref
+
## 0.7.1 - 2024-02-24
### ✨ Introduce new features
diff --git a/CodeMirror6/CodeMirror6.csproj b/CodeMirror6/CodeMirror6.csproj
index 3fad641..68bd8ad 100644
--- a/CodeMirror6/CodeMirror6.csproj
+++ b/CodeMirror6/CodeMirror6.csproj
@@ -9,7 +9,7 @@
GaelJ.BlazorCodeMirror6
true
GaelJ.BlazorCodeMirror6
- 0.7.1
+ 0.7.2
true
snupkg
true
diff --git a/CodeMirror6/NodeLib/src/CmColumns.ts b/CodeMirror6/NodeLib/src/CmColumns.ts
index 4547248..f6ed22c 100644
--- a/CodeMirror6/NodeLib/src/CmColumns.ts
+++ b/CodeMirror6/NodeLib/src/CmColumns.ts
@@ -114,11 +114,11 @@ export function columnStylingPlugin(separator: string): Extension {
if (e.ctrlKey === true || e.metaKey === true || e.altKey === true || e.shiftKey === true)
return
if (e.key === "ArrowLeft") {
- moveCursors(view, true, separator)
+ moveCursorsByColumn(view, true, separator)
e.preventDefault()
}
else if (e.key === "ArrowRight") {
- moveCursors(view, false, separator)
+ moveCursorsByColumn(view, false, separator)
e.preventDefault()
}
}
@@ -128,12 +128,12 @@ export function columnStylingPlugin(separator: string): Extension {
export const getColumnStylingKeymap = (separator: string): KeyBinding[] => [
{ key: 'Tab', run: (view) => {
- moveCursors(view, false, separator)
+ moveCursorsByColumn(view, false, separator)
insertTabulationAtEndOfDocumentIfSelectionAtEnd(view)
return true
}},
{ key: 'Shift-Tab', run: (view) => {
- moveCursors(view, true, separator)
+ moveCursorsByColumn(view, true, separator)
return true
}},
]
@@ -167,7 +167,7 @@ export async function columnLintSource(id: string, view: EditorView, separator:
}
}
-function moveCursors(view: EditorView, previous: boolean, separator: string) {
+function moveCursorsByColumn(view: EditorView, previous: boolean, separator: string) {
const { state } = view
const newSelectionRanges: SelectionRange[] = []
for (const range of state.selection.ranges) {
diff --git a/CodeMirror6/NodeLib/src/CmKeymap.ts b/CodeMirror6/NodeLib/src/CmKeymap.ts
index 311ede3..d5ec61a 100644
--- a/CodeMirror6/NodeLib/src/CmKeymap.ts
+++ b/CodeMirror6/NodeLib/src/CmKeymap.ts
@@ -1,6 +1,11 @@
import { toggleMarkdownBold, toggleMarkdownItalic } from "./CmCommands"
-import { KeyBinding } from '@codemirror/view';
-import { deleteCharBackward, deleteCharForward, deleteGroupBackward, deleteGroupForward } from '@codemirror/commands'
+import { KeyBinding, EditorView } from '@codemirror/view'
+import { Extension, RangeSetBuilder, Transaction, EditorSelection, SelectionRange, Text } from "@codemirror/state"
+import {
+ deleteCharBackward, deleteCharForward, deleteGroupBackward, deleteGroupForward,
+ cursorGroupLeft, cursorGroupRight, selectGroupLeft, selectGroupRight,
+ cursorCharLeft, cursorCharRight, selectCharLeft, selectCharRight,
+} from '@codemirror/commands'
export const customMarkdownKeymap: KeyBinding[] = [
{ key: 'Mod-b', run: toggleMarkdownBold }, // Cmd/Ctrl + B for bold
@@ -13,3 +18,86 @@ export const customDeleteKeymap = [
{ key: "Mod-Delete", run: deleteGroupForward },
{ key: "Mod-Backspace", run: deleteGroupBackward },
]
+
+export const customArrowKeymap: KeyBinding[] = [
+ {
+ key: "ArrowLeft",
+ run: (view) => moveCursorsByCharacter(view, true, false),
+ shift: (view) => moveCursorsByCharacter(view, true, true),
+ },
+ {
+ key: "ArrowRight",
+ run: (view) => moveCursorsByCharacter(view, false, false),
+ shift: (view) => moveCursorsByCharacter(view, false, true),
+ },
+ {
+ key: "Mod-ArrowLeft",
+ run: (view) => moveCursorsByWord(view, true, false),
+ shift: (view) => moveCursorsByWord(view, true, true),
+ },
+ {
+ key: "Mod-ArrowRight",
+ run: (view) => moveCursorsByWord(view, false, false),
+ shift: (view) => moveCursorsByWord(view, false, true),
+ },
+]
+
+function moveCursorsByCharacter(view: EditorView, previous: boolean, headOnly: boolean) {
+ const { state } = view
+ const newSelectionRanges: SelectionRange[] = []
+ for (const range of state.selection.ranges) {
+ const offset = previous ? -1 : 1
+ const newAnchor = headOnly ? range.anchor : Math.max(Math.min(state.doc.length, range.head + offset), 0)
+ const newHead = !headOnly ? newAnchor : Math.max(Math.min(state.doc.length, range.head + offset), 0)
+ newSelectionRanges.push(EditorSelection.range(newAnchor, newHead))
+ }
+ view.dispatch(state.update({
+ selection: EditorSelection.create(newSelectionRanges),
+ scrollIntoView: true,
+ userEvent: 'input'
+ }))
+ return true
+}
+
+function moveCursorsByWord(view: EditorView, previous: boolean, headOnly: boolean): boolean {
+ const { state } = view
+ const newSelectionRanges: SelectionRange[] = []
+
+ for (const range of state.selection.ranges) {
+ const currentPos = range.head
+ const wordBoundary = findWordBoundary(state.doc, currentPos, previous, true)
+
+ const newAnchor = headOnly ? range.anchor : wordBoundary
+ const newHead = !headOnly ? newAnchor : wordBoundary
+
+ newSelectionRanges.push(EditorSelection.range(newAnchor, newHead))
+ }
+ view.dispatch(state.update({
+ selection: EditorSelection.create(newSelectionRanges),
+ scrollIntoView: true,
+ userEvent: 'input'
+ }))
+ return true
+}
+
+function findWordBoundary(doc: Text, pos: number, previous: boolean, firstRun: boolean): number {
+ if (previous && pos === 0) return 0
+ if (!previous && pos === doc.length) return doc.length
+ if (isWordBoundary(doc, pos) && firstRun) {
+ pos += previous ? -1 : 1
+ return findWordBoundary(doc, pos, previous, false)
+ }
+ for (let i = pos; previous ? i >= 0 : i < doc.length; i += (previous ? -1 : 1)) {
+ if (isWordBoundary(doc, i)) {
+ return i
+ }
+ }
+ return previous ? 0 : doc.length
+}
+
+function isWordBoundary(doc: Text, pos: number): boolean {
+ if (pos < 0 || pos >= doc.length) return true
+ const charBefore = doc.sliceString(pos - 1, pos)
+ const charAfter = doc.sliceString(pos, pos + 1)
+ return /\s/.test(charBefore) !== /\s/.test(charAfter)
+}
diff --git a/CodeMirror6/NodeLib/src/index.ts b/CodeMirror6/NodeLib/src/index.ts
index 0eabf39..b4d1c5d 100644
--- a/CodeMirror6/NodeLib/src/index.ts
+++ b/CodeMirror6/NodeLib/src/index.ts
@@ -61,7 +61,7 @@ import { getColumnStylingKeymap, columnStylingPlugin, columnLintSource, getSepar
import { consoleLog } from "./CmLogging"
import { createEditorWithId } from "./CmId"
import { hyperLink } from './CmHyperlink'
-import { customDeleteKeymap } from "./CmKeymap"
+import { customArrowKeymap, customDeleteKeymap } from "./CmKeymap"
export { csvToMarkdownTable, getCmInstance, cut, copy, paste }
@@ -181,6 +181,7 @@ export async function initCodeMirror(
...completionKeymap,
...lintKeymap,
...customDeleteKeymap,
+ ...customArrowKeymap,
])
]
diff --git a/Examples.BlazorServer/Examples.BlazorServer.csproj b/Examples.BlazorServer/Examples.BlazorServer.csproj
index df8cd6a..aeb8832 100644
--- a/Examples.BlazorServer/Examples.BlazorServer.csproj
+++ b/Examples.BlazorServer/Examples.BlazorServer.csproj
@@ -4,7 +4,7 @@
enable
false
enable
- 0.7.1
+ 0.7.2
diff --git a/Examples.BlazorServerInteractive/Examples.BlazorServerInteractive.csproj b/Examples.BlazorServerInteractive/Examples.BlazorServerInteractive.csproj
index 5317e9e..fbc9bc4 100644
--- a/Examples.BlazorServerInteractive/Examples.BlazorServerInteractive.csproj
+++ b/Examples.BlazorServerInteractive/Examples.BlazorServerInteractive.csproj
@@ -4,7 +4,7 @@
enable
enable
false
- 0.7.1
+ 0.7.2
diff --git a/Examples.BlazorWasm/Examples.BlazorWasm.csproj b/Examples.BlazorWasm/Examples.BlazorWasm.csproj
index 572aec0..3d4547a 100644
--- a/Examples.BlazorWasm/Examples.BlazorWasm.csproj
+++ b/Examples.BlazorWasm/Examples.BlazorWasm.csproj
@@ -4,7 +4,7 @@
enable
enable
false
- 0.7.1
+ 0.7.2
diff --git a/Examples.Common/Example.razor b/Examples.Common/Example.razor
index 570f316..9d9410d 100644
--- a/Examples.Common/Example.razor
+++ b/Examples.Common/Example.razor
@@ -106,7 +106,15 @@
})
>Change local storage key
+
+
enable
enable
false
- 0.7.1
+ 0.7.2
diff --git a/NEW_CHANGELOG.md b/NEW_CHANGELOG.md
index 82258d9..575b317 100644
--- a/NEW_CHANGELOG.md
+++ b/NEW_CHANGELOG.md
@@ -1,15 +1,7 @@
### ✨ Introduce new features
-- Add DeleteTrailingWhitespace command
-- Export clipboard functions (#144)
-- Tab inserts a tabulation when selection is at the end of document, in csv / tsv modes
+- Support multiple cursors for arrow / shift / ctrl / ctrl-shift + arrow keys
-### 🐛 Fix a bug
+### 📝 Add or update documentation
-- Fix backspace and delete with multiple cursors (#148)
-- Fix multi cursor clipboard operations (#148)
-- Fix multiple selections and tab switching in csv / tsv
-
-### ⬆️ Upgrade dependencies
-
-- Update js dependencies: (uiw)
+- Add an example using ref