# `Minga.Editing.Motion`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga/editing/motion.ex#L1)

Pure cursor-motion functions for the Minga editor.

Each function takes a `Readable.t()` and a current `position()`, and
returns the new `position()` after applying the motion.  No buffer state
is mutated — the caller is responsible for moving the buffer cursor via
`Document.move_to/2` or `Buffer.Server.move_to/2`.

This module is a facade over focused sub-modules:

* `Motion.Word`     — `w`/`b`/`e`/`W`/`B`/`E` word motions
* `Motion.Line`     — `0`/`$`/`^` line motions
* `Motion.Document` — `gg`/`G`/`{`/`}` document & paragraph motions
* `Motion.Char`     — `f`/`F`/`t`/`T` find-char and `%` bracket match

## Word boundary rules

A **word character** is any alphanumeric character or underscore
(`[a-zA-Z0-9_]`).  This matches Vim's lowercase `w`/`b`/`e` motions.
Whitespace (space, tab, newline) acts as word separator.

## Line and document motions

| Function          | Vim key | Description                              |
|-------------------|---------|------------------------------------------|
| `line_start/2`    | `0`     | First column of the current line         |
| `line_end/2`      | `$`     | Last column of the current line          |
| `first_non_blank/2` | `^`   | First non-whitespace column on the line  |
| `document_start/1` | `gg`  | First character of the buffer            |
| `document_end/1`  | `G`     | Last character of the last line          |

# `position`

```elixir
@type position() :: {non_neg_integer(), non_neg_integer()}
```

A zero-indexed {line, col} cursor position.

# `document_end`

```elixir
@spec document_end(Minga.Editing.Text.Readable.t()) :: position()
```

Move to the last character of the last line (like Vim's `G`).

## Examples

    iex> buf = Minga.Buffer.Document.new("hello\nworld")
    iex> Minga.Editing.Motion.document_end(buf)
    {1, 4}

# `document_start`

```elixir
@spec document_start(Minga.Editing.Text.Readable.t()) :: position()
```

Move to the very start of the buffer (like Vim's `gg`).
Always returns `{0, 0}`.

## Examples

    iex> Minga.Editing.Motion.document_start(Minga.Buffer.Document.new("hello\nworld"))
    {0, 0}

# `find_char_backward`

```elixir
@spec find_char_backward(Minga.Editing.Text.Readable.t(), position(), String.t()) ::
  position()
```

Move backward to the previous occurrence of `char` on the current line (Vim's `F`).
Returns the original position if `char` is not found.

## Examples

    iex> buf = Minga.Buffer.Document.new("hello world")
    iex> Minga.Editing.Motion.find_char_backward(buf, {0, 7}, "o")
    {0, 4}

# `find_char_forward`

```elixir
@spec find_char_forward(Minga.Editing.Text.Readable.t(), position(), String.t()) ::
  position()
```

Move forward to the next occurrence of `char` on the current line (Vim's `f`).
Returns the original position if `char` is not found.

## Examples

    iex> buf = Minga.Buffer.Document.new("hello world")
    iex> Minga.Editing.Motion.find_char_forward(buf, {0, 0}, "o")
    {0, 4}

# `first_non_blank`

```elixir
@spec first_non_blank(Minga.Editing.Text.Readable.t(), position()) :: position()
```

Move to the first non-blank character on the current line (like Vim's `^`).
Falls back to `{line, 0}` when the line is entirely blank.

## Examples

    iex> buf = Minga.Buffer.Document.new("  hello")
    iex> Minga.Editing.Motion.first_non_blank(buf, {0, 0})
    {0, 2}

# `line_end`

```elixir
@spec line_end(Minga.Editing.Text.Readable.t(), position()) :: position()
```

Move to the last column of the current line (like Vim's `$`).
Returns the position of the last grapheme on the line.
For an empty line, returns `{line, 0}`.

## Examples

    iex> buf = Minga.Buffer.Document.new("hello\nworld")
    iex> Minga.Editing.Motion.line_end(buf, {0, 0})
    {0, 4}

# `line_start`

```elixir
@spec line_start(Minga.Editing.Text.Readable.t(), position()) :: position()
```

Move to the first column of the current line (like Vim's `0`).

## Examples

    iex> buf = Minga.Buffer.Document.new("  hello")
    iex> Minga.Editing.Motion.line_start(buf, {0, 4})
    {0, 0}

# `match_bracket`

```elixir
@spec match_bracket(Minga.Editing.Text.Readable.t(), position()) :: position()
```

Jump to the matching bracket/paren/brace (Vim's `%`).

## Examples

    iex> buf = Minga.Buffer.Document.new("(hello)")
    iex> Minga.Editing.Motion.match_bracket(buf, {0, 0})
    {0, 6}

# `paragraph_backward`

```elixir
@spec paragraph_backward(Minga.Editing.Text.Readable.t(), position()) :: position()
```

Move to the previous blank line (Vim's `{`).

## Examples

    iex> buf = Minga.Buffer.Document.new("hello\nworld\n\nfoo")
    iex> Minga.Editing.Motion.paragraph_backward(buf, {3, 0})
    {2, 0}

# `paragraph_forward`

```elixir
@spec paragraph_forward(Minga.Editing.Text.Readable.t(), position()) :: position()
```

Move to the next blank line (Vim's `}`).

## Examples

    iex> buf = Minga.Buffer.Document.new("hello\nworld\n\nfoo")
    iex> Minga.Editing.Motion.paragraph_forward(buf, {0, 0})
    {2, 0}

# `till_char_backward`

```elixir
@spec till_char_backward(Minga.Editing.Text.Readable.t(), position(), String.t()) ::
  position()
```

Move to one after the previous occurrence of `char` on the current line (Vim's `T`).
Returns the original position if `char` is not found.

## Examples

    iex> buf = Minga.Buffer.Document.new("hello world")
    iex> Minga.Editing.Motion.till_char_backward(buf, {0, 7}, "o")
    {0, 5}

# `till_char_forward`

```elixir
@spec till_char_forward(Minga.Editing.Text.Readable.t(), position(), String.t()) ::
  position()
```

Move to one before the next occurrence of `char` on the current line (Vim's `t`).
Returns the original position if `char` is not found.

## Examples

    iex> buf = Minga.Buffer.Document.new("hello world")
    iex> Minga.Editing.Motion.till_char_forward(buf, {0, 0}, "o")
    {0, 3}

# `word_backward`

```elixir
@spec word_backward(Minga.Editing.Text.Readable.t(), position()) :: position()
```

Move backward to the start of the previous word (like Vim's `b`).

## Examples

    iex> buf = Minga.Buffer.Document.new("hello world")
    iex> Minga.Editing.Motion.word_backward(buf, {0, 6})
    {0, 0}

# `word_backward_big`

```elixir
@spec word_backward_big(Minga.Editing.Text.Readable.t(), position()) :: position()
```

Move backward to the start of the previous WORD (Vim's `B`).

## Examples

    iex> buf = Minga.Buffer.Document.new("foo.bar baz")
    iex> Minga.Editing.Motion.word_backward_big(buf, {0, 8})
    {0, 0}

# `word_end`

```elixir
@spec word_end(Minga.Editing.Text.Readable.t(), position()) :: position()
```

Move to the end of the current or next word (like Vim's `e`).

## Examples

    iex> buf = Minga.Buffer.Document.new("hello world")
    iex> Minga.Editing.Motion.word_end(buf, {0, 0})
    {0, 4}

# `word_end_big`

```elixir
@spec word_end_big(Minga.Editing.Text.Readable.t(), position()) :: position()
```

Move to the end of the current or next WORD (Vim's `E`).

## Examples

    iex> buf = Minga.Buffer.Document.new("foo.bar baz")
    iex> Minga.Editing.Motion.word_end_big(buf, {0, 0})
    {0, 6}

# `word_forward`

```elixir
@spec word_forward(Minga.Editing.Text.Readable.t(), position()) :: position()
```

Move forward to the start of the next word (like Vim's `w`).

## Examples

    iex> buf = Minga.Buffer.Document.new("hello world")
    iex> Minga.Editing.Motion.word_forward(buf, {0, 0})
    {0, 6}

# `word_forward_big`

```elixir
@spec word_forward_big(Minga.Editing.Text.Readable.t(), position()) :: position()
```

Move forward to the start of the next WORD (Vim's `W`).

## Examples

    iex> buf = Minga.Buffer.Document.new("foo.bar baz")
    iex> Minga.Editing.Motion.word_forward_big(buf, {0, 0})
    {0, 8}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
