# More granular UserEvent categorization for transactions

**URL:** https://discuss.codemirror.net/t/more-granular-userevent-categorization-for-transactions/6137
**Category:** v6
**Created:** [March 29, 2023, 8:57pm UTC](https://discuss.codemirror.net/t/more-granular-userevent-categorization-for-transactions/6137 "2023-03-29T20:57:05Z")
**Posts on this page:** 3
**Page:** 1

<div class="post-metadata">

### Author: ![Fevol](https://discuss.codemirror.net/user_avatar/discuss.codemirror.net/fevol/32/3031_2.png) [@Fevol](https://discuss.codemirror.net/u/Fevol)
#### Post date: [March 29, 2023, 8:57pm UTC](https://discuss.codemirror.net/t/more-granular-userevent-categorization-for-transactions/6137/1 "2023-03-29T20:57:05Z")

</div>

## Problem

For the purposes of an editor extension I’m writing (specifically, a `transactionFilter`), I _need_ to know the direction a transaction was applied in, and whether it was applied as a group operation.

In some operations, this information is already contained within the [userevent](https://codemirror.net/docs/ref/#state.Transaction%5EuserEvent); e.g.:  
`Backspace` → Userevent: `delete.backward`

However, most other operations do not:  
`Backspace` (selection) → Userevent: `delete.selection` &nbsp;&nbsp; (expecting `delete.selection` **and** `delete.backward`)  
`Ctrl + Arrow Left` → Userevent: `select` &nbsp;&nbsp; (expecting `select.forward` **and** `select.group`)

> **Example usage**
>
> Below code would work properly if I do a regular delete (via `Backspace`/`Delete`), but not if I have made a selection.
> 
> ```ts
> const extension = EditorState.transactionFilter.of(tr => {
> if (tr.isUserEvent('select.forward') && /* OTHER CONDITIONS */) {
> // Adapt transaction selection range to account for specifc case
> }
> });
> // Register extension ...
> 
> ```

* * *

## Current Situation

This situation wouldn’t be too difficult to resolve, were it not for the fact that the [standardKeymap](https://github.com/codemirror/commands/blob/fa1b2f75bf48642a871e8a84cf70a938017533b3/src/commands.ts#L839-L875) keybinds are already assigned to the editor as an extension – **I am unable to change this.**

For now, I’ve bodged around this issue by redefining/overwriting all the relevant commands in [@codemirror/commands](https://github.com/codemirror/commands/blob/main/src/commands.ts) and adding a custom userevent annotation to each of them (to indicate direction and group-_ness_).

> **Example overwrite**
>
> Only the `setSel(...)` has been changed; a forward parameter has been added to the function, based on which the `select.forward`/`select.backward` annotation would be added.
> 
> ```ts
> function setSel(state: EditorState, selection: EditorSelection | {anchor: number, head?: number}, forward?: boolean) {
> let annotations = undefined;
> if (forward !== undefined) {
> annotations = [Transaction.userEvent.of(forward ? "select.forward" : "select.backward")]
> }
> return state.update({selection, scrollIntoView: true, userEvent: "select", annotations})
> }
> 
> function moveSel({state, dispatch}: CommandTarget, how: (range: SelectionRange) => SelectionRange, forward?: boolean): boolean {
> const selection = updateSel(state.selection, how)
> if (selection.eq(state.selection)) return false
> dispatch(setSel(state, selection, forward))
> return true
> }
> 
> function cursorByChar(view: EditorView, forward: boolean) {
> return moveSel(view, range => range.empty ? view.moveByChar(range, forward) : rangeEnd(range, forward), forward)
> }
> 
> export const cursorCharLeft: Command = view => cursorByChar(view, !ltrAtCursor(view))
> 
> ```

&nbsp;

However, there are several (huge) problems with this approach:

1. To define all these events, I need to re-implement _every_ keybind and their code for my extension.
2. Furthermore, I’d also have to add this behaviour for all other keybind extensions (emacs/vim) – though this is not necessarily the highest priority.
3. Some of the keybinds can _not_ be overwritten: specifically all the arrow up/down keybinds. Is this due to the fact that they do not have the `preventDefault` parameter set to `true` (compared to ArrowLeft/Right)?

* * *

## (Preferred) Solution

In a perfect world, I’d want to get all the necessary context of the action taken within the transaction itself. However, I realise that is probably not very feasible.

In all honesty, the solution I have currently, is not too bad – it does what I need it to do. However, the only dealbreaker is those keybinds that I cannot replace (see **[3]** above).

* * *

Any advice would be incredibly appreciated.

Many thanks in advance!

---

<div class="post-metadata">

### Author: ![marijn](https://discuss.codemirror.net/user_avatar/discuss.codemirror.net/marijn/32/136_2.png) [@marijn](https://discuss.codemirror.net/u/marijn)
#### Post date: [March 29, 2023, 10:15pm UTC](https://discuss.codemirror.net/t/more-granular-userevent-categorization-for-transactions/6137/2 "2023-03-29T22:15:33Z")

</div>

> [@Fevol](#):
>
> Some of the keybinds can _not_ be overwritten: specifically all the arrow up/down keybinds. Is this due to the fact that they do not have the `preventDefault` parameter set to `true` (compared to ArrowLeft/Right)?

All keybindings can be overwritten with higher-precedence bindings (or by leaving them out of the configuration altogether). If you are sure it’s not working like that, please set up a minimal script that clearly demonstrates the problem.

---

<div class="post-metadata">

### Author: ![Fevol](https://discuss.codemirror.net/user_avatar/discuss.codemirror.net/fevol/32/3031_2.png) [@Fevol](https://discuss.codemirror.net/u/Fevol)
#### Post date: [March 30, 2023, 12:29am UTC](https://discuss.codemirror.net/t/more-granular-userevent-categorization-for-transactions/6137/3 "2023-03-30T00:29:34Z")

</div>

Many thanks for your quick and insightful response. I did not realize that the precedence rules could also be applied here.

`Prec.high(...)` didn’t seem to be sufficient to override the original binds, but `Prec.highest(...)` did. Everything seems to behave correctly now.

Thanks again for your help, I really appreciate it.
