From 6643ff4c14cc27bf80189aadaf65c36ac2e7307f Mon Sep 17 00:00:00 2001 From: pommicket Date: Thu, 23 Mar 2023 15:40:43 -0400 Subject: increment-number and decrement-number --- buffer.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ command.c | 9 ++++ command.h | 3 ++ control | 2 +- main.c | 1 + ted.cfg | 30 ++++++++++++ ted.h | 6 ++- 7 files changed, 205 insertions(+), 2 deletions(-) diff --git a/buffer.c b/buffer.c index c440ed4..898dd07 100644 --- a/buffer.c +++ b/buffer.c @@ -1791,6 +1791,153 @@ static void buffer_shorten_line(Line *line, u32 new_len) { line->len = new_len; // @TODO(optimization,memory): decrease line capacity } +// returns -1 if c is not a digit of base +static int base_digit(char c, int base) { + int value = -1; + if (c >= '0' && c <= '9') { + value = c - '0'; + } else if (c >= 'a' && c <= 'f') { + value = c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + value = c - 'A' + 10; + } + return value >= base ? -1 : value; +} + +// e.g. returns "0x1b" for num = "0x1a", by = 1 +// turns out it's surprisingly difficult to handle all cases +static char *change_number(const char *num, i64 by) { + if (!isdigit(*num) && *num != '-') { + return NULL; + } + + bool negative = *num == '-'; + if (negative) ++num; + + int start = 0; + int base = 10; + if (num[0] == '0') { + switch (num[1]) { + case '\0': + start = 0; + break; + case 'x': + case 'X': + start = 2; + base = 16; + break; + case 'o': + case 'O': + start = 2; + base = 8; + break; + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': + start = 1; + base = 8; + break; + case 'b': + case 'B': + // not handling binary yet since sprintf doesnt have binary gah + return NULL; + default: + return NULL; + } + } + + // find end of number + int end; + for (end = start + 1; base_digit(num[end], base) != -1; ++end); + + if (base_digit(num[end], 16) != -1) { + // we're probably wrong about the base. let's not do anything. + return NULL; + } + + if (num[end] != '\0' + && !isalnum(num[end]) // number suffixes e.g. 0xffu + ) { + // probably not a number. + // at least not one we understand. + return NULL; + } + + bool uppercase = false; + for (int i = 0; i < end; ++i) + if (isupper(num[i])) + uppercase = true; + + long long number = strtoll(&num[start], NULL, base); + if (number == LLONG_MIN || number == LLONG_MAX) + return NULL; + if (negative) number = -number; + // overflow checks + if (by > 0 && number > LLONG_MAX - by) + return NULL; + if (by < 0 && number <= LLONG_MIN - by) + return NULL; + number += by; + printf("%lld\n",number); + negative = number < 0; + if (negative) number = -number; + + char new_number[128] = {0}; + switch (uppercase * 1000 + base) { + case 1010: case 10: sprintf(new_number, "%lld", number); break; + case 1008: case 8: sprintf(new_number, "%llo", number); break; + case 1016: sprintf(new_number, "%llX", number); break; + case 16: sprintf(new_number, "%llx", number); break; + } + + size_t capacity = strlen(num) + 128; + char *changed = calloc(1, capacity); + printf("%s %.*s %s %s\n",negative ? "-" : "", start, num, new_number, &num[end]); + str_printf(changed, capacity, "%s%.*s%s%s", negative ? "-" : "", start, num, new_number, &num[end]); + return changed; + +} + +void buffer_change_number_at_cursor(TextBuffer *buffer, i64 by) { + BufferPos pos = buffer->cursor_pos; + + // move to start of number + if (is32_alnum(buffer_char_before_pos(buffer, pos))) { + buffer_pos_move_left_words(buffer, &pos, 1); + } + char32_t c = buffer_char_after_pos(buffer, pos); + if (c >= 127 || !isdigit((char)c)) { + if (c != '-') { + // not a number + return; + } + } + + BufferPos end = pos; + if (c == '-') { + buffer_pos_move_right(buffer, &end, 1); + } + buffer_pos_move_right_words(buffer, &end, 1); + if (buffer_char_at_pos(buffer, end) == '.') { + // floating-point number. dont try to increment it + return; + } + + if (buffer_char_before_pos(buffer, pos) == '-') { + // include negative sign + buffer_pos_move_left(buffer, &pos, 1); + } + + u32 nchars = (u32)buffer_pos_diff(buffer, pos, end); + char *word = buffer_get_utf8_text_at_pos(buffer, pos, nchars); + char *newnum = change_number(word, by); + if (newnum) { + buffer_delete_chars_between(buffer, pos, end); + buffer_insert_utf8_at_pos(buffer, pos, newnum); + buffer_cursor_move_to_pos(buffer, pos); + free(newnum); + } + free(word); +} + // decrease the number of lines in the buffer. // DOES NOT DO ANYTHING TO THE LINES REMOVED! YOU NEED TO FREE THEM YOURSELF! static void buffer_shorten(TextBuffer *buffer, u32 new_nlines) { @@ -2013,6 +2160,15 @@ void buffer_insert_char_at_cursor(TextBuffer *buffer, char32_t c) { buffer_insert_text_at_cursor(buffer, s); } + +void buffer_insert_utf8_at_pos(TextBuffer *buffer, BufferPos pos, const char *utf8) { + String32 s32 = str32_from_utf8(utf8); + if (s32.str) { + buffer_insert_text_at_pos(buffer, pos, s32); + str32_free(&s32); + } +} + void buffer_insert_utf8_at_cursor(TextBuffer *buffer, const char *utf8) { String32 s32 = str32_from_utf8(utf8); if (s32.str) { diff --git a/command.c b/command.c index 168597a..ed42e87 100644 --- a/command.c +++ b/command.c @@ -100,6 +100,8 @@ static CommandName command_names[] = { {"macro-record", CMD_MACRO_RECORD}, {"macro-stop", CMD_MACRO_STOP}, {"macro-execute", CMD_MACRO_EXECUTE}, + {"increment-number", CMD_INCREMENT_NUMBER}, + {"decrement-number", CMD_DECREMENT_NUMBER}, }; static_assert_if_possible(arr_count(command_names) == CMD_COUNT) @@ -177,6 +179,13 @@ void command_execute_ex(Ted *ted, Command c, CommandArgument full_argument, Comm case CMD_NOOP: break; + case CMD_INCREMENT_NUMBER: + buffer_change_number_at_cursor(buffer, argument); + break; + case CMD_DECREMENT_NUMBER: + buffer_change_number_at_cursor(buffer, -argument); + break; + case CMD_LEFT: if (buffer) buffer_cursor_move_left(buffer, argument); autocomplete_close(ted); diff --git a/command.h b/command.h index a246439..408bfbe 100644 --- a/command.h +++ b/command.h @@ -77,6 +77,9 @@ typedef enum { CMD_DELETE, CMD_BACKSPACE_WORD, CMD_DELETE_WORD, + + CMD_INCREMENT_NUMBER, + CMD_DECREMENT_NUMBER, /// open a file CMD_OPEN, diff --git a/control b/control index a44bba7..0eae890 100644 --- a/control +++ b/control @@ -1,5 +1,5 @@ Package: ted -Version: 2.1r1 +Version: 2.2 Section: text Priority: optional Architecture: amd64 diff --git a/main.c b/main.c index b13a7a3..9e9f1c2 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,6 @@ /* - ctrl+9/0 to inc/dec number +- handle leading zeroes FUTURE FEATURES: - better undo chaining (dechain on backspace?) - font setting & support for multiple fonts to cover more characters diff --git a/ted.cfg b/ted.cfg index 95f6d33..592cfb4 100644 --- a/ted.cfg +++ b/ted.cfg @@ -300,6 +300,9 @@ Ctrl+x = :cut Ctrl+v = :paste Ctrl+Shift+p = :command-selector +Ctrl+9 = :decrement-number +Ctrl+0 = :increment-number + # tabs Ctrl+w = :tab-close Ctrl+PageUp = :tab-prev @@ -354,6 +357,33 @@ Ctrl+Shift+tab = :split-swap Escape = :escape +# keyboard macros +Ctrl+F1 = 1 :macro-record +Ctrl+F2 = 2 :macro-record +Ctrl+F3 = 3 :macro-record +Ctrl+F4 = 4 :macro-record +Ctrl+F5 = 5 :macro-record +Ctrl+F6 = 6 :macro-record +Ctrl+F7 = 7 :macro-record +Ctrl+F8 = 8 :macro-record +Ctrl+F9 = 9 :macro-record +Ctrl+F10 = 10 :macro-record +Ctrl+F11 = 11 :macro-record +Ctrl+F12 = 12 :macro-record + +Shift+F1 = 1 :macro-execute +Shift+F2 = 2 :macro-execute +Shift+F3 = 3 :macro-execute +Shift+F4 = 4 :macro-execute +Shift+F5 = 5 :macro-execute +Shift+F6 = 6 :macro-execute +Shift+F7 = 7 :macro-execute +Shift+F8 = 8 :macro-execute +Shift+F9 = 9 :macro-execute +Shift+F10 = 10 :macro-execute +Shift+F11 = 11 :macro-execute +Shift+F12 = 12 :macro-execute + [extensions] # text is the default if the extension doesn't match any of the ones below Text = .txt diff --git a/ted.h b/ted.h index 7dc9602..94660ff 100644 --- a/ted.h +++ b/ted.h @@ -28,7 +28,7 @@ extern "C" { #include "sdl-inc.h" /// Version number -#define TED_VERSION "2.1r1" +#define TED_VERSION "2.2" /// Version string #define TED_VERSION_FULL "ted v. " TED_VERSION /// Maximum path size ted handles. @@ -1043,6 +1043,8 @@ String32 buffer_word_at_cursor(TextBuffer *buffer); /// Get a UTF-8 string consisting of the word at the cursor. /// The return value should be freed. char *buffer_word_at_cursor_utf8(TextBuffer *buffer); +/// Used for CMD_INCREMENT_NUMBER/CMD_DECREMENT_NUMBER +void buffer_change_number_at_cursor(TextBuffer *buffer, i64 argument); /// Buffer position corresponding to the start of line `line` (0-indexed). BufferPos buffer_pos_start_of_line(TextBuffer *buffer, u32 line); /// Buffer position corresponding to the end of line `line` (0-indexed). @@ -1127,6 +1129,8 @@ i64 buffer_delete_selection(TextBuffer *buffer); void buffer_insert_text_at_cursor(TextBuffer *buffer, String32 str); /// Insert a single character at the cursor, and move the cursor past it. void buffer_insert_char_at_cursor(TextBuffer *buffer, char32_t c); +/// Insert UTF-8 text at the position. +void buffer_insert_utf8_at_pos(TextBuffer *buffer, BufferPos pos, const char *utf8); /// Insert UTF-8 text at the cursor, and move the cursor to the end of it. void buffer_insert_utf8_at_cursor(TextBuffer *buffer, const char *utf8); /// Insert a "tab" at the cursor position, and move the cursor past it. -- cgit v1.2.3