diff options
-rw-r--r-- | Makefile | 17 | ||||
-rw-r--r-- | base.h | 2 | ||||
-rw-r--r-- | buffer.c | 50 | ||||
-rw-r--r-- | build.c | 10 | ||||
-rw-r--r-- | command.c | 6 | ||||
-rw-r--r-- | config.c | 25 | ||||
-rw-r--r-- | find.c | 4 | ||||
-rw-r--r-- | gl.c | 10 | ||||
-rw-r--r-- | ide-hover.c | 4 | ||||
-rw-r--r-- | ide-rename.c | 0 | ||||
-rw-r--r-- | ide-signature-help.c | 5 | ||||
-rw-r--r-- | lsp-json.c | 6 | ||||
-rw-r--r-- | lsp-parse.c | 2 | ||||
-rw-r--r-- | lsp-write.c | 2 | ||||
-rw-r--r-- | lsp.c | 11 | ||||
-rw-r--r-- | lsp.h | 20 | ||||
-rw-r--r-- | main.c | 10 | ||||
-rw-r--r-- | menu.c | 2 | ||||
-rw-r--r-- | os-posix.c | 3 | ||||
-rw-r--r-- | os-win.c | 2 | ||||
-rw-r--r-- | os.h | 6 | ||||
-rw-r--r-- | pcre-inc.h | 3 | ||||
-rw-r--r-- | sdl-inc.h | 15 | ||||
-rw-r--r-- | syntax.c | 2 | ||||
-rw-r--r-- | tags.c | 1 | ||||
-rw-r--r-- | ted.c | 33 | ||||
-rw-r--r-- | ted.h | 53 | ||||
-rw-r--r-- | text.c | 19 | ||||
-rw-r--r-- | text.h | 5 | ||||
-rw-r--r-- | ui.c | 1 | ||||
-rw-r--r-- | util.c | 9 |
31 files changed, 202 insertions, 136 deletions
@@ -10,10 +10,21 @@ PROFILE_CFLAGS=$(ALL_CFLAGS) -O3 -g -DPROFILE=1 GLOBAL_DATA_DIR=/usr/share/ted LOCAL_DATA_DIR=/home/`logname`/.local/share/ted INSTALL_BIN_DIR=/usr/bin -ted: *.[ch] libpcre2-32.a stb_truetype.o stb_image.o - $(CC) main.c stb_truetype.o stb_image.o -o ted $(DEBUG_CFLAGS) $(LIBS) -%.o: %.c +OBJECTS=obj/buffer.o obj/build.o obj/colors.o obj/command.o\ + obj/config.o obj/find.o obj/gl.o obj/ide-autocomplete.o\ + obj/ide-definitions.o obj/ide-highlights.o obj/ide-hover.o\ + obj/ide-signature-help.o obj/ide-usages.o obj/lsp.o obj/lsp-json.o\ + obj/lsp-parse.o obj/lsp-write.o obj/main.o obj/menu.o obj/node.o\ + obj/os-posix.o obj/session.o obj/stb_image.o obj/stb_truetype.o\ + obj/syntax.o obj/tags.o obj/ted.o obj/text.o obj/ui.o obj/util.o +ted: *.[ch] libpcre2-32.a $(OBJECTS) + $(CC) $(OBJECTS) -o ted $(DEBUG_CFLAGS) $(LIBS) +obj/stb_%.o: stb_%.c obj $(CC) -O3 -Wall $< -c -o $@ +obj/%.o: %.c *.h obj + $(CC) -Wall $< -c -o $@ $(DEBUG_CFLAGS) +obj: + mkdir obj release: *.[ch] libpcre2-32.a $(CC) main.c -o ted $(RELEASE_CFLAGS) $(LIBS) profile: *.[ch] libpcre2-32.a @@ -40,6 +40,8 @@ #include <limits.h> #include <assert.h> #include <time.h> +#include <math.h> +#include <errno.h> #if __linux__ || _WIN32 #include <uchar.h> #else @@ -49,7 +49,7 @@ static void buffer_clear_undo_history(TextBuffer *buffer) { buffer->undo_history_write_pos = U32_MAX; } -static void buffer_clear_undo_redo(TextBuffer *buffer) { +void buffer_clear_undo_redo(TextBuffer *buffer) { buffer_clear_undo_history(buffer); buffer_clear_redo_history(buffer); } @@ -127,8 +127,7 @@ void line_buffer_create(TextBuffer *buffer, Ted *ted) { } } -// ensures that `p` refers to a valid position. -static void buffer_pos_validate(TextBuffer *buffer, BufferPos *p) { +void buffer_pos_validate(TextBuffer *buffer, BufferPos *p) { if (p->line >= buffer->nlines) p->line = buffer->nlines - 1; u32 line_len = buffer->lines[p->line].len; @@ -167,7 +166,7 @@ static void buffer_pos_handle_inserted_chars(BufferPos *pos, BufferPos ins_pos, } } -static bool buffer_pos_valid(TextBuffer *buffer, BufferPos p) { +bool buffer_pos_valid(TextBuffer *buffer, BufferPos p) { return p.line < buffer->nlines && p.index <= buffer->lines[p.line].len; } @@ -317,40 +316,13 @@ LSP *buffer_lsp(TextBuffer *buffer) { } -// score is higher if context is closer match. -static long context_score(const char *path, Language lang, const SettingsContext *context) { - long score = 0; - - if (context->language) { - if (lang == context->language) { - score += 10000; - } else { - // dont use this. it's language-specific and for the wrong language. - return INT_MIN; - } - } - - if (context->path) { - if (path && str_has_path_prefix(path, context->path)) { - score += (long)strlen(context->path); - } else { - // dont use this. it's path-specific and for the wrong path. - return INT_MIN; - } - } - - return score; -} -// Get the settings used for this buffer. Settings *buffer_settings(TextBuffer *buffer) { return ted_get_settings(buffer->ted, buffer->filename, buffer_language(buffer)); } -// NOTE: this string will be invalidated when the line is edited!!! -// only use it briefly!! -static String32 buffer_get_line(TextBuffer *buffer, u32 line_number) { +String32 buffer_get_line(TextBuffer *buffer, u32 line_number) { Line *line = &buffer->lines[line_number]; return (String32) { .str = line->str, .len = line->len @@ -518,12 +490,7 @@ static i64 buffer_pos_diff(TextBuffer *buffer, BufferPos p1, BufferPos p2) { return total * factor; } -// returns: -// -1 if p1 comes before p2 -// +1 if p1 comes after p2 -// 0 if p1 = p2 -// faster than buffer_pos_diff (constant time) -static int buffer_pos_cmp(BufferPos p1, BufferPos p2) { +int buffer_pos_cmp(BufferPos p1, BufferPos p2) { if (p1.line < p2.line) { return -1; } else if (p1.line > p2.line) { @@ -539,7 +506,7 @@ static int buffer_pos_cmp(BufferPos p1, BufferPos p2) { } } -static bool buffer_pos_eq(BufferPos p1, BufferPos p2) { +bool buffer_pos_eq(BufferPos p1, BufferPos p2) { return p1.line == p2.line && p1.index == p2.index; } @@ -990,8 +957,7 @@ bool buffer_pixels_to_pos(TextBuffer *buffer, v2 pixel_coords, BufferPos *pos) { return ret; } -// clip the rectangle so it's all inside the buffer. returns true if there's any rectangle left. -static bool buffer_clip_rect(TextBuffer *buffer, Rect *r) { +bool buffer_clip_rect(TextBuffer *buffer, Rect *r) { float x1, y1, x2, y2; rect_coords(*r, &x1, &y1, &x2, &y2); if (x1 > buffer->x2 || y1 > buffer->y2 || x2 < buffer->x1 || y2 < buffer->y1) { @@ -3018,7 +2984,7 @@ void buffer_toggle_comment_selection(TextBuffer *buffer) { void buffer_highlight_lsp_range(TextBuffer *buffer, LSPRange range) { Font *font = buffer_font(buffer); const u32 *colors = buffer_settings(buffer)->colors; - float char_height = font->char_height; + const float char_height = text_font_char_height(font); BufferPos range_start = buffer_pos_from_lsp(buffer, range.start); BufferPos range_end = buffer_pos_from_lsp(buffer, range.end); // draw the highlight @@ -2,7 +2,7 @@ void build_stop(Ted *ted) { if (ted->building) - process_kill(&ted->build_process); + process_kill(ted->build_process); ted->building = false; ted->build_shown = false; arr_foreach_ptr(ted->build_errors, BuildError, err) { @@ -38,7 +38,9 @@ static bool build_run_next_command_in_queue(Ted *ted) { char *command = ted->build_queue[0]; arr_remove(ted->build_queue, 0); if (ted_save_all(ted)) { - if (process_run(&ted->build_process, command)) { + ted->build_process = process_run(command); + const char *error = process_geterr(ted->build_process); + if (!error) { ted->building = true; ted->build_shown = true; TextBuffer *build_buffer = &ted->build_buffer; @@ -50,7 +52,7 @@ static bool build_run_next_command_in_queue(Ted *ted) { free(command); return true; } else { - ted_seterr(ted, "Couldn't start build: %s", process_geterr(&ted->build_process)); + ted_seterr(ted, "Couldn't start build: %s", error); build_stop(ted); return false; } @@ -238,7 +240,7 @@ void build_check_for_errors(Ted *ted) { void build_frame(Ted *ted, float x1, float y1, float x2, float y2) { TextBuffer *buffer = &ted->build_buffer; - Process *process = &ted->build_process; + Process *process = ted->build_process; assert(ted->build_shown); char buf[256]; if (ted->building) { @@ -311,7 +311,7 @@ void command_execute(Ted *ted, Command c, i64 argument) { menu_open(ted, MENU_OPEN); break; case CMD_NEW: - ted_new_file(ted, NULL); + (void)ted_new_file(ted, NULL); break; case CMD_SAVE: ted->last_save_time = ted->frame_time; @@ -331,7 +331,7 @@ void command_execute(Ted *ted, Command c, i64 argument) { break; case CMD_SAVE_ALL: ted->last_save_time = ted->frame_time; - ted_save_all(ted); + (void)ted_save_all(ted); break; case CMD_RELOAD_ALL: ted_reload_all(ted); @@ -412,7 +412,7 @@ void command_execute(Ted *ted, Command c, i64 argument) { case CMD_OPEN_CONFIG: { char local_config_filename[TED_PATH_MAX]; strbuf_printf(local_config_filename, "%s" PATH_SEPARATOR_STR TED_CFG, ted->local_data_dir); - ted_open_file(ted, local_config_filename); + (void)ted_open_file(ted, local_config_filename); } break; case CMD_COMMAND_SELECTOR: menu_open(ted, MENU_COMMAND_SELECTOR); @@ -170,6 +170,31 @@ static void context_copy(SettingsContext *dest, const SettingsContext *src) { dest->path = str_dup(src->path); } +// score is higher if context is closer match. +long context_score(const char *path, Language lang, const SettingsContext *context) { + long score = 0; + + if (context->language) { + if (lang == context->language) { + score += 10000; + } else { + // dont use this. it's language-specific and for the wrong language. + return INT_MIN; + } + } + + if (context->path) { + if (path && str_has_path_prefix(path, context->path)) { + score += (long)strlen(context->path); + } else { + // dont use this. it's path-specific and for the wrong path. + return INT_MIN; + } + } + + return score; +} + /* does being in the context of `parent` imply you are in the context of `child`? */ static bool context_is_parent(const SettingsContext *parent, const SettingsContext *child) { if (child->language == 0 && parent->language != 0) @@ -1,8 +1,6 @@ #include "ted.h" +#include "pcre-inc.h" -#define PCRE2_STATIC -#define PCRE2_CODE_UNIT_WIDTH 32 -#include <pcre2.h> #define FIND_MAX_GROUPS 50 @@ -167,7 +167,7 @@ GLuint gl_compile_and_link_shaders(char error_buf[256], const char *vshader_code return program; } -GLuint gl_attrib_loc(GLuint program, const char *attrib) { +GLuint gl_attrib_location(GLuint program, const char *attrib) { GLint loc = glGetAttribLocation(program, attrib); if (loc == -1) { debug_print("Couldn't find vertex attribute %s.\n", attrib); @@ -176,7 +176,7 @@ GLuint gl_attrib_loc(GLuint program, const char *attrib) { return (GLuint)loc; } -GLint gl_uniform_loc(GLuint program, const char *uniform) { +GLint gl_uniform_location(GLuint program, const char *uniform) { GLint loc = glGetUniformLocation(program, uniform); if (loc == -1) { debug_print("Couldn't find uniform: %s.\n", uniform); @@ -218,9 +218,9 @@ void gl_geometry_init(void) { "; gl_geometry_program = gl_compile_and_link_shaders(NULL, vshader_code, fshader_code); - gl_geometry_v_pos = gl_attrib_loc(gl_geometry_program, "v_pos"); - gl_geometry_v_color = gl_attrib_loc(gl_geometry_program, "v_color"); - gl_geometry_u_window_size = gl_uniform_loc(gl_geometry_program, "u_window_size"); + gl_geometry_v_pos = gl_attrib_location(gl_geometry_program, "v_pos"); + gl_geometry_v_color = gl_attrib_location(gl_geometry_program, "v_color"); + gl_geometry_u_window_size = gl_uniform_location(gl_geometry_program, "u_window_size"); glGenBuffers(1, &gl_geometry_vbo); if (gl_version_major >= 3) diff --git a/ide-hover.c b/ide-hover.c index ecca8d8..254f56d 100644 --- a/ide-hover.c +++ b/ide-hover.c @@ -120,8 +120,8 @@ void hover_frame(Ted *ted, double dt) { const u32 *colors = settings->colors; const char *text = hover->text; Font *font = ted->font; - float x = ted->mouse_pos.x, y = ted->mouse_pos.y + font->char_height; - float char_height = font->char_height; + float char_height = text_font_char_height(font); + float x = ted->mouse_pos.x, y = ted->mouse_pos.y + char_height; buffer_highlight_lsp_range(buffer, hover->range); diff --git a/ide-rename.c b/ide-rename.c deleted file mode 100644 index e69de29..0000000 --- a/ide-rename.c +++ /dev/null diff --git a/ide-signature-help.c b/ide-signature-help.c index 0e6fd91..e573f3e 100644 --- a/ide-signature-help.c +++ b/ide-signature-help.c @@ -117,9 +117,10 @@ void signature_help_frame(Ted *ted) { float width = buffer->x2 - buffer->x1; float height = FLT_MAX; + const float char_height = text_font_char_height(font); // make sure signature help doesn't take up too much space while (1) { - height = font->char_height * signature_count; + height = char_height * signature_count; if (height < (buffer->y2 - buffer->y1) * 0.25f) break; --signature_count; @@ -146,7 +147,7 @@ void signature_help_frame(Ted *ted) { text_utf8_with_state(font, &state, signature->label_pre); text_utf8_with_state(font_bold, &state, signature->label_active); text_utf8_with_state(font, &state, signature->label_post); - y += font->char_height; + y += char_height; } gl_geometry_draw(); @@ -4,7 +4,8 @@ #define LSP_INTERNAL 1 #include "lsp.h" - +#include "util.h" +#include "unicode.h" #define SKIP_WHITESPACE while (json_is_space(text[index])) ++index; @@ -587,8 +588,7 @@ void json_string_get(const JSON *json, JSONString string, char *buf, size_t buf_ *buf = '\0'; } -// returns a malloc'd null-terminated string. -static char *json_string_get_alloc(const JSON *json, JSONString string) { +char *json_string_get_alloc(const JSON *json, JSONString string) { u32 n = string.len + 1; if (n == 0) --n; // extreme edge case char *buf = calloc(1, n); diff --git a/lsp-parse.c b/lsp-parse.c index 17650c3..7b5849f 100644 --- a/lsp-parse.c +++ b/lsp-parse.c @@ -1,5 +1,7 @@ #define LSP_INTERNAL 1 #include "lsp.h" +#include "util.h" +#include "unicode.h" static WarnUnusedResult bool lsp_expect_type(LSP *lsp, JSONValue value, JSONValueType type, const char *what) { if (value.type != type) { diff --git a/lsp-write.c b/lsp-write.c index 1a1093c..ecb8527 100644 --- a/lsp-write.c +++ b/lsp-write.c @@ -324,7 +324,7 @@ static void message_writer_write_and_free(LSP *lsp, JSONWriter *o) { #endif // @TODO: does write always write the full amount? probably not. this should be fixed. - process_write(&lsp->process, content, strlen(content)); + process_write(lsp->process, content, strlen(content)); str_builder_free(&builder); } @@ -1,5 +1,8 @@ #define LSP_INTERNAL 1 #include "lsp.h" +#include "util.h" + +const char *language_to_str(Language language); // it's nice to have request IDs be totally unique, including across LSP servers. static LSPRequestID get_request_id(void) { @@ -249,7 +252,7 @@ static void lsp_receive(LSP *lsp, size_t max_size) { // read stderr. if all goes well, we shouldn't get anything over stderr. char stderr_buf[1024] = {0}; for (size_t i = 0; i < (max_size + sizeof stderr_buf) / sizeof stderr_buf; ++i) { - ssize_t nstderr = process_read_stderr(&lsp->process, stderr_buf, sizeof stderr_buf - 1); + ssize_t nstderr = process_read_stderr(lsp->process, stderr_buf, sizeof stderr_buf - 1); if (nstderr > 0) { // uh oh stderr_buf[nstderr] = '\0'; @@ -262,7 +265,7 @@ static void lsp_receive(LSP *lsp, size_t max_size) { size_t received_so_far = arr_len(lsp->received_data); arr_reserve(lsp->received_data, received_so_far + max_size + 1); - long long bytes_read = process_read(&lsp->process, lsp->received_data + received_so_far, max_size); + long long bytes_read = process_read(lsp->process, lsp->received_data + received_so_far, max_size); if (bytes_read <= 0) { // no data return; @@ -449,7 +452,7 @@ LSP *lsp_create(const char *root_dir, Language language, const char *analyzer_co .separate_stderr = true, .working_directory = root_dir, }; - process_run_ex(&lsp->process, analyzer_command, &settings); + lsp->process = process_run_ex(analyzer_command, &settings); LSPRequest initialize = { .type = LSP_REQUEST_INITIALIZE }; @@ -513,7 +516,7 @@ void lsp_free(LSP *lsp) { SDL_DestroyMutex(lsp->workspace_folders_mutex); SDL_DestroyMutex(lsp->error_mutex); SDL_DestroySemaphore(lsp->quit_sem); - process_kill(&lsp->process); + process_kill(lsp->process); arr_free(lsp->received_data); @@ -3,10 +3,14 @@ #include "base.h" #include "ds.h" +#include "os.h" typedef u32 LSPDocumentID; typedef u32 LSPID; typedef u32 LSPRequestID; +typedef struct SDL_mutex *LSPMutex; +typedef struct SDL_semaphore *LSPSemaphore; +typedef struct SDL_Thread *LSPThread; typedef struct { u32 line; @@ -496,14 +500,14 @@ typedef struct LSP { // thread-safety: created in lsp_create, then only accessed by the communication thread Process *process; - SDL_mutex *document_mutex; + LSPMutex document_mutex; // for our purposes, folders are "documents" // the spec kinda does this too: WorkspaceFolder has a `uri: DocumentUri` member. StrHashTable document_ids; // values are u32. they are indices into document_data. // this is a dynamic array which just keeps growing. // but the user isn't gonna open millions of files so it's fine. LSPDocumentData *document_data; - SDL_mutex *messages_mutex; + LSPMutex messages_mutex; LSPMessage *messages_server2client; LSPMessage *messages_client2server; // we keep track of client-to-server requests @@ -516,8 +520,8 @@ typedef struct LSP { _Atomic bool initialized; // thread-safety: only set once in lsp_create. Language language; - SDL_Thread *communication_thread; - SDL_sem *quit_sem; + LSPThread communication_thread; + LSPSemaphore quit_sem; // thread-safety: only accessed in communication thread char *received_data; // dynamic array // thread-safety: in the communication thread, we fill this in, then set `initialized = true`. @@ -530,9 +534,9 @@ typedef struct LSP { char32_t *signature_help_trigger_chars; // dynamic array // thread-safety: same as `capabilities` char32_t *signature_help_retrigger_chars; // dynamic array - SDL_mutex *workspace_folders_mutex; + LSPMutex workspace_folders_mutex; LSPDocumentID *workspace_folders; // dynamic array of root directories of LSP workspace folders - SDL_mutex *error_mutex; + LSPMutex error_mutex; char error[256]; } LSP; @@ -578,6 +582,8 @@ void lsp_free(LSP *lsp); #if defined LSP_INTERNAL && !defined LSP_INTERNAL_H_ #define LSP_INTERNAL_H_ +#include "sdl-inc.h" + #define lsp_set_error(lsp, ...) do {\ SDL_LockMutex(lsp->error_mutex);\ strbuf_printf(lsp->error, __VA_ARGS__);\ @@ -679,6 +685,8 @@ JSONValue json_root(const JSON *json); JSONValue json_get(const JSON *json, const char *path); bool json_has(const JSON *json, const char *path); void json_string_get(const JSON *json, JSONString string, char *buf, size_t buf_sz); +// returns a malloc'd null-terminated string. +char *json_string_get_alloc(const JSON *json, JSONString string); void json_debug_print(const JSON *json); size_t json_escape_to(char *out, size_t out_sz, const char *in); char *json_escape(const char *str); @@ -1,5 +1,6 @@ /* @TODO: +- don't include SDL when possible - rename v[234] to vec[234] - make ctrl+up/ctrl+down move to next/prev blank line - broken session fix: close buffers not in any used node @@ -17,11 +18,9 @@ - when searching files, put exact matches at the top - auto-set build command for cmake (both for windows and unix) --- LSP MERGE --- -- improve structure of ted source code to make LSP completions better - (make every c file a valid translation unit) - - some way of opening + closing all C files in directory for clangd workspace/symbols to work? - is this still necessary? - - maybe it can be done with the clangd config instead.s +- some way of opening + closing all C files in directory for clangd workspace/symbols to work? + is this still necessary? + - maybe it can be done with the clangd config instead. - CSS highlighting - styles ([color] sections) - more documentation generally (development.md or something?) @@ -57,7 +56,6 @@ FUTURE FEATURES: #include "ted.h" #include <locale.h> -#include <wctype.h> #include <signal.h> #if __linux__ #include <execinfo.h> @@ -181,7 +181,7 @@ void menu_update(Ted *ted) { if (selected_file) { // open that file! menu_close(ted); - ted_open_file(ted, selected_file); + (void)ted_open_file(ted, selected_file); free(selected_file); } } break; @@ -1,4 +1,5 @@ #include "os.h" +#include "util.h" #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> @@ -178,7 +179,6 @@ Process *process_run_ex(const char *command, const ProcessSettings *settings) { } } - bool success = false; pid_t pid = fork(); if (pid == 0) { // child process @@ -295,6 +295,7 @@ void process_kill(Process *proc) { waitpid(proc->pid, NULL, 0); proc->pid = 0; process_close_pipes(proc); + free(proc); } int process_check_status(Process *proc, char *message, size_t message_size) { @@ -1,4 +1,4 @@ -#include "filesystem.h" +#include "os.h" #include <sys/types.h> #include <sys/stat.h> #include <io.h> @@ -2,6 +2,8 @@ #ifndef OS_H_ #define OS_H_ +#include "base.h" + typedef enum { FS_NON_EXISTENT, FS_FILE, @@ -55,7 +57,9 @@ static void fs_dir_entries_free(FsDirectoryEntry **entries) { } static double time_get_seconds(void) { - return timespec_to_seconds(time_get()); + struct timespec t = time_get(); + return (double)t.tv_sec + + (double)t.tv_nsec * 1e-9; } diff --git a/pcre-inc.h b/pcre-inc.h new file mode 100644 index 0000000..44088ec --- /dev/null +++ b/pcre-inc.h @@ -0,0 +1,3 @@ +#define PCRE2_STATIC +#define PCRE2_CODE_UNIT_WIDTH 32 +#include <pcre2.h> diff --git a/sdl-inc.h b/sdl-inc.h new file mode 100644 index 0000000..7bb613b --- /dev/null +++ b/sdl-inc.h @@ -0,0 +1,15 @@ +#ifndef SDL_INC_H_ +#define SDL_INC_H_ + +no_warn_start +#if _WIN32 + #include <SDL.h> +#else + #if DEBUG || __TINYC__ // speed up compile time on debug, also tcc doesn't have immintrin.h + #define SDL_DISABLE_IMMINTRIN_H + #endif + #include <SDL2/SDL.h> +#endif +no_warn_end + +#endif // SDL_INC_H_ @@ -1253,7 +1253,7 @@ static void syntax_highlight_javascript_like( // this is not foolproof for detecting regex literals // but should handle all "reasonable" uses of regex. bool is_regex = i == 0 // slash is first char in line - || (line[i-1] <= WCHAR_MAX && iswspace((wint_t)line[i-1])) // slash preceded by space + || is32_space(line[i-1]) // slash preceded by space || (line[i-1] <= 128 && strchr(";({[=,:", (char)line[i-1])); // slash preceded by any of these characters if (is_regex) { in_string = true; @@ -1,4 +1,5 @@ #include "ted.h" +#include "pcre-inc.h" static const char *tags_filename(Ted *ted, bool error_if_does_not_exist) { change_directory(ted->cwd); @@ -39,19 +39,19 @@ static void ted_out_of_mem(Ted *ted) { ted_seterr(ted, "Out of memory."); } -static void *ted_malloc(Ted *ted, size_t size) { +void *ted_malloc(Ted *ted, size_t size) { void *ret = malloc(size); if (!ret) ted_out_of_mem(ted); return ret; } -static void *ted_calloc(Ted *ted, size_t n, size_t size) { +void *ted_calloc(Ted *ted, size_t n, size_t size) { void *ret = calloc(n, size); if (!ret) ted_out_of_mem(ted); return ret; } -static void *ted_realloc(Ted *ted, void *p, size_t new_size) { +void *ted_realloc(Ted *ted, void *p, size_t new_size) { void *ret = realloc(p, new_size); if (!ret) ted_out_of_mem(ted); return ret; @@ -175,7 +175,7 @@ u32 ted_color(Ted *ted, ColorSetting color) { return ted_active_settings(ted)->colors[color]; } -static void ted_path_full(Ted *ted, const char *relpath, char *abspath, size_t abspath_size) { +void ted_path_full(Ted *ted, const char *relpath, char *abspath, size_t abspath_size) { path_full(ted->cwd, relpath, abspath, abspath_size); } @@ -269,7 +269,7 @@ void ted_switch_to_buffer(Ted *ted, TextBuffer *buffer) { } // set ted->active_buffer to something nice -static void ted_reset_active_buffer(Ted *ted) { +void ted_reset_active_buffer(Ted *ted) { if (ted->nodes_used[0]) { Node *node = &ted->nodes[0]; while (!node->tabs) @@ -282,8 +282,7 @@ static void ted_reset_active_buffer(Ted *ted) { } -// returns the index of an available buffer, or -1 if none are available -static i32 ted_new_buffer(Ted *ted) { +i32 ted_new_buffer(Ted *ted) { bool *buffers_used = ted->buffers_used; for (i32 i = 1; // start from 1, so as not to use the null buffer i < TED_MAX_BUFFERS; ++i) { @@ -296,9 +295,7 @@ static i32 ted_new_buffer(Ted *ted) { return -1; } -// Opposite of ted_new_buffer -// Make sure you set active_buffer to something else if you delete it! -static void ted_delete_buffer(Ted *ted, u16 index) { +void ted_delete_buffer(Ted *ted, u16 index) { TextBuffer *buffer = &ted->buffers[index]; if (buffer == ted->active_buffer) ted_switch_to_buffer(ted, NULL); // make sure we don't set the active buffer to something invalid @@ -308,8 +305,7 @@ static void ted_delete_buffer(Ted *ted, u16 index) { ted->buffers_used[index] = false; } -// Returns the index of an available node, or -1 if none are available -static i32 ted_new_node(Ted *ted) { +i32 ted_new_node(Ted *ted) { bool *nodes_used = ted->nodes_used; for (i32 i = 0; i < TED_MAX_NODES; ++i) { if (!nodes_used[i]) { @@ -323,14 +319,12 @@ static i32 ted_new_node(Ted *ted) { } -// how tall is a line buffer? -static float ted_line_buffer_height(Ted *ted) { +float ted_line_buffer_height(Ted *ted) { const float char_height = text_font_char_height(ted->font); return char_height + 2 * ted_active_settings(ted)->border_thickness; } -// switch to this node -static void ted_node_switch(Ted *ted, Node *node) { +void ted_node_switch(Ted *ted, Node *node) { assert(node->tabs); ted_switch_to_buffer(ted, &ted->buffers[node->tabs[node->active_tab]]); } @@ -370,8 +364,7 @@ static Status ted_open_buffer(Ted *ted, u16 *buffer_idx, u16 *tab) { } } -// Returns the buffer containing the file at `path`, or NULL if there is none. -static TextBuffer *ted_get_buffer_with_file(Ted *ted, const char *path) { +TextBuffer *ted_get_buffer_with_file(Ted *ted, const char *path) { bool *buffers_used = ted->buffers_used; TextBuffer *buffers = ted->buffers; for (u16 i = 0; i < TED_MAX_BUFFERS; ++i) { @@ -440,7 +433,7 @@ Status ted_new_file(Ted *ted, const char *filename) { Status ted_save_all(Ted *ted) { - Status success = true; + bool success = true; bool *buffers_used = ted->buffers_used; for (u16 i = 0; i < TED_MAX_BUFFERS; ++i) { if (buffers_used[i]) { @@ -463,7 +456,7 @@ Status ted_save_all(Ted *ted) { return success; } -static void ted_reload_all(Ted *ted) { +void ted_reload_all(Ted *ted) { bool *buffers_used = ted->buffers_used; for (u64 i = 0; i < TED_MAX_BUFFERS; ++i) { if (buffers_used[i]) { @@ -2,16 +2,7 @@ #define TED_H_ #include "base.h" -no_warn_start -#if _WIN32 - #include <SDL.h> -#else - #if DEBUG || __TINYC__ // speed up compile time on debug, also tcc doesn't have immintrin.h - #define SDL_DISABLE_IMMINTRIN_H - #endif - #include <SDL2/SDL.h> -#endif -no_warn_end +#include "sdl-inc.h" #include "util.h" #include "os.h" #include "unicode.h" @@ -615,6 +606,7 @@ typedef struct Ted { bool buffer_haserr(TextBuffer *buffer); const char *buffer_geterr(TextBuffer *buffer); void buffer_clearerr(TextBuffer *buffer); +void buffer_clear_undo_redo(TextBuffer *buffer); bool buffer_empty(TextBuffer *buffer); const char *buffer_get_filename(TextBuffer *buffer); bool buffer_is_untitled(TextBuffer *buffer); @@ -629,16 +621,24 @@ char32_t buffer_char_before_cursor(TextBuffer *buffer); char32_t buffer_char_after_cursor(TextBuffer *buffer); BufferPos buffer_pos_start_of_file(TextBuffer *buffer); BufferPos buffer_pos_end_of_file(TextBuffer *buffer); +// ensures that `p` refers to a valid position, moving it if needed. +void buffer_pos_validate(TextBuffer *buffer, BufferPos *p); +bool buffer_pos_valid(TextBuffer *buffer, BufferPos p); Language buffer_language(TextBuffer *buffer); +// clip the rectangle so it's all inside the buffer. returns true if there's any rectangle left. +bool buffer_clip_rect(TextBuffer *buffer, Rect *r); LSP *buffer_lsp(TextBuffer *buffer); +// Get the settings used for this buffer. Settings *buffer_settings(TextBuffer *buffer); +// NOTE: this string will be invalidated when the line is edited!!! +// only use it briefly!! +String32 buffer_get_line(TextBuffer *buffer, u32 line_number); size_t buffer_get_text_at_pos(TextBuffer *buffer, BufferPos pos, char32_t *text, size_t nchars); String32 buffer_get_str32_text_at_pos(TextBuffer *buffer, BufferPos pos, size_t nchars); char *buffer_get_utf8_text_at_pos(TextBuffer *buffer, BufferPos pos, size_t nchars); size_t buffer_contents_utf8(TextBuffer *buffer, char *out); char *buffer_contents_utf8_alloc(TextBuffer *buffer); void buffer_check_valid(TextBuffer *buffer); -void buffer_check_valid(TextBuffer *buffer); void buffer_free(TextBuffer *buffer); void buffer_clear(TextBuffer *buffer); void buffer_text_dimensions(TextBuffer *buffer, u32 *lines, u32 *columns); @@ -749,6 +749,14 @@ void buffer_toggle_comment_lines(TextBuffer *buffer, u32 first_line, u32 last_li void buffer_toggle_comment_selection(TextBuffer *buffer); // make sure to call gl_geometry_draw after this void buffer_highlight_lsp_range(TextBuffer *buffer, LSPRange range); +bool buffer_pos_eq(BufferPos p1, BufferPos p2); + +// returns: +// -1 if p1 comes before p2 +// +1 if p1 comes after p2 +// 0 if p1 = p2 +// faster than buffer_pos_diff (constant time) +int buffer_pos_cmp(BufferPos p1, BufferPos p2); // === build.c === // clear build errors and stop @@ -805,6 +813,7 @@ void config_read(Ted *ted, ConfigPart **parts, const char *filename); void config_parse(Ted *ted, ConfigPart **pparts); void config_free(Ted *ted); char *settings_get_root_dir(Settings *settings, const char *path); +long context_score(const char *path, Language lang, const SettingsContext *context); // === find.c === TextBuffer *find_search_buffer(Ted *ted); @@ -923,6 +932,7 @@ void autocomplete_frame(Ted *ted); // Note: the document position is required for LSP requests because of overloading (where the name // alone isn't sufficient) void definition_goto(Ted *ted, LSP *lsp, const char *name, LSPDocumentPosition pos); +void definition_cancel_lookup(Ted *ted); void definitions_process_lsp_response(Ted *ted, LSP *lsp, const LSPResponse *response); void definitions_selector_open(Ted *ted); void definitions_selector_update(Ted *ted); @@ -1021,9 +1031,19 @@ SymbolInfo *tags_get_symbols(Ted *ted); snprintf((ted)->error, sizeof (ted)->error - 1, __VA_ARGS__) // for fatal errors void die(PRINTF_FORMAT_STRING const char *fmt, ...) ATTRIBUTE_PRINTF(1, 2); +void *ted_malloc(Ted *ted, size_t size); +void *ted_calloc(Ted *ted, size_t n, size_t size); +void *ted_realloc(Ted *ted, void *p, size_t new_size); Status ted_get_file(Ted const *ted, const char *name, char *out, size_t outsz); +// get full path relative to ted->cwd. +void ted_path_full(Ted *ted, const char *relpath, char *abspath, size_t abspath_size); +void ted_reset_active_buffer(Ted *ted); void ted_seterr_to_buferr(Ted *ted, TextBuffer *buffer); bool ted_haserr(Ted *ted); +// Returns the buffer containing the file at `path`, or NULL if there is none. +TextBuffer *ted_get_buffer_with_file(Ted *ted, const char *path); +Status ted_save_all(Ted *ted); +void ted_reload_all(Ted *ted); const char *ted_geterr(Ted *ted); // Load all the fonts ted will use. void ted_load_fonts(Ted *ted); @@ -1039,11 +1059,20 @@ LSP *ted_active_lsp(Ted *ted); u32 ted_color(Ted *ted, ColorSetting color); Status ted_open_file(Ted *ted, const char *filename); Status ted_new_file(Ted *ted, const char *filename); +// returns the index of an available buffer, or -1 if none are available +i32 ted_new_buffer(Ted *ted); +// Returns the index of an available node, or -1 if none are available +i32 ted_new_node(Ted *ted); +// Opposite of ted_new_buffer +// Make sure you set active_buffer to something else if you delete it! +void ted_delete_buffer(Ted *ted, u16 index); // save all changes to all buffers with unsaved changes. Status ted_save_all(Ted *ted); // sets the active buffer to this buffer, and updates active_node, etc. accordingly // you can pass NULL to buffer to make it so no buffer is active. void ted_switch_to_buffer(Ted *ted, TextBuffer *buffer); +// switch to this node +void ted_node_switch(Ted *ted, Node *node); void ted_load_configs(Ted *ted, bool reloading); void ted_press_key(Ted *ted, SDL_Scancode scancode, SDL_Keymod modifier); bool ted_get_mouse_buffer_pos(Ted *ted, TextBuffer **pbuffer, BufferPos *ppos); @@ -1051,6 +1080,8 @@ void ted_flash_error_cursor(Ted *ted); void ted_go_to_position(Ted *ted, const char *path, u32 line, u32 index, bool is_lsp); void ted_go_to_lsp_document_position(Ted *ted, LSP *lsp, LSPDocumentPosition position); void ted_cancel_lsp_request(Ted *ted, LSPID lsp, LSPRequestID request); +// how tall is a line buffer? +float ted_line_buffer_height(Ted *ted); // === ui.c === void selector_up(Ted *ted, Selector *s, i64 n); @@ -1,6 +1,5 @@ -#include "base.h" -#include "text.h" -#include "unicode.h" +#include "ted.h" + #if DEBUG typedef struct { @@ -23,8 +22,6 @@ no_warn_end #endif -#include <stdlib.h> - // We split up code points into a bunch of pages, so we don't have to load all of the font at // once into one texture. #define CHAR_PAGE_SIZE 2048 @@ -52,7 +49,7 @@ struct Font { TextTriangle *triangles[CHAR_PAGE_COUNT]; // triangles to render for each page }; -TextRenderState const text_render_state_default = { +const TextRenderState text_render_state_default = { .render = true, .wrap = false, .x = 0, .y = 0, @@ -114,11 +111,11 @@ void main() {\n\ "; text_program = gl_compile_and_link_shaders(NULL, vshader_code, fshader_code); - text_v_pos = gl_attrib_loc(text_program, "v_pos"); - text_v_color = gl_attrib_loc(text_program, "v_color"); - text_v_tex_coord = gl_attrib_loc(text_program, "v_tex_coord"); - text_u_sampler = gl_uniform_loc(text_program, "sampler"); - text_u_window_size = gl_uniform_loc(text_program, "u_window_size"); + text_v_pos = gl_attrib_location(text_program, "v_pos"); + text_v_color = gl_attrib_location(text_program, "v_color"); + text_v_tex_coord = gl_attrib_location(text_program, "v_tex_coord"); + text_u_sampler = gl_uniform_location(text_program, "sampler"); + text_u_window_size = gl_uniform_location(text_program, "u_window_size"); glGenBuffers(1, &text_vbo); glGenVertexArrays(1, &text_vao); @@ -1,6 +1,9 @@ #ifndef TEXT_H_ #define TEXT_H_ +#include "base.h" +#include "util.h" + // A text-rendering interface. // Example usage: // Font *font = text_font_load("font.ttf", 18); @@ -73,6 +76,6 @@ void text_render(Font *font); // TextRenderState state = text_render_state_default; // (set a few options) // text_render_with_state(font, &state, ...) -const TextRenderState text_render_state_default; +extern const TextRenderState text_render_state_default; #endif @@ -2,6 +2,7 @@ #if __unix__ #include <fcntl.h> +#include <unistd.h> #endif static Status file_selector_cd_(Ted *ted, FileSelector *fs, const char *path, int symlink_depth); @@ -1,3 +1,7 @@ +#include "base.h" +#include "util.h" +#include "unicode.h" + #if _WIN32 #include <intrin.h> #include <direct.h> @@ -6,10 +10,7 @@ #else #error "Unrecognized operating system." #endif - -#include "base.h" -#include "util.h" -#include "unicode.h" +#include <wctype.h> // on 16-bit systems, this is 16383. on 32/64-bit systems, this is 1073741823 // it is unusual to have a string that long. |