From b847c7208f15e40999118443d5a808dfe389a8fb Mon Sep 17 00:00:00 2001 From: pommicket Date: Sat, 5 Aug 2023 18:03:55 -0400 Subject: separate ted-internal.h --- buffer.c | 2 +- build.c | 2 +- colors.c | 2 +- command.c | 2 +- config.c | 2 +- find.c | 2 +- gl.c | 2 +- ide-autocomplete.c | 2 +- ide-definitions.c | 2 +- ide-document-link.c | 2 +- ide-highlights.c | 2 +- ide-hover.c | 2 +- ide-rename-symbol.c | 2 +- ide-signature-help.c | 2 +- ide-usages.c | 2 +- macro.c | 2 +- main.c | 2 +- menu.c | 2 +- node.c | 2 +- session.c | 2 +- syntax.c | 2 +- tags.c | 2 +- ted-internal.h | 788 +++++++++++++++++++++++++++++++++++++++++++++++++ ted.c | 26 +- ted.h | 809 +-------------------------------------------------- text.c | 2 +- ui.c | 2 +- 27 files changed, 829 insertions(+), 842 deletions(-) create mode 100644 ted-internal.h diff --git a/buffer.c b/buffer.c index 097e4ed..bf54386 100644 --- a/buffer.c +++ b/buffer.c @@ -2,7 +2,7 @@ // NOTE: All text editing should be done through the two functions // buffer_insert_text_at_pos and buffer_delete_chars_at_pos -#include "ted.h" +#include "ted-internal.h" #include diff --git a/build.c b/build.c index 5e95b32..c37e2f5 100644 --- a/build.c +++ b/build.c @@ -1,7 +1,7 @@ // :build command // also handles :shell. -#include "ted.h" +#include "ted-internal.h" void build_stop(Ted *ted) { if (ted->building) diff --git a/colors.c b/colors.c index 7157dae..e626b0f 100644 --- a/colors.c +++ b/colors.c @@ -1,6 +1,6 @@ // color names and functions for dealing with colors -#include "ted.h" +#include "ted-internal.h" typedef struct { ColorSetting setting; diff --git a/command.c b/command.c index 8611f3e..9abdcbf 100644 --- a/command.c +++ b/command.c @@ -1,7 +1,7 @@ // the main highlight here is command_execute, which // determines what to do when a command is executed. -#include "ted.h" +#include "ted-internal.h" typedef struct { const char *name; diff --git a/config.c b/config.c index 8198485..192549b 100644 --- a/config.c +++ b/config.c @@ -7,7 +7,7 @@ // [section2] // asdf = 123 -#include "ted.h" +#include "ted-internal.h" /// Sections of `ted.cfg` typedef enum { diff --git a/find.c b/find.c index 34bc354..d6eb406 100644 --- a/find.c +++ b/find.c @@ -1,6 +1,6 @@ // handles ted's find and replace menu. -#include "ted.h" +#include "ted-internal.h" #include "pcre-inc.h" #define FIND_MAX_GROUPS 50 diff --git a/gl.c b/gl.c index c1ec812..64c3220 100644 --- a/gl.c +++ b/gl.c @@ -1,7 +1,7 @@ // various functions for dealing with OpenGL. // also houses all of the basic rendering functions ted uses. -#include "ted.h" +#include "ted-internal.h" #include "lib/glcorearb.h" float gl_window_width, gl_window_height; diff --git a/ide-autocomplete.c b/ide-autocomplete.c index 6b98526..ec6c83f 100644 --- a/ide-autocomplete.c +++ b/ide-autocomplete.c @@ -1,6 +1,6 @@ // auto-completion for ted -#include "ted.h" +#include "ted-internal.h" #define TAGS_MAX_COMPLETIONS 200 // max # of tag completions to scroll through #define AUTOCOMPLETE_NCOMPLETIONS_VISIBLE 10 // max # of completions to show at once diff --git a/ide-definitions.c b/ide-definitions.c index f5e37b6..c37ae69 100644 --- a/ide-definitions.c +++ b/ide-definitions.c @@ -1,7 +1,7 @@ // this file deals with ctrl+click "go to definition", and // the definitions menu (Ctrl+D) -#include "ted.h" +#include "ted-internal.h" void definition_cancel_lookup(Ted *ted) { Definitions *defs = &ted->definitions; diff --git a/ide-document-link.c b/ide-document-link.c index 9325c21..5de107f 100644 --- a/ide-document-link.c +++ b/ide-document-link.c @@ -1,4 +1,4 @@ -#include "ted.h" +#include "ted-internal.h" typedef struct DocumentLink DocumentLink; struct DocumentLink { diff --git a/ide-highlights.c b/ide-highlights.c index ae79053..32874a1 100644 --- a/ide-highlights.c +++ b/ide-highlights.c @@ -1,6 +1,6 @@ // highlight uses of identifier (LSP request textDocument/highlight) -#include "ted.h" +#include "ted-internal.h" struct Highlights { LSPServerRequestID last_request; diff --git a/ide-hover.c b/ide-hover.c index 91eb185..af70544 100644 --- a/ide-hover.c +++ b/ide-hover.c @@ -1,6 +1,6 @@ // LSP hover information (textDocument/hover request) -#include "ted.h" +#include "ted-internal.h" struct Hover { LSPServerRequestID last_request; diff --git a/ide-rename-symbol.c b/ide-rename-symbol.c index cef2005..2d95df8 100644 --- a/ide-rename-symbol.c +++ b/ide-rename-symbol.c @@ -1,4 +1,4 @@ -#include "ted.h" +#include "ted-internal.h" struct RenameSymbol { LSPServerRequestID request_id; diff --git a/ide-signature-help.c b/ide-signature-help.c index a18eea0..ba6f216 100644 --- a/ide-signature-help.c +++ b/ide-signature-help.c @@ -1,7 +1,7 @@ // deals with textDocument/signatureHelp LSP requests // this is the little thing which shows you the signature of the function and the current argument -#include "ted.h" +#include "ted-internal.h" /// a single signature in the signature help. typedef struct { diff --git a/ide-usages.c b/ide-usages.c index 0547b5c..ff393f1 100644 --- a/ide-usages.c +++ b/ide-usages.c @@ -1,6 +1,6 @@ // find usages of symbol -#include "ted.h" +#include "ted-internal.h" struct Usages { LSPServerRequestID last_request; double last_request_time; diff --git a/macro.c b/macro.c index 19ebcd8..75c2eae 100644 --- a/macro.c +++ b/macro.c @@ -1,4 +1,4 @@ -#include "ted.h" +#include "ted-internal.h" static void macro_clear(Macro *macro) { arr_foreach_ptr(macro->actions, Action, act) { diff --git a/main.c b/main.c index 9dbb713..03820a5 100644 --- a/main.c +++ b/main.c @@ -21,7 +21,7 @@ FUTURE FEATURES: - LSP request timeout */ -#include "ted.h" +#include "ted-internal.h" #include #include diff --git a/menu.c b/menu.c index 70d9e3a..f4759d6 100644 --- a/menu.c +++ b/menu.c @@ -1,6 +1,6 @@ // deals with all of ted's menus ("open" menu, "save as" menu, etc.) -#include "ted.h" +#include "ted-internal.h" static void menu_close_with_next(Ted *ted, Menu next) { ted_switch_to_buffer(ted, ted->prev_active_buffer); diff --git a/node.c b/node.c index 7d8a71a..e4fd043 100644 --- a/node.c +++ b/node.c @@ -1,6 +1,6 @@ // deals with ted's split-screen feature -#include "ted.h" +#include "ted-internal.h" void node_switch_to_tab(Ted *ted, Node *node, u16 new_tab_index) { node->active_tab = new_tab_index; diff --git a/session.c b/session.c index af55eba..7ba41bd 100644 --- a/session.c +++ b/session.c @@ -1,4 +1,4 @@ -#include "ted.h" +#include "ted-internal.h" #define SESSION_FILENAME "session.txt" #define SESSION_VERSION "\x7fTED0002" diff --git a/syntax.c b/syntax.c index fa539f1..be6ebbf 100644 --- a/syntax.c +++ b/syntax.c @@ -1,6 +1,6 @@ // syntax highlighting for ted -#include "ted.h" +#include "ted-internal.h" #include "keywords.h" diff --git a/tags.c b/tags.c index 95f5e5b..fa87c49 100644 --- a/tags.c +++ b/tags.c @@ -1,6 +1,6 @@ // support for ctags go-to-definition and completion -#include "ted.h" +#include "ted-internal.h" #include "pcre-inc.h" static const char *tags_filename(Ted *ted, bool error_if_does_not_exist) { diff --git a/ted-internal.h b/ted-internal.h new file mode 100644 index 0000000..2761557 --- /dev/null +++ b/ted-internal.h @@ -0,0 +1,788 @@ +/// \file +/// +/// this file is included by (almost) all of ted's source files. +/// it may change arbitrarily so plugin authors should not include it! + +#ifndef TED_INTERNAL_H_ +#define TED_INTERNAL_H_ + +#include "ted.h" +#include "lsp.h" +#include "os.h" +#include "unicode.h" +#include "ds.h" +#include "sdl-inc.h" +#include "lib/glcorearb.h" + +/// Minimum text size +#define TEXT_SIZE_MIN 6 +/// Maximum text size +#define TEXT_SIZE_MAX 70 +/// max number of LSPs running at once +#define TED_LSP_MAX 200 +/// max number of macros +#define TED_MACRO_MAX 256 + +/// Version string +#define TED_VERSION_FULL "ted v. " TED_VERSION + +typedef struct { + const char *string; + i64 number; +} CommandArgument; + +typedef struct { + // did this command come from executing a macro? + bool running_macro; +} CommandContext; + +/// Thing to do when a key combo is pressed. +typedef struct { + KeyCombo key_combo; + Command command; + 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 { + /// The settings apply to this language. + Language language; + /// The settings apply to all paths which start with this string, or all paths if path=NULL + char *path; +} SettingsContext; + +/// Need to use reference counting for textures because of Settings: +/// We copy parent settings to children +/// e.g. +/// ``` +/// [core] +/// bg-texture = "blablabla.png" +/// [Javascript.core] +/// some random shit +/// ``` +/// the main Settings' bg_texture will get copied to javascript's Settings, +/// so we need to be extra careful about when we delete textures. +typedef struct { + u32 ref_count; + GLuint texture; +} GlRcTexture; + +/// Reference-counted shader-array-buffer combo. +typedef struct { + u32 ref_count; + GLuint shader; + GLuint array; + GLuint buffer; +} GlRcSAB; + +typedef struct { + Language language; + char extension[16]; +} LanguageExtension; + +/// All of ted's settings +/// +/// NOTE: to add more options to ted, add fields here, +/// and change the settings_ global constant near the top of config.c +struct Settings { + SettingsContext context; + u32 colors[COLOR_COUNT]; + float cursor_blink_time_on, cursor_blink_time_off; + float hover_time; + float ctrl_scroll_adjust_text_size; + u32 max_file_size; + u32 max_file_size_view_only; + u16 framerate_cap; + u16 text_size_no_dpi; + u16 text_size; + u16 max_menu_width; + u16 error_display_time; + bool auto_indent; + bool auto_add_newline; + bool syntax_highlighting; + bool line_numbers; + bool auto_reload; + bool auto_reload_config; + bool restore_session; + bool regenerate_tags_if_not_found; + bool indent_with_spaces; + bool phantom_completions; + bool trigger_characters; + bool identifier_trigger_characters; + bool signature_help_enabled; + bool lsp_enabled; + bool lsp_log; + bool hover_enabled; + bool highlight_enabled; + bool highlight_auto; + bool document_links; + bool vsync; + bool save_backup; + bool crlf_windows; + bool jump_to_build_error; + bool force_monospace; + KeyCombo hover_key; + KeyCombo highlight_key; + u8 tab_width; + u8 cursor_width; + u8 undo_save_time; + u8 border_thickness; + u8 padding; + u8 scrolloff; + u8 tags_max_depth; + GlRcSAB *bg_shader; + GlRcTexture *bg_texture; + /// string used to start comments + char comment_start[16]; + /// string used to end comments + char comment_end[16]; + /// Comma-separated list of file names which identify the project root + char root_identifiers[4096]; + /// LSP server command + char lsp[512]; + /// LSP "configuration" JSON + char lsp_configuration[4096]; + /// Build command. If non-empty, this overrides running `cargo build` if `Cargo.toml` exists, etc. + char build_command[1024]; + /// Default build command for if `Cargo.toml`, `Makefile`, etc. do not exist. + char build_default_command[1024]; + /// Comma separated list of paths to font files. + char font[4096]; + /// Comma separated list of paths to bold font files. + char font_bold[4096]; + LanguageExtension *language_extensions; + /// dynamic array, sorted by KEY_COMBO(modifier, key) + KeyAction *key_actions; +}; + +/// A single line in a buffer +typedef struct Line Line; + +/// This structure is used temporarily when loading settings +/// It's needed because we want more specific contexts to be dealt with last. +typedef struct ConfigPart ConfigPart; + +/// A single undoable edit to a buffer +typedef struct BufferEdit BufferEdit; + +struct TextBuffer { + /// NULL if this buffer is untitled or doesn't correspond to a file (e.g. line buffers) + char *path; + /// we keep a back-pointer to the ted instance so we don't have to pass it in to every buffer function + struct Ted *ted; + /// number of characters scrolled in the x direction (multiply by space width to get pixels) + double scroll_x; + /// number of characters scrolled in the y direction + double scroll_y; + /// last write time to \ref path + double last_write_time; + /// the language the buffer has been manually set to, or \ref LANG_NONE if it hasn't been set to anything + i64 manual_language; + /// position of cursor + BufferPos cursor_pos; + /// if \ref selection is true, the text between \ref selection_pos and \ref cursor_pos is selected. + BufferPos selection_pos; + /// "previous" position of cursor, for \ref CMD_PREVIOUS_POSITION + BufferPos prev_cursor_pos; + /// "line buffers" are buffers which can only have one line of text (used for inputs) + bool is_line_buffer; + /// is anything selected? + bool selection; + /// set to false to disable undo events + bool store_undo_events; + /// This is set to true whenever a change is made to the buffer, and never set to false by buffer_ functions. + /// (Distinct from \ref buffer_unsaved_changes) + bool modified; + /// will the next undo event be chained with the ones after? + bool will_chain_edits; + /// will the next undo event be chained with the previous one? + bool chaining_edits; + /// view-only mode + bool view_only; + /// (line buffers only) set to true when submitted. you have to reset it to false. + bool line_buffer_submitted; + /// If set to true, buffer will be scrolled to the cursor position next frame. + /// This is to fix the problem that \ref x1, \ref y1, \ref x2, \ref y2 are not updated until the buffer is rendered. + bool center_cursor_next_frame; + /// x coordinate of left side of buffer + float x1; + /// y coordinate of top side of buffer + float y1; + /// x coordinate of right side of buffer + float x2; + /// y coordinate of bottom side of buffer + float y2; + /// number of lines in buffer + u32 nlines; + /// capacity of \ref lines + u32 lines_capacity; + + /// cached settings index (into ted->all_settings), or -1 if has not been computed yet + i32 settings_idx; + + /// which LSP this document is open in + LSPID lsp_opened_in; + /// determining which LSP to use for a buffer takes some work, + /// so we don't want to do it every single frame. + /// this keeps track of the last time we actually checked what the correct LSP is. + double last_lsp_check; + + /// where in the undo history was the last write? used by \ref buffer_unsaved_changes + u32 undo_history_write_pos; + /// which lines are on screen? updated when \ref buffer_render is called. + u32 first_line_on_screen, last_line_on_screen; + + /// to cache syntax highlighting properly, it is important to keep track of the + /// first and last line modified since last frame. + u32 frame_earliest_line_modified; + /// see \ref frame_earliest_line_modified. + u32 frame_latest_line_modified; + + /// lines + Line *lines; + /// last error + char error[256]; + /// dynamic array of undo history + BufferEdit *undo_history; + /// dynamic array of redo history + BufferEdit *redo_history; +}; + +/// an entry in a selector menu (e.g. the "open" menu) +typedef struct { + /// label + const char *name; + /// if not NULL, this will show on the right side of the entry. + const char *detail; + /// color to draw text in + u32 color; + /// use this for whatever you want + u64 userdata; +} SelectorEntry; + +struct Selector { + SelectorEntry *entries; + u32 n_entries; + Rect bounds; + /// index where the selector thing is + u32 cursor; + float scroll; + /// whether or not we should let the user select entries using a cursor. + bool enable_cursor; +}; + +/// file entries for file selectors +typedef struct { + /// just the file name + char *name; + /// full path + char *path; + FsType type; +} FileEntry; + +struct FileSelector { + Selector sel; + Rect bounds; + u32 n_entries; + FileEntry *entries; + char cwd[TED_PATH_MAX]; + /// indicates that this is for creating files, not opening files + bool create_menu; +}; + +// A node is a collection of tabs OR a split of two node +struct Node { + /// dynamic array of indices into ted->buffers, or `NULL` if this is a split + u16 *tabs; + /// number from 0 to 1 indicating where the split is. + float split_pos; + /// index of active tab in `tabs`. + u16 active_tab; + /// is the split vertical? if false, this split looks like a|b + bool split_vertical; + /// split left/upper half; index into `ted->nodes` + u16 split_a; + /// split right/lower half + u16 split_b; +}; + +/// max number of buffers open at one time +#define TED_MAX_BUFFERS 256 +/// max number of nodes open at one time +#define TED_MAX_NODES 256 +/// max tabs per node +#define TED_MAX_TABS 100 +/// max strings in all config files +#define TED_MAX_STRINGS 1000 + +/// "find" menu result +typedef struct FindResult FindResult; + +typedef struct { + char *path; + 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; + +/// `LSPSymbolKind`s are translated to these. this is a much coarser categorization +typedef enum { + SYMBOL_OTHER, + SYMBOL_FUNCTION, + SYMBOL_FIELD, + SYMBOL_TYPE, + SYMBOL_VARIABLE, + SYMBOL_CONSTANT, + SYMBOL_KEYWORD +} SymbolKind; + +/// data needed for autocompletion +typedef struct Autocomplete Autocomplete; + +/// data needed for finding usages +typedef struct Usages Usages; + +/// max number of signatures to display at a time. +#define SIGNATURE_HELP_MAX 5 + +/// "signature help" (LSP) is thing that shows the current parameter, etc. +typedef struct SignatureHelp SignatureHelp; + + +/// "document link" information (LSP) +typedef struct DocumentLinks DocumentLinks; + +/// information for symbol rename (LSP) +typedef struct RenameSymbol RenameSymbol; + +/// "hover" information from LSP server +typedef struct Hover Hover; + +/// symbol information for the definitions menu +typedef struct { + char *name; + char *detail; + u32 color; + /// is this from a LSP server (as opposed to ctags)? + bool from_lsp; + /// only set if `from_lsp = true` + LSPDocumentPosition position; +} SymbolInfo; + +typedef struct { + LSPServerRequestID last_request; + double last_request_time; + /// last query string which we sent a request for + char *last_request_query; + /// for "go to definition of..." menu + Selector selector; + /// an array of all definitions (gotten from workspace/symbols) for "go to definition" menu + SymbolInfo *all_definitions; +} Definitions; + +/// "highlight" information from LSP server +typedef struct Highlights Highlights; + +typedef struct { + Command command; + CommandArgument argument; +} Action; + +typedef struct { + // dynamic array + Action *actions; +} Macro; + +typedef struct { + char *path; + Font *font; +} LoadedFont; + +typedef struct { + vec2 pos; + u8 times; +} MouseClick; + +typedef struct { + vec2 pos; +} MouseRelease; + +struct Ted { + /// all running LSP servers + LSP *lsps[TED_LSP_MAX + 1]; + /// 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; + LoadedFont *all_fonts; + Font *font_bold; + Font *font; + TextBuffer *active_buffer; + /// buffer we are currently drag-to-selecting in, if any + TextBuffer *drag_buffer; + /// while a menu or something is open, there is no active buffer. when the menu is closed, + /// the old active buffer needs to be restored. that's what this stores. + TextBuffer *prev_active_buffer; + Node *active_node; + /// dynamic array of Settings. use Settings.context to figure out which one to use. + Settings *all_settings; + /// settings to use when no buffer is open + Settings *default_settings; + float window_width, window_height; + vec2 mouse_pos; + u32 mouse_state; + /// `mouse_clicks[SDL_BUTTON_RIGHT]`, for example, is all the right mouse-clicks that have happened this frame + MouseClick *mouse_clicks[4]; + MouseRelease *mouse_releases[4]; + /// total amount scrolled this frame + int scroll_total_x, scroll_total_y; + /// currently open menu, or \ref MENU_NONE if no menu is open. + Menu menu; + FileSelector file_selector; + Selector command_selector; + /// general-purpose line buffer for inputs -- used for menus + TextBuffer line_buffer; + /// use for "find" term in find/find+replace + TextBuffer find_buffer; + /// "replace" for find+replace + TextBuffer replace_buffer; + /// buffer for build output (view only) + TextBuffer build_buffer; + /// used for command selector + TextBuffer argument_buffer; + /// time which the cursor error animation started (cursor turns red, e.g. when there's no autocomplete suggestion) + double cursor_error_time; + /// should start_cwd be searched for files? set to true if the executable isn't "installed" + bool search_start_cwd; + /// CWD `ted` was started in + char start_cwd[TED_PATH_MAX]; + /// if set to true, the window will close next frame. NOTE: this doesn't check for unsaved changes!! + bool quit; + /// is the find or find+replace menu open? + bool find; + /// is the find+replace menu open? + bool replace; + /// find options + bool find_regex, find_case_sensitive; + /// flags used last time search term was compiled + u32 find_flags; + struct pcre2_real_code_32 *find_code; + struct pcre2_real_match_data_32 *find_match_data; + FindResult *find_results; + /// invalid regex? + bool find_invalid_pattern; + /// if non-zero, the user is trying to execute this command, but there are unsaved changes + Command warn_unsaved; + /// are we showing the build output? + bool build_shown; + /// is the build process running? + bool building; + Autocomplete *autocomplete; + SignatureHelp *signature_help; + DocumentLinks *document_links; + Hover *hover; + Definitions definitions; + Highlights *highlights; + Usages *usages; + RenameSymbol *rename_symbol; + + FILE *log; + + /// dynamic array of build errors + BuildError *build_errors; + /// build error we are currently "on" + u32 build_error; + + /// used by menus to keep track of the scroll position so we can return to it. + vec2d prev_active_buffer_scroll; + + SDL_Cursor *cursor_arrow, *cursor_ibeam, *cursor_wait, + *cursor_resize_h, *cursor_resize_v, *cursor_hand, *cursor_move; + /// which cursor to use this frame + /// this should be set to one of the cursor_* members above, or NULL for no cursor + SDL_Cursor *cursor; + + /// node containing tab user is dragging around, NULL if user is not dragging a tab + Node *dragging_tab_node; + /// index in dragging_tab_node->tabs + u16 dragging_tab_idx; + /// where the tab is being dragged from (i.e. mouse pos at start of drag action) + vec2 dragging_tab_origin; + + /// if not `NULL`, points to the node whose split the user is currently resizing. + Node *resizing_split; + + /// dynamic array of history of commands run with :shell (UTF-8) + char **shell_history; + /// for keeping track of where we are in the shell history. + u32 shell_history_pos; + + // points to a selector if any is open, otherwise NULL. + Selector *selector_open; + + /// what % of the screen the build output takes up + float build_output_height; + bool resizing_build_output; + + /// last time a save command was executed. used for bg-shaders. + double last_save_time; + + 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]; + /// allows execution of multiple commands -- needed for tags generation + char **build_queue; + /// comma-separated list of files with unsaved changes (only applicable if warn_unsaved != 0) + char warn_unsaved_names[TED_PATH_MAX]; + /// file name user is trying to overwrite + char warn_overwrite[TED_PATH_MAX]; + /// file name which we want to reload + char ask_reload[TED_PATH_MAX]; + char local_data_dir[TED_PATH_MAX]; + char global_data_dir[TED_PATH_MAX]; + /// home directory + char home[TED_PATH_MAX]; + /// current working directory + char cwd[TED_PATH_MAX]; + /// directory where we run the build command + char build_dir[TED_PATH_MAX]; + /// where we are reading tags from + char tags_dir[TED_PATH_MAX]; + bool nodes_used[TED_MAX_NODES]; + /// `nodes[0]` is always the "root node", if any buffers are open. + Node nodes[TED_MAX_NODES]; + /// NOTE: the buffer at index 0 is reserved as a "null buffer" and should not be used. + bool buffers_used[TED_MAX_BUFFERS]; + TextBuffer buffers[TED_MAX_BUFFERS]; + /// number of config file strings + u32 nstrings; + /// config file strings + char *strings[TED_MAX_STRINGS]; + char window_title[256]; + + /// little box used to display errors and info. + char message[512]; + /// time message box was opened + double message_time; + MessageType message_type; + MessageType message_shown_type; + char message_shown[512]; +}; + +// === buffer.c === +/// Get the LSPDocumentID corresponding to the file this buffer contains. +/// The return value is only useful if `buffer_lsp(buffer) != NULL`. +LSPDocumentID buffer_lsp_document_id(TextBuffer *buffer); +/// Get LSPPosition corresponding to position in buffer. +LSPPosition buffer_pos_to_lsp_position(TextBuffer *buffer, BufferPos pos); +/// Get LSPDocumentPosition corresponding to position in buffer. +LSPDocumentPosition buffer_pos_to_lsp_document_position(TextBuffer *buffer, BufferPos pos); +/// Convert LSPPosition to BufferPos. +BufferPos buffer_pos_from_lsp(TextBuffer *buffer, LSPPosition lsp_pos); +/// Get the cursor position as an LSPPosition. +LSPPosition buffer_cursor_pos_as_lsp_position(TextBuffer *buffer); +/// Get the cursor position as an LSPDocumentPosition. +LSPDocumentPosition buffer_cursor_pos_as_lsp_document_position(TextBuffer *buffer); +/// highlight an \ref LSPRange in this buffer. +/// +/// make sure to call \ref gl_geometry_draw after this +void buffer_highlight_lsp_range(TextBuffer *buffer, LSPRange range, ColorSetting color); + +// === colors.c ==== +void color_init(void); +/// which color setting should be used for the given symbol kind. +/// this is the color used in the autocomplete selector, for example. +ColorSetting color_for_symbol_kind(SymbolKind kind); + +// === command.c === +void command_init(void); +void command_execute_ex(Ted *ted, Command c, const CommandArgument *argument, const CommandContext *context); + +// === config.c === +/// first, we read all config files, then we parse them. +/// this is because we want less specific settings (e.g. settings applied +/// to all languages instead of one particular language) to be applied first, +/// then more specific settings are based off of those. +/// +/// EXAMPLE: +/// ``` +/// ---config file 1--- +/// [Javascript.core] +/// syntax-highlighting = off +/// (inherits tab-width = 4) +/// [CSS.core] +/// tab-width = 2 (overrides tab-width = 4) +/// ---config file 2--- +/// [core] +/// tab-width = 4 +/// ``` +void config_read(Ted *ted, ConfigPart **pparts, const char *filename); +void config_parse(Ted *ted, ConfigPart **pparts); +void config_free(Ted *ted); +/// how well does this settings context fit the given path and language? +/// the context with the highest score will be chosen. +long context_score(const char *path, Language lang, const SettingsContext *context); + +// === gl.c === +/// set by main() +extern float gl_window_width, gl_window_height; +/// set by main() +extern int gl_version_major, gl_version_minor; +/// macro trickery to avoid having to write every GL function multiple times +#define gl_for_each_proc(do)\ + do(DRAWARRAYS, DrawArrays)\ + do(GENTEXTURES, GenTextures)\ + do(DELETETEXTURES, DeleteTextures)\ + do(GENERATEMIPMAP, GenerateMipmap)\ + do(TEXIMAGE2D, TexImage2D)\ + do(BINDTEXTURE, BindTexture)\ + do(TEXPARAMETERI, TexParameteri)\ + do(GETERROR, GetError)\ + do(GETINTEGERV, GetIntegerv)\ + do(ENABLE, Enable)\ + do(DISABLE, Disable)\ + do(BLENDFUNC, BlendFunc)\ + do(VIEWPORT, Viewport)\ + do(CLEARCOLOR, ClearColor)\ + do(CLEAR, Clear)\ + do(FINISH, Finish)\ + do(CREATESHADER, CreateShader)\ + do(DELETESHADER, DeleteShader)\ + do(CREATEPROGRAM, CreateProgram)\ + do(SHADERSOURCE, ShaderSource)\ + do(GETSHADERIV, GetShaderiv)\ + do(GETSHADERINFOLOG, GetShaderInfoLog)\ + do(COMPILESHADER, CompileShader)\ + do(CREATEPROGRAM, CreateProgram)\ + do(DELETEPROGRAM, DeleteProgram)\ + do(ATTACHSHADER, AttachShader)\ + do(LINKPROGRAM, LinkProgram)\ + do(GETPROGRAMIV, GetProgramiv)\ + do(GETPROGRAMINFOLOG, GetProgramInfoLog)\ + do(USEPROGRAM, UseProgram)\ + do(GETATTRIBLOCATION, GetAttribLocation)\ + do(GETUNIFORMLOCATION, GetUniformLocation)\ + do(GENBUFFERS, GenBuffers)\ + do(DELETEBUFFERS, DeleteBuffers)\ + do(BINDBUFFER, BindBuffer)\ + do(BUFFERDATA, BufferData)\ + do(VERTEXATTRIBPOINTER, VertexAttribPointer)\ + do(ENABLEVERTEXATTRIBARRAY, EnableVertexAttribArray)\ + do(DISABLEVERTEXATTRIBARRAY, DisableVertexAttribArray)\ + do(GENVERTEXARRAYS, GenVertexArrays)\ + do(DELETEVERTEXARRAYS, DeleteVertexArrays)\ + do(BINDVERTEXARRAY, BindVertexArray)\ + do(ACTIVETEXTURE, ActiveTexture)\ + do(UNIFORM1F, Uniform1f)\ + do(UNIFORM2F, Uniform2f)\ + do(UNIFORM3F, Uniform3f)\ + do(UNIFORM4F, Uniform4f)\ + do(UNIFORM1I, Uniform1i)\ + do(UNIFORM2I, Uniform2i)\ + do(UNIFORM3I, Uniform3i)\ + do(UNIFORM4I, Uniform4i)\ + do(UNIFORMMATRIX4FV, UniformMatrix4fv)\ + do(DEBUGMESSAGECALLBACK, DebugMessageCallback)\ + do(DEBUGMESSAGECONTROL, DebugMessageControl)\ + +#define gl_declare_proc(upper, lower) extern PFNGL##upper##PROC gl##lower; +gl_for_each_proc(gl_declare_proc) +#undef gl_declare_proc +/// get addresses of GL functions +void gl_get_procs(void); +/// create a new reference-counted shader-array-buffer object. +GlRcSAB *gl_rc_sab_new(GLuint shader, GLuint array, GLuint buffer); +/// increase reference count on `s`. +void gl_rc_sab_incref(GlRcSAB *s); +/// decrease reference count on `*ps`, and set `*ps` to NULL if the reference count is 0. +void gl_rc_sab_decref(GlRcSAB **ps); +/// create a new reference-counted texture. +GlRcTexture *gl_rc_texture_new(GLuint texture); +/// increase reference count on `t`. +void gl_rc_texture_incref(GlRcTexture *t); +/// decrease reference count on `*t`, and set `*t` to NULL if the reference count is 0. +void gl_rc_texture_decref(GlRcTexture **pt); +/// initialize geometry stuff +void gl_geometry_init(void); + +// === ide-autocomplete.c === +void autocomplete_init(Ted *ted); +void autocomplete_quit(Ted *ted); +void autocomplete_process_lsp_response(Ted *ted, const LSPResponse *response); + +// === ide-definitions.c === +/// go to the definition of `name`. +/// if `lsp` is NULL, tags will be used. +/// 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, GotoType type); +void definitions_process_lsp_response(Ted *ted, LSP *lsp, const LSPResponse *response); + +// === ide-document-link.c === +void document_link_init(Ted *ted); +void document_link_quit(Ted *ted); +void document_link_frame(Ted *ted); +void document_link_process_lsp_response(Ted *ted, const LSPResponse *response); + +// === ide-highlights.c === +void highlights_init(Ted *ted); +void highlights_quit(Ted *ted); +void highlights_frame(Ted *ted); +void highlights_process_lsp_response(Ted *ted, const LSPResponse *response); + +// === ide-hover.c === +void hover_init(Ted *ted); +void hover_frame(Ted *ted, double dt); +void hover_process_lsp_response(Ted *ted, const LSPResponse *response); +void hover_quit(Ted *ted); + +// === ide-rename-symbol.c === +void rename_symbol_init(Ted *ted); +void rename_symbol_quit(Ted *ted); +void rename_symbol_frame(Ted *ted); +void rename_symbol_process_lsp_response(Ted *ted, const LSPResponse *response); + +// === ide-signature-help.c === +void signature_help_init(Ted *ted); +void signature_help_quit(Ted *ted); +void signature_help_frame(Ted *ted); +void signature_help_process_lsp_response(Ted *ted, const LSPResponse *response); + +// === ide-usages.c === +void usages_init(Ted *ted); +void usages_process_lsp_response(Ted *ted, const LSPResponse *response); +void usages_frame(Ted *ted); +void usages_quit(Ted *ted); + +// === macro.c === +void macro_add(Ted *ted, Command command, const CommandArgument *argument); + +// === tags.c === +/// get all tags in the tags file as SymbolInfos. +SymbolInfo *tags_get_symbols(Ted *ted); + +// === ted.c === +/// Get LSP by ID. Returns NULL if there is no LSP with that ID. +LSP *ted_get_lsp_by_id(Ted *ted, LSPID id); +/// go to this LSP document position, opening a new buffer containing the file if necessary. +void ted_go_to_lsp_document_position(Ted *ted, LSP *lsp, LSPDocumentPosition position); +/// cancel this LSP request. also zeroes *request +/// if *request is zeroed, this does nothing. +void ted_cancel_lsp_request(Ted *ted, LSPServerRequestID *request); +/// convert LSPWindowMessageType to MessageType +MessageType ted_message_type_from_lsp(LSPWindowMessageType type); + +#endif // TED_INTERNAL_H_ diff --git a/ted.c b/ted.c index 7d51e04..c623f0a 100644 --- a/ted.c +++ b/ted.c @@ -1,6 +1,6 @@ // various core ted functions (opening files, displaying errors, etc.) -#include "ted.h" +#include "ted-internal.h" #if _WIN32 #include #endif @@ -763,20 +763,12 @@ void ted_flash_error_cursor(Ted *ted) { ted->cursor_error_time = ted->frame_time; } -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) { + if (!lsp) lsp = ted_active_lsp(ted); + const char *path = lsp_document_path(lsp, position.document); if (ted_open_file(ted, path)) { TextBuffer *buffer = ted->active_buffer; - BufferPos pos = {0}; - if (is_lsp) { - LSPPosition lsp_pos = { - .line = line, - .character = index - }; - pos = buffer_pos_from_lsp(buffer, lsp_pos); - } else { - pos.line = line; - pos.index = index; - } + BufferPos pos = buffer_pos_from_lsp(buffer, position.pos); buffer_cursor_move_to_pos(buffer, pos); buffer->center_cursor_next_frame = true; } else { @@ -784,14 +776,6 @@ void ted_go_to_position(Ted *ted, const char *path, u32 line, u32 index, bool is } } -void ted_go_to_lsp_document_position(Ted *ted, LSP *lsp, LSPDocumentPosition position) { - if (!lsp) lsp = ted_active_lsp(ted); - const char *path = lsp_document_path(lsp, position.document); - u32 line = position.pos.line; - u32 character = position.pos.character; - ted_go_to_position(ted, path, line, character, true); -} - void ted_cancel_lsp_request(Ted *ted, LSPServerRequestID *request) { if (!request) return; LSP *lsp_obj = ted_get_lsp_by_id(ted, request->lsp); diff --git a/ted.h b/ted.h index 2e07c18..53a2cb4 100644 --- a/ted.h +++ b/ted.h @@ -11,44 +11,23 @@ #ifndef TED_H_ #define TED_H_ -#ifdef TED_PLUGIN -#undef TED_PLUGIN -#define TED_PLUGIN 1 -#endif - #ifdef __cplusplus extern "C" { #endif #include "base.h" #include "util.h" -#include "os.h" -#include "unicode.h" -#include "ds.h" -#include "lsp.h" #include "text.h" #include "colors.h" #include "command.h" -#include "lib/glcorearb.h" -#include "sdl-inc.h" /// Version number #define TED_VERSION "2.4.3" -/// Version string -#define TED_VERSION_FULL "ted v. " TED_VERSION /// Maximum path size ted handles. #define TED_PATH_MAX 1024 /// Config filename #define TED_CFG "ted.cfg" -/// Minimum text size -#define TEXT_SIZE_MIN 6 -/// Maximum text size -#define TEXT_SIZE_MAX 70 -/// max number of LSPs running at once -#define TED_LSP_MAX 200 -/// max number of macros -#define TED_MACRO_MAX 256 // 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! @@ -284,568 +263,6 @@ typedef enum { MENU_RENAME_SYMBOL, } Menu; - - -#if !TED_PLUGIN - -typedef struct { - const char *string; - i64 number; -} CommandArgument; - -typedef struct { - // did this command come from executing a macro? - bool running_macro; -} CommandContext; - -/// Thing to do when a key combo is pressed. -typedef struct { - KeyCombo key_combo; - Command command; - 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 { - /// The settings apply to this language. - Language language; - /// The settings apply to all paths which start with this string, or all paths if path=NULL - char *path; -} SettingsContext; - -/// Need to use reference counting for textures because of Settings: -/// We copy parent settings to children -/// e.g. -/// ``` -/// [core] -/// bg-texture = "blablabla.png" -/// [Javascript.core] -/// some random shit -/// ``` -/// the main Settings' bg_texture will get copied to javascript's Settings, -/// so we need to be extra careful about when we delete textures. -typedef struct { - u32 ref_count; - GLuint texture; -} GlRcTexture; - -/// Reference-counted shader-array-buffer combo. -typedef struct { - u32 ref_count; - GLuint shader; - GLuint array; - GLuint buffer; -} GlRcSAB; - -typedef struct { - Language language; - char extension[16]; -} LanguageExtension; - -/// All of ted's settings -/// -/// NOTE: to add more options to ted, add fields here, -/// and change the settings_ global constant near the top of config.c -struct Settings { - SettingsContext context; - u32 colors[COLOR_COUNT]; - float cursor_blink_time_on, cursor_blink_time_off; - float hover_time; - float ctrl_scroll_adjust_text_size; - u32 max_file_size; - u32 max_file_size_view_only; - u16 framerate_cap; - u16 text_size_no_dpi; - u16 text_size; - u16 max_menu_width; - u16 error_display_time; - bool auto_indent; - bool auto_add_newline; - bool syntax_highlighting; - bool line_numbers; - bool auto_reload; - bool auto_reload_config; - bool restore_session; - bool regenerate_tags_if_not_found; - bool indent_with_spaces; - bool phantom_completions; - bool trigger_characters; - bool identifier_trigger_characters; - bool signature_help_enabled; - bool lsp_enabled; - bool lsp_log; - bool hover_enabled; - bool highlight_enabled; - bool highlight_auto; - bool document_links; - bool vsync; - bool save_backup; - bool crlf_windows; - bool jump_to_build_error; - bool force_monospace; - KeyCombo hover_key; - KeyCombo highlight_key; - u8 tab_width; - u8 cursor_width; - u8 undo_save_time; - u8 border_thickness; - u8 padding; - u8 scrolloff; - u8 tags_max_depth; - GlRcSAB *bg_shader; - GlRcTexture *bg_texture; - /// string used to start comments - char comment_start[16]; - /// string used to end comments - char comment_end[16]; - /// Comma-separated list of file names which identify the project root - char root_identifiers[4096]; - /// LSP server command - char lsp[512]; - /// LSP "configuration" JSON - char lsp_configuration[4096]; - /// Build command. If non-empty, this overrides running `cargo build` if `Cargo.toml` exists, etc. - char build_command[1024]; - /// Default build command for if `Cargo.toml`, `Makefile`, etc. do not exist. - char build_default_command[1024]; - /// Comma separated list of paths to font files. - char font[4096]; - /// Comma separated list of paths to bold font files. - char font_bold[4096]; - LanguageExtension *language_extensions; - /// dynamic array, sorted by KEY_COMBO(modifier, key) - KeyAction *key_actions; -}; - -/// A single line in a buffer -typedef struct Line Line; - -/// This structure is used temporarily when loading settings -/// It's needed because we want more specific contexts to be dealt with last. -typedef struct ConfigPart ConfigPart; - -/// A single undoable edit to a buffer -typedef struct BufferEdit BufferEdit; - -struct TextBuffer { - /// NULL if this buffer is untitled or doesn't correspond to a file (e.g. line buffers) - char *path; - /// we keep a back-pointer to the ted instance so we don't have to pass it in to every buffer function - struct Ted *ted; - /// number of characters scrolled in the x direction (multiply by space width to get pixels) - double scroll_x; - /// number of characters scrolled in the y direction - double scroll_y; - /// last write time to \ref path - double last_write_time; - /// the language the buffer has been manually set to, or \ref LANG_NONE if it hasn't been set to anything - i64 manual_language; - /// position of cursor - BufferPos cursor_pos; - /// if \ref selection is true, the text between \ref selection_pos and \ref cursor_pos is selected. - BufferPos selection_pos; - /// "previous" position of cursor, for \ref CMD_PREVIOUS_POSITION - BufferPos prev_cursor_pos; - /// "line buffers" are buffers which can only have one line of text (used for inputs) - bool is_line_buffer; - /// is anything selected? - bool selection; - /// set to false to disable undo events - bool store_undo_events; - /// This is set to true whenever a change is made to the buffer, and never set to false by buffer_ functions. - /// (Distinct from \ref buffer_unsaved_changes) - bool modified; - /// will the next undo event be chained with the ones after? - bool will_chain_edits; - /// will the next undo event be chained with the previous one? - bool chaining_edits; - /// view-only mode - bool view_only; - /// (line buffers only) set to true when submitted. you have to reset it to false. - bool line_buffer_submitted; - /// If set to true, buffer will be scrolled to the cursor position next frame. - /// This is to fix the problem that \ref x1, \ref y1, \ref x2, \ref y2 are not updated until the buffer is rendered. - bool center_cursor_next_frame; - /// x coordinate of left side of buffer - float x1; - /// y coordinate of top side of buffer - float y1; - /// x coordinate of right side of buffer - float x2; - /// y coordinate of bottom side of buffer - float y2; - /// number of lines in buffer - u32 nlines; - /// capacity of \ref lines - u32 lines_capacity; - - /// cached settings index (into ted->all_settings), or -1 if has not been computed yet - i32 settings_idx; - - /// which LSP this document is open in - LSPID lsp_opened_in; - /// determining which LSP to use for a buffer takes some work, - /// so we don't want to do it every single frame. - /// this keeps track of the last time we actually checked what the correct LSP is. - double last_lsp_check; - - /// where in the undo history was the last write? used by \ref buffer_unsaved_changes - u32 undo_history_write_pos; - /// which lines are on screen? updated when \ref buffer_render is called. - u32 first_line_on_screen, last_line_on_screen; - - /// to cache syntax highlighting properly, it is important to keep track of the - /// first and last line modified since last frame. - u32 frame_earliest_line_modified; - /// see \ref frame_earliest_line_modified. - u32 frame_latest_line_modified; - - /// lines - Line *lines; - /// last error - char error[256]; - /// dynamic array of undo history - BufferEdit *undo_history; - /// dynamic array of redo history - BufferEdit *redo_history; -}; - -/// an entry in a selector menu (e.g. the "open" menu) -typedef struct { - /// label - const char *name; - /// if not NULL, this will show on the right side of the entry. - const char *detail; - /// color to draw text in - u32 color; - /// use this for whatever you want - u64 userdata; -} SelectorEntry; - -struct Selector { - SelectorEntry *entries; - u32 n_entries; - Rect bounds; - /// index where the selector thing is - u32 cursor; - float scroll; - /// whether or not we should let the user select entries using a cursor. - bool enable_cursor; -}; - -/// file entries for file selectors -typedef struct { - /// just the file name - char *name; - /// full path - char *path; - FsType type; -} FileEntry; - -struct FileSelector { - Selector sel; - Rect bounds; - u32 n_entries; - FileEntry *entries; - char cwd[TED_PATH_MAX]; - /// indicates that this is for creating files, not opening files - bool create_menu; -}; - -// A node is a collection of tabs OR a split of two node -struct Node { - /// dynamic array of indices into ted->buffers, or `NULL` if this is a split - u16 *tabs; - /// number from 0 to 1 indicating where the split is. - float split_pos; - /// index of active tab in `tabs`. - u16 active_tab; - /// is the split vertical? if false, this split looks like a|b - bool split_vertical; - /// split left/upper half; index into `ted->nodes` - u16 split_a; - /// split right/lower half - u16 split_b; -}; - -/// max number of buffers open at one time -#define TED_MAX_BUFFERS 256 -/// max number of nodes open at one time -#define TED_MAX_NODES 256 -/// max tabs per node -#define TED_MAX_TABS 100 -/// max strings in all config files -#define TED_MAX_STRINGS 1000 - -/// "find" menu result -typedef struct FindResult FindResult; - -typedef struct { - char *path; - 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; - -/// `LSPSymbolKind`s are translated to these. this is a much coarser categorization -typedef enum { - SYMBOL_OTHER, - SYMBOL_FUNCTION, - SYMBOL_FIELD, - SYMBOL_TYPE, - SYMBOL_VARIABLE, - SYMBOL_CONSTANT, - SYMBOL_KEYWORD -} SymbolKind; - -/// data needed for autocompletion -typedef struct Autocomplete Autocomplete; - -/// data needed for finding usages -typedef struct Usages Usages; - -/// max number of signatures to display at a time. -#define SIGNATURE_HELP_MAX 5 - -/// "signature help" (LSP) is thing that shows the current parameter, etc. -typedef struct SignatureHelp SignatureHelp; - - -/// "document link" information (LSP) -typedef struct DocumentLinks DocumentLinks; - -/// information for symbol rename (LSP) -typedef struct RenameSymbol RenameSymbol; - -/// "hover" information from LSP server -typedef struct Hover Hover; - -/// symbol information for the definitions menu -typedef struct { - char *name; - char *detail; - u32 color; - /// is this from a LSP server (as opposed to ctags)? - bool from_lsp; - /// only set if `from_lsp = true` - LSPDocumentPosition position; -} SymbolInfo; - -typedef struct { - LSPServerRequestID last_request; - double last_request_time; - /// last query string which we sent a request for - char *last_request_query; - /// for "go to definition of..." menu - Selector selector; - /// an array of all definitions (gotten from workspace/symbols) for "go to definition" menu - SymbolInfo *all_definitions; -} Definitions; - -/// "highlight" information from LSP server -typedef struct Highlights Highlights; - -typedef struct { - Command command; - CommandArgument argument; -} Action; - -typedef struct { - // dynamic array - Action *actions; -} Macro; - -typedef struct { - char *path; - Font *font; -} LoadedFont; - -typedef struct { - vec2 pos; - u8 times; -} MouseClick; - -typedef struct { - vec2 pos; -} MouseRelease; - -struct Ted { - /// all running LSP servers - LSP *lsps[TED_LSP_MAX + 1]; - /// 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; - LoadedFont *all_fonts; - Font *font_bold; - Font *font; - TextBuffer *active_buffer; - /// buffer we are currently drag-to-selecting in, if any - TextBuffer *drag_buffer; - /// while a menu or something is open, there is no active buffer. when the menu is closed, - /// the old active buffer needs to be restored. that's what this stores. - TextBuffer *prev_active_buffer; - Node *active_node; - /// dynamic array of Settings. use Settings.context to figure out which one to use. - Settings *all_settings; - /// settings to use when no buffer is open - Settings *default_settings; - float window_width, window_height; - vec2 mouse_pos; - u32 mouse_state; - /// `mouse_clicks[SDL_BUTTON_RIGHT]`, for example, is all the right mouse-clicks that have happened this frame - MouseClick *mouse_clicks[4]; - MouseRelease *mouse_releases[4]; - /// total amount scrolled this frame - int scroll_total_x, scroll_total_y; - /// currently open menu, or \ref MENU_NONE if no menu is open. - Menu menu; - FileSelector file_selector; - Selector command_selector; - /// general-purpose line buffer for inputs -- used for menus - TextBuffer line_buffer; - /// use for "find" term in find/find+replace - TextBuffer find_buffer; - /// "replace" for find+replace - TextBuffer replace_buffer; - /// buffer for build output (view only) - TextBuffer build_buffer; - /// used for command selector - TextBuffer argument_buffer; - /// time which the cursor error animation started (cursor turns red, e.g. when there's no autocomplete suggestion) - double cursor_error_time; - /// should start_cwd be searched for files? set to true if the executable isn't "installed" - bool search_start_cwd; - /// CWD `ted` was started in - char start_cwd[TED_PATH_MAX]; - /// if set to true, the window will close next frame. NOTE: this doesn't check for unsaved changes!! - bool quit; - /// is the find or find+replace menu open? - bool find; - /// is the find+replace menu open? - bool replace; - /// find options - bool find_regex, find_case_sensitive; - /// flags used last time search term was compiled - u32 find_flags; - struct pcre2_real_code_32 *find_code; - struct pcre2_real_match_data_32 *find_match_data; - FindResult *find_results; - /// invalid regex? - bool find_invalid_pattern; - /// if non-zero, the user is trying to execute this command, but there are unsaved changes - Command warn_unsaved; - /// are we showing the build output? - bool build_shown; - /// is the build process running? - bool building; - Autocomplete *autocomplete; - SignatureHelp *signature_help; - DocumentLinks *document_links; - Hover *hover; - Definitions definitions; - Highlights *highlights; - Usages *usages; - RenameSymbol *rename_symbol; - - FILE *log; - - /// dynamic array of build errors - BuildError *build_errors; - /// build error we are currently "on" - u32 build_error; - - /// used by menus to keep track of the scroll position so we can return to it. - vec2d prev_active_buffer_scroll; - - SDL_Cursor *cursor_arrow, *cursor_ibeam, *cursor_wait, - *cursor_resize_h, *cursor_resize_v, *cursor_hand, *cursor_move; - /// which cursor to use this frame - /// this should be set to one of the cursor_* members above, or NULL for no cursor - SDL_Cursor *cursor; - - /// node containing tab user is dragging around, NULL if user is not dragging a tab - Node *dragging_tab_node; - /// index in dragging_tab_node->tabs - u16 dragging_tab_idx; - /// where the tab is being dragged from (i.e. mouse pos at start of drag action) - vec2 dragging_tab_origin; - - /// if not `NULL`, points to the node whose split the user is currently resizing. - Node *resizing_split; - - /// dynamic array of history of commands run with :shell (UTF-8) - char **shell_history; - /// for keeping track of where we are in the shell history. - u32 shell_history_pos; - - // points to a selector if any is open, otherwise NULL. - Selector *selector_open; - - /// what % of the screen the build output takes up - float build_output_height; - bool resizing_build_output; - - /// last time a save command was executed. used for bg-shaders. - double last_save_time; - - 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]; - /// allows execution of multiple commands -- needed for tags generation - char **build_queue; - /// comma-separated list of files with unsaved changes (only applicable if warn_unsaved != 0) - char warn_unsaved_names[TED_PATH_MAX]; - /// file name user is trying to overwrite - char warn_overwrite[TED_PATH_MAX]; - /// file name which we want to reload - char ask_reload[TED_PATH_MAX]; - char local_data_dir[TED_PATH_MAX]; - char global_data_dir[TED_PATH_MAX]; - /// home directory - char home[TED_PATH_MAX]; - /// current working directory - char cwd[TED_PATH_MAX]; - /// directory where we run the build command - char build_dir[TED_PATH_MAX]; - /// where we are reading tags from - char tags_dir[TED_PATH_MAX]; - bool nodes_used[TED_MAX_NODES]; - /// `nodes[0]` is always the "root node", if any buffers are open. - Node nodes[TED_MAX_NODES]; - /// NOTE: the buffer at index 0 is reserved as a "null buffer" and should not be used. - bool buffers_used[TED_MAX_BUFFERS]; - TextBuffer buffers[TED_MAX_BUFFERS]; - /// number of config file strings - u32 nstrings; - /// config file strings - char *strings[TED_MAX_STRINGS]; - char window_title[256]; - - /// little box used to display errors and info. - char message[512]; - /// time message box was opened - double message_time; - MessageType message_type; - MessageType message_shown_type; - char message_shown[512]; -}; - -#endif // !TED_PLUGIN - // === buffer.c === /// Returns `true` if the buffer is in view-only mode. bool buffer_is_view_only(TextBuffer *buffer); @@ -903,7 +320,7 @@ bool buffer_clip_rect(TextBuffer *buffer, Rect *r); /// Get the font used for this buffer. Font *buffer_font(TextBuffer *buffer); /// get LSP server which deals with this buffer -LSP *buffer_lsp(TextBuffer *buffer); +struct LSP *buffer_lsp(TextBuffer *buffer); /// Get the settings used for this buffer. Settings *buffer_settings(TextBuffer *buffer); /// Get tab width for this buffer @@ -1043,19 +460,6 @@ void buffer_cursor_move_to_end_of_line(TextBuffer *buffer); void buffer_cursor_move_to_start_of_file(TextBuffer *buffer); /// Move cursor to the end of the file, like Ctrl+End does. void buffer_cursor_move_to_end_of_file(TextBuffer *buffer); -/// Get the LSPDocumentID corresponding to the file this buffer contains. -/// The return value is only useful if `buffer_lsp(buffer) != NULL`. -LSPDocumentID buffer_lsp_document_id(TextBuffer *buffer); -/// Get LSPPosition corresponding to position in buffer. -LSPPosition buffer_pos_to_lsp_position(TextBuffer *buffer, BufferPos pos); -/// Get LSPDocumentPosition corresponding to position in buffer. -LSPDocumentPosition buffer_pos_to_lsp_document_position(TextBuffer *buffer, BufferPos pos); -/// Convert LSPPosition to BufferPos. -BufferPos buffer_pos_from_lsp(TextBuffer *buffer, LSPPosition lsp_pos); -/// Get the cursor position as an LSPPosition. -LSPPosition buffer_cursor_pos_as_lsp_position(TextBuffer *buffer); -/// Get the cursor position as an LSPDocumentPosition. -LSPDocumentPosition buffer_cursor_pos_as_lsp_document_position(TextBuffer *buffer); /// Put text at a position. All text insertion should eventually go through this function. BufferPos buffer_insert_text_at_pos(TextBuffer *buffer, BufferPos pos, String32 str); /// Insert a single character at a position. @@ -1204,10 +608,6 @@ void buffer_uncomment_lines(TextBuffer *buffer, u32 first_line, u32 last_line); void buffer_toggle_comment_lines(TextBuffer *buffer, u32 first_line, u32 last_line); /// comment the selected lines, or uncomment them if they're all commented void buffer_toggle_comment_selection(TextBuffer *buffer); -/// highlight an \ref LSPRange in this buffer. -/// -/// make sure to call \ref gl_geometry_draw after this -void buffer_highlight_lsp_range(TextBuffer *buffer, LSPRange range, ColorSetting color); /// returns true if `p1` and `p2` are equal bool buffer_pos_eq(BufferPos p1, BufferPos p2); /// returns `-1` if `p1` comes before `p2` @@ -1247,12 +647,6 @@ void build_check_for_errors(Ted *ted); void build_frame(Ted *ted, float x1, float y1, float x2, float y2); // === colors.c === -#if !TED_PLUGIN -void color_init(void); -/// which color setting should be used for the given symbol kind. -/// this is the color used in the autocomplete selector, for example. -ColorSetting color_for_symbol_kind(SymbolKind kind); -#endif ColorSetting color_setting_from_str(const char *str); const char *color_setting_to_str(ColorSetting s); Status color_from_str(const char *str, u32 *color); @@ -1262,41 +656,12 @@ u32 color_blend(u32 bg, u32 fg); u32 color_apply_opacity(u32 color, float opacity); // === command.c === -#if !TED_PLUGIN -void command_init(void); -void command_execute_ex(Ted *ted, Command c, const CommandArgument *argument, const CommandContext *context); -#endif 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_string_argument(Ted *ted, Command c, const char *string); // === config.c === -#if !TED_PLUGIN -/// first, we read all config files, then we parse them. -/// this is because we want less specific settings (e.g. settings applied -/// to all languages instead of one particular language) to be applied first, -/// then more specific settings are based off of those. -/// -/// EXAMPLE: -/// ``` -/// ---config file 1--- -/// [Javascript.core] -/// syntax-highlighting = off -/// (inherits tab-width = 4) -/// [CSS.core] -/// tab-width = 2 (overrides tab-width = 4) -/// ---config file 2--- -/// [core] -/// tab-width = 4 -/// ``` -void config_read(Ted *ted, ConfigPart **pparts, const char *filename); -void config_parse(Ted *ted, ConfigPart **pparts); -void config_free(Ted *ted); -/// how well does this settings context fit the given path and language? -/// the context with the highest score will be chosen. -long context_score(const char *path, Language lang, const SettingsContext *context); -#endif // !TED_PLUGIN /// returns the best guess for the root directory of the project containing `path` /// (which should be an absolute path). /// the return value should be freed. @@ -1325,100 +690,16 @@ void find_open(Ted *ted, bool replace); void find_close(Ted *ted); // === gl.c === -/// set by main() -extern float gl_window_width, gl_window_height; -/// set by main() -extern int gl_version_major, gl_version_minor; - -/// macro trickery to avoid having to write every GL function multiple times -#define gl_for_each_proc(do)\ - do(DRAWARRAYS, DrawArrays)\ - do(GENTEXTURES, GenTextures)\ - do(DELETETEXTURES, DeleteTextures)\ - do(GENERATEMIPMAP, GenerateMipmap)\ - do(TEXIMAGE2D, TexImage2D)\ - do(BINDTEXTURE, BindTexture)\ - do(TEXPARAMETERI, TexParameteri)\ - do(GETERROR, GetError)\ - do(GETINTEGERV, GetIntegerv)\ - do(ENABLE, Enable)\ - do(DISABLE, Disable)\ - do(BLENDFUNC, BlendFunc)\ - do(VIEWPORT, Viewport)\ - do(CLEARCOLOR, ClearColor)\ - do(CLEAR, Clear)\ - do(FINISH, Finish)\ - do(CREATESHADER, CreateShader)\ - do(DELETESHADER, DeleteShader)\ - do(CREATEPROGRAM, CreateProgram)\ - do(SHADERSOURCE, ShaderSource)\ - do(GETSHADERIV, GetShaderiv)\ - do(GETSHADERINFOLOG, GetShaderInfoLog)\ - do(COMPILESHADER, CompileShader)\ - do(CREATEPROGRAM, CreateProgram)\ - do(DELETEPROGRAM, DeleteProgram)\ - do(ATTACHSHADER, AttachShader)\ - do(LINKPROGRAM, LinkProgram)\ - do(GETPROGRAMIV, GetProgramiv)\ - do(GETPROGRAMINFOLOG, GetProgramInfoLog)\ - do(USEPROGRAM, UseProgram)\ - do(GETATTRIBLOCATION, GetAttribLocation)\ - do(GETUNIFORMLOCATION, GetUniformLocation)\ - do(GENBUFFERS, GenBuffers)\ - do(DELETEBUFFERS, DeleteBuffers)\ - do(BINDBUFFER, BindBuffer)\ - do(BUFFERDATA, BufferData)\ - do(VERTEXATTRIBPOINTER, VertexAttribPointer)\ - do(ENABLEVERTEXATTRIBARRAY, EnableVertexAttribArray)\ - do(DISABLEVERTEXATTRIBARRAY, DisableVertexAttribArray)\ - do(GENVERTEXARRAYS, GenVertexArrays)\ - do(DELETEVERTEXARRAYS, DeleteVertexArrays)\ - do(BINDVERTEXARRAY, BindVertexArray)\ - do(ACTIVETEXTURE, ActiveTexture)\ - do(UNIFORM1F, Uniform1f)\ - do(UNIFORM2F, Uniform2f)\ - do(UNIFORM3F, Uniform3f)\ - do(UNIFORM4F, Uniform4f)\ - do(UNIFORM1I, Uniform1i)\ - do(UNIFORM2I, Uniform2i)\ - do(UNIFORM3I, Uniform3i)\ - do(UNIFORM4I, Uniform4i)\ - do(UNIFORMMATRIX4FV, UniformMatrix4fv)\ - do(DEBUGMESSAGECALLBACK, DebugMessageCallback)\ - do(DEBUGMESSAGECONTROL, DebugMessageControl)\ - -#define gl_declare_proc(upper, lower) extern PFNGL##upper##PROC gl##lower; -gl_for_each_proc(gl_declare_proc) -#undef gl_declare_proc - -#if !TED_PLUGIN -/// get addresses of GL functions -void gl_get_procs(void); -/// create a new reference-counted shader-array-buffer object. -GlRcSAB *gl_rc_sab_new(GLuint shader, GLuint array, GLuint buffer); -/// increase reference count on `s`. -void gl_rc_sab_incref(GlRcSAB *s); -/// decrease reference count on `*ps`, and set `*ps` to NULL if the reference count is 0. -void gl_rc_sab_decref(GlRcSAB **ps); -/// create a new reference-counted texture. -GlRcTexture *gl_rc_texture_new(GLuint texture); -/// increase reference count on `t`. -void gl_rc_texture_incref(GlRcTexture *t); -/// decrease reference count on `*t`, and set `*t` to NULL if the reference count is 0. -void gl_rc_texture_decref(GlRcTexture **pt); -/// initialize geometry stuff -void gl_geometry_init(void); -#endif // !TED_PLUGIN /// create and compile a shader -GLuint gl_compile_shader(char error_buf[256], const char *code, GLenum shader_type); +u32 gl_compile_shader(char error_buf[256], const char *code, u32 shader_type); /// create new shader program from shaders -GLuint gl_link_program(char error_buf[256], GLuint *shaders, size_t count); +u32 gl_link_program(char error_buf[256], u32 *shaders, size_t count); /// create a shader program from vertex shader and fragment shader source -GLuint gl_compile_and_link_shaders(char error_buf[256], const char *vshader_code, const char *fshader_code); +u32 gl_compile_and_link_shaders(char error_buf[256], const char *vshader_code, const char *fshader_code); /// prints a debug message if `attrib` is not found -GLuint gl_attrib_location(GLuint program, const char *attrib); +u32 gl_attrib_location(u32 program, const char *attrib); /// prints a debug message if `uniform` is not found -GLint gl_uniform_location(GLuint program, const char *uniform); +i32 gl_uniform_location(u32 program, const char *uniform); /// queue a filled rectangle with the given color. void gl_geometry_rect(Rect r, u32 color_rgba); /// queue the border of a rectangle with the given color. @@ -1426,14 +707,9 @@ void gl_geometry_rect_border(Rect r, float border_thickness, u32 color); /// draw all queued geometry void gl_geometry_draw(void); /// create an OpenGL texture object from an image file. -GLuint gl_load_texture_from_image(const char *path); +u32 gl_load_texture_from_image(const char *path); // === ide-autocomplete.c === -#if !TED_PLUGIN -void autocomplete_init(Ted *ted); -void autocomplete_quit(Ted *ted); -void autocomplete_process_lsp_response(Ted *ted, const LSPResponse *response); -#endif /// is the autocomplete box open? bool autocomplete_is_open(Ted *ted); /// is there a phantom completion being displayed? @@ -1457,14 +733,8 @@ void autocomplete_close(Ted *ted); void autocomplete_frame(Ted *ted); // === ide-definitions.c === -/// go to the definition of `name`. -/// if `lsp` is NULL, tags will be used. -/// 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, GotoType type); /// cancel the last go-to-definition / find symbols request. void definition_cancel_lookup(Ted *ted); -void definitions_process_lsp_response(Ted *ted, LSP *lsp, const LSPResponse *response); /// open the definitions menu void definitions_selector_open(Ted *ted); /// update the definitions menu @@ -1475,12 +745,6 @@ void definitions_selector_close(Ted *ted); void definitions_frame(Ted *ted); // === ide-document-link.c === -#if !TED_PLUGIN -void document_link_init(Ted *ted); -void document_link_quit(Ted *ted); -void document_link_frame(Ted *ted); -void document_link_process_lsp_response(Ted *ted, const LSPResponse *response); -#endif /// get document link at this position in the active buffer. /// /// the returned pointer won't be freed immediately, but could be on the next frame, @@ -1489,44 +753,20 @@ const char *document_link_at_buffer_pos(Ted *ted, BufferPos pos); void document_link_clear(Ted *ted); // === ide-highlights.c === -#if !TED_PLUGIN -void highlights_init(Ted *ted); -void highlights_quit(Ted *ted); -void highlights_frame(Ted *ted); -void highlights_process_lsp_response(Ted *ted, const LSPResponse *response); -#endif void highlights_close(Ted *ted); // === ide-hover.c === -#if !TED_PLUGIN -void hover_init(Ted *ted); -void hover_frame(Ted *ted, double dt); -void hover_quit(Ted *ted); -#endif /// called for example whenever the mouse moves to reset the timer before hover info is displayed void hover_reset_timer(Ted *ted); void hover_close(Ted *ted); -void hover_process_lsp_response(Ted *ted, const LSPResponse *response); // === ide-rename-symbol.c === -#if !TED_PLUGIN -void rename_symbol_init(Ted *ted); -void rename_symbol_quit(Ted *ted); -void rename_symbol_frame(Ted *ted); -void rename_symbol_process_lsp_response(Ted *ted, const LSPResponse *response); -#endif void rename_symbol_at_cursor(Ted *ted, TextBuffer *buffer, const char *new_name); /// returns true if we are currently waiting for the LSP to send us a response bool rename_symbol_is_loading(Ted *ted); void rename_symbol_clear(Ted *ted); // === ide-signature-help.c === -#if !TED_PLUGIN -void signature_help_init(Ted *ted); -void signature_help_quit(Ted *ted); -void signature_help_frame(Ted *ted); -void signature_help_process_lsp_response(Ted *ted, const LSPResponse *response); -#endif /// figure out new signature help void signature_help_retrigger(Ted *ted); /// open signature help. `trigger` should either be the trigger character (e.g. ',') @@ -1536,21 +776,12 @@ bool signature_help_is_open(Ted *ted); void signature_help_close(Ted *ted); // === ide-usages.c === -#if !TED_PLUGIN -void usages_init(Ted *ted); -void usages_process_lsp_response(Ted *ted, const LSPResponse *response); -void usages_frame(Ted *ted); -void usages_quit(Ted *ted); -#endif /// cancel the last "find usages" request void usages_cancel_lookup(Ted *ted); /// find usages for word under the cursor in the active buffer. void usages_find(Ted *ted); // === macro.c === -#if !TED_PLUGIN -void macro_add(Ted *ted, Command command, const CommandArgument *argument); -#endif void macro_start_recording(Ted *ted, u32 index); void macro_stop_recording(Ted *ted); void macro_execute(Ted *ted, u32 index); @@ -1644,10 +875,6 @@ void tags_generate(Ted *ted, bool run_in_build_window); size_t tags_beginning_with(Ted *ted, const char *prefix, char **out, size_t out_size, bool error_if_tags_does_not_exist); /// go to the definition of the given tag bool tag_goto(Ted *ted, const char *tag); -#if !TED_PLUGIN -/// get all tags in the tags file as SymbolInfos. -SymbolInfo *tags_get_symbols(Ted *ted); -#endif // === ted.c === /// for fatal errors @@ -1656,8 +883,8 @@ void die(PRINTF_FORMAT_STRING const char *fmt, ...) ATTRIBUTE_PRINTF(1, 2); TextBuffer *ted_get_active_buffer(Ted *ted); /// set title of ted window void ted_set_window_title(Ted *ted, const char *title); -/// returns `true` if the given key is down -bool ted_is_key_down(Ted *ted, SDL_Keycode key); +/// returns `true` if the given SDL key code is down +bool ted_is_key_down(Ted *ted, i32 key); /// returns `true` if the given \ref KeyCombo is down bool ted_is_key_combo_down(Ted *ted, KeyCombo key_combo); /// returns `true` if either ctrl key is down @@ -1721,17 +948,15 @@ char *ted_get_root_dir(Ted *ted); Settings *ted_active_settings(Ted *ted); /// Get the settings for a file at the given path in the given language. Settings *ted_get_settings(Ted *ted, const char *path, Language language); -/// Get LSP by ID. Returns NULL if there is no LSP with that ID. -LSP *ted_get_lsp_by_id(Ted *ted, LSPID id); /// Get LSP which should be used for the given path and language. /// If no running LSP server would cover the path and language, a new one is /// started if possible. /// Returns NULL on failure (e.g. there is no LSP server /// specified for the given path and language). -LSP *ted_get_lsp(Ted *ted, const char *path, Language language); +struct LSP *ted_get_lsp(Ted *ted, const char *path, Language language); /// Get the LSP of the active buffer/ted->cwd. /// Returns NULL if there is no such server. -LSP *ted_active_lsp(Ted *ted); +struct LSP *ted_active_lsp(Ted *ted); /// get the value of the given color setting, according to `ted_active_settings(ted)`. u32 ted_active_color(Ted *ted, ColorSetting color); /// open the given file, or switch to it if it's already open. @@ -1758,26 +983,16 @@ void ted_node_switch(Ted *ted, Node *node); /// load ted.cfg files void ted_load_configs(Ted *ted, bool reloading); /// handle a key press -void ted_press_key(Ted *ted, SDL_Keycode keycode, SDL_Keymod modifier); +void ted_press_key(Ted *ted, i32 keycode, u32 modifier); /// get the buffer and buffer position where the mouse is. /// returns false if the mouse is not in a buffer. Status ted_get_mouse_buffer_pos(Ted *ted, TextBuffer **pbuffer, BufferPos *ppos); /// make the cursor red for a bit to indicate an error (e.g. no autocompletions) void ted_flash_error_cursor(Ted *ted); -/// go to `path` at line `line` and index `index`, opening a new buffer if necessary. -/// if `is_lsp` is set to true, `index` is interpreted as a UTF-16 offset rather than a UTF-32 offset. -void ted_go_to_position(Ted *ted, const char *path, u32 line, u32 index, bool is_lsp); -/// go to this LSP document position, opening a new buffer containing the file if necessary. -void ted_go_to_lsp_document_position(Ted *ted, LSP *lsp, LSPDocumentPosition position); -/// cancel this LSP request. also zeroes *request -/// if *request is zeroed, this does nothing. -void ted_cancel_lsp_request(Ted *ted, LSPServerRequestID *request); /// how tall is a line buffer? float ted_line_buffer_height(Ted *ted); /// check for orphaned nodes and node cycles void ted_check_for_node_problems(Ted *ted); -/// convert LSPWindowMessageType to MessageType -MessageType ted_message_type_from_lsp(LSPWindowMessageType type); /// get colors to use for message box void ted_color_settings_for_message_type(MessageType type, ColorSetting *bg_color, ColorSetting *border_color); diff --git a/text.c b/text.c index de5438c..2736ab7 100644 --- a/text.c +++ b/text.c @@ -1,4 +1,4 @@ -#include "ted.h" +#include "ted-internal.h" no_warn_start #if DEBUG diff --git a/ui.c b/ui.c index 636f164..8d98fac 100644 --- a/ui.c +++ b/ui.c @@ -1,6 +1,6 @@ // various UI elements used by ted -#include "ted.h" +#include "ted-internal.h" #if __unix__ #include -- cgit v1.2.3