# Caret Navigation in a Hybrid Markdown Editor

> Well-made Markdown editors such as Obsidian and Typora feel comfortable to use in ways that can be hard to pinpoint. There are many reasons, but one that stands out is how carefully they handle caret navigation. Looking at caret movement in these two editors, I have tried to put together a set of principles of my own. Some apply only to Obsidian, some only to Typora, and some to neither.

Well-made Markdown editors such as [Obsidian](https://wiki.g15e.com/pages/Obsidian.txt) and <Typora> feel comfortable to use in ways that can be hard to pinpoint. There are many reasons, but one that stands out is how carefully they handle caret navigation. Looking at caret movement in these two editors, I have tried to put together a set of principles of my own. Some apply only to Obsidian, some only to Typora, and some to neither.

## Minimize layout shifts

The first principle that comes to mind is minimizing layout shifts caused by caret movement. In the following example, entering an inline code span makes the backticks appear, pushing the content to the right.

```caret live legacy
Inline⌶ `code` and backticks.

--- Right

Inline ⌶`code` and backticks.
```

Inline code usually has a gray background, so there is often already a little space on either side of the text. If we use that space to display the backticks, there is no layout shift. Here is an example of this approach.

```caret live
Inline⌶ `code` and backticks.

--- Right

Inline ⌶`code` and backticks.
```

The same problem arises with headings (`## Heading`). Handling them as shown below causes a layout shift.

```caret live legacy
⌶Some paragrah

## Level 2 Heading

Another paragraph

--- Down

Some paragrah

## ⌶Level 2 Heading

Another paragraph

--- Down

Some paragrah

## Level 2 Heading

Ano⌶ther paragraph
```

By contrast, displaying the `#` characters in a smaller font in the left margin avoids the layout shift. When needed, you can also move the caret naturally to the left to change the heading level.

```caret live
⌶Some paragrah

## Level 2 Heading

Another paragraph

--- Down

Some paragrah

## ⌶Level 2 Heading

Another paragraph

--- Left

Some paragrah

##⌶ Level 2 Heading

Another paragraph

--- Left

Some paragrah

#⌶# Level 2 Heading

Another paragraph

--- Down

Some paragrah

## Level 2 Heading

⌶Another paragraph
```

## An immediate connection

<Bret Victor>'s talk [Inventing on Principle](https://wiki.g15e.com/pages/Inventing%20on%20principle.txt) has been a great source of inspiration for me. In it, Victor introduces his principle: "Creators need an immediate connection to what they're creating." Applied to an editor, I take this to mean that when I change something, I should see the result immediately.

<WYSIWYG> editors generally follow this principle, though there is still plenty of room for improvement through approaches such as Result Oriented UI. This article is about hybrid editors, which sit between WYSIWYG editors and source editors, so let us consider how they might offer a more immediate connection.

For example, how should we handle a LaTeX math block? Most hybrid editors work like this.

```caret live legacy
⌶Here's the equation:

$$
E = mc^2
$$

And some sentences after the equation.

--- Down

Here's the equation:

⌶$$
E = mc^2
$$

And some sentences after the equation.
```

While editing the code, you cannot see the rendered result. What if we displayed the two side by side, as below?

```caret live
⌶Here's the equation:

$$
E = mc^2
$$

And some sentences after the equation.

--- Down

Here's the equation:

⌶$$
E = mc^2
$$

And some sentences after the equation.
```

The existing content moves smoothly to the side, and revealing the math block's source does not push down the sentence below it. You can also see the result in real time as you edit the equation.

## Keep the caret from jumping somewhere unexpected

What should happen when the code is taller than its rendered output? This is often the case with Mermaid diagrams that render as wide, horizontal images. Avoiding a layout shift seems difficult here.

One problem with layout shifts is that they often move the caret somewhere other than where the user expected it to be. Even when we cannot prevent the layout shift itself, keeping it from sending the caret somewhere unexpected could at least reduce unnecessary eye movements.

When the caret enters from above, as below, the code expands downward, pushing the content beneath it out of the way. The caret stays where the user expected it, so the change does not catch them off guard.

````caret live
⌶A wide diagram and a long line of code.

```mermaid
flowchart LR
  A[Input]
  B[Check]
  C[Transform]
  D[Output]

  A --> B
  B --> C
  C --> D
```

A sentence below the diagram.

--- Down

A wide diagram and a long line of code.

⌶```mermaid
flowchart LR
  A[Input]
  B[Check]
  C[Transform]
  D[Output]

  A --> B
  B --> C
  C --> D
```

A sentence below the diagram.

--- Up

⌶A wide diagram and a long line of code.

```mermaid
flowchart LR
  A[Input]
  B[Check]
  C[Transform]
  D[Output]

  A --> B
  B --> C
  C --> D
```

A sentence below the diagram.

--- Down

A wide diagram and a long line of code.

⌶```mermaid
flowchart LR
  A[Input]
  B[Check]
  C[Transform]
  D[Output]

  A --> B
  B --> C
  C --> D
```

A sentence below the diagram.
````

Conversely, when the caret enters from below, the code should appear by pushing the content above it upward. That keeps the caret from jumping.

````caret live
A wide diagram and a long line of code.

```mermaid
flowchart LR
  A[Input]
  B[Check]
  C[Transform]
  D[Output]

  A --> B
  B --> C
  C --> D
```

⌶A sentence below the diagram.

--- Up

A wide diagram and a long line of code.

```mermaid
flowchart LR
  A[Input]
  B[Check]
  C[Transform]
  D[Output]

  A --> B
  B --> C
  C --> D
⌶```

A sentence below the diagram.

--- Down

A wide diagram and a long line of code.

```mermaid
flowchart LR
  A[Input]
  B[Check]
  C[Transform]
  D[Output]

  A --> B
  B --> C
  C --> D
```

⌶A sentence below the diagram.

--- Up

A wide diagram and a long line of code.

```mermaid
flowchart LR
  A[Input]
  B[Check]
  C[Transform]
  D[Output]

  A --> B
  B --> C
  C --> D
⌶```

A sentence below the diagram.
````

What if the caret enters from above and then exits below, as in the next example? On entry, the code should expand by pushing the content below it downward. On exit, it should collapse by pulling the content above it downward. That keeps the caret from jumping.

````caret live
⌶A wide diagram and a long line of code.

```mermaid
flowchart LR
  A[Input]
  B[Check]
  C[Transform]
  D[Output]

  A --> B
  B --> C
  C --> D
```

A sentence below the diagram.

--- Down

A wide diagram and a long line of code.

⌶```mermaid
flowchart LR
  A[Input]
  B[Check]
  C[Transform]
  D[Output]

  A --> B
  B --> C
  C --> D
```

A sentence below the diagram.

--- Down [keydown]

A wide diagram and a long line of code.

```mermaid
⌶flowchart LR
  A[Input]
  B[Check]
  C[Transform]
  D[Output]

  A --> B
  B --> C
  C --> D
```

A sentence below the diagram.

--- Down [keydown]

A wide diagram and a long line of code.

```mermaid
flowchart LR
⌶  A[Input]
  B[Check]
  C[Transform]
  D[Output]

  A --> B
  B --> C
  C --> D
```

A sentence below the diagram.

--- Down [keydown]

A wide diagram and a long line of code.

```mermaid
flowchart LR
  A[Input]
⌶  B[Check]
  C[Transform]
  D[Output]

  A --> B
  B --> C
  C --> D
```

A sentence below the diagram.

--- Down [keydown]

A wide diagram and a long line of code.

```mermaid
flowchart LR
  A[Input]
  B[Check]
⌶  C[Transform]
  D[Output]

  A --> B
  B --> C
  C --> D
```

A sentence below the diagram.

--- Down [keydown]

A wide diagram and a long line of code.

```mermaid
flowchart LR
  A[Input]
  B[Check]
  C[Transform]
⌶  D[Output]

  A --> B
  B --> C
  C --> D
```

A sentence below the diagram.

--- Down [keydown]

A wide diagram and a long line of code.

```mermaid
flowchart LR
  A[Input]
  B[Check]
  C[Transform]
  D[Output]
⌶
  A --> B
  B --> C
  C --> D
```

A sentence below the diagram.

--- Down [keydown]

A wide diagram and a long line of code.

```mermaid
flowchart LR
  A[Input]
  B[Check]
  C[Transform]
  D[Output]

⌶  A --> B
  B --> C
  C --> D
```

A sentence below the diagram.

--- Down [keydown]

A wide diagram and a long line of code.

```mermaid
flowchart LR
  A[Input]
  B[Check]
  C[Transform]
  D[Output]

  A --> B
⌶  B --> C
  C --> D
```

A sentence below the diagram.

--- Down [keydown]

A wide diagram and a long line of code.

```mermaid
flowchart LR
  A[Input]
  B[Check]
  C[Transform]
  D[Output]

  A --> B
  B --> C
⌶  C --> D
```

A sentence below the diagram.

--- Down [keydown]

A wide diagram and a long line of code.

```mermaid
flowchart LR
  A[Input]
  B[Check]
  C[Transform]
  D[Output]

  A --> B
  B --> C
  C --> D
⌶```

A sentence below the diagram.

--- Down [keyup]

A wide diagram and a long line of code.

```mermaid
flowchart LR
  A[Input]
  B[Check]
  C[Transform]
  D[Output]

  A --> B
  B --> C
  C --> D
⌶```

A sentence below the diagram.

--- Down

A wide diagram and a long line of code.

```mermaid
flowchart LR
  A[Input]
  B[Check]
  C[Transform]
  D[Output]

  A --> B
  B --> C
  C --> D
```

⌶A sentence below the diagram.
````

## Do not reveal source without an intent to edit

In the backtick example we started with, displaying the backticks in the available space let us avoid layout shifts altogether.

```caret live
Inline⌶ `code` and backticks.

--- Right

Inline ⌶`code` and backticks.
```

But what about links? We cannot eliminate the layout shift. We can, however, reduce it by adjusting the font size.

````caret live
Let's visit⌶ [W3C](https://w3.org) - The World-Wide Web Consortium.

--- Right

Let's visit ⌶[W3C](https://w3.org) - The World-Wide Web Consortium.
````

Is that the best we can do? There is a small improvement we can make: leave the source hidden when the user holds down a key to move quickly past it. If it is clear that they are simply passing through, with no intention of editing, there is no need to cause a layout shift.

````caret live
Let's visit⌶ [W3C](https://w3.org) - The World-Wide Web Consortium.

--- Right [keydown]

Let's visit [⌶W3C](https://w3.org) - The World-Wide Web Consortium.

--- Right [keydown]

Let's visit [W⌶3C](https://w3.org) - The World-Wide Web Consortium.

--- Right [keydown]

Let's visit [W3⌶C](https://w3.org) - The World-Wide Web Consortium.

--- Right [keydown]

Let's visit [W3C⌶](https://w3.org) - The World-Wide Web Consortium.

--- Right [keydown]

Let's visit [W3C](https://w3.org) ⌶- The World-Wide Web Consortium.

--- Right [keyup]

Let's visit [W3C](https://w3.org) ⌶- The World-Wide Web Consortium.
````

## When intent is ambiguous, minimize the cost of getting it wrong

What should we do when the user's expectations are unclear and there is no single right answer? For example, where should the caret go when the user presses `caret::mac:Cmd+Right win:End linux:End` in the following situation?

```caret live
⌶Let's visit [W3C](https://w3.org)
```

Some people will expect the caret to land after the "C," as shown below. Let us call this option A.

```caret live
Let's visit [W3C⌶](https://w3.org)
```

Others will expect it to land at the end of the sentence, as below. Let us call this option B.

```caret live
Let's visit [W3C](https://w3.org)⌶
```

Whose expectation is correct? There is no right answer. In this situation, we should choose the behavior that takes less effort to recover from when it differs from what the user expected.

If someone expecting A uses a system that behaves like B, they have to reach for the mouse and click next to the "C," or press the left arrow several times to get the caret there. That is a nuisance. By contrast, someone expecting B who encounters A can simply press `caret::mac:Cmd+Right win:End linux:End` once more.

So in this case, I chose to make the caret behave like option A.

````caret live
⌶Let's visit [W3C](https://w3.org)

--- mac:Cmd+Right win:End linux:End

Let's visit [W3C⌶](https://w3.org)

--- mac:Cmd+Right win:End linux:End

Let's visit [W3C](https://w3.org)⌶
````

The same applies to selecting all with `caret::mac:Cmd+A win:Ctrl+A linux:Ctrl+A`. When the caret is inside a code block, as below, should Select All select just that block or the entire document? Following the same principle, I made it select the code block's contents first, then the whole document on a second press. (Although I am not particularly fond of it, the block editor Notion also works this way.)

````caret live
Before the code block.

```js
const x = ⌶2
const y = 3
console.log(x + y)
```

After the code block.

--- mac:Cmd+A win:Ctrl+A linux:Ctrl+A

Before the code block.

```js
⌈const x = 2
const y = 3
console.log(x + y)
⌋```

After the code block.

--- mac:Cmd+A win:Ctrl+A linux:Ctrl+A

⌈Before the code block.

```js
const x = 2
const y = 3
console.log(x + y)
```

After the code block.⌋
````

## Reversible caret movement

The final principle is reversibility. Put simply, if you move up once and then down once, the caret should, as far as possible, return to exactly where it started. Let us compare examples that follow this principle with ones that do not.

For example, where should the caret go if we move it one position to the right in the following situation?

````caret
Before the code block.⌶

```js
const x = 2
const y = 3
console.log(x + y)
```
````

One possibility is to place it at the beginning of the block, as below. Let us call this option A.

````caret
Before the code block.

⌶```js
const x = 2
const y = 3
console.log(x + y)
```
````

Another is to place it where the actual code begins. Let us call this option B.

````caret
Before the code block.

```js
⌶const x = 2
const y = 3
console.log(x + y)
```
````

You might think B is better because it saves a few keystrokes compared with A. But from the perspective of reversible caret movement, it may be a poor choice that undermines the user's ability to build habits.

With A, pressing the left arrow once returns the caret to exactly where you expect it. In other words, pressing the right arrow once and then the left arrow once brings it back to its original position. With B, this principle does not hold. The following example shows how B breaks reversibility.

````caret live
Before the code block.⌶

```js
const x = 2
const y = 3
console.log(x + y)
```

--- Right

Before the code block.

```js
⌶const x = 2
const y = 3
console.log(x + y)
```

--- Left

Before the code block.

```js⌶
const x = 2
const y = 3
console.log(x + y)
```
````

With A, by contrast, reversibility is preserved.

````caret live
Before the code block.⌶

```js
const x = 2
const y = 3
console.log(x + y)
```

--- Right

Before the code block.

⌶```js
const x = 2
const y = 3
console.log(x + y)
```

--- Left

Before the code block.⌶

```js
const x = 2
const y = 3
console.log(x + y)
```
````

## Conclusion

These principles are less a set of definitive answers than a record of my thinking about how to offer a consistent editing experience. I plan to build an editor that follows them, use it regularly, and keep refining them.