summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2021-02-22 12:36:50 -0500
committerLeo Tenenbaum <pommicket@gmail.com>2021-02-22 12:36:50 -0500
commit1631f38d4dba64577c8f064225599273148ad83d (patch)
tree740747615e12e0d45681875cdb2e2d4f9f681e77
parent37ce64c167e12c0d652442b2ff3deb9327d1317d (diff)
go to definition menu
-rw-r--r--buffer.c2
-rw-r--r--command.c11
-rw-r--r--command.h3
-rw-r--r--main.c15
-rw-r--r--menu.c66
-rw-r--r--tags.c75
-rw-r--r--ted.cfg2
-rw-r--r--ted.h4
-rw-r--r--ui.c1
-rw-r--r--util.c19
10 files changed, 156 insertions, 42 deletions
diff --git a/buffer.c b/buffer.c
index 0bf78d2..e00377e 100644
--- a/buffer.c
+++ b/buffer.c
@@ -2124,7 +2124,7 @@ void buffer_render(TextBuffer *buffer, Rect r) {
buffer->center_cursor_next_frame = false;
}
- if (rect_contains_point(rect4(x1, y1, x2, y2), ted->mouse_pos)) {
+ if (rect_contains_point(rect4(x1, y1, x2, y2), ted->mouse_pos) && !ted->menu) {
// scroll with mouse wheel
double scroll_speed = 2.5;
buffer_scroll(buffer, ted->scroll_total_x * scroll_speed, ted->scroll_total_y * scroll_speed);
diff --git a/command.c b/command.c
index 78d5b24..4488289 100644
--- a/command.c
+++ b/command.c
@@ -125,7 +125,7 @@ void command_execute(Ted *ted, Command c, i64 argument) {
else
find_prev(ted);
}
- } else {
+ } else if (buffer) {
buffer_newline(buffer);
}
break;
@@ -269,10 +269,12 @@ void command_execute(Ted *ted, Command c, i64 argument) {
break;
case CMD_FIND:
- find_open(ted, false);
+ if (buffer)
+ find_open(ted, false);
break;
case CMD_FIND_REPLACE:
- find_open(ted, true);
+ if (buffer)
+ find_open(ted, true);
break;
case CMD_ESCAPE:
@@ -299,5 +301,8 @@ void command_execute(Ted *ted, Command c, i64 argument) {
case CMD_BUILD_PREV_ERROR:
build_prev_error(ted);
break;
+ case CMD_OPEN_DEFINITION_MENU:
+ menu_open(ted, MENU_GOTO_DEFINITION);
+ break;
}
}
diff --git a/command.h b/command.h
index 61fd04b..8f61424 100644
--- a/command.h
+++ b/command.h
@@ -68,6 +68,8 @@ ENUM_U16 {
CMD_BUILD,
CMD_BUILD_PREV_ERROR,
CMD_BUILD_NEXT_ERROR,
+
+ CMD_OPEN_DEFINITION_MENU, // "go to definition of..."
CMD_ESCAPE, // by default this is the escape key. closes menus, etc.
@@ -135,6 +137,7 @@ static CommandName const command_names[CMD_COUNT] = {
{"build", CMD_BUILD},
{"build-prev-error", CMD_BUILD_PREV_ERROR},
{"build-next-error", CMD_BUILD_NEXT_ERROR},
+ {"open-definition-menu", CMD_OPEN_DEFINITION_MENU},
{"escape", CMD_ESCAPE},
};
diff --git a/main.c b/main.c
index 0de7610..4a0f420 100644
--- a/main.c
+++ b/main.c
@@ -1,5 +1,4 @@
// @TODO:
-// - Ctrl+D = :find-definition --- tag menu (see all tags, select one)
// - fix automatic horizontal scrolling
// - goto line
@@ -65,10 +64,10 @@ no_warn_end
bool tag_goto(Ted *ted, char const *tag);
#include "buffer.c"
#include "ted.c"
-#include "tags.c"
#include "ui.c"
#include "find.c"
#include "node.c"
+#include "tags.c"
#include "menu.c"
#include "build.c"
#include "command.c"
@@ -588,6 +587,10 @@ int main(int argc, char **argv) {
buffer_scroll(active_buffer, +scroll_amount_x, 0);
}
+ if (ted->menu) {
+ menu_update(ted);
+ }
+
ted_update_window_dimensions(ted);
float window_width = ted->window_width, window_height = ted->window_height;
@@ -607,6 +610,7 @@ int main(int argc, char **argv) {
// default window title
strcpy(ted->window_title, "ted");
+
if (ted->active_node) {
float const padding = settings->padding;
float x1 = padding, y = window_height-padding, x2 = window_width-padding;
@@ -632,12 +636,10 @@ int main(int argc, char **argv) {
text_render(font);
}
- Menu menu = ted->menu;
- if (menu) {
- menu_frame(ted, menu);
+ if (ted->menu) {
+ menu_render(ted);
}
-
if (text_has_err()) {
ted_seterr(ted, "Couldn't render text: %s", text_get_err());
}
@@ -742,6 +744,7 @@ int main(int argc, char **argv) {
SDL_DestroyWindow(window);
SDL_Quit();
find_close(ted);
+ tag_selector_close(ted);
for (u16 i = 0; i < TED_MAX_BUFFERS; ++i)
if (ted->buffers_used[i])
buffer_free(&ted->buffers[i]);
diff --git a/menu.c b/menu.c
index c4502e0..e9c9488 100644
--- a/menu.c
+++ b/menu.c
@@ -20,6 +20,9 @@ static void menu_open(Ted *ted, Menu menu) {
case MENU_ASK_RELOAD:
assert(*ted->ask_reload);
break;
+ case MENU_GOTO_DEFINITION:
+ tag_selector_open(ted);
+ break;
}
}
@@ -40,6 +43,9 @@ static void menu_close(Ted *ted) {
case MENU_ASK_RELOAD:
*ted->ask_reload = 0;
break;
+ case MENU_GOTO_DEFINITION:
+ tag_selector_close(ted);
+ break;
}
ted->menu = MENU_NONE;
}
@@ -71,7 +77,9 @@ static Rect menu_rect(Ted *ted) {
);
}
-static void menu_update(Ted *ted, Menu menu) {
+static void menu_update(Ted *ted) {
+ Menu menu = ted->menu;
+ assert(menu);
switch (menu) {
case MENU_NONE: break;
case MENU_SAVE_AS: {
@@ -179,10 +187,20 @@ static void menu_update(Ted *ted, Menu menu) {
break;
}
break;
+ case MENU_GOTO_DEFINITION: {
+ char *chosen_tag = tag_selector_update(ted);
+ if (chosen_tag) {
+ menu_close(ted);
+ tag_goto(ted, chosen_tag);
+ free(chosen_tag);
+ }
+ } break;
}
}
-static void menu_render(Ted *ted, Menu menu) {
+static void menu_render(Ted *ted) {
+ Menu menu = ted->menu;
+ assert(menu);
Settings const *settings = &ted->settings;
u32 const *colors = settings->colors;
float window_width = ted->window_width, window_height = ted->window_height;
@@ -202,6 +220,26 @@ static void menu_render(Ted *ted, Menu menu) {
popup_render(ted, POPUP_YES_NO_CANCEL, title, body);
return;
}
+
+
+ float padding = settings->padding;
+ Rect rect = menu_rect(ted);
+ float menu_x1, menu_y1, menu_x2, menu_y2;
+ rect_coords(rect, &menu_x1, &menu_y1, &menu_x2, &menu_y2);
+
+ if (menu == MENU_OPEN || menu == MENU_SAVE_AS || menu == MENU_GOTO_DEFINITION) {
+ // menu rectangle & border
+ gl_geometry_rect(rect, colors[COLOR_MENU_BG]);
+ gl_geometry_rect_border(rect, settings->border_thickness, colors[COLOR_BORDER]);
+ gl_geometry_draw();
+
+ menu_x1 += padding;
+ menu_y1 += padding;
+ menu_x2 -= padding;
+ menu_y2 -= padding;
+ }
+
+
switch (menu) {
case MENU_NONE: assert(0); break;
case MENU_WARN_UNSAVED: {
@@ -218,21 +256,6 @@ static void menu_render(Ted *ted, Menu menu) {
} break;
case MENU_OPEN:
case MENU_SAVE_AS: {
- float padding = settings->padding;
- Rect rect = menu_rect(ted);
- float menu_x1, menu_y1, menu_x2, menu_y2;
- rect_coords(rect, &menu_x1, &menu_y1, &menu_x2, &menu_y2);
-
- // menu rectangle & border
- gl_geometry_rect(rect, colors[COLOR_MENU_BG]);
- gl_geometry_rect_border(rect, settings->border_thickness, colors[COLOR_BORDER]);
- gl_geometry_draw();
-
- menu_x1 += padding;
- menu_y1 += padding;
- menu_x2 -= padding;
- menu_y2 -= padding;
-
if (menu == MENU_OPEN) {
text_utf8(font_bold, "Open...", menu_x1, menu_y1, colors[COLOR_TEXT]);
@@ -247,11 +270,8 @@ static void menu_render(Ted *ted, Menu menu) {
fs->bounds = rect4(menu_x1, menu_y1, menu_x2, menu_y2);
file_selector_render(ted, fs);
} break;
+ case MENU_GOTO_DEFINITION: {
+ tag_selector_render(ted, rect4(menu_x1, menu_y1, menu_x2, menu_y2));
+ } break;
}
}
-
-static void menu_frame(Ted *ted, Menu menu) {
- menu_update(ted, menu);
- if (ted->menu)
- menu_render(ted, ted->menu);
-}
diff --git a/tags.c b/tags.c
index c7330b3..5fe192e 100644
--- a/tags.c
+++ b/tags.c
@@ -1,3 +1,5 @@
+static char const TED_ERR_NO_TAGS[] = "No tags file. Try running ctags.";
+
static int tag_try(FILE *fp, char const *tag) {
if (ftell(fp) != 0) {
while (1) {
@@ -29,7 +31,7 @@ bool tag_goto(Ted *ted, char const *tag) {
char const *tags_filename = settings->tags_filename;
FILE *file = fopen(tags_filename, "rb");
if (!file) {
- ted_seterr(ted, "No tags file. Try running ctags.");
+ ted_seterr(ted, TED_ERR_NO_TAGS);
return false;
}
fseek(file, 0, SEEK_END);
@@ -97,9 +99,10 @@ bool tag_goto(Ted *ted, char const *tag) {
TextBuffer *buffer = ted->active_buffer;
int line_number = atoi(address);
if (line_number > 0) {
- // the tags file gives us a line number
- BufferPos pos = {.line = (u32)line_number, .index = 0};
+ // the tags file gives us a (1-indexed) line number
+ BufferPos pos = {.line = (u32)line_number - 1, .index = 0};
buffer_cursor_move_to_pos(buffer, pos);
+ buffer->center_cursor_next_frame = true;
success = true;
} else if (address[0] == '/') {
// the tags file gives us a pattern to look for
@@ -149,8 +152,8 @@ bool tag_goto(Ted *ted, char const *tag) {
} else {
ted_seterr(ted, "Unrecognized tag address: %s", address);
}
- break;
}
+ break;
}
}
}
@@ -159,3 +162,67 @@ bool tag_goto(Ted *ted, char const *tag) {
fclose(file);
return success;
}
+
+static void tag_selector_open(Ted *ted) {
+ // read tags file and extract tag names
+ FILE *fp = fopen("tags", "r");
+ arr_clear(ted->tag_selector_entries);
+ if (fp) {
+ char line[1024];
+ while (fgets(line, sizeof line, fp)) {
+ size_t len = strcspn(line, "\t");
+ arr_add(ted->tag_selector_entries, strn_dup(line, len));
+ }
+ ted->active_buffer = &ted->line_buffer;
+ buffer_select_all(ted->active_buffer);
+
+ ted->tag_selector.cursor = 0;
+
+ fclose(fp);
+ } else {
+ ted_seterr(ted, TED_ERR_NO_TAGS);
+ }
+}
+
+static void tag_selector_close(Ted *ted) {
+ Selector *sel = &ted->tag_selector;
+ arr_clear(sel->entries);
+ sel->n_entries = 0;
+ arr_foreach_ptr(ted->tag_selector_entries, char *, entry) {
+ free(*entry);
+ }
+ arr_clear(ted->tag_selector_entries);
+}
+
+// returns tag selected (should be free'd), or NULL if none was.
+static char *tag_selector_update(Ted *ted) {
+ Selector *sel = &ted->tag_selector;
+ u32 color = ted->settings.colors[COLOR_TEXT];
+ sel->enable_cursor = true;
+
+ // create selector entries based on search term
+ char *search_term = str32_to_utf8_cstr(buffer_get_line(&ted->line_buffer, 0));
+
+ arr_clear(sel->entries);
+
+ arr_foreach_ptr(ted->tag_selector_entries, char *, tagp) {
+ char const *tag = *tagp;
+ if (!search_term || stristr(tag, search_term)) {
+ SelectorEntry entry = {
+ .name = tag,
+ .color = color
+ };
+ arr_add(sel->entries, entry);
+ }
+ }
+
+ sel->n_entries = arr_len(sel->entries);
+
+ return selector_update(ted, sel);
+}
+
+static void tag_selector_render(Ted *ted, Rect bounds) {
+ Selector *sel = &ted->tag_selector;
+ sel->bounds = bounds;
+ selector_render(ted, sel);
+}
diff --git a/ted.cfg b/ted.cfg
index ed665e0..e66c93e 100644
--- a/ted.cfg
+++ b/ted.cfg
@@ -118,6 +118,8 @@ F4 = :build
Ctrl+[ = :build-prev-error
Ctrl+] = :build-next-error
+Ctrl+d = :open-definition-menu
+
Escape = :escape
[colors]
diff --git a/ted.h b/ted.h
index 0ad4887..18c8d15 100644
--- a/ted.h
+++ b/ted.h
@@ -167,6 +167,7 @@ ENUM_U16 {
MENU_SAVE_AS,
MENU_WARN_UNSAVED, // warn about unsaved changes
MENU_ASK_RELOAD, // prompt about whether to reload file which has ben changed by another program
+ MENU_GOTO_DEFINITION,
} ENUM_U16_END(Menu);
typedef struct {
@@ -248,6 +249,7 @@ typedef struct Ted {
int scroll_total_x, scroll_total_y; // total amount scrolled in the x and y direction this frame
Menu menu;
FileSelector file_selector;
+ Selector tag_selector; // for "go to definition of..." menu
TextBuffer line_buffer; // general-purpose line buffer for inputs -- used for menus
TextBuffer find_buffer; // use for "find" term in find/find+replace
TextBuffer replace_buffer; // "replace" for find+replace
@@ -271,6 +273,8 @@ typedef struct Ted {
BuildError *build_errors; // dynamic array of build errors
u32 build_error; // build error we are currently "on"
+ char **tag_selector_entries; // an array of all tags (see tag_selector_open)
+
// points to a selector if any is open, otherwise NULL.
Selector *selector_open;
diff --git a/ui.c b/ui.c
index 737b7ae..d2f0d23 100644
--- a/ui.c
+++ b/ui.c
@@ -97,6 +97,7 @@ static char *selector_update(Ted *ted, Selector *s) {
// apply scroll
float scroll_speed = 2.5f;
s->scroll += scroll_speed * (float)ted->scroll_total_y;
+ selector_clamp_scroll(ted, s);
return ret;
}
diff --git a/util.c b/util.c
index 81c4ce3..9ceb4cc 100644
--- a/util.c
+++ b/util.c
@@ -65,15 +65,24 @@ static bool streq(char const *a, char const *b) {
return strcmp(a, b) == 0;
}
-// duplicates a null-terminated string. the returned string should be passed to free()
-static char *str_dup(char const *src) {
+// duplicates at most n characters from src
+static char *strn_dup(char const *src, size_t n) {
size_t len = strlen(src);
- char *ret = malloc(len + 1);
- if (ret)
- memcpy(ret, src, len + 1);
+ if (n > len)
+ n = len;
+ char *ret = malloc(n + 1);
+ if (ret) {
+ memcpy(ret, src, n);
+ ret[n] = 0;
+ }
return ret;
}
+// duplicates a null-terminated string. the returned string should be passed to free()
+static char *str_dup(char const *src) {
+ return strn_dup(src, SIZE_MAX);
+}
+
// like snprintf, but not screwed up on windows
#define str_printf(str, size, ...) (str)[(size) - 1] = '\0', snprintf((str), (size) - 1, __VA_ARGS__)
// like snprintf, but the size is taken to be the length of the array str.