4coder/power/4coder_miblo_numbers.cpp

409 lines
13 KiB
C++

/*
4coder_miblo_numbers.cpp - Commands for so called "Miblo Number Operations" which involve incrementing
and decrementing various forms of number as numerical objects despite being encoded as text objects.
TYPE: 'drop-in-command-pack'
*/
// TOP
#if !defined(FCODER_MIBLO_NUMBERS_CPP)
#define FCODER_MIBLO_NUMBERS_CPP
#include "4coder_API/custom.h"
#include "4coder_helper/4coder_helper.h"
#include "4coder_helper/4coder_streaming.h"
static bool32
get_numeric_string_at_cursor(Application_Links *app, Buffer_Summary *buffer, size_t start_pos, size_t *numeric_start, size_t *numeric_end){
bool32 result = false;
char current = buffer_get_char(app, buffer, start_pos);
if (char_is_numeric(current)){
char chunk[1024];
int32_t chunk_size = sizeof(chunk);
Stream_Chunk stream = {0};
size_t pos = start_pos;
size_t pos1 = 0;
size_t pos2 = 0;
if (init_stream_chunk(&stream, app, buffer, start_pos, chunk, chunk_size)){
int32_t still_looping = 1;
while (still_looping){
for (; pos >= (size_t)stream.start; --pos){
char at_pos = stream.data[pos];
if (!char_is_numeric(at_pos)){
++pos;
goto double_break_1;
}
}
still_looping = backward_stream_chunk(&stream);
}
double_break_1:;
pos1 = pos;
if (init_stream_chunk(&stream, app, buffer, start_pos, chunk, chunk_size)){
still_looping = 1;
while (still_looping){
for (; pos < (size_t)stream.end; ++pos){
char at_pos = stream.data[pos];
if (!char_is_numeric(at_pos)){
goto double_break_2;
}
}
still_looping = forward_stream_chunk(&stream);
}
double_break_2:;
pos2 = pos;
result = true;
*numeric_start = pos1;
*numeric_end = pos2;
}
}
}
return(result);
}
struct Miblo_Number_Info{
size_t start, end;
int32_t x;
};
static bool32
get_numeric_at_cursor(Application_Links *app, Buffer_Summary *buffer, size_t pos, Miblo_Number_Info *info){
bool32 result = false;
size_t numeric_start = 0, numeric_end = 0;
if (get_numeric_string_at_cursor(app, buffer, pos, &numeric_start, &numeric_end)){
int32_t string_length = (int32_t)(numeric_end - numeric_start);
char numeric_string[1024];
String str = make_string(numeric_string, string_length, sizeof(numeric_string));
if (str.size < str.memory_size){
buffer_read_range(app, buffer, numeric_start, numeric_end, numeric_string);
int32_t x = str_to_int(str);
int_to_str(&str, x+1);
info->start = numeric_start;
info->end = numeric_end;
info->x = x;
result = true;
}
}
return(result);
}
CUSTOM_COMMAND_SIG(miblo_increment_basic){
View_Summary view = get_active_view(app, AccessOpen);
Buffer_Summary buffer = get_buffer(app, view.buffer_id, AccessOpen);
Miblo_Number_Info number = {0};
if (get_numeric_at_cursor(app, &buffer, view.cursor.pos, &number)){
char str_space[1024];
String str = make_fixed_width_string(str_space);
int_to_str(&str, number.x + 1);
buffer_replace_range(app, &buffer, number.start, number.end, str.str, str.size);
view_set_cursor(app, &view, seek_pos(number.start + str.size - 1), 1);
}
}
CUSTOM_COMMAND_SIG(miblo_decrement_basic){
View_Summary view = get_active_view(app, AccessOpen);
Buffer_Summary buffer = get_buffer(app, view.buffer_id, AccessOpen);
Miblo_Number_Info number = {0};
if (get_numeric_at_cursor(app, &buffer, view.cursor.pos, &number)){
char str_space[1024];
String str = make_fixed_width_string(str_space);
int_to_str(&str, number.x - 1);
buffer_replace_range(app, &buffer, number.start, number.end, str.str, str.size);
view_set_cursor(app, &view, seek_pos(number.start + str.size - 1), 1);
}
}
// NOTE(allen): miblo time stamp format
// (h+:)?m?m:ss
static bool32
get_timestamp_string_at_cursor(Application_Links *app, Buffer_Summary *buffer, size_t start_pos, size_t *timestamp_start, size_t *timestamp_end){
bool32 result = false;
char current = buffer_get_char(app, buffer, start_pos);
if (char_is_numeric(current) || current == ':'){
char chunk[1024];
int32_t chunk_size = sizeof(chunk);
Stream_Chunk stream = {0};
size_t pos = start_pos;
size_t pos1 = 0;
size_t pos2 = 0;
if (init_stream_chunk(&stream, app, buffer, start_pos, chunk, chunk_size)){
int32_t still_looping = 1;
while (still_looping){
for (; pos >= (size_t)stream.start; --pos){
char at_pos = stream.data[pos];
if (!(char_is_numeric(at_pos) || at_pos == ':')){
++pos;
goto double_break_1;
}
}
still_looping = backward_stream_chunk(&stream);
}
double_break_1:;
pos1 = pos;
if (init_stream_chunk(&stream, app, buffer, start_pos, chunk, chunk_size)){
still_looping = 1;
while (still_looping){
for (; pos < (size_t)stream.end; ++pos){
char at_pos = stream.data[pos];
if (!(char_is_numeric(at_pos) || at_pos == ':')){
goto double_break_2;
}
}
still_looping = forward_stream_chunk(&stream);
}
double_break_2:;
pos2 = pos;
result = true;
*timestamp_start = pos1;
*timestamp_end = pos2;
}
}
}
return(result);
}
struct Miblo_Timestamp{
int32_t hour, minute, second;
};
static Miblo_Timestamp null_miblo_timestamp = {0};
enum{
MIBLO_SECOND,
MIBLO_MINUTE,
MIBLO_HOUR
};
static Miblo_Timestamp
increment_timestamp(Miblo_Timestamp t, int32_t type, int32_t amt){
Miblo_Timestamp r = t;
switch (type){
case MIBLO_SECOND:
r.second += amt;
amt = 0;
// TODO(allen): someday do the math, instead of being lazy.
while (r.second < 0){
--amt;
r.second += 60;
}
while (r.second >= 60){
++amt;
r.second -= 60;
}
case MIBLO_MINUTE:
r.minute += amt;
amt = 0;
// TODO(allen): someday do the math, instead of being lazy.
while (r.minute < 0){
--amt;
r.minute += 60;
}
while (r.minute >= 60){
++amt;
r.minute -= 60;
}
case MIBLO_HOUR:
r.hour += amt;
}
return(r);
}
static void
timestamp_to_str(String *dest, Miblo_Timestamp t){
dest->size = 0;
if (t.hour > 0){
append_int_to_str(dest, t.hour);
append(dest, ":");
}
if (t.minute >= 10){
append_int_to_str(dest, t.minute);
}
else if (t.hour > 0){
append(dest, "0");
append_int_to_str(dest, t.minute);
}
else{
append_int_to_str(dest, t.minute);
}
append(dest, ":");
if (t.second >= 10){
append_int_to_str(dest, t.second);
}
else{
append(dest, "0");
append_int_to_str(dest, t.second);
}
}
struct Miblo_Timestamp_Info{
size_t start, end;
Miblo_Timestamp time;
};
static bool32
get_timestamp_at_cursor(Application_Links *app, Buffer_Summary *buffer, size_t pos, Miblo_Timestamp_Info *info){
bool32 result = false;
size_t timestamp_start = 0, timestamp_end = 0;
if (get_timestamp_string_at_cursor(app, buffer, pos, &timestamp_start, &timestamp_end)){
int32_t string_length = (int32_t)(timestamp_end - timestamp_start);
char timestamp_string[1024];
String str = make_string(timestamp_string, string_length, sizeof(timestamp_string));
if (str.size < str.memory_size){
buffer_read_range(app, buffer, timestamp_start, timestamp_end, timestamp_string);
int32_t count_colons = 0;
for (int32_t i = 0; i < str.size; ++i){
if (str.str[i] == ':'){
++count_colons;
}
}
if (count_colons == 1 || count_colons == 2){
Miblo_Timestamp t = {0};
bool32 success = false;
size_t i = 0;
size_t number_start[3], number_end[3];
for (int32_t k = 0; k < 3; ++k){
number_start[k] = i;
for (; i <= str.size; ++i){
if (i == str.size || str.str[i] == ':'){
number_end[k] = i;
break;
}
}
++i;
if (i >= timestamp_end){
break;
}
}
if (count_colons == 2){
int32_t number_length = (int32_t)(number_end[0] - number_start[0]);
String number_string = make_string(str.str + number_start[0], number_length);
t.hour = str_to_int(number_string);
number_length = (int32_t)(number_end[1] - number_start[1]);
if (number_length == 2){
number_string = make_string(str.str + number_start[1], number_length);
t.minute = str_to_int(number_string);
number_length = (int32_t)(number_end[2] - number_start[2]);
if (number_length == 2){
number_string = make_string(str.str + number_start[2], number_length);
t.second = str_to_int(number_string);
success = true;
}
}
}
else{
int32_t number_length = (int32_t)(number_end[0] - number_start[0]);
if (number_length == 2 || number_length == 1){
String number_string = make_string(str.str + number_start[0], number_length);
t.minute = str_to_int(number_string);
number_length = (int32_t)(number_end[1] - number_start[1]);
if (number_length == 2){
number_string = make_string(str.str + number_start[1], number_length);
t.second = str_to_int(number_string);
success = true;
}
}
}
if (success){
info->start = timestamp_start;
info->end = timestamp_end;
info->time = t;
result = true;
}
}
}
}
return(result);
}
static void
miblo_time_stamp_alter(Application_Links *app, int32_t unit_type, int32_t amt){
View_Summary view = get_active_view(app, AccessOpen);
Buffer_Summary buffer = get_buffer(app, view.buffer_id, AccessOpen);
Miblo_Timestamp_Info timestamp = {0};
if (get_timestamp_at_cursor(app, &buffer, view.cursor.pos, &timestamp)){
char str_space[1024];
String str = make_fixed_width_string(str_space);
Miblo_Timestamp inc_timestamp = increment_timestamp(timestamp.time, unit_type, amt);
timestamp_to_str(&str, inc_timestamp);
buffer_replace_range(app, &buffer, timestamp.start, timestamp.end, str.str, str.size);
view_set_cursor(app, &view, seek_pos(timestamp.start + str.size - 1), 1);
}
}
CUSTOM_COMMAND_SIG(miblo_increment_time_stamp){
miblo_time_stamp_alter(app, MIBLO_SECOND, 1);
}
CUSTOM_COMMAND_SIG(miblo_decrement_time_stamp){
miblo_time_stamp_alter(app, MIBLO_SECOND, -1);
}
CUSTOM_COMMAND_SIG(miblo_increment_time_stamp_minute){
miblo_time_stamp_alter(app, MIBLO_MINUTE, 1);
}
CUSTOM_COMMAND_SIG(miblo_decrement_time_stamp_minute){
miblo_time_stamp_alter(app, MIBLO_MINUTE, -1);
}
#endif
// BOTTOM