tAdd basic uppercase/lowercase support - 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 <[email protected]> | |
Date: Thu, 5 Oct 2023 12:10:03 +0200 | |
Add basic uppercase/lowercase support | |
Diffstat: | |
M Makefile | 8 ++++++-- | |
M README | 9 ++++++--- | |
M keys_basic.c | 99 ++++++++++++++++++++++++++++-… | |
M leditrc.5 | 13 ++++++++++++- | |
M leditrc.example | 2 ++ | |
5 files changed, 118 insertions(+), 13 deletions(-) | |
--- | |
diff --git a/Makefile b/Makefile | |
t@@ -13,6 +13,7 @@ MISCFILES = Makefile README LICENSE IDEAS NOTES TODO | |
DEBUG=0 | |
SANITIZE=0 | |
+ENABLE_UTF8PROC=1 | |
OBJ = \ | |
assert.o \ | |
t@@ -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… | |
# of ctrlsel yet, but since it's moderately likely that I will use that in the… | |
# decided to just leave it in. | |
-CFLAGS_LEDIT = ${EXTRA_FLAGS_SANITIZE${SANITIZE}} ${EXTRA_CFLAGS_DEBUG${DEBUG}… | |
-LDFLAGS_LEDIT = ${EXTRA_FLAGS_SANITIZE${SANITIZE}} ${EXTRA_LDFLAGS_DEBUG${DEBU… | |
+CFLAGS_LEDIT = ${EXTRA_FLAGS_SANITIZE${SANITIZE}} ${EXTRA_CFLAGS_DEBUG${DEBUG}… | |
+LDFLAGS_LEDIT = ${EXTRA_FLAGS_SANITIZE${SANITIZE}} ${EXTRA_LDFLAGS_DEBUG${DEBU… | |
all: ${BIN} | |
diff --git a/README b/README | |
t@@ -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 | |
t@@ -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> | |
t@@ -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" | |
t@@ -121,6 +126,8 @@ static struct action delete_graphemes_forwards_multiline(l… | |
static struct action delete_graphemes_backwards_multiline(ledit_view *view, ch… | |
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… | |
static struct action join_lines(ledit_view *view, char *text, size_t len); | |
t@@ -160,6 +167,7 @@ static struct basic_key_cb basic_key_cb_map[] = { | |
{"append-after-eol", &append_after_eol, KEY_FLAG_JUMP_TO_CURSOR, NORMA… | |
{"append-line-above", &append_line_above, KEY_FLAG_JUMP_TO_CURSOR, NOR… | |
{"append-line-below", &append_line_below, KEY_FLAG_JUMP_TO_CURSOR, NOR… | |
+ {"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_AL… | |
t@@ -185,17 +193,17 @@ static struct basic_key_cb basic_key_cb_map[] = { | |
{"enter-searchedit-backwards", &enter_searchedit_backward, KEY_FLAG_NO… | |
{"enter-searchedit-forwards", &enter_searchedit_forward, KEY_FLAG_NONE… | |
{"enter-visual", &enter_visual, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT… | |
- {"return-to-normal", &return_to_normal, KEY_FLAG_JUMP_TO_CURSOR|KEY_FL… | |
{"find-char-backwards", &find_char_backwards, KEY_FLAG_JUMP_TO_CURSOR|… | |
{"find-char-forwards", &find_char_forwards, KEY_FLAG_JUMP_TO_CURSOR|KE… | |
{"find-next-char-backwards", &find_next_char_backwards, KEY_FLAG_JUMP_… | |
{"find-next-char-forwards", &find_next_char_forwards, KEY_FLAG_JUMP_TO… | |
{"insert-at-beginning", &insert_at_beginning, KEY_FLAG_JUMP_TO_CURSOR,… | |
+ {"insert-mark", &insert_mark, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NOR… | |
{"insert-text", &insert_mode_insert_text, KEY_FLAG_JUMP_TO_CURSOR, INS… | |
{"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_… | |
{"key-0", &key_0, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMA… | |
- {"insert-mark", &insert_mark, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NOR… | |
+ {"lowercase", &lowercase, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, | |
{"move-to-eol", &move_to_eol, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_AL… | |
{"move-to-line", &move_to_line, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_… | |
{"next-bigword", &next_bigword, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_… | |
t@@ -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_FL… | |
{"screen-down", &screen_down, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_AL… | |
{"screen-up", &screen_up, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWE… | |
{"scroll-lines-down", &scroll_lines_down, KEY_FLAG_JUMP_TO_CURSOR|KEY_… | |
t@@ -232,6 +240,7 @@ static struct basic_key_cb basic_key_cb_map[] = { | |
{"switch-selection-end", &switch_selection_end, KEY_FLAG_JUMP_TO_CURSO… | |
{"toggle-hard-line-based", &toggle_hard_line_based, KEY_FLAG_NONE|KEY_… | |
{"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|… | |
{"yank-lines", &yank_lines, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLO… | |
}; | |
t@@ -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 anythin… | |
+ utf8proc_ssize_t origlen = utf8proc_iterate((utf8proc_uint8_t *)line->… | |
+ /* 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… | |
+ ); | |
+ 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_ind… | |
+ 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; | |
t@@ -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_ind… | |
grab_char_cb = NULL; | |
finalize_repetition_stack(); | |
return (struct action){ACTION_NONE, NULL}; | |
diff --git a/leditrc.5 b/leditrc.5 | |
t@@ -1,4 +1,4 @@ | |
-.Dd October 2, 2023 | |
+.Dd October 5, 2023 | |
.Dt LEDITRC 5 | |
.Os | |
.Sh NAME | |
t@@ -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/lowerc… | |
+If utf8proc support is not enabled, this will use the standard C library funct… | |
+.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 chara… | |
+more complex handling (e.g. characters that require multiple characters when c… | |
+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 | |
t@@ -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 = { |