summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2023-03-23 16:18:38 -0400
committerpommicket <pommicket@gmail.com>2023-03-23 16:18:38 -0400
commitaf61b9be6a746421a0417d282134491213f2c03f (patch)
treeb81b5fe297cc2d4f029ec111b9ac6a9228b1c4f8
parent6643ff4c14cc27bf80189aadaf65c36ac2e7307f (diff)
handle various :increment-number edge cases and binary
-rw-r--r--buffer.c86
-rw-r--r--config.c4
-rw-r--r--main.c2
-rw-r--r--ted.h9
-rw-r--r--util.c22
-rw-r--r--util.h2
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