From a32ef2ff53bd1a3175a69b42e1113289bb02bf09 Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Tue, 25 Nov 2025 00:20:32 -0800 Subject: [PATCH] move to first non-whitespace on next line --- CMakeLists.txt | 2 +- default.nix | 2 +- main.c | 195 ++++++++++++++++++++++++++++++------------------- 3 files changed, 123 insertions(+), 76 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 925e10a..b56bd5d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.15) project(ke C) # Specify C language explicitly set(CMAKE_C_STANDARD 99) -set(KE_VERSION "1.3.3") +set(KE_VERSION "1.3.4") set(CMAKE_C_FLAGS "-Wall -Wextra -pedantic -Wshadow -Werror -std=c99 -g") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_DEFAULT_SOURCE -D_XOPEN_SOURCE") diff --git a/default.nix b/default.nix index 70b2b83..9fef89d 100644 --- a/default.nix +++ b/default.nix @@ -6,7 +6,7 @@ }: stdenv.mkDerivation { pname = "ke"; - version = "1.3.3"; + version = "1.3.4"; src = lib.cleanSource ./.; diff --git a/main.c b/main.c index daeeb2b..6d22f0d 100644 --- a/main.c +++ b/main.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -1589,6 +1590,41 @@ editor_openfile(void) } +int +first_nonwhitespace(struct erow *row) +{ + int pos; + wchar_t wc; + mbstate_t state; + size_t len; + + if (row == NULL) { + return 0; + } + + memset(&state, 0, sizeof(state)); + pos = 0; + while (pos < row->size) { + len = mbrtowc(&wc, &row->line[pos], row->size - pos, &state); + if (len == (size_t) -1 || len == (size_t) -2) { + /* Invalid or incomplete sequence, stop here */ + break; + } + if (len == 0) { + /* Null character, stop here */ + break; + } + if (!iswspace(wc)) { + /* Found non-whitespace character */ + break; + } + pos += len; + } + + return pos; +} + + void move_cursor(int16_t c) { @@ -1598,86 +1634,97 @@ move_cursor(int16_t c) row = (editor.cury >= editor.nrows) ? NULL : &editor.row[editor.cury]; switch (c) { - case ARROW_UP: - case CTRL_KEY('p'): - if (editor.cury > 0) { - editor.cury--; - } - break; - case ARROW_DOWN: - case CTRL_KEY('n'): - if (editor.cury < editor.nrows) { - editor.cury++; - } - break; - case ARROW_RIGHT: - case CTRL_KEY('f'): - if (row && editor.curx < row->size) { + case ARROW_UP: + case CTRL_KEY('p'): + if (editor.cury > 0) { + editor.cury--; + row = (editor.cury >= editor.nrows) + ? NULL + : &editor.row[editor.cury]; + editor.curx = first_nonwhitespace(row); + } + break; + case ARROW_DOWN: + case CTRL_KEY('n'): + if (editor.cury < editor.nrows) { + editor.cury++; + row = (editor.cury >= editor.nrows) + ? NULL + : &editor.row[editor.cury]; + editor.curx = first_nonwhitespace(row); + } + break; + case ARROW_RIGHT: + case CTRL_KEY('f'): + if (row && editor.curx < row->size) { + editor.curx++; + /* skip over UTF-8 continuation bytes */ + while (row && editor.curx < row->size && + ((unsigned char) row->line[editor.curx] & + 0xC0) == 0x80) { editor.curx++; - /* skip over UTF-8 continuation bytes */ - while (row && editor.curx < row->size && - ((unsigned char) row->line[editor.curx] & - 0xC0) == 0x80) { - editor.curx++; - } - } else if (row && editor.curx == row->size) { - editor.cury++; - editor.curx = 0; } - break; - case ARROW_LEFT: - case CTRL_KEY('b'): - if (editor.curx > 0) { + } else if (row && editor.curx == row->size) { + editor.cury++; + row = (editor.cury >= editor.nrows) + ? NULL + : &editor.row[editor.cury]; + editor.curx = first_nonwhitespace(row); + } + break; + case ARROW_LEFT: + case CTRL_KEY('b'): + if (editor.curx > 0) { + editor.curx--; + /* move to the start byte if we landed on a continuation */ + while (editor.curx > 0 && + ((unsigned char) row->line[editor.curx] & + 0xC0) == 0x80) { editor.curx--; - /* move to the start byte if we landed on a continuation */ - while (editor.curx > 0 && - ((unsigned char) row->line[editor.curx] & - 0xC0) == 0x80) { - editor.curx--; - } - } else if (editor.cury > 0) { - editor.cury--; - editor.curx = editor.row[editor.cury].size; - /* ensure at a codepoint boundary at end of previous line */ - row = &editor.row[editor.cury]; - while (editor.curx > 0 && - ((unsigned char) row->line[editor.curx] & - 0xC0) == 0x80) { - editor.curx--; - } - } - break; - case PG_UP: - case PG_DN: - if (c == PG_UP) { - editor.cury = editor.rowoffs; - } else if (c == PG_DN) { - editor.cury = editor.rowoffs + editor.rows - 1; - if (editor.cury > editor.nrows) { - editor.cury = editor.nrows; - } - } - - reps = editor.rows; - while (--reps) { - move_cursor(c == PG_UP ? ARROW_UP : ARROW_DOWN); - } - - break; - - case HOME_KEY: - case CTRL_KEY('a'): - editor.curx = 0; - break; - case END_KEY: - case CTRL_KEY('e'): - if (editor.nrows == 0) { - break; } + } else if (editor.cury > 0) { + editor.cury--; editor.curx = editor.row[editor.cury].size; + /* ensure at a codepoint boundary at end of previous line */ + row = &editor.row[editor.cury]; + while (editor.curx > 0 && + ((unsigned char) row->line[editor.curx] & + 0xC0) == 0x80) { + editor.curx--; + } + } + break; + case PG_UP: + case PG_DN: + if (c == PG_UP) { + editor.cury = editor.rowoffs; + } else if (c == PG_DN) { + editor.cury = editor.rowoffs + editor.rows - 1; + if (editor.cury > editor.nrows) { + editor.cury = editor.nrows; + } + } + + reps = editor.rows; + while (--reps) { + move_cursor(c == PG_UP ? ARROW_UP : ARROW_DOWN); + } + + break; + + case HOME_KEY: + case CTRL_KEY('a'): + editor.curx = 0; + break; + case END_KEY: + case CTRL_KEY('e'): + if (editor.nrows == 0) { break; - default: - break; + } + editor.curx = editor.row[editor.cury].size; + break; + default: + break; }