diff options
-rw-r--r-- | buffer.c | 21 | ||||
-rw-r--r-- | build.c | 96 | ||||
-rw-r--r-- | command.c | 9 | ||||
-rw-r--r-- | command.h | 3 | ||||
-rw-r--r-- | find.c | 6 | ||||
-rw-r--r-- | main.c | 73 | ||||
-rw-r--r-- | process-posix.c | 2 | ||||
-rw-r--r-- | string32.c | 2 | ||||
-rw-r--r-- | ted.cfg | 2 | ||||
-rw-r--r-- | ted.h | 8 | ||||
-rw-r--r-- | text.c | 2 | ||||
-rw-r--r-- | unicode.h | 9 | ||||
-rw-r--r-- | util.c | 2 |
13 files changed, 164 insertions, 71 deletions
@@ -1291,6 +1291,11 @@ BufferPos buffer_insert_text_at_pos(TextBuffer *buffer, BufferPos pos, String32 return b; } +void buffer_insert_char_at_pos(TextBuffer *buffer, BufferPos pos, char32_t c) { + String32 s = {&c, 1}; + buffer_insert_text_at_pos(buffer, pos, s); +} + // Select (or add to selection) everything between the cursor and pos, and move the cursor to pos void buffer_select_to_pos(TextBuffer *buffer, BufferPos pos) { if (!buffer->selection) @@ -1812,7 +1817,7 @@ Status buffer_load_file(TextBuffer *buffer, char const *filename) { // null character c = 0; ++p; - } else if (n == (size_t)(-1)) { + } else if (n >= (size_t)(-2)) { // invalid UTF-8 success = false; buffer_seterr(buffer, "Invalid UTF-8 (position: %td).", p - file_contents); @@ -1905,7 +1910,8 @@ bool buffer_externally_changed(TextBuffer *buffer) { void buffer_new_file(TextBuffer *buffer, char const *filename) { buffer_clear(buffer); - buffer->filename = buffer_strdup(buffer, filename); + if (filename) + buffer->filename = buffer_strdup(buffer, filename); buffer->lines_capacity = 4; buffer->lines = buffer_calloc(buffer, buffer->lines_capacity, sizeof *buffer->lines); buffer->nlines = 1; @@ -2045,6 +2051,8 @@ void buffer_render(TextBuffer *buffer, Rect r) { float render_start_y = y1 - (float)(buffer->scroll_y - start_line) * char_height; // where the 1st line is rendered + bool render_cursor = buffer == ted->active_buffer; + // line numbering if (!buffer->is_line_buffer && settings->line_numbers) { float line_number_width = ndigits_u64(buffer->nlines) * char_width + padding; @@ -2054,7 +2062,7 @@ void buffer_render(TextBuffer *buffer, Rect r) { text_state.max_y = y2; float y = render_start_y; - u32 cursor_line = buffer->cursor_pos.line; + u32 cursor_line = render_cursor ? buffer->cursor_pos.line : U32_MAX; for (u32 line = start_line; line < nlines; ++line) { char str[32] = {0}; strbuf_printf(str, U32_FMT, line + 1); // convert line number to string @@ -2082,9 +2090,8 @@ void buffer_render(TextBuffer *buffer, Rect r) { // the rectangle that the cursor is rendered as Rect cursor_rect = rect(cursor_display_pos, V2(settings->cursor_width, char_height)); - - // highlight line cursor is on - { + if (render_cursor) { + // highlight line cursor is on Rect hl_rect = rect(V2(x1, cursor_display_pos.y), V2(x2-x1-1, char_height)); buffer_clip_rect(buffer, &hl_rect); gl_geometry_rect(hl_rect, colors[COLOR_CURSOR_LINE_BG]); @@ -2226,7 +2233,7 @@ void buffer_render(TextBuffer *buffer, Rect r) { text_render(font); - if (buffer == ted->active_buffer) { + if (render_cursor) { // render cursor float time_on = settings->cursor_blink_time_on; float time_off = settings->cursor_blink_time_off; @@ -0,0 +1,96 @@ +static void build_start(Ted *ted) { +#if __unix__ + char *program = "/bin/sh"; + char *argv[] = { + program, "-c", "make", NULL + }; +#else + #error "TODO" +#endif + + process_exec(&ted->build_process, program, argv); + ted->building = true; + ted->build_shown = true; + buffer_new_file(&ted->build_buffer, NULL); + ted->build_buffer.store_undo_events = false; + ted->build_buffer.view_only = true; +} + +static void build_stop(Ted *ted) { + if (ted->building) + process_kill(&ted->build_process); + ted->building = false; + ted->build_shown = false; +} + +static void build_frame(Ted *ted, float x1, float y1, float x2, float y2) { + TextBuffer *buffer = &ted->build_buffer; + Process *process = &ted->build_process; + assert(ted->build_shown); + char buf[256]; + if (ted->building) { + buffer->view_only = false; // disable view only temporarily so we can edit it + bool any_text_inserted = false; + while (1) { + char incomplete[4]; + memcpy(ted->build_incomplete_codepoint, incomplete, sizeof incomplete); + *ted->build_incomplete_codepoint = 0; + + i64 bytes_read = (i64)process_read(process, buf + 3, sizeof buf - 3); + if (bytes_read == -2) { + ted_seterr(ted, "Error reading command output: %s.", process_geterr(process)); + build_stop(ted); + break; + } else if (bytes_read == -1) { + // no data right now. + break; + } else if (bytes_read == 0) { + // end of file + break; + } else { + any_text_inserted = true; + // got some data. + char *p = buf + 3 - strlen(incomplete); + char *end = buf + 3 + bytes_read; + // start off data with incomplete code point from last time + memcpy(p, incomplete, strlen(incomplete)); + while (p != end) { + char32_t c = 0; + size_t ret = unicode_utf8_to_utf32(&c, p, (size_t)(end - p)); + if (ret == (size_t)-1) { + // invalid UTF-8. skip this byte. + ++p; + } else if (ret == (size_t)-2) { + // incomplete UTF-8 + size_t leftovers = (size_t)(end - p); + assert(leftovers < 4); + memcpy(ted->build_incomplete_codepoint, p, leftovers); + ted->build_incomplete_codepoint[leftovers] = '\0'; + p = end; + } else { + if (ret == 0) ret = 1; + // got a code point + buffer_insert_char_at_pos(buffer, buffer_end_of_file(buffer), c); + p += ret; + } + } + } + } + + if (any_text_inserted) { + buffer->cursor_pos = buffer_end_of_file(buffer); + buffer_scroll_to_cursor(buffer); + } + + char message[64]; + int status = process_check_status(process, message, sizeof message); + if (status == 0) { + // hasn't exited yet + } else { + buffer_insert_utf8_at_cursor(buffer, message); + ted->building = false; + } + buffer->view_only = true; + } + buffer_render(buffer, rect4(x1, y1, x2, y2)); +} @@ -297,9 +297,18 @@ void command_execute(Ted *ted, Command c, i64 argument) { menu_escape(ted); } else if (ted->find) { find_close(ted); + } else if (ted->build_shown) { + build_stop(ted); } else if (buffer) { buffer_disable_selection(buffer); } break; + + case CMD_BUILD: + if (ted->building) { + build_stop(ted); + } + build_start(ted); + break; } } @@ -65,6 +65,8 @@ ENUM_U16 { CMD_VIEW_ONLY, // toggle view-only mode + CMD_BUILD, + CMD_ESCAPE, // by default this is the escape key. closes menus, etc. CMD_COUNT @@ -128,6 +130,7 @@ static CommandName const command_names[CMD_COUNT] = { {"increase-text-size", CMD_TEXT_SIZE_INCREASE}, {"decrease-text-size", CMD_TEXT_SIZE_DECREASE}, {"view-only", CMD_VIEW_ONLY}, + {"build", CMD_BUILD}, {"escape", CMD_ESCAPE}, }; @@ -292,15 +292,13 @@ static void find_replace_all(Ted *ted) { } } -static void find_menu_frame(Ted *ted) { +static void find_menu_frame(Ted *ted, float x1, float y1, float x2, float y2) { Font *font = ted->font, *font_bold = ted->font_bold; float const char_height = text_font_char_height(font), char_height_bold = text_font_char_height(font_bold); Settings const *settings = &ted->settings; float const padding = settings->padding; - float const menu_height = find_menu_height(ted); - float const window_width = ted->window_width, window_height = ted->window_height; u32 const *colors = settings->colors; bool const replace = ted->replace; @@ -311,8 +309,6 @@ static void find_menu_frame(Ted *ted) { u32 last_rendered_line = buffer_last_rendered_line(buffer); - float x1 = padding, y1 = window_height - menu_height + padding, x2 = window_width - padding, y2 = window_height - padding; - Rect menu_bounds = rect4(x1, y1, x2, y2); x1 += padding; @@ -66,6 +66,7 @@ no_warn_end #include "find.c" #include "node.c" #include "menu.c" +#include "build.c" #include "command.c" #include "config.c" @@ -146,52 +147,6 @@ int main(int argc, char **argv) { #endif setlocale(LC_ALL, ""); // allow unicode - - char *program = "/usr/bin/sleep"; - char *args[] = {program, "5", NULL}; - Process process = {0}, *proc = &process; - - if (!process_exec(proc, program, args)) { - printf("Error: %s\n", process_geterr(proc)); - return EXIT_FAILURE; - } - #if 0 - { - i64 bytes = 0; - char buf[256]; - while (1) { - bytes = process_read(proc, buf, sizeof buf); - if (bytes == -2) { - printf("Error: %s\n", process_geterr(proc)); - } else if (bytes == -1) { - usleep(1000); - } else if (bytes == 0) { - break; - } else { - fwrite(buf, 1, (size_t)bytes, stdout); - } - } - } - #endif - #if 1 - { - char message[256]; - while (1) { - int status = process_check_status(proc, message, sizeof message); - if (status == -1) { - printf("%s!!!\n",message); - break; - } else if (status == +1) { - printf("%s\n", message); - break; - } else { - usleep(1000); - } - } - } - #endif - return 0; - // read command-line arguments char const *starting_filename = NULL; switch (argc) { @@ -373,8 +328,8 @@ int main(int argc, char **argv) { } line_buffer_create(&ted->find_buffer, ted); line_buffer_create(&ted->replace_buffer, ted); - - + buffer_create(&ted->build_buffer, ted); + { u16 buffer_index = (u16)ted_new_buffer(ted); assert(buffer_index == 0); @@ -627,13 +582,24 @@ int main(int argc, char **argv) { Font *font = ted->font; if (ted->active_node) { - float x1 = 25, y1 = 25, x2 = window_width-25, y2 = window_height-25; + float const padding = settings->padding; + float x1 = padding, y = window_height-padding, x2 = window_width-padding; Node *node = ted->root; - if (ted->find) y2 -= find_menu_height(ted); - node_frame(ted, node, rect4(x1, y1, x2, y2)); if (ted->find) { - find_menu_frame(ted); + float y2 = y; + y -= find_menu_height(ted); + find_menu_frame(ted, x1, y, x2, y2); + y -= padding; } + if (ted->build_shown) { + float y2 = y; + y -= 0.3f * ted->window_height; + build_frame(ted, x1, y, x2, y2); + y -= padding; + } + + float y1 = padding; + node_frame(ted, node, rect4(x1, y1, x2, y)); } else { text_utf8_anchored(font, "Press Ctrl+O to open a file or Ctrl+N to create a new one.", window_width * 0.5f, window_height * 0.5f, colors[COLOR_TEXT_SECONDARY], ANCHOR_MIDDLE); @@ -738,6 +704,8 @@ int main(int argc, char **argv) { } + build_stop(ted); + if (ted->menu) menu_close(ted); // free any memory used by the current menu @@ -757,6 +725,7 @@ int main(int argc, char **argv) { buffer_free(&ted->line_buffer); buffer_free(&ted->find_buffer); buffer_free(&ted->replace_buffer); + buffer_free(&ted->build_buffer); text_font_free(ted->font); text_font_free(ted->font_bold); settings_free(&ted->settings); diff --git a/process-posix.c b/process-posix.c index da0448d..9be7e10 100644 --- a/process-posix.c +++ b/process-posix.c @@ -10,6 +10,8 @@ struct Process { }; bool process_exec(Process *proc, char const *program, char **argv) { + memset(proc, 0, sizeof *proc); + bool success = false; int pipefd[2]; @@ -36,7 +36,7 @@ String32 str32_from_utf8(char const *utf8) { char32_t c = 0; size_t n = unicode_utf8_to_utf32(&c, utf8_p, (size_t)(utf8_end - utf8_p)); if (n == 0 // null character. this shouldn't happen. - || n == (size_t)(-1) // invalid UTF-8 + || n >= (size_t)(-2) // invalid UTF-8 ) { free(widestr); widestr = wide_p = NULL; @@ -111,6 +111,8 @@ Ctrl+- = 3 :decrease-text-size Ctrl+Alt+Shift+v = :view-only +F4 = :build + Escape = :escape [colors] @@ -225,6 +225,7 @@ typedef struct Ted { 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 + TextBuffer build_buffer; // buffer for build output (view only) double error_time; // time error box was opened (in seconds -- see time_get_seconds) KeyAction key_actions[KEY_COMBO_COUNT]; bool search_cwd; // should the working directory be searched for files? set to true if the executable isn't "installed" @@ -238,6 +239,13 @@ typedef struct Ted { FindResult *find_results; bool find_invalid_pattern; // invalid regex? Command warn_unsaved; // if non-zero, the user is trying to execute this command, but there are unsaved changes + bool build_shown; // are we showing the build output? + bool building; // is the build process running? + Process build_process; + // When we read the stdout from the build process, the tail end of the read could be an + // incomplete UTF-8 code point. This is where we store that "tail end" until more + // data is available. (This is up to 3 bytes, null terminated) + char build_incomplete_codepoint[4]; char warn_unsaved_names[TED_PATH_MAX]; // comma-separated list of files with unsaved changes (only applicable if warn_unsaved != 0) char warn_overwrite[TED_PATH_MAX]; // file name user is trying to overwrite char ask_reload[TED_PATH_MAX]; // file name which we want to reload @@ -323,7 +323,7 @@ void text_utf8_with_state(Font *font, TextRenderState *state, char const *str) { size_t ret = unicode_utf8_to_utf32(&c, str, (size_t)(end - str)); if (ret == 0) { break; - } else if (ret == (size_t)-1) { + } else if (ret >= (size_t)-2) { // invalid UTF-8 text_char_with_state(font, state, '?'); ++str; @@ -14,7 +14,8 @@ static bool unicode_is_start_of_code_point(u8 byte) { // number of bytes that can be read from `str`. // Returns: // 0 - if a NULL character was encountered -// (size_t)-1 - on invalid UTF-8 / incomplete code point +// (size_t)-1 - on invalid UTF-8 +// (size_t)-2 - on incomplete code point (str should be longer) // other - the number of bytes read from `str`. static size_t unicode_utf8_to_utf32(char32_t *c, char const *str, size_t bytes) { if (bytes == 0) { @@ -39,7 +40,7 @@ static size_t unicode_utf8_to_utf32(char32_t *c, char const *str, size_t bytes) } else { // incomplete code point *c = 0; - return (size_t)-1; + return (size_t)-2; } } if ((first_byte & 0xF0) == 0xE0) { @@ -63,7 +64,7 @@ static size_t unicode_utf8_to_utf32(char32_t *c, char const *str, size_t bytes) } else { // incomplete *c = 0; - return (size_t)-1; + return (size_t)-2; } } if ((first_byte & 0xF8) == 0xF0) { @@ -90,7 +91,7 @@ static size_t unicode_utf8_to_utf32(char32_t *c, char const *str, size_t bytes) } else { // incomplete *c = 0; - return (size_t)-1; + return (size_t)-2; } } // invalid UTF-8 @@ -164,7 +164,7 @@ static char *stristr(char const *haystack, char const *needle) { char32_t pchar = 0, qchar = 0; size_t bytes_p = unicode_utf8_to_utf32(&pchar, p, (size_t)(haystack_end - p)); size_t bytes_q = unicode_utf8_to_utf32(&qchar, q, (size_t)(needle_end - q)); - if (bytes_p == (size_t)-1 || bytes_q == (size_t)-1) return NULL; // invalid UTF-8 + if (bytes_p >= (size_t)-2 || bytes_q >= (size_t)-2) return NULL; // invalid UTF-8 bool same = pchar == qchar; if (pchar < WINT_MAX && qchar < WINT_MAX) // on Windows, there is no way of finding the lower-case version of a codepoint outside the BMP. ): same = towlower((wint_t)pchar) == towlower((wint_t)qchar); |