summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--buffer.c8
-rw-r--r--build.c52
-rw-r--r--command.c60
-rw-r--r--command.h8
-rw-r--r--config.c29
-rw-r--r--control2
-rw-r--r--macro.c65
-rw-r--r--main.c48
-rw-r--r--menu.c12
-rw-r--r--syntax.c5
-rw-r--r--ted.c3
-rw-r--r--ted.cfg1
-rw-r--r--ted.h46
14 files changed, 290 insertions, 51 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 15caed1..994f286 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,7 +4,7 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(SOURCES buffer.c build.c colors.c command.c config.c find.c gl.c ide-autocomplete.c
ide-definitions.c ide-highlights.c ide-hover.c ide-signature-help.c ide-usages.c
lsp.c lsp-json.c lsp-parse.c lsp-write.c main.c menu.c node.c os.c session.c
- stb_image.c stb_truetype.c syntax.c tags.c ted.c text.c ui.c util.c)
+ stb_image.c stb_truetype.c syntax.c tags.c ted.c text.c ui.c util.c macro.c)
else()
set(SOURCES main.c)
endif()
diff --git a/buffer.c b/buffer.c
index fadeb59..c440ed4 100644
--- a/buffer.c
+++ b/buffer.c
@@ -308,6 +308,9 @@ bool buffer_indent_with_spaces(TextBuffer *buffer) {
}
String32 buffer_get_line(TextBuffer *buffer, u32 line_number) {
+ if (line_number >= buffer->nlines) {
+ return str32(NULL, 0);
+ }
Line *line = &buffer->lines[line_number];
return (String32) {
.str = line->str, .len = line->len
@@ -1998,6 +2001,11 @@ void buffer_insert_text_at_cursor(TextBuffer *buffer, String32 str) {
buffer_delete_selection(buffer); // delete any selected text
BufferPos endpos = buffer_insert_text_at_pos(buffer, buffer->cursor_pos, str);
buffer_cursor_move_to_pos(buffer, endpos);
+ if (str.len) {
+ // this actually isn't handled by the move above since buffer->cursor_pos
+ // should be moved to endpos anyways. might as well keep that there though
+ buffer_scroll_to_cursor(buffer);
+ }
}
void buffer_insert_char_at_cursor(TextBuffer *buffer, char32_t c) {
diff --git a/build.c b/build.c
index 0311b09..9d7e3ad 100644
--- a/build.c
+++ b/build.c
@@ -118,8 +118,35 @@ static void build_go_to_error(Ted *ted) {
if (ted_open_file(ted, error.path)) {
TextBuffer *buffer = ted->active_buffer;
assert(buffer);
+
+ u32 index = error.column;
+
+ if (error.columns_per_tab > 1) {
+ // get correct index
+ String32 line = buffer_get_line(buffer, error.line);
+ u32 column = 0;
+ index = 0;
+ while (column < error.column) {
+ if (index >= line.len)
+ break;
+ if (line.str[index] == '\t') {
+ column += error.columns_per_tab;
+ } else {
+ column += 1;
+ }
+ ++index;
+ }
+ }
+
+
+ BufferPos pos = {
+ .line = error.line,
+ .index = index,
+ };
+
+
// move cursor to error
- buffer_cursor_move_to_pos(buffer, error.pos);
+ buffer_cursor_move_to_pos(buffer, pos);
buffer->center_cursor_next_frame = true;
// move cursor to error in build output
@@ -180,16 +207,30 @@ void build_check_for_errors(Ted *ted) {
continue;
}
bool is_error = true;
+ // well, for a bit of time i thought rust was weird
+ // and treated tabs as 4 columns
+ // apparently its just a bug, which ive filed here
+ // https://github.com/rust-lang/rust/issues/109537
+ // we could treat ::: references as 4-columns-per-tab,
+ // but then that would be wrong if the bug gets fixed.
+ // all this is to say that columns_per_tab is currently always 1,
+ // but might be useful at some point.
+ u8 columns_per_tab = 1;
char32_t *p = line->str, *end = p + line->len;
{
// rust errors look like:
// " --> file:line:column"
+ // and can also include stuff like
+ // " ::: file:line:column"
while (p != end && *p == ' ') {
++p;
}
- if (end - p >= 4 && p[0] == '-' && p[1] == '-' && p[2] == '>' && p[3] == ' ') {
- p += 4;
+ if (end - p >= 4) {
+ String32 first4 = str32(p, 4);
+ if (str32_cmp_ascii(first4, "::: ") == 0 || str32_cmp_ascii(first4, "--> ") == 0) {
+ p += 4;
+ }
}
}
@@ -243,9 +284,12 @@ void build_check_for_errors(Ted *ted) {
pfilename += 3;
path_full(ted->build_dir, pfilename, full_path, sizeof full_path);
}
+
BuildError error = {
.path = str_dup(full_path),
- .pos = {.line = (u32)line_number, .index = (u32)column_number},
+ .line = (u32)line_number,
+ .column = (u32)column_number,
+ .columns_per_tab = columns_per_tab,
.build_output_line = line_idx
};
arr_add(ted->build_errors, error);
diff --git a/command.c b/command.c
index 0bad2a6..168597a 100644
--- a/command.c
+++ b/command.c
@@ -37,6 +37,7 @@ static CommandName command_names[] = {
{"select-all", CMD_SELECT_ALL},
{"select-up-blank-line", CMD_SELECT_UP_BLANK_LINE},
{"select-down-blank-line", CMD_SELECT_DOWN_BLANK_LINE},
+ {"clear-selection", CMD_CLEAR_SELECTION},
{"page-up", CMD_PAGE_UP},
{"page-down", CMD_PAGE_DOWN},
{"previous-position", CMD_PREVIOUS_POSITION},
@@ -96,6 +97,9 @@ static CommandName command_names[] = {
{"split-switch", CMD_SPLIT_SWITCH},
{"split-swap", CMD_SPLIT_SWAP},
{"escape", CMD_ESCAPE},
+ {"macro-record", CMD_MACRO_RECORD},
+ {"macro-stop", CMD_MACRO_STOP},
+ {"macro-execute", CMD_MACRO_EXECUTE},
};
static_assert_if_possible(arr_count(command_names) == CMD_COUNT)
@@ -135,23 +139,36 @@ const char *command_to_str(Command c) {
return "???";
}
-// get the string corresponding to this argument; returns NULL if it's not a string argument
-static const char *arg_get_string(Ted *ted, i64 argument) {
- if (argument < 0) return NULL;
- if (argument & ARG_STRING) {
- argument -= ARG_STRING;
- if (argument < ted->nstrings)
- return ted->strings[argument];
- }
- return NULL;
+void command_execute(Ted *ted, Command c, i64 argument) {
+ CommandArgument arg = {
+ .number = argument,
+ .string = NULL,
+ };
+ command_execute_ex(ted, c, arg, (CommandContext){0});
}
-void command_execute(Ted *ted, Command c, i64 argument) {
+void command_execute_ex(Ted *ted, Command c, CommandArgument full_argument, CommandContext context) {
TextBuffer *buffer = ted->active_buffer;
Node *node = ted->active_node;
Settings *settings = ted_active_settings(ted);
-
-
+ if (ted->recording_macro)
+ macro_add(ted, c, full_argument);
+ i64 argument = full_argument.number;
+ const char *argument_str = full_argument.string;
+ /*
+ it's important that when we're playing back a macro,
+ we only execute commands specifically from the macro.
+ for example, suppose the user opens the find menu and searches for "apple".
+ this might generate the macro:
+ open_find_menu()
+ insert_text("apple")
+ newline()
+ find_next("apple") // (generated by find.c)
+ if we ran these commands as-is, we'd end up searching for "apple" twice!
+ */
+ if (ted->executing_macro && !context.from_macro)
+ return;
+
switch (c) {
case CMD_UNKNOWN:
case CMD_COUNT:
@@ -194,6 +211,9 @@ void command_execute(Ted *ted, Command c, i64 argument) {
if (buffer) buffer_select_left(buffer, argument);
autocomplete_close(ted);
break;
+ case CMD_CLEAR_SELECTION:
+ if (buffer) buffer_deselect(buffer);
+ break;
case CMD_SELECT_RIGHT:
if (buffer) buffer_select_right(buffer, argument);
autocomplete_close(ted);
@@ -267,11 +287,12 @@ void command_execute(Ted *ted, Command c, i64 argument) {
autocomplete_close(ted);
break;
case CMD_PREVIOUS_POSITION:
- buffer_cursor_move_to_prev_pos(buffer);
+ if (buffer)
+ buffer_cursor_move_to_prev_pos(buffer);
break;
case CMD_INSERT_TEXT: {
- const char *str = arg_get_string(ted, argument);
+ const char *str = argument_str;
if (str) {
buffer_insert_utf8_at_cursor(buffer, str);
}
@@ -597,7 +618,7 @@ void command_execute(Ted *ted, Command c, i64 argument) {
build_prev_error(ted);
break;
case CMD_SHELL: {
- const char *str = arg_get_string(ted, argument);
+ const char *str = argument_str;
if (str) {
strbuf_cpy(ted->build_dir, ted->cwd);
build_start_with_command(ted, str);
@@ -628,5 +649,14 @@ void command_execute(Ted *ted, Command c, i64 argument) {
case CMD_SPLIT_SWAP:
if (node) node_split_swap(ted);
break;
+ case CMD_MACRO_RECORD:
+ macro_start_recording(ted, (u32)argument);
+ break;
+ case CMD_MACRO_STOP:
+ macro_stop_recording(ted);
+ break;
+ case CMD_MACRO_EXECUTE:
+ macro_execute(ted, (u32)argument);
+ break;
}
}
diff --git a/command.h b/command.h
index 9c91011..a246439 100644
--- a/command.h
+++ b/command.h
@@ -4,9 +4,6 @@
#ifndef COMMAND_H_
#define COMMAND_H_
-/// `i | ARG_STRING` when used as an argument refers to `ted->strings[i]`
-#define ARG_STRING 0x4000000000000000
-
/// command enum
///
/// more documentation in `ted.cfg`.
@@ -57,6 +54,7 @@ typedef enum {
CMD_SELECT_PAGE_DOWN,
CMD_SELECT_UP_BLANK_LINE,
CMD_SELECT_DOWN_BLANK_LINE,
+ CMD_CLEAR_SELECTION,
// insertion
/// insert text
@@ -147,6 +145,10 @@ typedef enum {
/// by default this is the escape key. closes menus, etc.
CMD_ESCAPE,
+
+ CMD_MACRO_RECORD,
+ CMD_MACRO_STOP,
+ CMD_MACRO_EXECUTE,
CMD_COUNT
} Command;
diff --git a/config.c b/config.c
index c094218..c2f8230 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 i64 config_read_string(Ted *ted, ConfigReader *cfg, char **ptext) {
+static const char *config_read_string(Ted *ted, ConfigReader *cfg, char **ptext) {
char *p;
int backslashes = 0;
u32 start_line = cfg->line_number;
@@ -642,7 +642,7 @@ static i64 config_read_string(Ted *ted, ConfigReader *cfg, char **ptext) {
config_err(cfg, "Unrecognized escape sequence: '\\%c'.", *p);
*ptext += strlen(*ptext);
arr_clear(str);
- return -1;
+ return NULL;
}
break;
case '\n':
@@ -654,22 +654,21 @@ static i64 config_read_string(Ted *ted, ConfigReader *cfg, char **ptext) {
config_err(cfg, "String doesn't end.");
*ptext += strlen(*ptext);
arr_clear(str);
- return -1;
+ return NULL;
}
if (*p == delimiter)
break;
arr_add(str, *p);
}
- i64 str_idx = -1;
+ char *s = NULL;
if (ted->nstrings < TED_MAX_STRINGS) {
- char *s = strn_dup(str, arr_len(str));
- str_idx = ted->nstrings;
+ s = strn_dup(str, arr_len(str));
ted->strings[ted->nstrings++] = s;
}
arr_clear(str);
*ptext = p + 1;
- return str_idx;
+ return s;
}
static void settings_load_bg_shader(Ted *ted, Settings **applicable_settings, const char *bg_shader_text) {
@@ -834,11 +833,14 @@ static void config_parse_line(ConfigReader *cfg, Settings **applicable_settings,
KeyCombo key_combo = config_parse_key_combo(cfg, key);
KeyAction action = {0};
action.key_combo = key_combo;
- llong argument = 1; // default argument = 1
+ CommandArgument argument = {
+ .number = 1, // default argument = 1
+ .string = NULL
+ };
if (isdigit(*value)) {
// read the argument
char *endp;
- argument = strtoll(value, &endp, 10);
+ argument.number = strtoll(value, &endp, 10);
value = endp;
} else if (*value == '"' || *value == '`') {
// string argument
@@ -846,7 +848,7 @@ static void config_parse_line(ConfigReader *cfg, Settings **applicable_settings,
// restore newline to handle multi-line strings
// a little bit hacky oh well
*newline = '\n';
- argument = config_read_string(ted, cfg, &value);
+ argument.string = config_read_string(ted, cfg, &value);
newline = strchr(value, '\n');
if (!newline) {
@@ -950,7 +952,7 @@ static void config_parse_line(ConfigReader *cfg, Settings **applicable_settings,
// a little bit hacky oh well
*newline = '\n';
- i64 string = config_read_string(ted, cfg, &value);
+ const char *string = config_read_string(ted, cfg, &value);
newline = strchr(value, '\n');
if (!newline) {
@@ -960,9 +962,8 @@ static void config_parse_line(ConfigReader *cfg, Settings **applicable_settings,
}
*newline = '\0';
*pline = newline + 1;
- if (string >= 0 && string < TED_MAX_STRINGS) {
- value = ted->strings[string];
- }
+ if (string)
+ value = string;
}
diff --git a/control b/control
index 235a8ec..a44bba7 100644
--- a/control
+++ b/control
@@ -1,5 +1,5 @@
Package: ted
-Version: 2.1
+Version: 2.1r1
Section: text
Priority: optional
Architecture: amd64
diff --git a/macro.c b/macro.c
new file mode 100644
index 0000000..14ec016
--- /dev/null
+++ b/macro.c
@@ -0,0 +1,65 @@
+#include "ted.h"
+
+static void macro_clear(Macro *macro) {
+ arr_foreach_ptr(macro->actions, Action, act) {
+ free((char *)act->argument.string);
+ }
+ arr_free(macro->actions);
+
+ memset(macro, 0, sizeof *macro);
+}
+
+void macro_start_recording(Ted *ted, u32 index) {
+ if (index >= TED_MACRO_MAX) return;
+ if (ted->executing_macro) return;
+ if (ted->recording_macro) {
+ macro_stop_recording(ted);
+ return;
+ }
+
+ command_execute(ted, CMD_CLEAR_SELECTION, 0);
+
+ ted->recording_macro = &ted->macros[index];
+ macro_clear(ted->recording_macro);
+}
+
+void macro_stop_recording(Ted *ted) {
+ ted->recording_macro = NULL;
+}
+
+void macro_add(Ted *ted, Command command, CommandArgument argument) {
+ if (!ted->recording_macro) return;
+ if (command == CMD_MACRO_EXECUTE || command == CMD_MACRO_RECORD || command == CMD_MACRO_STOP)
+ return;
+ if (argument.string)
+ argument.string = str_dup(argument.string);
+ Action action = {
+ .command = command,
+ .argument = argument
+ };
+ arr_add(ted->recording_macro->actions, action);
+}
+
+void macro_execute(Ted *ted, u32 index) {
+ if (index >= TED_MACRO_MAX) return;
+ Macro *macro = &ted->macros[index];
+ if (ted->recording_macro == macro) {
+ // don't allow running a macro while it's being recorded
+ return;
+ }
+
+ ted->executing_macro = true;
+ CommandContext context = {0};
+ context.from_macro = true;
+ arr_foreach_ptr(macro->actions, Action, act) {
+ command_execute_ex(ted, act->command, act->argument, context);
+ }
+ ted->executing_macro = false;
+}
+
+void macros_free(Ted *ted) {
+ for (int i = 0; i < TED_MACRO_MAX; ++i) {
+ Macro *macro = &ted->macros[i];
+ macro_clear(macro);
+ }
+}
diff --git a/main.c b/main.c
index d3cdbed..5b243a2 100644
--- a/main.c
+++ b/main.c
@@ -1,4 +1,5 @@
/*
+- ctrl+9/0 to inc/dec number
FUTURE FEATURES:
- better undo chaining (dechain on backspace?)
- font setting & support for multiple fonts to cover more characters
@@ -16,11 +17,8 @@ FUTURE FEATURES:
- TED_PLUGIN macro defined before including ted.h
this can remove struct definitions to guarantee forwards compatibility
- language dynamic registration
-- keyboard macros
- - ctrl+9/0 to inc/dec number would be useful here
- - with macros we can really test performance of buffer_insert_text_at_pos, etc. (which should ideally be fast)
+- with macros we can really test performance of buffer_insert_text_at_pos, etc. (which should ideally be fast)
- manual.md
-- auto-reload config even for %included files
- LSP request timeout
BUG REPORTS IM TO LAZY TO FILE (RIGHT NOW)
- rust-analyzer:
@@ -84,6 +82,7 @@ BUG REPORTS IM TO LAZY TO FILE (RIGHT NOW)
#include "ide-highlights.c"
#include "ide-usages.c"
#include "command.c"
+#include "macro.c"
#include "config.c"
#include "session.c"
#include "lsp.c"
@@ -637,6 +636,9 @@ int main(int argc, char **argv) {
}
} break;
case SDL_MOUSEBUTTONDOWN: {
+ if (ted->recording_macro)
+ break; // ignore mouse input during macros
+
Uint32 button = event.button.button;
u8 times = event.button.clicks; // number of clicks
float x = (float)event.button.x, y = (float)event.button.y;
@@ -694,6 +696,9 @@ int main(int argc, char **argv) {
}
} break;
case SDL_MOUSEBUTTONUP: {
+ if (ted->recording_macro)
+ break; // ignore mouse input during macros
+
Uint8 button = event.button.button;
if (button < arr_count(ted->nmouse_releases)) {
vec2 pos = Vec2((float)event.button.x, (float)event.button.y);
@@ -703,6 +708,9 @@ int main(int argc, char **argv) {
}
} break;
case SDL_MOUSEMOTION: {
+ if (ted->recording_macro)
+ break; // ignore mouse input during macros
+
float x = (float)event.motion.x, y = (float)event.motion.y;
if (ted->drag_buffer != ted->active_buffer)
ted->drag_buffer = NULL;
@@ -726,7 +734,14 @@ int main(int argc, char **argv) {
// unfortunately, some key combinations like ctrl+minus still register as a "-" text input event
&& (key_modifier & ~KEY_MODIFIER_SHIFT) == 0) {
// insert the text
- buffer_insert_utf8_at_cursor(buffer, text);
+ {
+ CommandContext ctx = {0};
+ CommandArgument arg = {
+ .number = 0,
+ .string = text
+ };
+ command_execute_ex(ted, CMD_INSERT_TEXT, arg, ctx);
+ }
// check for trigger character
LSP *lsp = buffer_lsp(buffer);
Settings *settings = buffer_settings(buffer);
@@ -1065,6 +1080,28 @@ int main(int argc, char **argv) {
}
}
+ if (ted->recording_macro) {
+ Font *font_bold = ted->font_bold;
+ u32 bg_color = ted_active_color(ted, COLOR_ERROR_BG);
+ u32 color = ted_active_color(ted, COLOR_TEXT);
+ u8 padding = ted_active_settings(ted)->padding;
+ const char *text = "Recording macro...";
+ vec2 size = text_get_size_vec2(font_bold, text);
+ Rect r = {
+ .pos = vec2_sub(Vec2(window_width - 3 * padding, window_height - 3 * padding), size),
+ .size = vec2_add(size, Vec2(padding, padding)),
+ };
+ gl_geometry_rect(r, bg_color);
+ Rect full_screen = {
+ .pos = {0, 0},
+ .size = {window_width, window_height}
+ };
+ gl_geometry_rect(full_screen, bg_color & 0xffffff0f);
+ text_utf8_anchored(font_bold, text, window_width - 2.5f * padding, window_height - 2.5f * padding, color, ANCHOR_BOTTOM_RIGHT);
+ gl_geometry_draw();
+ text_render(font_bold);
+ }
+
ted_check_for_node_problems(ted);
#if !NDEBUG
@@ -1174,6 +1211,7 @@ int main(int argc, char **argv) {
text_font_free(ted->font);
text_font_free(ted->font_bold);
config_free(ted);
+ macros_free(ted);
free(ted);
#if _WIN32
for (int i = 0; i < argc; ++i)
diff --git a/menu.c b/menu.c
index b894c35..5a65ea4 100644
--- a/menu.c
+++ b/menu.c
@@ -289,9 +289,15 @@ void menu_update(Ted *ted) {
Command c = command_from_str(chosen_command);
if (c != CMD_UNKNOWN) {
char *argument = str32_to_utf8_cstr(buffer_get_line(&ted->argument_buffer, 0)), *endp = NULL;
- long long arg = strtoll(argument, &endp, 0);
-
- if (*endp == '\0') {
+ long long arg = 1;
+ bool execute = true;
+ if (*argument) {
+ strtoll(argument, &endp, 0);
+ if (*endp != '\0')
+ execute = false;
+ }
+
+ if (execute) {
menu_close(ted);
command_execute(ted, c, arg);
}
diff --git a/syntax.c b/syntax.c
index 05bf04b..f57f7c1 100644
--- a/syntax.c
+++ b/syntax.c
@@ -1937,6 +1937,9 @@ void syntax_highlight(SyntaxState *state, Language lang, const char32_t *line, u
static void syntax_highlight_c(SyntaxState *state, const char32_t *line, u32 line_len, SyntaxCharType *char_types) {
syntax_highlight_c_cpp(state, line, line_len, char_types, LANG_C);
}
+static void syntax_highlight_glsl(SyntaxState *state, const char32_t *line, u32 line_len, SyntaxCharType *char_types) {
+ syntax_highlight_c_cpp(state, line, line_len, char_types, LANG_GLSL);
+}
static void syntax_highlight_cpp(SyntaxState *state, const char32_t *line, u32 line_len, SyntaxCharType *char_types) {
syntax_highlight_c_cpp(state, line, line_len, char_types, LANG_CPP);
}
@@ -2066,7 +2069,7 @@ void syntax_register_builtin_languages(void) {
.name = "GLSL",
// not specified as of LSP 3.17, but this seems like the natural choice
.lsp_identifier = "glsl",
- .highlighter = syntax_highlight_html,
+ .highlighter = syntax_highlight_glsl,
},
{
.id = LANG_CSS,
diff --git a/ted.c b/ted.c
index 3c66703..865e9fe 100644
--- a/ted.c
+++ b/ted.c
@@ -619,7 +619,8 @@ void ted_press_key(Ted *ted, SDL_Keycode keycode, SDL_Keymod modifier) {
} else if (key_actions[mid].key_combo.value > key_combo.value) {
hi = mid;
} else {
- command_execute(ted, key_actions[mid].command, key_actions[mid].argument);
+ CommandContext context = {0};
+ command_execute_ex(ted, key_actions[mid].command, key_actions[mid].argument, context);
return;
}
}
diff --git a/ted.cfg b/ted.cfg
index 1dc27a7..95f6d33 100644
--- a/ted.cfg
+++ b/ted.cfg
@@ -222,6 +222,7 @@ Ctrl+Shift+End = :select-end-of-file
Ctrl+a = :select-all
# go to previous cursor position
Ctrl+p = :previous-position
+# Ctrl+Shift+d = :clear-selection
# insertion
Tab = :tab
diff --git a/ted.h b/ted.h
index 59bdf65..7dc9602 100644
--- a/ted.h
+++ b/ted.h
@@ -28,7 +28,7 @@ extern "C" {
#include "sdl-inc.h"
/// Version number
-#define TED_VERSION "2.1"
+#define TED_VERSION "2.1r1"
/// Version string
#define TED_VERSION_FULL "ted v. " TED_VERSION
/// Maximum path size ted handles.
@@ -181,13 +181,23 @@ typedef struct {
/// extract key modifier from \ref KeyCombo
#define KEY_COMBO_MODIFIER(combo) ((u32)((combo.value) & 0xff))
+typedef struct {
+ const char *string;
+ i64 number;
+} CommandArgument;
+
+typedef struct {
+ bool from_macro;
+} CommandContext;
+
/// Thing to do when a key combo is pressed.
typedef struct {
KeyCombo key_combo;
Command command;
- i64 argument;
+ CommandArgument argument;
} KeyAction;
+
/// A SettingsContext is a context where a specific set of settings are applied.
/// this corresponds to `[PATH//LANGUAGE.(section)]` in config files.
typedef struct {
@@ -531,7 +541,12 @@ typedef struct {
typedef struct {
char *path;
- BufferPos pos;
+ u32 line;
+ u32 column;
+ /// if this is 1, then column == UTF-32 index.
+ /// if this is 4, for example, then column 4 in a line starting with a tab would
+ /// be the character right after the tab.
+ u8 columns_per_tab;
/// which line in the build output corresponds to this error
u32 build_output_line;
} BuildError;
@@ -695,6 +710,18 @@ typedef enum {
MESSAGE_ERROR
} MessageType;
+typedef struct {
+ Command command;
+ CommandArgument argument;
+} Action;
+
+typedef struct {
+ // dynamic array
+ Action *actions;
+} Macro;
+
+#define TED_MACRO_MAX 256
+
/// (almost) all data used by the ted application
typedef struct Ted {
/// all running LSP servers
@@ -702,6 +729,10 @@ typedef struct Ted {
/// current time (see time_get_seconds), as of the start of this frame
double frame_time;
+ Macro macros[TED_MACRO_MAX];
+ Macro *recording_macro;
+ bool executing_macro;
+
SDL_Window *window;
Font *font_bold;
Font *font;
@@ -914,6 +945,7 @@ u8 buffer_tab_width(TextBuffer *buffer);
bool buffer_indent_with_spaces(TextBuffer *buffer);
/// NOTE: this string will be invalidated when the line is edited!!!
/// only use it briefly!!
+/// returns an empty string if `line_number` is out of range.
String32 buffer_get_line(TextBuffer *buffer, u32 line_number);
/// get at most `nchars` characters starting from position `pos`.
/// returns the number of characters actually available.
@@ -1242,6 +1274,7 @@ void command_init(void);
Command command_from_str(const char *str);
const char *command_to_str(Command c);
void command_execute(Ted *ted, Command c, i64 argument);
+void command_execute_ex(Ted *ted, Command c, CommandArgument argument, CommandContext context);
// === config.c ===
/// first, we read all config files, then we parse them.
@@ -1461,6 +1494,13 @@ void usages_find(Ted *ted);
void usages_process_lsp_response(Ted *ted, const LSPResponse *response);
void usages_frame(Ted *ted);
+// === macro.c ===
+void macro_start_recording(Ted *ted, u32 index);
+void macro_stop_recording(Ted *ted);
+void macro_add(Ted *ted, Command command, CommandArgument argument);
+void macro_execute(Ted *ted, u32 index);
+void macros_free(Ted *ted);
+
// === menu.c ===
void menu_close(Ted *ted);
void menu_open(Ted *ted, Menu menu);