diff options
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | buffer.c | 7 | ||||
-rw-r--r-- | build.c | 57 | ||||
-rw-r--r-- | command.c | 2 | ||||
-rw-r--r-- | control | 2 | ||||
-rw-r--r-- | development.md | 6 | ||||
-rw-r--r-- | ide-usages.c | 4 | ||||
-rw-r--r-- | main.c | 2 | ||||
-rw-r--r-- | menu.c | 2 | ||||
-rw-r--r-- | tags.c | 70 | ||||
-rw-r--r-- | ted-internal.h | 2 | ||||
-rw-r--r-- | ted.h | 56 |
12 files changed, 125 insertions, 87 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 3641e65..652f407 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) project(ted) 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 @@ -229,6 +229,8 @@ u32 buffer_last_line_on_screen(TextBuffer *buffer) { void buffer_set_undo_enabled(TextBuffer *buffer, bool enabled) { buffer->store_undo_events = enabled; + if (!enabled) + buffer_clear_undo_redo(buffer); } Rect buffer_rect(TextBuffer *buffer) { @@ -2234,10 +2236,11 @@ bool buffer_change_number_at_pos(TextBuffer *buffer, BufferPos *ppos, i64 by) { return ret; } -void buffer_change_number_at_cursor(TextBuffer *buffer, i64 by) { +bool buffer_change_number_at_cursor(TextBuffer *buffer, i64 by) { buffer_start_edit_chain(buffer); - buffer_change_number_at_pos(buffer, &buffer->cursor_pos, by); + bool ret = buffer_change_number_at_pos(buffer, &buffer->cursor_pos, by); buffer_end_edit_chain(buffer); + return ret; } // decrease the number of lines in the buffer. @@ -50,11 +50,12 @@ static bool build_run_next_command_in_queue(Ted *ted) { return false; assert(!ted->build_process); assert(*ted->build_dir); - change_directory(ted->build_dir); char *command = ted->build_queue[0]; arr_remove(ted->build_queue, 0); if (ted_save_all(ted)) { - ted->build_process = process_run(command); + ProcessSettings settings = {0}; + settings.working_directory = ted->build_dir; + ted->build_process = process_run_ex(command, &settings); const char *error = process_geterr(ted->build_process); if (!error) { ted->building = true; @@ -90,6 +91,11 @@ void build_queue_finish(Ted *ted) { build_run_next_command_in_queue(ted); // run the first command } +void build_set_working_directory(Ted *ted, const char *dir) { + assert(strlen(dir) < TED_PATH_MAX - 1); + strbuf_cpy(ted->build_dir, dir); +} + void build_start_with_command(Ted *ted, const char *command) { build_queue_start(ted); build_queue_command(ted, command); @@ -98,27 +104,39 @@ void build_start_with_command(Ted *ted, const char *command) { void build_start(Ted *ted) { Settings *settings = ted_active_settings(ted); - char *command = settings->build_command; - char *root = ted_get_root_dir(ted); - strbuf_cpy(ted->build_dir, root); + const char *command = settings->build_command; + + { + char *root = ted_get_root_dir(ted); + build_set_working_directory(ted, root); + free(root); + } + if (*command == 0) { command = settings->build_default_command; - change_directory(root); - - #if _WIN32 - if (fs_file_exists("make.bat")) { - command = "make.bat"; - } else - #endif - if (fs_file_exists("Cargo.toml")) { - command = "cargo build"; - } else if (fs_file_exists("Makefile")) { - command = "make -j16"; - } else if (fs_file_exists("go.mod")) { - command = "go build"; + typedef struct { + const char *filename; + const char *command; + } Assoc; + + Assoc associations[] = { + #if _WIN32 + {"make.bat", "make.bat"}, + #endif + {"Cargo.toml", "cargo build"}, + {"Makefile", "make -j16"}, + {"go.mod", "go build"}, + }; + for (size_t i = 0; i < arr_count(associations); ++i) { + Assoc *assoc = &associations[i]; + char path[TED_PATH_MAX]; + path_full(ted->build_dir, assoc->filename, path, sizeof path); + if (fs_file_exists(path)) { + command = assoc->command; + break; + } } } - free(root); if (*command) build_start_with_command(ted, command); } @@ -209,7 +227,6 @@ static bool is_source_path(char32_t c) { || strchr(allowed_ascii_symbols_in_path, (char)c); } -// make sure you set ted->build_dir before running this! void build_check_for_errors(Ted *ted) { const Settings *settings = ted_active_settings(ted); @@ -653,7 +653,7 @@ void command_execute_ex(Ted *ted, Command c, const CommandArgument *full_argumen case CMD_SHELL: { const char *str = argument_str; if (str) { - strbuf_cpy(ted->build_dir, ted->cwd); + build_set_working_directory(ted, ted->cwd); build_start_with_command(ted, str); } else { menu_open(ted, MENU_SHELL); @@ -1,5 +1,5 @@ Package: ted -Version: 2.4.3 +Version: 3.0 Section: text Priority: optional Architecture: amd64 diff --git a/development.md b/development.md index c633885..086b46a 100644 --- a/development.md +++ b/development.md @@ -106,6 +106,12 @@ At the top of `syntax.c` there are a bunch of `SYNTAX_STATE_*` constants. Create a new enum for your language, and add any state that needs to be remembered across lines. Then implement the `syntax_highlight_<language>` function similar to the other ones. +## Glossary + +- **buffer** - a text document +- **column** - one space width +- **line buffer** - a single-line buffer, e.g. the file name input in the "open file" menu + ## Releasing When releasing a new version of `ted`: diff --git a/ide-usages.c b/ide-usages.c index 4caa644..9ae31e4 100644 --- a/ide-usages.c +++ b/ide-usages.c @@ -124,10 +124,10 @@ void usages_process_lsp_response(Ted *ted, const LSPResponse *response) { fclose(last_file); buffer_set_view_only(buffer, true); - // the build_dir doesn't really matter since we're using absolute paths + // the build directory doesn't really matter since we're using absolute paths // but might as well set it to something reasonable. char *root = ted_get_root_dir(ted); - strbuf_cpy(ted->build_dir, root); + build_set_working_directory(ted, root); free(root); build_check_for_errors(ted); @@ -3,9 +3,11 @@ TODO: - fix find - public Node API - public Selector/FileSelector API +- public Settings API FUTURE FEATURES: - autodetect indentation (tabs vs spaces) +- custom file/build command associations - config variables - bind key to series of commands - convert macro to command list @@ -514,7 +514,7 @@ static void shell_menu_update(Ted *ted) { arr_add(ted->shell_history, command); } menu_close(ted); - strbuf_cpy(ted->build_dir, ted->cwd); + build_set_working_directory(ted, ted->cwd); build_start_with_command(ted, command); } } @@ -3,24 +3,23 @@ #include "ted-internal.h" #include "pcre-inc.h" -static const char *tags_filename(Ted *ted, bool error_if_does_not_exist) { - change_directory(ted->cwd); - const char *filename = "tags"; - ted_path_full(ted, ".", ted->tags_dir, sizeof ted->tags_dir); - if (!fs_file_exists(filename)) { - filename = "../tags"; - ted_path_full(ted, "..", ted->tags_dir, sizeof ted->tags_dir); - if (!fs_file_exists(filename)) { - filename = "../../tags"; - ted_path_full(ted, "../..", ted->tags_dir, sizeof ted->tags_dir); - if (!fs_file_exists(filename)) { - if (error_if_does_not_exist) - ted_error(ted, "No tags file. Try running ctags."); - filename = NULL; - } - } +static bool get_tags_dir(Ted *ted, bool error_if_does_not_exist) { + char prev_dir[TED_PATH_MAX]; + *prev_dir = '\0'; + strbuf_cpy(ted->tags_dir, ted->cwd); + while (!streq(prev_dir, ted->tags_dir)) { + strbuf_cpy(prev_dir, ted->tags_dir); + char path[TED_PATH_MAX]; + path_full(ted->tags_dir, "tags", path, sizeof path); + if (fs_file_exists(path)) + return true; + + path_full(ted->tags_dir, "..", path, sizeof path); + strbuf_cpy(ted->tags_dir, path); } - return filename; + if (error_if_does_not_exist) + ted_error(ted, "No tags file. Try running ctags."); + return false; } // is this a file we can generate tags for? @@ -99,19 +98,22 @@ static void tags_generate_at_dir(Ted *ted, bool run_in_build_window, const char // generate/re-generate tags. void tags_generate(Ted *ted, bool run_in_build_window) { - const char *filename = tags_filename(ted, false); - if (!filename) { + if (!get_tags_dir(ted, false)) { char *root = ted_get_root_dir(ted); strcpy(ted->tags_dir, root); free(root); } - change_directory(ted->tags_dir); - strcpy(ted->build_dir, ted->tags_dir); - remove("tags"); // delete old tags file + build_set_working_directory(ted, ted->tags_dir); + + { + char path[TED_PATH_MAX]; + path_full(ted->tags_dir, "tags", path, sizeof path); + remove(path); // delete old tags file + } + if (run_in_build_window) build_queue_start(ted); tags_generate_at_dir(ted, run_in_build_window, ted->tags_dir, 0); if (run_in_build_window) build_queue_finish(ted); - change_directory(ted->cwd); } static int tag_try(FILE *fp, const char *tag) { @@ -140,8 +142,10 @@ static int tag_try(FILE *fp, const char *tag) { size_t tags_beginning_with(Ted *ted, const char *prefix, char **out, size_t out_size, bool error_if_tags_does_not_exist) { assert(out_size); - const char *tags_name = tags_filename(ted, error_if_tags_does_not_exist); - if (!tags_name) return 0; + if (!get_tags_dir(ted, error_if_tags_does_not_exist)) + return 0; + char tags_name[TED_PATH_MAX]; + path_full(ted->tags_dir, "tags", tags_name, sizeof tags_name); FILE *file = fopen(tags_name, "rb"); if (!file) return 0; @@ -200,13 +204,14 @@ size_t tags_beginning_with(Ted *ted, const char *prefix, char **out, size_t out_ // returns true if the tag exists. bool tag_goto(Ted *ted, const char *tag) { - bool already_regenerated_tags; - already_regenerated_tags = false; + bool already_regenerated_tags = false; top:; const Settings *settings = ted_active_settings(ted); + if (!get_tags_dir(ted, true)) + return false; + char tags_name[TED_PATH_MAX]; + path_full(ted->tags_dir, "tags", tags_name, sizeof tags_name); - const char *tags_name = tags_filename(ted, true); - if (!tags_name) return false; FILE *file = fopen(tags_name, "rb"); if (!file) return false; @@ -370,9 +375,10 @@ top:; SymbolInfo *tags_get_symbols(Ted *ted) { // read tags file and extract tag names - const char *filename = tags_filename(ted, true); - if (!filename) return NULL; - FILE *file = fopen(filename, "rb"); + if (!get_tags_dir(ted, true)) return NULL; + char tags_name[TED_PATH_MAX]; + path_full(ted->tags_dir, "tags", tags_name, sizeof tags_name); + FILE *file = fopen(tags_name, "rb"); if (!file) return NULL; SymbolInfo *infos = NULL; diff --git a/ted-internal.h b/ted-internal.h index 6de2cfd..64b3b6e 100644 --- a/ted-internal.h +++ b/ted-internal.h @@ -513,6 +513,8 @@ bool buffer_handle_click(Ted *ted, TextBuffer *buffer, vec2 click, u8 times); /// currently needed to avoid bad positioning when a buffer is created /// and buffer_center_cursor is called immediately after void buffer_center_cursor_next_frame(TextBuffer *buffer); +/// perform a series of checks to make sure the buffer doesn't have any invalid values +void buffer_check_valid(TextBuffer *buffer); // === build.c === void build_frame(Ted *ted, float x1, float y1, float x2, float y2); @@ -22,15 +22,12 @@ extern "C" { #include "command.h" /// Version number -#define TED_VERSION "2.4.3" +#define TED_VERSION "3.0" /// Maximum path size ted handles. #define TED_PATH_MAX 1024 /// Config filename #define TED_CFG "ted.cfg" - -// If you are adding new languages, DO NOT change the constant values -// of the previous languages. It will mess up config files which use :set-language! enum { /// avoid using this and use LANG_TEXT instead. LANG_NONE = 0, @@ -87,7 +84,6 @@ typedef u32 SyntaxState; /// types of syntax highlighting enum SyntaxCharType { - // do not change these numbers as it will break backwards compatibility with plugins SYNTAX_NORMAL = 0, SYNTAX_KEYWORD = 1, SYNTAX_BUILTIN = 2, @@ -143,8 +139,6 @@ typedef struct { /// line number (0-indexed) u32 line; /// UTF-32 index of character in line - /// - /// (not the same as column, since a tab is `settings->tab_width` columns) u32 index; } BufferPos; @@ -155,9 +149,9 @@ enum { }; /// see \ref KEY_COMBO enum { - KEY_MODIFIER_CTRL_BIT, - KEY_MODIFIER_SHIFT_BIT, - KEY_MODIFIER_ALT_BIT + KEY_MODIFIER_CTRL_BIT = 0, + KEY_MODIFIER_SHIFT_BIT = 1, + KEY_MODIFIER_ALT_BIT = 2, }; /// see \ref KEY_COMBO #define KEY_MODIFIER_CTRL ((u32)1<<KEY_MODIFIER_CTRL_BIT) @@ -193,15 +187,15 @@ enum { /// determines which thing associated with a symbol to go to typedef enum { - GOTO_DECLARATION, - GOTO_DEFINITION, - GOTO_IMPLEMENTATION, - GOTO_TYPE_DEFINITION, + GOTO_DECLARATION = 0, + GOTO_DEFINITION = 1, + GOTO_IMPLEMENTATION = 2, + GOTO_TYPE_DEFINITION = 3, } GotoType; /// options for a pop-up menu typedef enum { - POPUP_NONE, + POPUP_NONE = 0, /// "Yes" button POPUP_YES = 1<<1, /// "No" button @@ -345,9 +339,11 @@ bool buffer_selection_pos(TextBuffer *buffer, BufferPos *pos); /// /// This string can be freed if the buffer is saved under a different name or closed, so don't keep it around for long. const char *buffer_get_path(TextBuffer *buffer); -/// clear undo and redo history +/// clear undo/redo history void buffer_clear_undo_redo(TextBuffer *buffer); /// set whether undo history should be kept +/// +/// if `enabled` is false, any previous undo/redo history will be cleared. void buffer_set_undo_enabled(TextBuffer *buffer, bool enabled); /// set manual language override for buffer. /// @@ -375,11 +371,11 @@ bool buffer_is_line_buffer(TextBuffer *buffer); bool line_buffer_is_submitted(TextBuffer *buffer); /// clear submission status of line buffer. void line_buffer_clear_submitted(TextBuffer *buffer); -/// returns the character after position pos, or 0 if pos is invalid +/// returns the character after position `pos`, or 0 if `pos` is invalid or at the end of a line char32_t buffer_char_at_pos(TextBuffer *buffer, BufferPos pos); -/// returns the character after the cursor +/// returns the character after the cursor, or 0 if the cursor is at the end of a line char32_t buffer_char_at_cursor(TextBuffer *buffer); -/// returns the character before position pos, or 0 if pos is invalid or at the start of a line +/// returns the character before position `pos`, or 0 if pos is invalid or at the start of a line char32_t buffer_char_before_pos(TextBuffer *buffer, BufferPos pos); /// returns the character to the left of the cursor, or 0 if the cursor at the start of the line. char32_t buffer_char_before_cursor(TextBuffer *buffer); @@ -431,17 +427,18 @@ size_t buffer_get_text_at_pos(TextBuffer *buffer, BufferPos pos, char32_t *text, /// the string should be passed to str32_free. String32 buffer_get_str32_text_at_pos(TextBuffer *buffer, BufferPos pos, size_t nchars); /// get UTF-8 string at position, up to `nchars` code points (NOT bytes). +/// /// the resulting string should be freed. char *buffer_get_utf8_text_at_pos(TextBuffer *buffer, BufferPos pos, size_t nchars); /// Puts a UTF-8 string containing the contents of the buffer into out. +/// /// Returns the number of bytes, including a null terminator. /// To use this function, first pass NULL for out to get the number of bytes you need to allocate. size_t buffer_contents_utf8(TextBuffer *buffer, char *out); /// Returns a UTF-8 string containing the contents of `buffer`. -/// The return value should be freed.. +/// +/// The return value should be freed. char *buffer_contents_utf8_alloc(TextBuffer *buffer); -/// perform a series of checks to make sure the buffer doesn't have any invalid values -void buffer_check_valid(TextBuffer *buffer); /// clear contents, undo history, etc. of a buffer void buffer_clear(TextBuffer *buffer); /// returns the length of the `line_number`th line (0-indexed), @@ -454,7 +451,7 @@ void buffer_text_dimensions(TextBuffer *buffer, u32 *lines, u32 *columns); float buffer_display_lines(TextBuffer *buffer); /// returns the number of columns of text that can fit in the buffer float buffer_display_cols(TextBuffer *buffer); -/// scroll by deltas +/// scroll by deltas (measured in lines and columns) void buffer_scroll(TextBuffer *buffer, double dx, double dy); /// returns the screen position of the character at the given position in the buffer. vec2 buffer_pos_to_pixels(TextBuffer *buffer, BufferPos pos); @@ -527,7 +524,7 @@ char *buffer_word_at_cursor_utf8(TextBuffer *buffer); /// 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); +bool 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); /// Buffer position corresponding to the end of line `line` (0-indexed). @@ -592,7 +589,8 @@ void buffer_select_page_down(TextBuffer *buffer, i64 npages); /// Delete `nchars` characters starting from `pos`. void buffer_delete_chars_at_pos(TextBuffer *buffer, BufferPos pos, i64 nchars); /// Delete characters between the two positions. -/// The order of `p1` and `p2` is irrelevant. +/// +/// The order of `p1` and `p2` doesn't matter. i64 buffer_delete_chars_between(TextBuffer *buffer, BufferPos p1, BufferPos p2); /// Delete current selection. i64 buffer_delete_selection(TextBuffer *buffer); @@ -712,12 +710,16 @@ void build_queue_start(Ted *ted); /// add a command to the build queue. call \ref build_queue_start before this. void build_queue_command(Ted *ted, const char *command); /// call this after calling \ref build_queue_start, \ref build_queue_command. -/// make sure you set `ted->build_dir` before running this! +/// +/// make sure you call \ref build_set_working_directory before calling this! void build_queue_finish(Ted *ted); /// set up the build output buffer. void build_setup_buffer(Ted *ted); +/// set directory for build commands. +void build_set_working_directory(Ted *ted, const char *dir); /// run a single command in the build window. -/// make sure you set `ted->build_dir` before running this! +/// +/// make sure you call \ref build_set_working_directory before calling this! void build_start_with_command(Ted *ted, const char *command); /// figure out which build command to run, and run it. void build_start(Ted *ted); |