diff --git a/Makefile b/Makefile index 7d1e467..b5b794e 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ all: editor -editor: src/*.odin grep odin_highlighter - odin build src/ -out:bin/editor.o -build-mode:obj -debug -lld - dsymutil bin/editor.o -o bin/editor.dSYM +editor: src/*.odin # grep odin_highlighter + # odin build src/ -out:bin/editor.o -build-mode:obj -debug -lld + # dsymutil bin/editor.o -o bin/editor.dSYM odin build src/ -out:bin/editor -lld odin_highlighter: plugins/highlighter/src/*.odin diff --git a/plugins/lua/view.lua b/plugins/lua/view.lua index a10e735..47ae4b1 100644 --- a/plugins/lua/view.lua +++ b/plugins/lua/view.lua @@ -1,6 +1,9 @@ local BufferSearchOpen = false local BufferSearchOpenElapsed = 0 +local LogWindowOpen = false +local LogWindowOpenElapsed = 0 + local CurrentPreviewBufferIndex = Editor.get_current_buffer_index() local BufferSearchIndex = 0 @@ -99,9 +102,11 @@ function ui_sidemenu(ctx) end if UI.advanced_button(ctx, " x ", flags, UI.FitText, UI.FitText).clicked then - print("hahah, you can't close buffers yet silly") - Editor.set_current_buffer_from_index(i) - add_buffer_to_code_view(ActiveCodeView+1, buffer_info.file_path, i) + Editor.log("hahah, you can't close buffers yet silly") + if ActiveCodeView ~= nil then + Editor.set_current_buffer_from_index(i) + add_buffer_to_code_view(ActiveCodeView+1, buffer_info.file_path, i) + end end tab_button_interaction = UI.advanced_button(ctx, " "..buffer_info.file_path.." ", flags, UI.Fill, UI.FitText) @@ -275,6 +280,7 @@ function render_ui_window(ctx) end render_buffer_search(ctx) + render_log_window(ctx) LastMouseX = x LastMouseY = y @@ -317,13 +323,54 @@ function render_buffer_search(ctx) end end +function render_log_window(ctx) + if Editor.get_current_buffer_index() ~= -2 then + LogWindowOpen = false + end + + if LogWindowOpen or LogWindowOpenElapsed > 0 then + if LogWindowOpen and LogWindowOpenElapsed < numFrames then + LogWindowOpenElapsed = LogWindowOpenElapsed + 1 + elseif not LogWindowOpen and LogWindowOpenElapsed > 0 then + LogWindowOpenElapsed = LogWindowOpenElapsed - 1 + end + end + + if LogWindowOpen or LogWindowOpenElapsed > 0 then + window_percent = 75 + if LogWindowOpenElapsed > 0 then + window_percent = ((LogWindowOpenElapsed/numFrames) * 75) + end + + UI.push_parent(ctx, UI.push_floating(ctx, "log window canvas", 0, 0)) + centered(ctx, "log window", UI.Horizontal, UI.PercentOfParent(window_percent), UI.PercentOfParent(window_percent), ( + function () + UI.push_parent(ctx, UI.push_rect(ctx, "window", true, true, UI.Horizontal, UI.Fill, UI.Fill)) + -- -2 is the log buffer + UI.buffer(ctx, -2) + UI.pop_parent(ctx) + end + )) + UI.pop_parent(ctx) + end +end + function handle_buffer_input() - -- print("you inputted into a buffer") end function OnInit() - print("Main View plugin initialized") + Editor.log("Main View plugin initialized") Editor.register_key_group({ + {Editor.Key.Backtick, "Open Editor Logs", (function () + if not LogWindowOpen then + LogWindowOpen = true + Editor.set_current_buffer_from_index(-2) + else + LogWindowOpen = false + local code_view = CodeViews[ActiveCodeView] + Editor.set_current_buffer_from_index(code_view.tabs[code_view.current_tab]["buffer_index"]) + end + end)}, {Editor.Key.Space, "", { {Editor.Key.B, "Buffer Search", ( function () diff --git a/src/core/core.odin b/src/core/core.odin index 52982a5..b7d6cba 100644 --- a/src/core/core.odin +++ b/src/core/core.odin @@ -3,6 +3,7 @@ package core import "core:runtime" import "core:reflect" import "core:fmt" +import "core:log" import "vendor:sdl2" import lua "vendor:lua/5.4" @@ -67,6 +68,8 @@ State :: struct { current_buffer: int, buffers: [dynamic]FileBuffer, + log_buffer: FileBuffer, + window: ^Window, should_close_window: bool, @@ -80,6 +83,22 @@ State :: struct { lua_hooks: map[plugin.Hook][dynamic]LuaHookRef, } +current_buffer :: proc(state: ^State) -> ^FileBuffer { + if state.current_buffer == -2 { + return &state.log_buffer; + } + + return &state.buffers[state.current_buffer]; +} + +buffer_from_index :: proc(state: ^State, buffer_index: int) -> ^FileBuffer { + if buffer_index == -2 { + return &state.log_buffer; + } + + return &state.buffers[buffer_index]; +} + add_hook :: proc(state: ^State, hook: plugin.Hook, hook_proc: plugin.OnHookProc) { if _, exists := state.hooks[hook]; !exists { state.hooks[hook] = make([dynamic]plugin.OnHookProc); @@ -155,7 +174,7 @@ delete_input_actions :: proc(input_map: ^InputActions) { register_plugin_key_action_single :: proc(input_map: ^InputActions, key: plugin.Key, action: PluginEditorAction, description: string = "") { if ok := key in input_map.key_actions; ok { // TODO: log that key is already registered - fmt.eprintln("plugin key already registered with single action", key); + log.error("plugin key already registered with single action", key); } input_map.key_actions[key] = Action { @@ -167,7 +186,7 @@ register_plugin_key_action_single :: proc(input_map: ^InputActions, key: plugin. register_key_action_single :: proc(input_map: ^InputActions, key: plugin.Key, action: EditorAction, description: string = "") { if ok := key in input_map.key_actions; ok { // TODO: log that key is already registered - fmt.eprintln("key already registered with single action", key); + log.error("key already registered with single action", key); } input_map.key_actions[key] = Action { @@ -191,7 +210,7 @@ register_key_action_group :: proc(input_map: ^InputActions, key: plugin.Key, inp register_ctrl_key_action_single :: proc(input_map: ^InputActions, key: plugin.Key, action: EditorAction, description: string = "") { if ok := key in input_map.key_actions; ok { // TODO: log that key is already registered - fmt.eprintln("key already registered with single action", key); + log.error("key already registered with single action", key); } input_map.ctrl_key_actions[key] = Action { @@ -203,7 +222,7 @@ register_ctrl_key_action_single :: proc(input_map: ^InputActions, key: plugin.Ke register_ctrl_key_action_group :: proc(input_map: ^InputActions, key: plugin.Key, input_group: InputGroup, description: string = "") { if ok := key in input_map.key_actions; ok { // TODO: log that key is already registered - fmt.eprintln("key already registered with single action", key); + log.error("key already registered with single action", key); } input_map.ctrl_key_actions[key] = Action { diff --git a/src/core/file_buffer.odin b/src/core/file_buffer.odin index b95f3c6..27cdc74 100644 --- a/src/core/file_buffer.odin +++ b/src/core/file_buffer.odin @@ -43,7 +43,7 @@ Selection :: struct { end: Cursor, } -Glyph :: struct #packed { +Glyph :: struct { codepoint: u8, color: theme.PaletteColor, } @@ -84,6 +84,17 @@ new_file_buffer_iter_with_cursor :: proc(file_buffer: ^FileBuffer, cursor: Curso } new_file_buffer_iter :: proc{new_file_buffer_iter_from_beginning, new_file_buffer_iter_with_cursor}; +file_buffer_end :: proc(buffer: ^FileBuffer) -> Cursor { + return Cursor { + col = 0, + line = 0, + index = FileBufferIndex { + slice_index = len(buffer.content_slices)-1, + content_index = len(buffer.content_slices[len(buffer.content_slices)-1])-1, + } + }; +} + iterate_file_buffer :: proc(it: ^FileBufferIter) -> (character: u8, idx: FileBufferIndex, cond: bool) { if it.cursor.index.slice_index >= len(it.buffer.content_slices) || it.cursor.index.content_index >= len(it.buffer.content_slices[it.cursor.index.slice_index]) { return; @@ -542,7 +553,7 @@ move_cursor_left :: proc(buffer: ^FileBuffer, cursor: Maybe(^Cursor) = nil) { } } -move_cursor_right :: proc(buffer: ^FileBuffer, stop_at_end: bool = true, cursor: Maybe(^Cursor) = nil) { +move_cursor_right :: proc(buffer: ^FileBuffer, stop_at_end: bool = true, amt: int = 1, cursor: Maybe(^Cursor) = nil) { cursor := cursor; if cursor == nil { @@ -552,9 +563,11 @@ move_cursor_right :: proc(buffer: ^FileBuffer, stop_at_end: bool = true, cursor: it := new_file_buffer_iter_with_cursor(buffer, cursor.?^); line_length := file_buffer_line_length(buffer, it.cursor.index); - if !stop_at_end || cursor.?.col < line_length-1 { - iterate_file_buffer(&it); - cursor.?^ = it.cursor; + for _ in 0.. plugin.BufferInf }; } into_buffer_info_from_index :: proc(state: ^State, buffer_index: int) -> plugin.BufferInfo { - buffer := &state.buffers[buffer_index]; + buffer := buffer_from_index(state, buffer_index); return into_buffer_info(state, buffer); } @@ -860,7 +873,7 @@ draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x: int, y: int, sho cursor_y -= begin * state.source_font_height; // draw cursor - if state.mode == .Normal { + if state.mode == .Normal || current_buffer(state) != buffer { draw_rect(state, cursor_x, cursor_y, state.source_font_width, state.source_font_height, .Background4); } else if state.mode == .Visual { start_sel_x := x + padding + buffer.selection.?.start.col * state.source_font_width; @@ -919,7 +932,7 @@ draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x: int, y: int, sho // NOTE: this requires transparent background color because it renders after the text // and its after the text because the line length needs to be calculated - if state.mode == .Visual { + if state.mode == .Visual && current_buffer(state) == buffer { sel_x := x + padding; width: int @@ -976,14 +989,14 @@ scroll_file_buffer :: proc(buffer: ^FileBuffer, dir: ScrollDir, cursor: Maybe(^C } } -insert_content :: proc(buffer: ^FileBuffer, to_be_inserted: []u8) { +insert_content :: proc(buffer: ^FileBuffer, to_be_inserted: []u8, append_to_end: bool = false) { if len(to_be_inserted) == 0 { return; } // TODO: is this even needed? would mean that the cursor isn't always in a valid state. - update_file_buffer_index_from_cursor(buffer); - it := new_file_buffer_iter(buffer, buffer.cursor); + // update_file_buffer_index_from_cursor(buffer); + it := new_file_buffer_iter_with_cursor(buffer, buffer.cursor) if !append_to_end else new_file_buffer_iter_with_cursor(buffer, file_buffer_end(buffer)); length := append(&buffer.added_content, ..to_be_inserted); inserted_slice: []u8 = buffer.added_content[len(buffer.added_content)-length:]; @@ -991,7 +1004,7 @@ insert_content :: proc(buffer: ^FileBuffer, to_be_inserted: []u8) { if it.cursor.index.content_index == 0 { // insertion happening in beginning of content slice - inject_at(&buffer.content_slices, buffer.cursor.index.slice_index, inserted_slice); + inject_at(&buffer.content_slices, it.cursor.index.slice_index, inserted_slice); } else { // insertion is happening in middle of content slice @@ -1004,7 +1017,10 @@ insert_content :: proc(buffer: ^FileBuffer, to_be_inserted: []u8) { inject_at(&buffer.content_slices, it.cursor.index.slice_index+2, end_slice); } - update_file_buffer_index_from_cursor(buffer); + if !append_to_end { + update_file_buffer_index_from_cursor(buffer); + move_cursor_right(buffer, false, amt = len(to_be_inserted)); + } } // TODO: potentially add FileBufferIndex as parameter diff --git a/src/core/logger.odin b/src/core/logger.odin new file mode 100644 index 0000000..108a17d --- /dev/null +++ b/src/core/logger.odin @@ -0,0 +1,40 @@ +package core + +import "core:runtime" +import "core:reflect" +import "core:fmt" +import "core:log" + +Level_Header := [?]string { + 0..<10 = "[DEBUG]: ", + 10..<20 = "[INFO ]: ", + 20..<30 = "[WARN ]: ", + 30..<40 = "[ERROR]: ", + 40..<50 = "[FATAL]: ", +}; + +new_logger :: proc(buffer: ^FileBuffer) -> runtime.Logger { + return runtime.Logger { + procedure = logger_proc, + data = buffer, + lowest_level = .Debug, + options = { + .Level, + .Date, + .Time, + .Short_File_Path, + .Thread_Id + }, + }; +} + +logger_proc :: proc(data: rawptr, level: runtime.Logger_Level, text: string, options: runtime.Logger_Options, location := #caller_location) { + buffer := cast(^FileBuffer)data; + + if .Level in options { + insert_content(buffer, transmute([]u8)(Level_Header[level]), true); + } + + insert_content(buffer, transmute([]u8)(text), true); + insert_content(buffer, {'\n'}, true); +} diff --git a/src/main.odin b/src/main.odin index 3df2cec..ca63937 100644 --- a/src/main.odin +++ b/src/main.odin @@ -6,6 +6,7 @@ import "core:math" import "core:strings" import "core:runtime" import "core:fmt" +import "core:log" import "core:mem" import "core:slice" import "vendor:sdl2" @@ -27,6 +28,7 @@ StateWithUi :: struct { ui_context: ^ui.Context, } +// TODO: why do I have this here again? // TODO: use buffer list in state do_normal_mode :: proc(state: ^State, buffer: ^FileBuffer) { if state.current_input_map != nil { @@ -90,11 +92,11 @@ register_default_leader_actions :: proc(input_map: ^core.InputActions) { register_default_go_actions :: proc(input_map: ^core.InputActions) { core.register_key_action(input_map, .H, proc(state: ^State) { - core.move_cursor_start_of_line(&state.buffers[state.current_buffer]); + core.move_cursor_start_of_line(core.current_buffer(state)); state.current_input_map = &state.input_map.mode[state.mode]; }, "move to beginning of line"); core.register_key_action(input_map, .L, proc(state: ^State) { - core.move_cursor_end_of_line(&state.buffers[state.current_buffer]); + core.move_cursor_end_of_line(core.current_buffer(state)); state.current_input_map = &state.input_map.mode[state.mode]; }, "move to end of line"); } @@ -103,34 +105,34 @@ register_default_input_actions :: proc(input_map: ^core.InputActions) { // Cursor Movement { core.register_key_action(input_map, .W, proc(state: ^State) { - core.move_cursor_forward_start_of_word(&state.buffers[state.current_buffer]); + core.move_cursor_forward_start_of_word(core.current_buffer(state)); }, "move forward one word"); core.register_key_action(input_map, .E, proc(state: ^State) { - core.move_cursor_forward_end_of_word(&state.buffers[state.current_buffer]); + core.move_cursor_forward_end_of_word(core.current_buffer(state)); }, "move forward to end of word"); core.register_key_action(input_map, .B, proc(state: ^State) { - core.move_cursor_backward_start_of_word(&state.buffers[state.current_buffer]); + core.move_cursor_backward_start_of_word(core.current_buffer(state)); }, "move backward one word"); core.register_key_action(input_map, .K, proc(state: ^State) { - core.move_cursor_up(&state.buffers[state.current_buffer]); + core.move_cursor_up(core.current_buffer(state)); }, "move up one line"); core.register_key_action(input_map, .J, proc(state: ^State) { - core.move_cursor_down(&state.buffers[state.current_buffer]); + core.move_cursor_down(core.current_buffer(state)); }, "move down one line"); core.register_key_action(input_map, .H, proc(state: ^State) { - core.move_cursor_left(&state.buffers[state.current_buffer]); + core.move_cursor_left(core.current_buffer(state)); }, "move left one char"); core.register_key_action(input_map, .L, proc(state: ^State) { - core.move_cursor_right(&state.buffers[state.current_buffer]); + core.move_cursor_right(core.current_buffer(state)); }, "move right one char"); core.register_ctrl_key_action(input_map, .U, proc(state: ^State) { - core.scroll_file_buffer(&state.buffers[state.current_buffer], .Up); + core.scroll_file_buffer(core.current_buffer(state), .Up); }, "scroll buffer up"); core.register_ctrl_key_action(input_map, .D, proc(state: ^State) { - core.scroll_file_buffer(&state.buffers[state.current_buffer], .Down); + core.scroll_file_buffer(core.current_buffer(state), .Down); }, "scroll buffer up"); } @@ -143,7 +145,7 @@ register_default_input_actions :: proc(input_map: ^core.InputActions) { state.font_atlas = core.gen_font_atlas(state, "/System/Library/Fonts/Supplemental/Andale Mono.ttf"); } - fmt.println(state.source_font_height); + log.debug(state.source_font_height); }, "increase font size"); core.register_ctrl_key_action(input_map, .EQUAL, proc(state: ^State) { state.source_font_height += 2; @@ -163,7 +165,7 @@ register_default_input_actions :: proc(input_map: ^core.InputActions) { state.mode = .Visual; state.current_input_map = &state.input_map.mode[.Visual]; - state.buffers[state.current_buffer].selection = core.new_selection(state.buffers[state.current_buffer].cursor); + core.current_buffer(state).selection = core.new_selection(core.current_buffer(state).cursor); }, "enter visual mode"); } @@ -173,58 +175,58 @@ register_default_visual_actions :: proc(input_map: ^core.InputActions) { state.mode = .Normal; state.current_input_map = &state.input_map.mode[.Normal]; - state.buffers[state.current_buffer].selection = nil; + core.current_buffer(state).selection = nil; }, "exit visual mode"); // Cursor Movement { core.register_key_action(input_map, .W, proc(state: ^State) { - sel_cur := &state.buffers[state.current_buffer].selection.?; + sel_cur := core.current_buffer(state).selection.?; - core.move_cursor_forward_start_of_word(&state.buffers[state.current_buffer], cursor = &sel_cur.end); + core.move_cursor_forward_start_of_word(core.current_buffer(state), cursor = &sel_cur.end); }, "move forward one word"); core.register_key_action(input_map, .E, proc(state: ^State) { - sel_cur := &(state.buffers[state.current_buffer].selection.?); + sel_cur := &(core.current_buffer(state).selection.?); - core.move_cursor_forward_end_of_word(&state.buffers[state.current_buffer], cursor = &sel_cur.end); + core.move_cursor_forward_end_of_word(core.current_buffer(state), cursor = &sel_cur.end); }, "move forward to end of word"); core.register_key_action(input_map, .B, proc(state: ^State) { - sel_cur := &(state.buffers[state.current_buffer].selection.?); + sel_cur := &(core.current_buffer(state).selection.?); - core.move_cursor_backward_start_of_word(&state.buffers[state.current_buffer], cursor = &sel_cur.end); + core.move_cursor_backward_start_of_word(core.current_buffer(state), cursor = &sel_cur.end); }, "move backward one word"); core.register_key_action(input_map, .K, proc(state: ^State) { - sel_cur := &(state.buffers[state.current_buffer].selection.?); + sel_cur := &(core.current_buffer(state).selection.?); - core.move_cursor_up(&state.buffers[state.current_buffer], cursor = &sel_cur.end); + core.move_cursor_up(core.current_buffer(state), cursor = &sel_cur.end); }, "move up one line"); core.register_key_action(input_map, .J, proc(state: ^State) { - sel_cur := &(state.buffers[state.current_buffer].selection.?); + sel_cur := &(core.current_buffer(state).selection.?); - core.move_cursor_down(&state.buffers[state.current_buffer], cursor = &sel_cur.end); + core.move_cursor_down(core.current_buffer(state), cursor = &sel_cur.end); }, "move down one line"); core.register_key_action(input_map, .H, proc(state: ^State) { - sel_cur := &(state.buffers[state.current_buffer].selection.?); + sel_cur := &(core.current_buffer(state).selection.?); - core.move_cursor_left(&state.buffers[state.current_buffer], cursor = &sel_cur.end); + core.move_cursor_left(core.current_buffer(state), cursor = &sel_cur.end); }, "move left one char"); core.register_key_action(input_map, .L, proc(state: ^State) { - sel_cur := &(state.buffers[state.current_buffer].selection.?); + sel_cur := &(core.current_buffer(state).selection.?); - core.move_cursor_right(&state.buffers[state.current_buffer], cursor = &sel_cur.end); + core.move_cursor_right(core.current_buffer(state), cursor = &sel_cur.end); }, "move right one char"); core.register_ctrl_key_action(input_map, .U, proc(state: ^State) { - sel_cur := &(state.buffers[state.current_buffer].selection.?); + sel_cur := &(core.current_buffer(state).selection.?); - core.scroll_file_buffer(&state.buffers[state.current_buffer], .Up, cursor = &sel_cur.end); + core.scroll_file_buffer(core.current_buffer(state), .Up, cursor = &sel_cur.end); }, "scroll buffer up"); core.register_ctrl_key_action(input_map, .D, proc(state: ^State) { - sel_cur := &(state.buffers[state.current_buffer].selection.?); + sel_cur := &(core.current_buffer(state).selection.?); - core.scroll_file_buffer(&state.buffers[state.current_buffer], .Down, cursor = &sel_cur.end); + core.scroll_file_buffer(core.current_buffer(state), .Down, cursor = &sel_cur.end); }, "scroll buffer up"); } } @@ -235,7 +237,7 @@ register_default_text_input_actions :: proc(input_map: ^core.InputActions) { sdl2.StartTextInput(); }, "enter insert mode"); core.register_key_action(input_map, .A, proc(state: ^State) { - core.move_cursor_right(&state.buffers[state.current_buffer], false); + core.move_cursor_right(core.current_buffer(state), false); state.mode = .Insert; sdl2.StartTextInput(); }, "enter insert mode after character (append)"); @@ -243,9 +245,9 @@ register_default_text_input_actions :: proc(input_map: ^core.InputActions) { // TODO: add shift+o to insert newline above current one core.register_key_action(input_map, .O, proc(state: ^State) { - core.move_cursor_end_of_line(&state.buffers[state.current_buffer], false); - core.insert_content(&state.buffers[state.current_buffer], []u8{'\n'}); - core.move_cursor_down(&state.buffers[state.current_buffer]); + core.move_cursor_end_of_line(core.current_buffer(state), false); + core.insert_content(core.current_buffer(state), []u8{'\n'}); + core.move_cursor_down(core.current_buffer(state)); state.mode = .Insert; sdl2.StartTextInput(); @@ -263,9 +265,9 @@ load_plugin :: proc(info: os.File_Info, in_err: os.Errno, state: rawptr) -> (err append(&state.plugins, loaded_plugin); if rel_error == .None { - fmt.println("Loaded", relative_file_path); + log.info("Loaded", relative_file_path); } else { - fmt.println("Loaded", info.fullpath); + log.info("Loaded", info.fullpath); } } } @@ -281,7 +283,7 @@ ui_font_height :: proc() -> i32 { } draw :: proc(state_with_ui: ^StateWithUi) { - buffer := &state_with_ui.state.buffers[state_with_ui.state.current_buffer]; + buffer := core.current_buffer(state_with_ui.state); buffer.glyph_buffer_height = math.min(256, int((state_with_ui.state.screen_height - state_with_ui.state.source_font_height*2) / state_with_ui.state.source_font_height)) + 1; buffer.glyph_buffer_width = math.min(256, int((state_with_ui.state.screen_width - state_with_ui.state.source_font_width) / state_with_ui.state.source_font_width)); @@ -420,7 +422,7 @@ init_plugin_vtable :: proc(ui_context: ^ui.Context) -> plugin.Plugin { extension := strings.clone(string(extension)); if _, exists := state.highlighters[extension]; exists { - fmt.eprintln("Highlighter already registered for", extension, "files"); + log.error("Highlighter already registered for", extension, "files"); } else { state.highlighters[extension] = on_color_buffer; } @@ -440,11 +442,11 @@ init_plugin_vtable :: proc(ui_context: ^ui.Context) -> plugin.Plugin { if action, exists := to_be_edited_map.key_actions[key]; exists { switch value in action.action { case core.LuaEditorAction: - fmt.eprintln("Plugin attempted to register input group on existing key action (added from Lua)"); + log.warn("Plugin attempted to register input group on existing key action (added from Lua)"); case core.PluginEditorAction: - fmt.eprintln("Plugin attempted to register input group on existing key action (added from Plugin)"); + log.warn("Plugin attempted to register input group on existing key action (added from Plugin)"); case core.EditorAction: - fmt.eprintln("Plugin attempted to register input group on existing key action"); + log.warn("Plugin attempted to register input group on existing key action"); case core.InputActions: input_map := &(&to_be_edited_map.key_actions[key]).action.(core.InputActions); register_group(state.plugin_vtable, transmute(rawptr)input_map); @@ -469,13 +471,13 @@ init_plugin_vtable :: proc(ui_context: ^ui.Context) -> plugin.Plugin { if action, exists := to_be_edited_map.key_actions[key]; exists { switch value in action.action { case core.LuaEditorAction: - fmt.eprintln("Plugin attempted to register key action on existing key action (added from Lua)"); + log.warn("Plugin attempted to register key action on existing key action (added from Lua)"); case core.PluginEditorAction: - fmt.eprintln("Plugin attempted to register key action on existing key action (added from Plugin)"); + log.warn("Plugin attempted to register key action on existing key action (added from Plugin)"); case core.EditorAction: - fmt.eprintln("Plugin attempted to register input key action on existing key action"); + log.warn("Plugin attempted to register input key action on existing key action"); case core.InputActions: - fmt.eprintln("Plugin attempted to register input key action on existing input group"); + log.warn("Plugin attempted to register input key action on existing input group"); } } else { core.register_key_action(to_be_edited_map, key, input_action, description); @@ -545,12 +547,12 @@ init_plugin_vtable :: proc(ui_context: ^ui.Context) -> plugin.Plugin { }, draw_buffer_from_index = proc "c" (buffer_index: int, x: int, y: int, glyph_buffer_width: int, glyph_buffer_height: int, show_line_numbers: bool) { context = state.ctx; - state.buffers[buffer_index].glyph_buffer_width = glyph_buffer_width; - state.buffers[buffer_index].glyph_buffer_height = glyph_buffer_height; + core.buffer_from_index(&state, buffer_index).glyph_buffer_width = glyph_buffer_width; + core.buffer_from_index(&state, buffer_index).glyph_buffer_height = glyph_buffer_height; core.draw_file_buffer( &state, - &state.buffers[buffer_index], + core.buffer_from_index(&state, buffer_index), x, y, show_line_numbers); @@ -573,7 +575,7 @@ init_plugin_vtable :: proc(ui_context: ^ui.Context) -> plugin.Plugin { get_current_buffer_iterator = proc "c" () -> plugin.BufferIter { context = state.ctx; - it := core.new_file_buffer_iter(&state.buffers[state.current_buffer]); + it := core.new_file_buffer_iter(core.current_buffer(&state)); // TODO: make this into a function return plugin.BufferIter { @@ -794,7 +796,7 @@ init_plugin_vtable :: proc(ui_context: ^ui.Context) -> plugin.Plugin { }, get_buffer_info_from_index = proc "c" (buffer_index: int) -> plugin.BufferInfo { context = state.ctx; - buffer := &state.buffers[buffer_index]; + buffer := core.buffer_from_index(&state, buffer_index); return core.into_buffer_info(&state, buffer); }, @@ -843,14 +845,14 @@ init_plugin_vtable :: proc(ui_context: ^ui.Context) -> plugin.Plugin { if should_create_buffer { new_buffer, err := core.new_file_buffer(context.allocator, strings.clone(path)); if err.type != .None { - fmt.println("Failed to open/create file buffer:", err); + log.error("Failed to open/create file buffer:", err); } else { runtime.append(&state.buffers, new_buffer); state.current_buffer = len(state.buffers)-1; - buffer = &state.buffers[state.current_buffer]; + buffer = core.current_buffer(&state); } } else { - buffer = &state.buffers[state.current_buffer]; + buffer = core.current_buffer(&state); } if buffer != nil { @@ -971,11 +973,11 @@ init_plugin_vtable :: proc(ui_context: ^ui.Context) -> plugin.Plugin { ui_file_buffer(ui_context, buffer); }, - buffer_from_index = proc "c" (ui_context: rawptr, buffer: int, show_line_numbers: bool) { + buffer_from_index = proc "c" (ui_context: rawptr, buffer_index: int, show_line_numbers: bool) { context = state.ctx; ui_context := transmute(^ui.Context)ui_context; - buffer := &state.buffers[buffer]; + buffer := core.buffer_from_index(&state, buffer_index); ui_file_buffer(ui_context, buffer); }, @@ -1026,8 +1028,13 @@ main :: proc() { highlighters = make(map[string]plugin.OnColorBufferProc), hooks = make(map[plugin.Hook][dynamic]plugin.OnHookProc), lua_hooks = make(map[plugin.Hook][dynamic]core.LuaHookRef), + + log_buffer = core.new_virtual_file_buffer(context.allocator), }; + context.logger = core.new_logger(&state.log_buffer); + state.ctx = context; + state.current_input_map = &state.input_map.mode[.Normal]; register_default_input_actions(&state.input_map.mode[.Normal]); register_default_visual_actions(&state.input_map.mode[.Visual]); @@ -1037,7 +1044,7 @@ main :: proc() { for arg in os.args[1:] { buffer, err := core.new_file_buffer(context.allocator, arg, state.directory); if err.type != .None { - fmt.println("Failed to create file buffer:", err); + log.error("Failed to create file buffer:", err); continue; } @@ -1045,12 +1052,12 @@ main :: proc() { } if sdl2.Init({.VIDEO}) < 0 { - fmt.eprintln("SDL failed to initialize:", sdl2.GetError()); + log.error("SDL failed to initialize:", sdl2.GetError()); return; } if ttf.Init() < 0 { - fmt.eprintln("SDL_TTF failed to initialize:", ttf.GetError()); + log.error("SDL_TTF failed to initialize:", ttf.GetError()); return; } defer ttf.Quit(); @@ -1068,7 +1075,7 @@ main :: proc() { } if sdl_window == nil { - fmt.eprintln("Failed to create window:", sdl2.GetError()); + log.error("Failed to create window:", sdl2.GetError()); return; } @@ -1078,7 +1085,7 @@ main :: proc() { } if state.sdl_renderer == nil { - fmt.eprintln("Failed to create renderer:", sdl2.GetError()); + log.error("Failed to create renderer:", sdl2.GetError()); return; } state.font_atlas = core.gen_font_atlas(&state, "/System/Library/Fonts/Supplemental/Andale Mono.ttf"); @@ -1139,13 +1146,13 @@ main :: proc() { } }, lua.L_Reg { - "print", + "log", proc "c" (L: ^lua.State) -> i32 { context = state.ctx; - a := lua.L_checkinteger(L, 1); + text := string(lua.L_checkstring(L, 1)); + log.info("[LUA]:", text); - fmt.printf("LUA: print(%d)\n", a); return i32(lua.OK); } }, @@ -1194,11 +1201,11 @@ main :: proc() { if action, exists := input_map.key_actions[key]; exists { switch value in action.action { case core.LuaEditorAction: - fmt.eprintln("Plugin attempted to register input group on existing key action (added from Lua)"); + log.warn("Plugin attempted to register input group on existing key action (added from Lua)"); case core.PluginEditorAction: - fmt.eprintln("Plugin attempted to register input group on existing key action (added from Plugin)"); + log.warn("Plugin attempted to register input group on existing key action (added from Plugin)"); case core.EditorAction: - fmt.eprintln("Plugin attempted to register input group on existing key action"); + log.warn("Plugin attempted to register input group on existing key action"); case core.InputActions: input_map := &(&input_map.key_actions[key]).action.(core.InputActions); table_to_action(L, lua.gettop(L), input_map); @@ -1258,7 +1265,7 @@ main :: proc() { context = state.ctx; buffer_index := int(lua.L_checkinteger(L, 1)); - if buffer_index < 0 || buffer_index >= len(state.buffers) { + if buffer_index != -2 && (buffer_index < 0 || buffer_index >= len(state.buffers)) { return i32(lua.ERRRUN); } else { state.current_buffer = buffer_index; @@ -1301,7 +1308,7 @@ main :: proc() { } } - push_lua_buffer_info(L, &state.buffers[buffer_index]); + push_lua_buffer_info(L, core.buffer_from_index(&state, buffer_index)); } return 1; @@ -1661,11 +1668,29 @@ main :: proc() { if ui_ctx != nil { buffer_index := int(lua.L_checkinteger(L, 2)); - if buffer_index < 0 || buffer_index >= len(state.buffers) { + if buffer_index != -2 && (buffer_index < 0 || buffer_index >= len(state.buffers)) { return i32(lua.ERRRUN); } - ui_file_buffer(ui_ctx, &state.buffers[buffer_index]); + ui_file_buffer(ui_ctx, core.buffer_from_index(&state, buffer_index)); + + return i32(lua.OK); + } + + return i32(lua.ERRRUN); + } + }, + lua.L_Reg { + "log_buffer", + proc "c" (L: ^lua.State) -> i32 { + context = state.ctx; + + lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA)); + lua.pushvalue(L, 1); + ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1); + + if ui_ctx != nil { + ui_file_buffer(ui_ctx, &state.log_buffer); return i32(lua.OK); } @@ -1675,6 +1700,7 @@ main :: proc() { } }; + // TODO: generate this from the plugin.Key enum lua.newtable(L); { lua.newtable(L); @@ -1702,6 +1728,9 @@ main :: proc() { lua.pushinteger(L, lua.Integer(plugin.Key.Q)); lua.setfield(L, -2, "Q"); + lua.pushinteger(L, lua.Integer(plugin.Key.BACKQUOTE)); + lua.setfield(L, -2, "Backtick"); + lua.pushinteger(L, lua.Integer(plugin.Key.ESCAPE)); lua.setfield(L, -2, "Escape"); @@ -1748,7 +1777,7 @@ main :: proc() { err := lua.tostring(L, lua.gettop(L)); lua.pop(L, lua.gettop(L)); - fmt.eprintln(err); + log.error(err); } // Initialize Lua Plugins @@ -1760,7 +1789,7 @@ main :: proc() { err := lua.tostring(L, lua.gettop(L)); lua.pop(L, lua.gettop(L)); - fmt.eprintln("failed to initialize plugin (OnInit):", err); + log.error("failed to initialize plugin (OnInit):", err); } } // ********************************************************************** @@ -1768,7 +1797,7 @@ main :: proc() { control_key_pressed: bool; for !state.should_close { // if false { - // buffer := &state.buffers[state.current_buffer]; + // buffer := core.current_buffer(&state); // ui.push_parent(&ui_context, ui.push_box(&ui_context, "main", {}, .Vertical, semantic_size = {ui.make_semantic_size(.Fill, 100), ui.make_semantic_size(.Fill, 100)})); // defer ui.pop_parent(&ui_context); @@ -1824,7 +1853,7 @@ main :: proc() { // defer ui.pop_parent(&ui_context); // { - // if ui_file_buffer(&ui_context, &state.buffers[state.current_buffer]).clicked { + // if ui_file_buffer(&ui_context, core.current_buffer(&state)).clicked { // state.current_buffer = 3; // } // } @@ -1872,6 +1901,15 @@ main :: proc() { // } // } + // TODO: move this to view.lua + // log_window, _ := ui.push_floating(&ui_context, "log", {0,0}, flags = {.Floating, .DrawBackground}, semantic_size = {ui.make_semantic_size(.PercentOfParent, 75), ui.make_semantic_size(.PercentOfParent, 75)}); + // ui.push_parent(&ui_context, log_window); + // { + // defer ui.pop_parent(&ui_context); + // ui_file_buffer(&ui_context, &state.log_buffer); + // } + + for hook_ref in state.lua_hooks[plugin.Hook.Draw] { lua.rawgeti(state.L, lua.REGISTRYINDEX, lua.Integer(hook_ref)); lua.pushlightuserdata(state.L, &ui_context); @@ -1879,7 +1917,7 @@ main :: proc() { err := lua.tostring(L, lua.gettop(L)); lua.pop(L, lua.gettop(L)); - fmt.eprintln(err); + log.error(err); } else { lua.pop(L, lua.gettop(L)); } @@ -1939,7 +1977,7 @@ main :: proc() { err := lua.tostring(L, lua.gettop(L)); lua.pop(L, lua.gettop(L)); - fmt.eprintln(err); + log.error(err); } else { lua.pop(L, lua.gettop(L)); } @@ -1964,7 +2002,7 @@ main :: proc() { err := lua.tostring(L, lua.gettop(L)); lua.pop(L, lua.gettop(L)); - fmt.eprintln(err); + log.error(err); } else { lua.pop(L, lua.gettop(L)); } @@ -1997,7 +2035,7 @@ main :: proc() { if state.window != nil && state.window.get_buffer != nil { buffer = transmute(^core.FileBuffer)(state.window.get_buffer(state.plugin_vtable, state.window.user_data)); } else { - buffer = &state.buffers[state.current_buffer]; + buffer = core.current_buffer(&state); } if sdl_event.type == .KEYDOWN { @@ -2024,7 +2062,7 @@ main :: proc() { err := lua.tostring(L, lua.gettop(L)); lua.pop(L, lua.gettop(L)); - fmt.eprintln(err); + log.error(err); } else { lua.pop(L, lua.gettop(L)); } @@ -2054,7 +2092,7 @@ main :: proc() { err := lua.tostring(L, lua.gettop(L)); lua.pop(L, lua.gettop(L)); - fmt.eprintln(err); + log.error(err); } else { lua.pop(L, lua.gettop(L)); } @@ -2077,7 +2115,7 @@ main :: proc() { buffer := transmute(^core.FileBuffer)(state.window.get_buffer(state.plugin_vtable, state.window.user_data)); do_normal_mode(&state, buffer); } else { - buffer := &state.buffers[state.current_buffer]; + buffer := core.current_buffer(&state); do_normal_mode(&state, buffer); } case .Insert: @@ -2085,14 +2123,14 @@ main :: proc() { buffer := transmute(^core.FileBuffer)(state.window.get_buffer(state.plugin_vtable, state.window.user_data)); do_insert_mode(&state, buffer); } else { - buffer := &state.buffers[state.current_buffer]; + buffer := core.current_buffer(&state); do_insert_mode(&state, buffer); } case .Visual: if state.window != nil && state.window.get_buffer != nil { // TODO } else { - buffer := &state.buffers[state.current_buffer]; + buffer := core.current_buffer(&state); do_visual_mode(&state, buffer); } } diff --git a/src/plugin/plugin.odin b/src/plugin/plugin.odin index 8c2b85f..a9e9ef5 100644 --- a/src/plugin/plugin.odin +++ b/src/plugin/plugin.odin @@ -3,6 +3,7 @@ package plugin; import "core:intrinsics" import "core:dynlib" import "core:fmt" +import "core:log" import "../theme" @@ -419,7 +420,7 @@ load_proc_address :: proc(lib_path: string, library: dynlib.Library, symbol: str if address, found := dynlib.symbol_address(library, symbol); found { return transmute(ProcType)address; } else { - fmt.println("Could not find symbol", symbol, "in library", lib_path); + log.warn("Could not find symbol", symbol, "in library", lib_path); } return nil; diff --git a/src/ui/imm.odin b/src/ui/imm.odin index 3eaf9ca..dec8fbe 100644 --- a/src/ui/imm.odin +++ b/src/ui/imm.odin @@ -3,6 +3,7 @@ package ui import "core:fmt" import "core:strings" import "core:math" +import "core:log" import "vendor:sdl2" import "../core" @@ -652,19 +653,19 @@ debug_print :: proc(ctx: ^Context, box: ^Box, depth: int = 0) { for box, idx in iterate_box(&iter, true) { for _ in 0..<(depth*6) { - fmt.print("-"); + log.debug("-"); } if depth > 0 { - fmt.print(">"); + log.debug(">"); } - fmt.println(idx, "Box _", box.label, "#", box.key.label, "ptr", transmute(rawptr)box); //, "_ first", transmute(rawptr)box.first, "parent", transmute(rawptr)box.parent, box.computed_size); + log.debug(idx, "Box _", box.label, "#", box.key.label, "ptr", transmute(rawptr)box); //, "_ first", transmute(rawptr)box.first, "parent", transmute(rawptr)box.parent, box.computed_size); debug_print(ctx, box, depth+1); } if depth == 0 { - fmt.println("persistent"); + log.debug("persistent"); for p in ctx.persistent { - fmt.println(p); + log.debug(p); } } } diff --git a/todo.md b/todo.md new file mode 100644 index 0000000..bdf3524 --- /dev/null +++ b/todo.md @@ -0,0 +1,12 @@ +- Finish selections + - Guarantee that start and end are always ordered + - Add in text actions + - Yank + - Delete + - Change +- Re-write the UI (again) +- Re-do dylib plugin system +- Re-do Lua plugin system +- Persist end of line cursor position +- Generate key mappings from the plugin.Key enum +- Fix jumping forward a word jumping past consecutive brackets