From af61b9be6a746421a0417d282134491213f2c03f Mon Sep 17 00:00:00 2001 From: pommicket Date: Thu, 23 Mar 2023 16:18:38 -0400 Subject: handle various :increment-number edge cases and binary --- buffer.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++-------------- config.c | 4 +-- main.c | 2 -- ted.h | 9 +++++-- util.c | 22 +++++++++++++++++ util.h | 2 ++ 6 files changed, 100 insertions(+), 25 deletions(-) diff --git a/buffer.c b/buffer.c index 898dd07..eaf2d4b 100644 --- a/buffer.c +++ b/buffer.c @@ -1807,6 +1807,14 @@ static int base_digit(char c, int base) { // 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) { + /* + we break up a number like "-0x00ff17u" into 4 parts: + - negative = true -- sign + - num[..start] = "0x00" -- base and leading zeroes + - num[start..end] = "ff17" -- main number + - num[end..] = "u" -- suffix + */ + if (!isdigit(*num) && *num != '-') { return NULL; } @@ -1837,8 +1845,9 @@ static char *change_number(const char *num, i64 by) { break; case 'b': case 'B': - // not handling binary yet since sprintf doesnt have binary gah - return NULL; + start = 2; + base = 2; + break; default: return NULL; } @@ -1848,6 +1857,12 @@ static char *change_number(const char *num, i64 by) { int end; for (end = start + 1; base_digit(num[end], base) != -1; ++end); + int leading_zeroes = 0; + while (num[start] == '0' && start + 1 < end) { + ++leading_zeroes; + ++start; + } + if (base_digit(num[end], 16) != -1) { // we're probably wrong about the base. let's not do anything. return NULL; @@ -1866,7 +1881,9 @@ static char *change_number(const char *num, i64 by) { if (isupper(num[i])) uppercase = true; - long long number = strtoll(&num[start], NULL, base); + char numcpy[128] = {0}; + strn_cpy(numcpy, sizeof numcpy, &num[start], (size_t)(end - start)); + long long number = strtoll(numcpy, NULL, base); if (number == LLONG_MIN || number == LLONG_MAX) return NULL; if (negative) number = -number; @@ -1876,28 +1893,48 @@ static char *change_number(const char *num, i64 by) { 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; + switch (base) { + case 2: + // aaa sprintf doesnt have binary yet + str_binary_number(new_number, (u64)number); + break; + case 8: sprintf(new_number, "%llo", number); break; + case 10: sprintf(new_number, "%lld", number); break; + case 16: + if (uppercase) + sprintf(new_number, "%llX", number); + else + sprintf(new_number, "%llx", number); + break; + } + + int digit_diff = (int)strlen(new_number) - (end - start); + char extra_leading_zeroes[128] = {0}; + if (digit_diff > 0) { + // e.g. 0x000ff should be incremented to 0x00100 + start -= min_i32(digit_diff, leading_zeroes); + } else if (digit_diff < 0) { + if (leading_zeroes) { + // e.g. 0x00100 should be decremented to 0x000ff + for (int i = 0; i < -digit_diff && i < (int)sizeof extra_leading_zeroes; ++i) { + extra_leading_zeroes[i] = '0'; + } + } } - 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; + // show the parts of the new number: + //printf("%s %.*s %s %s %s\n",negative ? "-" : "", start, num, extra_leading_zeroes, new_number, &num[end]); + return a_sprintf("%s%.*s%s%s%s", negative ? "-" : "", start, num, extra_leading_zeroes, new_number, &num[end]); } -void buffer_change_number_at_cursor(TextBuffer *buffer, i64 by) { - BufferPos pos = buffer->cursor_pos; +bool buffer_change_number_at_pos(TextBuffer *buffer, BufferPos *ppos, i64 by) { + bool ret = false; + BufferPos pos = *ppos; // move to start of number if (is32_alnum(buffer_char_before_pos(buffer, pos))) { @@ -1907,7 +1944,7 @@ void buffer_change_number_at_cursor(TextBuffer *buffer, i64 by) { if (c >= 127 || !isdigit((char)c)) { if (c != '-') { // not a number - return; + return ret; } } @@ -1918,7 +1955,7 @@ void buffer_change_number_at_cursor(TextBuffer *buffer, i64 by) { buffer_pos_move_right_words(buffer, &end, 1); if (buffer_char_at_pos(buffer, end) == '.') { // floating-point number. dont try to increment it - return; + return ret; } if (buffer_char_before_pos(buffer, pos) == '-') { @@ -1926,16 +1963,27 @@ void buffer_change_number_at_cursor(TextBuffer *buffer, i64 by) { buffer_pos_move_left(buffer, &pos, 1); } + if (buffer_char_before_pos(buffer, pos) == '.') { + // floating-point number. dont try to increment it + return ret; + } + 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); + *ppos = pos; + ret = true; } free(word); + return ret; +} + +void buffer_change_number_at_cursor(TextBuffer *buffer, i64 by) { + buffer_change_number_at_pos(buffer, &buffer->cursor_pos, by); } // decrease the number of lines in the buffer. diff --git a/config.c b/config.c index c2f8230..81fac91 100644 --- a/config.c +++ b/config.c @@ -610,7 +610,7 @@ static int config_part_qsort_cmp(const void *av, const void *bv) { return config_part_cmp(av, bv); } -static const char *config_read_string(Ted *ted, ConfigReader *cfg, char **ptext) { +static char *config_read_string(Ted *ted, ConfigReader *cfg, char **ptext) { char *p; int backslashes = 0; u32 start_line = cfg->line_number; @@ -952,7 +952,7 @@ static void config_parse_line(ConfigReader *cfg, Settings **applicable_settings, // a little bit hacky oh well *newline = '\n'; - const char *string = config_read_string(ted, cfg, &value); + char *string = config_read_string(ted, cfg, &value); newline = strchr(value, '\n'); if (!newline) { diff --git a/main.c b/main.c index 9e9f1c2..9d4e606 100644 --- a/main.c +++ b/main.c @@ -1,6 +1,4 @@ /* -- 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.h b/ted.h index 94660ff..b4f87ac 100644 --- a/ted.h +++ b/ted.h @@ -1043,7 +1043,12 @@ 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 +/// Used for \ref CMD_INCREMENT_NUMBER and \ref CMD_DECREMENT_NUMBER +/// +/// Moves `*ppos` to the start of the (new) number. +/// returns false if there was no number at `*ppos`. +bool buffer_change_number_at_pos(TextBuffer *buffer, BufferPos *ppos, i64 by); +/// Used for \ref CMD_INCREMENT_NUMBER and \ref 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); @@ -1058,7 +1063,7 @@ void buffer_cursor_move_to_start_of_file(TextBuffer *buffer); /// Move cursor to the end of the file, like Ctrl+End does. void buffer_cursor_move_to_end_of_file(TextBuffer *buffer); /// Get the LSPDocumentID corresponding to the file this buffer contains. -/// The return value is only useful if buffer_lsp(buffer) != NULL. +/// The return value is only useful if `buffer_lsp(buffer) != NULL`. LSPDocumentID buffer_lsp_document_id(TextBuffer *buffer); /// Get LSPPosition corresponding to position in buffer. LSPPosition buffer_pos_to_lsp_position(TextBuffer *buffer, BufferPos pos); diff --git a/util.c b/util.c index 38d201a..f441479 100644 --- a/util.c +++ b/util.c @@ -258,6 +258,28 @@ char *a_sprintf(const char *fmt, ...) { return str; } +void str_binary_number(char s[65], u64 n) { + if (n == 0) { + strcpy(s, "0"); + return; + } + + u64 digits = 0; + u64 m = n; + while (m) { + m >>= 1; + digits += 1; + } + + m = n; + s[digits] = '\0'; + char *p = s + digits - 1; + while (m) { + *p-- = (m & 1) + '0'; + m >>= 1; + } +} + // advances str to the start of the next UTF8 character static void utf8_next_char_const(const char **str) { diff --git a/util.h b/util.h index 8c65b35..9fb078c 100644 --- a/util.h +++ b/util.h @@ -98,6 +98,8 @@ void strn_cpy(char *dst, size_t dst_sz, const char *src, size_t src_len); void str_cpy(char *dst, size_t dst_sz, const char *src); /// equivalent to GNU function asprintf (like sprintf, but allocates the string with malloc). char *a_sprintf(const char *fmt, ...); +/// convert binary number to string. make sure `s` can hold at least 65 bytes!! +void str_binary_number(char s[65], u64 n); /// print some bytes. useful for debugging. void print_bytes(const u8 *bytes, size_t n); /// like strstr, but case-insensitive -- cgit v1.2.3