Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Implement `;!` to execute a shell command from the command bar
* Disabled continuous deployment in GHA. See `.github/workflows/build_artifact.yml`
for details
* `M<char>` now marks the current row to the given letter on the keyboard

* Resolved crash when the user tries to `delete word` past the end of a line

Expand Down
77 changes: 39 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,44 +37,45 @@ $ ./run.py --release
switch to `write` mode to insert any characters. In `read` mode, you can
perform the following actions (alphabetically ordered):

| Key | Description |
|---------------|-------------------------------------------------------|
| <kbd>a</kbd> | Go to write mode right of the current character |
| <kbd>A</kbd> | Go to write mode at the end of the current line |
| <kbd>b</kbd> | Move cursor back one word |
| <kbd>dl</kbd> | Delete the current line |
| <kbd>dw</kbd> | Delete the current word |
| <kbd>e</kbd> | Find next entered char ahead in file |
| <kbd>f</kbd> | Move cursor to end of current word |
| <kbd>F</kbd> | Find next entered char back in file |
| <kbd>g</kbd> | Go to top of file |
| <kbd>G</kbd> | Go to bottom of file |
| <kbd>h</kbd> | Move cursor left |
| <kbd>i</kbd> | Go to write mode left of the current character |
| <kbd>j</kbd> | Move cursor down |
| <kbd>J</kbd> | Move current line down one |
| <kbd>k</kbd> | Move cursor up |
| <kbd>K</kbd> | Move current line up one |
| <kbd>l</kbd> | Move cursor right |
| <kbd>o</kbd> | Enter newline below and go to write mode |
| <kbd>O</kbd> | Move cursor right |
| <kbd>r</kbd> | Replace char under cursor with next entered character |
| <kbd>R</kbd> | Redo |
| <kbd>tn</kbd> | Go to next tab |
| <kbd>tp</kbd> | Go to previous tab |
| <kbd>tt</kbd> | Open new tab |
| <kbd>u</kbd> | Undo |
| <kbd>w</kbd> | Move cursor forward one word |
| <kbd>x</kbd> | Delete character under the cursor |
| <kbd>z</kbd> | Center the current line in the screen |
| <kbd>;</kbd> | Go to command mode |
| <kbd>~</Kbd> | Switch case of char under cursor |
| <kbd>[</Kbd> | Go to beginning of paragraph (previous empty line) |
| <kbd>]</Kbd> | Go to end of paragraph (next empty line) |
| <kbd>_</Kbd> | Go to beginning of line |
| <kbd>$</Kbd> | Go to end of line |
| <kbd>></kbd> | Indent current line |
| <kbd><</kbd> | Dedent current line |
| Key | Description |
|---------------|----------------------------------------------------------|
| <kbd>a</kbd> | Go to write mode right of the current character |
| <kbd>A</kbd> | Go to write mode at the end of the current line |
| <kbd>b</kbd> | Move cursor back one word |
| <kbd>dl</kbd> | Delete the current line |
| <kbd>dw</kbd> | Delete the current word |
| <kbd>e</kbd> | Find next entered char ahead in file |
| <kbd>f</kbd> | Move cursor to end of current word |
| <kbd>F</kbd> | Find next entered char back in file |
| <kbd>g</kbd> | Go to top of file |
| <kbd>G</kbd> | Go to bottom of file |
| <kbd>h</kbd> | Move cursor left |
| <kbd>i</kbd> | Go to write mode left of the current character |
| <kbd>j</kbd> | Move cursor down |
| <kbd>J</kbd> | Move current line down one |
| <kbd>k</kbd> | Move cursor up |
| <kbd>K</kbd> | Move current line up one |
| <kbd>l</kbd> | Move cursor right |
| <kbd>m</kbd> | Mark the current row against a provided letter (ex `ma`) |
| <kbd>o</kbd> | Enter newline below and go to write mode |
| <kbd>O</kbd> | Move cursor right |
| <kbd>r</kbd> | Replace char under cursor with next entered character |
| <kbd>R</kbd> | Redo |
| <kbd>tn</kbd> | Go to next tab |
| <kbd>tp</kbd> | Go to previous tab |
| <kbd>tt</kbd> | Open new tab |
| <kbd>u</kbd> | Undo |
| <kbd>w</kbd> | Move cursor forward one word |
| <kbd>x</kbd> | Delete character under the cursor |
| <kbd>z</kbd> | Center the current line in the screen |
| <kbd>;</kbd> | Go to command mode |
| <kbd>~</Kbd> | Switch case of char under cursor |
| <kbd>[</Kbd> | Go to beginning of paragraph (previous empty line) |
| <kbd>]</Kbd> | Go to end of paragraph (next empty line) |
| <kbd>_</Kbd> | Go to beginning of line |
| <kbd>$</Kbd> | Go to end of line |
| <kbd>></kbd> | Indent current line |
| <kbd><</kbd> | Dedent current line |

### Command mode
Likewise, you can switch to `command` mode with the semicolon `;` key. The
Expand Down
2 changes: 1 addition & 1 deletion run.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ def test(testname: str | None, asan: bool) -> int:

create_read_only_file()
testname = testname if testname is not None else ""
test_flags: str = "-r compact --order rand"
test_flags: str = "-sr compact --order rand"
shell_cmd: str = f"./build/tests/test_exe {test_flags} {testname}"

return run_shell_cmd(
Expand Down
3 changes: 3 additions & 0 deletions src/action.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ template <typename T, typename U>

} break;

// At some point, we'll need to figure out how to redraw the whole screen faster
// to prevent flickering, but the whole screen will need redrawing when a mark
// is changed
case ActionType::MoveCursorUp: {
if constexpr (std::is_same_v<U, bool>) {
auto logger = spdlog::get("basic_logger");
Expand Down
4 changes: 3 additions & 1 deletion src/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ const int TAB_SIZE = 4;
const std::size_t LINE_BORDER = 100;

const rawterm::Color COLOR_UI_BG = rawterm::Colors::gray;
const rawterm::Color COLOR_DARK_YELLOW = rawterm::Color("#ffdd33");
const rawterm::Color COLOR_DARK_YELLOW = rawterm::Color("#FFdd33");
// Should match ansi color code 92
const rawterm::Color COLOR_GREEN = rawterm::Color("#5ffa68");

const std::string COLOR_ALERT = "\x1b[41m\x1b[37m";
const std::string WHITESPACE = " \t\n\r\f\v";
Expand Down
5 changes: 5 additions & 0 deletions src/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,11 @@ void Controller::start_action_engine() {
parse_action<void, bool>(&view, Action<void> {ActionType::MoveCursorRight});
redraw_all = redraw.value();

// Add mark
} else if (k.value() == rawterm::Key('m')) {
auto k2 = rawterm::wait_for_input();
if (k2.isCharInput()) { view.get_active_model()->add_mark(k2.code); }

// add new line and go to insert mode (below)
} else if (k.value() == rawterm::Key('o')) {
if (is_readonly_model()) { continue; }
Expand Down
10 changes: 10 additions & 0 deletions src/model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -550,3 +550,13 @@ void Model::dedent_curr_line() {
const std::size_t line_size = buf.at(current_line).size();
buf.at(current_line) = buf.at(current_line).substr(to_delete, line_size);
}

void Model::add_mark(const char c) {
marks.insert_or_assign(c, Mark(current_line, current_char));
}

[[nodiscard]] bool Model::is_marked(const std::size_t idx) const {
return std::find_if(marks.begin(), marks.end(), [&](const auto& map) {
return map.second.line_pos == idx;
}) != marks.end();
}
15 changes: 15 additions & 0 deletions src/model.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <stack>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>

#include <rawterm/screen.h>
Expand All @@ -26,6 +27,17 @@ struct WordPos {
uint_t lineno;
};

struct Mark {
unsigned int line_pos;
unsigned int char_pos;

explicit Mark(unsigned int l, unsigned int c) : line_pos(l), char_pos(c) {}

[[nodiscard]] bool operator==(const Mark& other) const {
return line_pos == other.line_pos && char_pos == other.char_pos;
}
};

struct Model {
ModelType type = ModelType::BUF;
std::vector<std::string> buf;
Expand All @@ -34,6 +46,7 @@ struct Model {
unsigned int current_char = 0; // 0-indexed
std::string search_str = "";
std::size_t vertical_offset = 0;
std::unordered_map<char, Mark> marks = {};

// Num of lines offset to view
unsigned int view_offset = 0;
Expand Down Expand Up @@ -73,6 +86,8 @@ struct Model {
std::optional<rawterm::Pos> find_next_str(std::string_view);
void indent_curr_line();
void dedent_curr_line();
void add_mark(const char);
[[nodiscard]] bool is_marked(const std::size_t) const;
};

#endif // MODEL_H
13 changes: 9 additions & 4 deletions src/view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ void View::draw_screen() {
if (LINE_NUMBERS) {
rawterm::Color c = COLOR_UI_BG;
if (idx == get_active_model()->current_line + 1) { c = COLOR_DARK_YELLOW; }
if (get_active_model()->is_marked(idx)) { c = COLOR_GREEN; }

screen +=
rawterm::set_foreground(std::format("{:>{}}\u2502", idx, line_number_offset), c);
}
Expand Down Expand Up @@ -227,10 +229,13 @@ void View::draw_line(const Draw_Line_dir::values redraw_prev) {
std::string_view curr_line = get_active_model()->buf.at(idx);

if (LINE_NUMBERS) {
const rawterm::Color color =
(idx == get_active_model()->current_line) ? COLOR_DARK_YELLOW : COLOR_UI_BG;
line += rawterm::set_foreground(
std::format("{:>{}}\u2502", idx + 1, line_number_offset), color);
// TODO: refactor lineno colour into it's own view method
rawterm::Color c = COLOR_UI_BG;
if (idx == get_active_model()->current_line) { c = COLOR_DARK_YELLOW; }
if (get_active_model()->is_marked(idx)) { c = COLOR_GREEN; }

line +=
rawterm::set_foreground(std::format("{:>{}}\u2502", idx + 1, line_number_offset), c);
}

const std::size_t vert_offset = get_active_model()->vertical_offset;
Expand Down
11 changes: 11 additions & 0 deletions tests/integration/normal_mode_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,3 +558,14 @@ def test_left_chevron_key(r: TmuxRunner):
def test_right_chevron_key(r: TmuxRunner):
r.press('>')
assert " This" in r.lines()[0]


@setup("tests/fixture/lorem_ipsum.txt")
def test_m_key(r: TmuxRunner):
r.type_str("j" * 3)
r.type_str("ma")
r.press('j')

target_line: str = r.color_screenshot()[3]
assert "4\u2502" in target_line
assert target_line.startswith("\x1b[38;2;95;250;104m")
19 changes: 19 additions & 0 deletions tests/model_test.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "model.h"

#include <stdexcept>

#include <catch2/catch_test_macros.hpp>
#include <rawterm/core.h>
#include <rawterm/text.h>
Expand Down Expand Up @@ -734,3 +736,20 @@ TEST_CASE("dedent_curr_line", "[model]") {
REQUIRE(m.buf.at(1).size() == 3);
REQUIRE(m.buf.at(0).at(0) == 'f');
}

TEST_CASE("add_mark", "[model]") {
auto m = Model({" foo", "bar"}, "");
m.add_mark('a');

REQUIRE(m.marks.size() == 1);
REQUIRE(m.marks.at('a') == Mark(0, 0));
REQUIRE_THROWS_AS(m.marks.at('b'), std::out_of_range);
}

TEST_CASE("is_marked", "[model]") {
auto m = Model({" foo", "bar"}, "");
m.add_mark('a');

REQUIRE(m.is_marked(0));
REQUIRE(!m.is_marked(1));
}
Loading