Skip to content

Commit

Permalink
Implement vi mode jumps
Browse files Browse the repository at this point in the history
  • Loading branch information
Yaraslaut committed Nov 7, 2024
1 parent 1cc177f commit 1383c17
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 25 deletions.
1 change: 1 addition & 0 deletions metainfo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
<li>Protect user from accidentally pasting too large input (#1198)</li>
<li>Add binding to exit normal mode with `Esc` (#1604)</li>
<li>Add config option to switch into insert mode after yank (#1604)</li>
<li>Add vi-like normal mode Jumps. `''`, `C-O` and `C-I` motions (#1101)</li>
<li>Improves window size/resize handling on HiDPI monitor settings (#1628)</li>
<li>Improves macOS key handling for Option+Left|Right keys to jump between words in the shell</li>
<li>Fixes cropping of underscore character for some fonts (#1603)</li>
Expand Down
2 changes: 2 additions & 0 deletions src/vtbackend/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ set(vtbackend_HEADERS
Viewport.h
ViInputHandler.h
ViCommands.h
JumpHistory.h
primitives.h
)

Expand Down Expand Up @@ -75,6 +76,7 @@ set(vtbackend_SOURCES
Viewport.cpp
ViInputHandler.cpp
ViCommands.cpp
JumpHistory.cpp
primitives.cpp
)

Expand Down
56 changes: 56 additions & 0 deletions src/vtbackend/JumpHistory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: Apache-2.0
#include <vtbackend/JumpHistory.h>

namespace vtbackend
{

CellLocation JumpHistory::jumpToLast(CellLocation current)
{
applyOffset();
CellLocation last = _history.back();
if (last == current)
{
_history.pop_back();
if (_history.empty())
{
return current;
}
last = _history.back();
}
_history.pop_back();
_history.push_back(current);
_current = _history.size();
return last;
}

CellLocation JumpHistory::jumpToMarkBackward([[maybe_unused]] CellLocation current)
{
applyOffset();
if (_current == 0)
{
// loop
_current = _history.size() - 1;
}
else
{
_current--;
}
return _history[_current];
}

CellLocation JumpHistory::jumpToMarkForward([[maybe_unused]] CellLocation current)
{
applyOffset();
if (_current == _history.size())
{
// loop
_current = 0;
}
else
{
_current++;
}
return _history[_current];
}

} // namespace vtbackend
42 changes: 42 additions & 0 deletions src/vtbackend/JumpHistory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once

#include <vtbackend/primitives.h>

#include <vector>

namespace vtbackend
{

class JumpHistory
{

public:
template <typename T>
void add(T&& cell)
{
applyOffset();
_history.push_back(std::forward<T>(cell));
}
CellLocation jumpToLast(CellLocation current);
CellLocation jumpToMarkBackward(CellLocation current);
CellLocation jumpToMarkForward(CellLocation current);
void addOffset(LineOffset offset) { _offsetSinceLastJump += offset; }

private:
std::vector<CellLocation> _history;
size_t _current = 0;
LineOffset _offsetSinceLastJump { 0 };
void applyOffset()
{
if (unbox(_offsetSinceLastJump) == 0)
return;
for (auto& cell: _history)
{
// minus since we are going in the history
cell.line -= _offsetSinceLastJump;
}
_offsetSinceLastJump = LineOffset { 0 };
}
};
} // namespace vtbackend
3 changes: 2 additions & 1 deletion src/vtbackend/Screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -727,7 +727,8 @@ void Screen<Cell>::linefeed(ColumnOffset newColumn)
{
_cursor.wrapPending = false;
_cursor.position.column = newColumn;

if (unbox(historyLineCount()) > 0)
_terminal->addLineOffsetToJumpHistory(LineOffset { 1 });
if (*realCursorPosition().line == *margin().vertical.to)
{
// TODO(perf) if we know that we text is following this LF
Expand Down
4 changes: 4 additions & 0 deletions src/vtbackend/Terminal.h
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,10 @@ class Terminal
return _viCommands.cursorPosition;
}
void moveNormalModeCursorTo(CellLocation pos) noexcept { _viCommands.moveCursorTo(pos); }
void addLineOffsetToJumpHistory(LineOffset offset) noexcept
{
_viCommands.addLineOffsetToJumpHistory(offset);
}

// {{{ cursor management
CursorDisplay cursorDisplay() const noexcept { return _settings.cursorDisplay; }
Expand Down
58 changes: 38 additions & 20 deletions src/vtbackend/ViCommands.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
#include <vtbackend/Terminal.h>
#include <vtbackend/ViCommands.h>
#include <vtbackend/ViInputHandler.h>
#include <vtbackend/logging.h>
#include <vtbackend/primitives.h>

Expand Down Expand Up @@ -195,7 +196,11 @@ bool ViCommands::jumpToNextMatch(unsigned count)
{
for (unsigned i = 0; i < count; ++i)
if (auto const nextPosition = _terminal->searchNextMatch(cursorPosition))
{
inputLog()("jumpToNextMatch");
_jumpHistory.add(nextPosition.value());
moveCursorTo(nextPosition.value());
}
else
return false;

Expand All @@ -206,7 +211,11 @@ bool ViCommands::jumpToPreviousMatch(unsigned count)
{
for (unsigned i = 0; i < count; ++i)
if (auto const nextPosition = _terminal->searchPrevMatch(cursorPosition))
{
inputLog()("jumpToPreviousMatch");
_jumpHistory.add(nextPosition.value());
moveCursorTo(nextPosition.value());
}
else
return false;

Expand Down Expand Up @@ -659,7 +668,7 @@ CellLocationRange ViCommands::translateToCellRange(TextObjectScope scope,
return { a, b };
}

CellLocationRange ViCommands::translateToCellRange(ViMotion motion, unsigned count) const noexcept
CellLocationRange ViCommands::translateToCellRange(ViMotion motion, unsigned count) noexcept
{
switch (motion)
{
Expand All @@ -668,7 +677,7 @@ CellLocationRange ViCommands::translateToCellRange(ViMotion motion, unsigned cou
{ cursorPosition.line, _terminal->pageSize().columns.as<ColumnOffset>() - 1 } };
default:
//.
return { cursorPosition, translateToCellLocation(motion, count) };
return { cursorPosition, translateToCellLocationAndRecord(motion, count) };
}
}

Expand Down Expand Up @@ -773,8 +782,13 @@ CellLocation ViCommands::globalCharDown(CellLocation location, char ch, unsigned
return result;
}

CellLocation ViCommands::translateToCellLocation(ViMotion motion, unsigned count) const noexcept
CellLocation ViCommands::translateToCellLocationAndRecord(ViMotion motion, unsigned count) noexcept
{
auto addJumpHistory = [this](CellLocation const& location) {
inputLog()("addJumpHistory: {}:{}", location.line, location.column);
_jumpHistory.add(location);
return location;
};
switch (motion)
{
case ViMotion::CharLeft: // h
Expand Down Expand Up @@ -806,7 +820,8 @@ CellLocation ViCommands::translateToCellLocation(ViMotion motion, unsigned count
{ LineOffset::cast_from(-_terminal->currentScreen().historyLineCount().as<int>()),
ColumnOffset(0) });
case ViMotion::FileEnd: // G
return snapToCell({ _terminal->pageSize().lines.as<LineOffset>() - 1, ColumnOffset(0) });
return addJumpHistory(
snapToCell({ _terminal->pageSize().lines.as<LineOffset>() - 1, ColumnOffset(0) }));
case ViMotion::PageTop: // <S-H>
return snapToCell({ boxed_cast<LineOffset>(-_terminal->viewport().scrollOffset())
+ *_terminal->viewport().scrollOff(),
Expand Down Expand Up @@ -837,9 +852,9 @@ CellLocation ViCommands::translateToCellLocation(ViMotion motion, unsigned count
-_terminal->currentScreen().historyLineCount().as<LineOffset>()),
cursorPosition.column };
case ViMotion::LinesCenter: // M
return { LineOffset::cast_from(_terminal->pageSize().lines / 2 - 1)
- boxed_cast<LineOffset>(_terminal->viewport().scrollOffset()),
cursorPosition.column };
return addJumpHistory({ LineOffset::cast_from(_terminal->pageSize().lines / 2 - 1)
- boxed_cast<LineOffset>(_terminal->viewport().scrollOffset()),
cursorPosition.column });
case ViMotion::PageDown:
return { min(cursorPosition.line + LineOffset::cast_from(_terminal->pageSize().lines / 2),
_terminal->pageSize().lines.as<LineOffset>() - 1),
Expand All @@ -864,16 +879,16 @@ CellLocation ViCommands::translateToCellLocation(ViMotion motion, unsigned count
prev.line = current.line;
current.line--;
}
return snapToCell(current);
return addJumpHistory(snapToCell(current));
}
case ViMotion::GlobalCurlyOpenUp: // [[
return globalCharUp(cursorPosition, '{', count);
return addJumpHistory(globalCharUp(cursorPosition, '{', count));
case ViMotion::GlobalCurlyOpenDown: // ]]
return globalCharDown(cursorPosition, '{', count);
return addJumpHistory(globalCharDown(cursorPosition, '{', count));
case ViMotion::GlobalCurlyCloseUp: // []
return globalCharUp(cursorPosition, '}', count);
return addJumpHistory(globalCharUp(cursorPosition, '}', count));
case ViMotion::GlobalCurlyCloseDown: // ][
return globalCharDown(cursorPosition, '}', count);
return addJumpHistory(globalCharDown(cursorPosition, '}', count));
case ViMotion::LineMarkUp: // [m
{
auto const gridTop = -_terminal->currentScreen().historyLineCount().as<LineOffset>();
Expand All @@ -888,7 +903,7 @@ CellLocation ViCommands::translateToCellLocation(ViMotion motion, unsigned count
--result.line;
--count;
}
return result;
return addJumpHistory(result);
}
case ViMotion::LineMarkDown: // ]m
{
Expand All @@ -906,7 +921,7 @@ CellLocation ViCommands::translateToCellLocation(ViMotion motion, unsigned count
}
--count;
}
return result;
return addJumpHistory(result);
}
case ViMotion::ParagraphForward: // }
{
Expand All @@ -922,7 +937,7 @@ CellLocation ViCommands::translateToCellLocation(ViMotion motion, unsigned count
prev.line = current.line;
current.line++;
}
return snapToCell(current);
return addJumpHistory(snapToCell(current));
}
case ViMotion::ParenthesisMatching: // % TODO
return findMatchingPairFrom(cursorPosition);
Expand All @@ -938,7 +953,7 @@ CellLocation ViCommands::translateToCellLocation(ViMotion motion, unsigned count

startPosition = *nextPosition;
}
return startPosition;
return addJumpHistory(startPosition);
}
case ViMotion::SearchResultForward: // n
{
Expand All @@ -951,7 +966,7 @@ CellLocation ViCommands::translateToCellLocation(ViMotion motion, unsigned count
return cursorPosition;
startPosition = *nextPosition;
}
return startPosition;
return addJumpHistory(startPosition);
}
case ViMotion::WordBackward: // b
{
Expand Down Expand Up @@ -1059,12 +1074,15 @@ CellLocation ViCommands::translateToCellLocation(ViMotion motion, unsigned count
return toCharLeft(count).value_or(cursorPosition);
case ViMotion::RepeatCharMove:
if (isValidCharMove(_lastCharMotion))
return translateToCellLocation(*_lastCharMotion, count);
return translateToCellLocationAndRecord(*_lastCharMotion, count);
return cursorPosition;
case ViMotion::RepeatCharMoveReverse:
if (isValidCharMove(_lastCharMotion))
return translateToCellLocation(invertCharMove(*_lastCharMotion), count);
return translateToCellLocationAndRecord(invertCharMove(*_lastCharMotion), count);
return cursorPosition;
case ViMotion::JumpToLastJumpPoint: return _jumpHistory.jumpToLast(cursorPosition);
case ViMotion::JumpToMarkBackward: return _jumpHistory.jumpToMarkBackward(cursorPosition);
case ViMotion::JumpToMarkForward: return _jumpHistory.jumpToMarkForward(cursorPosition);
}
crispy::unreachable();
}
Expand Down Expand Up @@ -1133,7 +1151,7 @@ void ViCommands::moveCursor(ViMotion motion, unsigned count, char32_t lastChar)
_lastChar = lastChar;
}

auto const nextPosition = translateToCellLocation(motion, count);
auto const nextPosition = translateToCellLocationAndRecord(motion, count);
inputLog()("Move cursor: {} to {}\n", motion, nextPosition);
moveCursorTo(nextPosition);
}
Expand Down
10 changes: 7 additions & 3 deletions src/vtbackend/ViCommands.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once

#include <vtbackend/JumpHistory.h>
#include <vtbackend/ViInputHandler.h>
#include <vtbackend/primitives.h>

#include <gsl/pointers>

#include <list>
#include <optional>

namespace vtbackend
Expand Down Expand Up @@ -48,8 +51,8 @@ class ViCommands: public ViInputHandler::Executor

void moveCursorTo(CellLocation position);

[[nodiscard]] CellLocation translateToCellLocation(ViMotion motion, unsigned count) const noexcept;
[[nodiscard]] CellLocationRange translateToCellRange(ViMotion motion, unsigned count) const noexcept;
[[nodiscard]] CellLocation translateToCellLocationAndRecord(ViMotion motion, unsigned count) noexcept;
[[nodiscard]] CellLocationRange translateToCellRange(ViMotion motion, unsigned count) noexcept;
[[nodiscard]] CellLocationRange translateToCellRange(TextObjectScope scope,
TextObject textObject) const noexcept;
[[nodiscard]] CellLocation prev(CellLocation location) const noexcept;
Expand Down Expand Up @@ -88,7 +91,7 @@ class ViCommands: public ViInputHandler::Executor
[[nodiscard]] CellLocation snapToCellRight(CellLocation location) const noexcept;

[[nodiscard]] bool compareCellTextAt(CellLocation position, char32_t codepoint) const noexcept;

void addLineOffsetToJumpHistory(LineOffset offset) { _jumpHistory.addOffset(offset); }
// Cursor offset into the grid.
CellLocation cursorPosition {};

Expand All @@ -99,6 +102,7 @@ class ViCommands: public ViInputHandler::Executor
mutable char32_t _lastChar = U'\0';
std::optional<ViMotion> _lastCharMotion = std::nullopt;
bool _lastCursorVisible = true;
JumpHistory _jumpHistory;
};

} // namespace vtbackend
6 changes: 5 additions & 1 deletion src/vtbackend/ViInputHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ void ViInputHandler::registerAllCommands()
std::array<std::pair<char, TextObjectScope>, 2> { { std::pair { 'i', TextObjectScope::Inner },
std::pair { 'a', TextObjectScope::A } } };

auto constexpr MotionMappings = std::array<std::pair<std::string_view, ViMotion>, 43> { {
auto constexpr MotionMappings = std::array<std::pair<std::string_view, ViMotion>, 47> { {
// clang-format off
{ "$", ViMotion::LineEnd },
{ "%", ViMotion::ParenthesisMatching },
Expand Down Expand Up @@ -114,6 +114,10 @@ void ViInputHandler::registerAllCommands()
{ "{", ViMotion::ParagraphBackward },
{ "|", ViMotion::ScreenColumn },
{ "}", ViMotion::ParagraphForward },
{ "''",ViMotion::JumpToLastJumpPoint },
{ "``",ViMotion::JumpToLastJumpPoint },
{ "C-O",ViMotion::JumpToMarkBackward },
{ "C-I",ViMotion::JumpToMarkForward },
// clang-format on
} };

Expand Down
Loading

0 comments on commit 1383c17

Please sign in to comment.