ledit

Text editor (WIP)
git clone git://lumidify.org/ledit.git (fast, but not encrypted)
git clone https://lumidify.org/git/ledit.git (encrypted, but very slow)
Log | Files | Refs | README | LICENSE

commit 4ee5ad27da617738b33a8e5e46aa75afcc2e54d0
parent a3b601c93fde71dd598f855980897a1e3ead492e
Author: lumidify <nobody@lumidify.org>
Date:   Thu,  5 Oct 2023 12:10:03 +0200

Add basic uppercase/lowercase support

Diffstat:
MMakefile | 8++++++--
MREADME | 9++++++---
Mkeys_basic.c | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Mleditrc.5 | 13++++++++++++-
Mleditrc.example | 2++
5 files changed, 118 insertions(+), 13 deletions(-)

diff --git a/Makefile b/Makefile @@ -13,6 +13,7 @@ MISCFILES = Makefile README LICENSE IDEAS NOTES TODO DEBUG=0 SANITIZE=0 +ENABLE_UTF8PROC=1 OBJ = \ assert.o \ @@ -70,12 +71,15 @@ EXTRA_CFLAGS_DEBUG0 = ${CFLAGS} EXTRA_LDFLAGS_DEBUG0 = ${LDFLAGS} EXTRA_CFLAGS_DEBUG1 = -DLEDIT_DEBUG -g EXTRA_FLAGS_SANITIZE1 = -fsanitize=address +EXTRA_CFLAGS_UTF8PROC0 = -DENABLE_UTF8PROC=0 +EXTRA_CFLAGS_UTF8PROC1 = `pkg-config --cflags libutf8proc` -DENABLE_UTF8PROC=1 +EXTRA_LDFLAGS_UTF8PROC1 = `pkg-config --libs libutf8proc` # Xcursor isn't actually needed right now since I'm not using the drag 'n drop functionality # of ctrlsel yet, but since it's moderately likely that I will use that in the future, I # decided to just leave it in. -CFLAGS_LEDIT = ${EXTRA_FLAGS_SANITIZE${SANITIZE}} ${EXTRA_CFLAGS_DEBUG${DEBUG}} -Wall -Wextra -pedantic -D_POSIX_C_SOURCE=200809L -std=c99 `pkg-config --cflags x11 xkbfile pangoxft xext xcursor` -LDFLAGS_LEDIT = ${EXTRA_FLAGS_SANITIZE${SANITIZE}} ${EXTRA_LDFLAGS_DEBUG${DEBUG}} `pkg-config --libs x11 xkbfile pangoxft xext xcursor` -lm +CFLAGS_LEDIT = ${EXTRA_FLAGS_SANITIZE${SANITIZE}} ${EXTRA_CFLAGS_DEBUG${DEBUG}} ${EXTRA_CFLAGS_UTF8PROC${ENABLE_UTF8PROC}} -Wall -Wextra -pedantic -D_POSIX_C_SOURCE=200809L -std=c99 `pkg-config --cflags x11 xkbfile pangoxft xext xcursor` +LDFLAGS_LEDIT = ${EXTRA_FLAGS_SANITIZE${SANITIZE}} ${EXTRA_LDFLAGS_DEBUG${DEBUG}} ${EXTRA_LDFLAGS_UTF8PROC${ENABLE_UTF8PROC}} `pkg-config --libs x11 xkbfile pangoxft xext xcursor` -lm all: ${BIN} diff --git a/README b/README @@ -8,15 +8,18 @@ layout. Additionally, it allows multiple views on a text buffer so that different parts of a file can be shown at the same time. -REQUIREMENTS: pango (with xft), xlib (extensions: xkb, xdbe), xcursor +REQUIREMENTS: +pango (with xft), xlib (extensions: xkb, xdbe), +xcursor, libutf8proc (if ENABLE_UTF8PROC is set) Note: xcursor technically wouldn't be needed since it's just a dependency of features in ctrlsel that currently aren't being used, but I will probably use those features later, so I was too lazy to remove those parts. Packages to install: -On OpenBSD: pango -On MX Linux: libpango1.0-dev, libx11-dev, libxkbfile-dev libxcursor-dev +On OpenBSD: pango, libutf8proc +On Debian-based systems: +libpango1.0-dev, libx11-dev, libxkbfile-dev libxcursor-dev, libutf8proc-dev (this is just from memory, I need to test it with a fresh system sometime) Installation: diff --git a/keys_basic.c b/keys_basic.c @@ -17,6 +17,7 @@ /* FIXME: sort functions a bit better, maybe split file */ /* FIXME: documentation */ #include <stdio.h> +#include <ctype.h> #include <stdlib.h> #include <X11/Xlib.h> @@ -27,6 +28,10 @@ #include <X11/XF86keysym.h> #include <X11/cursorfont.h> +#if ENABLE_UTF8PROC +#include "utf8proc.h" +#endif + #include "util.h" #include "assert.h" #include "memory.h" @@ -121,6 +126,8 @@ static struct action delete_graphemes_forwards_multiline(ledit_view *view, char static struct action delete_graphemes_backwards_multiline(ledit_view *view, char *text, size_t len); static struct action yank(ledit_view *view, char *text, size_t len); static struct action yank_lines(ledit_view *view, char *text, size_t len); +static struct action uppercase(ledit_view *view, char *text, size_t len); +static struct action lowercase(ledit_view *view, char *text, size_t len); static struct action replace(ledit_view *view, char *text, size_t len); static struct action cursor_to_first_non_ws(ledit_view *view, char *text, size_t len); static struct action join_lines(ledit_view *view, char *text, size_t len); @@ -160,6 +167,7 @@ static struct basic_key_cb basic_key_cb_map[] = { {"append-after-eol", &append_after_eol, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|VISUAL|INSERT}, {"append-line-above", &append_line_above, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|VISUAL|INSERT}, {"append-line-below", &append_line_below, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|VISUAL|INSERT}, + {"break-line", &break_line, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, {"change", &change, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|VISUAL|INSERT}, {"change-to-eol", &change_to_eol, KEY_FLAG_JUMP_TO_CURSOR, NORMAL}, {"clipboard-copy", &clipcopy, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, @@ -185,17 +193,17 @@ static struct basic_key_cb basic_key_cb_map[] = { {"enter-searchedit-backwards", &enter_searchedit_backward, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT|VISUAL}, {"enter-searchedit-forwards", &enter_searchedit_forward, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT|VISUAL}, {"enter-visual", &enter_visual, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, - {"return-to-normal", &return_to_normal, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, {"find-char-backwards", &find_char_backwards, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, {"find-char-forwards", &find_char_forwards, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, {"find-next-char-backwards", &find_next_char_backwards, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, {"find-next-char-forwards", &find_next_char_forwards, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, {"insert-at-beginning", &insert_at_beginning, KEY_FLAG_JUMP_TO_CURSOR, NORMAL}, + {"insert-mark", &insert_mark, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, {"insert-text", &insert_mode_insert_text, KEY_FLAG_JUMP_TO_CURSOR, INSERT}, {"join-lines", &join_lines, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, {"jump-to-mark", &jump_to_mark, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, {"key-0", &key_0, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, - {"insert-mark", &insert_mark, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, + {"lowercase", &lowercase, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, {"move-to-eol", &move_to_eol, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, {"move-to-line", &move_to_line, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, {"next-bigword", &next_bigword, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, @@ -219,7 +227,7 @@ static struct basic_key_cb basic_key_cb_map[] = { {"redo", &redo, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, {"repeat-command", &repeat_command, KEY_FLAG_JUMP_TO_CURSOR, NORMAL}, {"replace", &replace, KEY_FLAG_JUMP_TO_CURSOR, NORMAL}, - {"break-line", &break_line, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, + {"return-to-normal", &return_to_normal, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, {"screen-down", &screen_down, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT}, {"screen-up", &screen_up, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT}, {"scroll-lines-down", &scroll_lines_down, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT}, @@ -232,6 +240,7 @@ static struct basic_key_cb basic_key_cb_map[] = { {"switch-selection-end", &switch_selection_end, KEY_FLAG_JUMP_TO_CURSOR, VISUAL}, {"toggle-hard-line-based", &toggle_hard_line_based, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, {"undo", &undo, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, + {"uppercase", &uppercase, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, {"yank", &yank, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, {"yank-lines", &yank_lines, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT}, }; @@ -2495,6 +2504,80 @@ GEN_MOVE_TO_CHAR( ) static struct action +loweruppercase(ledit_view *view, int upper) { + /* FIXME: shouldn't CHECK_VIEW_LOCKED be in a lot more places? */ + CHECK_VIEW_LOCKED(view); + /* FIXME: move most of this to convenience functions in buffer.c */ + ledit_line *line = buffer_get_line(view->buffer, view->cur_line); + if (view->cur_index >= line->len) + return (struct action){ACTION_NONE, NULL}; + buffer_normalize_line(line); + size_t start_index = view->cur_index; +#if ENABLE_UTF8PROC + utf8proc_int32_t c; + /* FIXME: this cast to utf8proc_uint8_t probably doesn't break anything, but could it? */ + utf8proc_ssize_t origlen = utf8proc_iterate((utf8proc_uint8_t *)line->text + start_index, line->len - start_index, &c); + /* FIXME: show error message? */ + if (c < 0 || origlen < 1) + return (struct action){ACTION_NONE, NULL}; + utf8proc_int32_t u; + if (upper) + u = utf8proc_toupper(c); + else + u = utf8proc_tolower(c); + utf8proc_uint8_t u8[4]; + utf8proc_ssize_t newlen = utf8proc_encode_char(u, u8); + if (newlen < 1) + return (struct action){ACTION_NONE, NULL}; + delete_range( + view, 0, 0, + view->cur_line, start_index, view->cur_line, start_index + origlen, 0 + ); + insert_text( + view, view->cur_line, start_index, (char *)u8, newlen, + view->cur_line, start_index, view->cur_line, start_index, 1, 1, 0 + ); +#else + char c; + if (upper) + c = toupper((unsigned char)line->text[view->cur_index]); + else + c = tolower((unsigned char)line->text[view->cur_index]); + delete_range( + view, 0, 0, + view->cur_line, start_index, view->cur_line, start_index + 1, 0 + ); + insert_text( + view, view->cur_line, start_index, &c, 1, + view->cur_line, start_index, view->cur_line, start_index, 1, 1, 0 + ); +#endif + /* If the last character on a line is replaced, the cursor would jump + backwards due to the deletion, so it has to be set to the original + position again */ + view->cur_index = start_index; + push_undo_empty_insert(view, view->cur_line, view->cur_index, 0); + if (view->mode == NORMAL) + view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); + finalize_repetition_stack(); + return (struct action){ACTION_NONE, NULL}; +} + +static struct action +uppercase(ledit_view *view, char *text, size_t len) { + (void)text; + (void)len; + return loweruppercase(view, 1); +} + +static struct action +lowercase(ledit_view *view, char *text, size_t len) { + (void)text; + (void)len; + return loweruppercase(view, 0); +} + +static struct action replace_cb(ledit_view *view, char *text, size_t len) { CHECK_VIEW_LOCKED(view); size_t start_index = view->cur_index; @@ -2512,11 +2595,13 @@ replace_cb(ledit_view *view, char *text, size_t len) { view, view->cur_line, start_index, text, len, view->cur_line, start_index, view->cur_line, start_index, 1, 1, 0 ); - /* this should not be necessary, but just in case */ - view->cur_index = view_get_legal_normal_pos( - view, view->cur_line, view->cur_index - ); + /* If the last character on a line is replaced, the cursor would jump + backwards due to the deletion, so it has to be set to the original + position again */ + view->cur_index = start_index; push_undo_empty_insert(view, view->cur_line, view->cur_index, 0); + if (view->mode == NORMAL) + view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); grab_char_cb = NULL; finalize_repetition_stack(); return (struct action){ACTION_NONE, NULL}; diff --git a/leditrc.5 b/leditrc.5 @@ -1,4 +1,4 @@ -.Dd October 2, 2023 +.Dd October 5, 2023 .Dt LEDITRC 5 .Os .Sh NAME @@ -674,6 +674,17 @@ write, and current line number. Switch the end of the selection that can be moved. .It Ar toggle-hard-line-based Op normal, visual, insert Toggle the line mode between hardline and softline. +.It Ar uppercase Op normal, insert +.It Ar lowercase Op normal, insert +Replace the character at the current cursor position with the uppercase/lowercase version if it exists. +If utf8proc support is not enabled, this will use the standard C library functions +.Fn toupper +and +.Fn tolower , +so it will not work with most Unicode characters. +Note that even with utf8proc, it will not work in all cases because some characters require +more complex handling (e.g. characters that require multiple characters when converted to +uppercase), which is not supported. .It Ar undo Oo normal, insert Oc Oo num Oc Undo .Ar num diff --git a/leditrc.example b/leditrc.example @@ -107,6 +107,8 @@ bindings = { bind find-next-char-backwards text "T" modes normal|visual bind find-char-forwards text "f" modes normal|visual bind find-char-backwards text "F" modes normal|visual + bind uppercase text "U" modes normal|insert mods control + bind lowercase text "L" modes normal|insert mods control bind insert-text catchall modes insert } command-keys = {