From 111f2f8a70792c498c73335251b31e8bc34d8b39 Mon Sep 17 00:00:00 2001 From: Patrick Cleavelin Date: Wed, 19 Feb 2025 18:19:56 -0600 Subject: [PATCH] command stuff? --- Makefile | 4 +- flake.nix | 6 ++- liblua54.a => liblua5.4.a | Bin plugins/lua/view.lua | 74 +++++++++++++++++++++++++++++++ src/core/core.odin | 69 ++++++++++++++++++++++++++++- src/core/file_buffer.odin | 2 +- src/main.odin | 89 +++++++++++++++++++++++++++++++++++--- todo.md | 2 + 8 files changed, 235 insertions(+), 11 deletions(-) rename liblua54.a => liblua5.4.a (100%) diff --git a/Makefile b/Makefile index b5b794e..76f8687 100644 --- a/Makefile +++ b/Makefile @@ -3,10 +3,10 @@ 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 - odin build src/ -out:bin/editor -lld + ../Odin/odin build src/ -out:bin/editor -debug -lld -extra-linker-flags:"-L./" odin_highlighter: plugins/highlighter/src/*.odin - odin build plugins/highlighter/src/ -build-mode:dll -no-entry-point -out:bin/highlighter + ../Odin/odin build plugins/highlighter/src/ -build-mode:dll -no-entry-point -out:bin/highlighter grep: plugins/grep/src/*.rs nightly-cargo b --manifest-path=plugins/grep/Cargo.toml diff --git a/flake.nix b/flake.nix index 9a0eda0..1283e28 100755 --- a/flake.nix +++ b/flake.nix @@ -63,13 +63,17 @@ { devShell = pkgs.mkShell { buildInputs = with pkgs; (if pkgs.system == "aarch64-darwin" || pkgs.system == "x86_64-darwin" then [ - fixed-odin + # fixed-odin + llvmPackages_17.bintools + llvmPackages_17.lld + llvmPackages_17.clang local-rust nightly-cargo rust-analyzer lua54Packages.stdlib SDL2 SDL2_ttf + darwin.apple_sdk.frameworks.System darwin.apple_sdk.frameworks.CoreData darwin.apple_sdk.frameworks.Kernel darwin.apple_sdk.frameworks.CoreVideo diff --git a/liblua54.a b/liblua5.4.a similarity index 100% rename from liblua54.a rename to liblua5.4.a diff --git a/plugins/lua/view.lua b/plugins/lua/view.lua index 47ae4b1..a001a22 100644 --- a/plugins/lua/view.lua +++ b/plugins/lua/view.lua @@ -1,6 +1,10 @@ local BufferSearchOpen = false local BufferSearchOpenElapsed = 0 +local CommandSearchOpen = false +local CommandSearchOpenElapsed = 0 +local CommandList = {} + local LogWindowOpen = false local LogWindowOpenElapsed = 0 @@ -280,6 +284,7 @@ function render_ui_window(ctx) end render_buffer_search(ctx) + render_command_search(ctx) render_log_window(ctx) LastMouseX = x @@ -323,6 +328,45 @@ function render_buffer_search(ctx) end end +function render_command_search(ctx) + if CommandSearchOpen or CommandSearchOpenElapsed > 0 then + if CommandSearchOpen and CommandSearchOpenElapsed < numFrames then + CommandSearchOpenElapsed = CommandSearchOpenElapsed + 1 + elseif not CommandSearchOpen and CommandSearchOpenElapsed > 0 then + CommandSearchOpenElapsed = CommandSearchOpenElapsed - 1 + end + end + + if CommandSearchOpen or CommandSearchOpenElapsed > 0 then + window_percent_width = 75 + window_percent_height = 25 + if CommandSearchOpenElapsed > 0 then + window_percent_width = ((CommandSearchOpenElapsed/numFrames) * 75) + window_percent_height = ((CommandSearchOpenElapsed/numFrames) * 25) + end + + UI.push_parent(ctx, UI.push_floating(ctx, "buffer search canvas", 0, 0)) + centered(ctx, "command search window", UI.Horizontal, UI.PercentOfParent(window_percent_width), UI.PercentOfParent(window_percent_height), ( + function () + UI.push_parent(ctx, UI.push_rect(ctx, "window", true, true, UI.Horizontal, UI.Fill, UI.Fill)) + UI.push_parent(ctx, UI.push_rect(ctx, "command list", false, false, UI.Vertical, UI.Fill, UI.Fill)) + -- local commands = Editor.query_command_group("nl.spacegirl.editor.core") + for i, cmd in ipairs(CommandList) do + flags = {"DrawText"} + + if i == CommandSearchIndex then + table.insert(flags, 1, "DrawBorder") + end + interaction = UI.advanced_button(ctx, " "..cmd.name..": "..cmd.description.." ", flags, UI.Fill, UI.FitText) + end + UI.pop_parent(ctx) + UI.pop_parent(ctx) + end + )) + UI.pop_parent(ctx) + end +end + function render_log_window(ctx) if Editor.get_current_buffer_index() ~= -2 then LogWindowOpen = false @@ -372,6 +416,36 @@ function OnInit() end end)}, {Editor.Key.Space, "", { + {Editor.Key.Backtick, "Command Palette", + (function () + CommandSearchOpen = true + CommandSearchIndex = 1 + + CommandList = Editor.query_command_group("nl.spacegirl.editor.core") + end), + { + {Editor.Key.Escape, "Close Window", ( + function () + Editor.request_window_close() + CommandSearchOpen = false + end + )}, + {Editor.Key.Enter, "Run Command", ( + function () + if CommandList[CommandSearchIndex] ~= nil then + Editor.run_command("nl.spacegirl.editor.core", CommandList[CommandSearchIndex]["name"]) + CommandList = {} + + Editor.request_window_close() + CommandSearchOpen = false + end + end + )}, + -- TODO: don't scroll past selections + {Editor.Key.K, "Move Selection Up", (function () CommandSearchIndex = CommandSearchIndex - 1 end)}, + {Editor.Key.J, "Move Selection Down", (function () CommandSearchIndex = CommandSearchIndex + 1 end)}, + } + }, {Editor.Key.B, "Buffer Search", ( function () BufferSearchOpen = true diff --git a/src/core/core.odin b/src/core/core.odin index b7d6cba..986618c 100644 --- a/src/core/core.odin +++ b/src/core/core.odin @@ -46,6 +46,7 @@ close_window_and_free :: proc(state: ^State) { } LuaHookRef :: i32; +EditorCommandList :: map[string][dynamic]EditorCommand; State :: struct { ctx: runtime.Context, L: ^lua.State, @@ -76,6 +77,8 @@ State :: struct { input_map: InputMap, current_input_map: ^InputActions, + commands: EditorCommandList, + plugins: [dynamic]plugin.Interface, plugin_vtable: plugin.Plugin, highlighters: map[string]plugin.OnColorBufferProc, @@ -83,6 +86,12 @@ State :: struct { lua_hooks: map[plugin.Hook][dynamic]LuaHookRef, } +EditorCommand :: struct { + name: string, + description: string, + action: EditorAction, +} + current_buffer :: proc(state: ^State) -> ^FileBuffer { if state.current_buffer == -2 { return &state.log_buffer; @@ -142,7 +151,7 @@ new_input_map :: proc() -> InputMap { ti := runtime.type_info_base(type_info_of(Mode)); if v, ok := ti.variant.(runtime.Type_Info_Enum); ok { for i in &v.values { - input_map.mode[(cast(^Mode)(&i))^] = new_input_actions(); + input_map.mode[cast(Mode)i] = new_input_actions(); } } @@ -158,7 +167,7 @@ new_input_actions :: proc() -> InputActions { return input_actions; } delete_input_map :: proc(input_map: ^InputMap) { - for _, actions in &input_map.mode { + for _, &actions in &input_map.mode { delete_input_actions(&actions); } delete(input_map.mode); @@ -233,3 +242,59 @@ register_ctrl_key_action_group :: proc(input_map: ^InputActions, key: plugin.Key register_key_action :: proc{register_plugin_key_action_single, register_key_action_single, register_key_action_group}; register_ctrl_key_action :: proc{register_ctrl_key_action_single, register_ctrl_key_action_group}; + +register_editor_command :: proc(command_list: ^EditorCommandList, command_group, name, description: string, action: EditorAction) { + if _, ok := command_list[command_group]; !ok { + command_list[command_group] = make([dynamic]EditorCommand); + } + + runtime.append(&command_list[command_group], EditorCommand { + name = name, + description = description, + action = action, + }); +} + +query_editor_commands_by_name :: proc(command_list: ^EditorCommandList, name: string, allocator: runtime.Allocator) -> []EditorCommand { + context.allocator = allocator; + commands := make([dynamic]EditorCommand) + + for group, list in command_list { + for cmd in list { + if cmd.name == name { + append(&commands, cmd); + } + } + } + + return commands[:]; +} + +query_editor_commands_by_group :: proc(command_list: ^EditorCommandList, name: string, allocator: runtime.Allocator) -> []EditorCommand { + context.allocator = allocator; + commands := make([dynamic]EditorCommand) + + for group, list in command_list { + if group == name { + for cmd in list { + append(&commands, cmd); + } + } + } + + return commands[:]; +} + +run_command :: proc(state: ^State, group: string, name: string) { + if cmds, ok := state.commands[group]; ok { + for cmd in cmds { + if cmd.name == name { + log.info("Running command", group, name); + cmd.action(state); + return; + } + } + } + + log.error("no command", group, name); +} diff --git a/src/core/file_buffer.odin b/src/core/file_buffer.odin index 27cdc74..2dc292c 100644 --- a/src/core/file_buffer.odin +++ b/src/core/file_buffer.odin @@ -679,7 +679,7 @@ new_file_buffer :: proc(allocator: mem.Allocator, file_path: string, base_dir: s defer os.close(fd); fi, fstat_err := os.fstat(fd); - if fstat_err > 0 { + if fstat_err != nil { return FileBuffer{}, make_error(ErrorType.FileIOError, fmt.aprintf("failed to get file info: errno=%x", fstat_err)); } diff --git a/src/main.odin b/src/main.odin index ca63937..62c477e 100644 --- a/src/main.odin +++ b/src/main.odin @@ -21,6 +21,9 @@ import "plugin" State :: core.State; FileBuffer :: core.FileBuffer; +// TODO: should probably go into state +scratch: mem.Scratch; +scratch_alloc: runtime.Allocator; state := core.State {}; StateWithUi :: struct { @@ -1022,6 +1025,8 @@ main :: proc() { source_font_width = 8, source_font_height = 16, input_map = core.new_input_map(), + commands = make(core.EditorCommandList), + window = nil, directory = os.get_current_directory(), plugins = make([dynamic]plugin.Interface), @@ -1035,19 +1040,55 @@ main :: proc() { context.logger = core.new_logger(&state.log_buffer); state.ctx = context; + mem.scratch_allocator_init(&scratch, 1024*1024); + scratch_alloc = mem.scratch_allocator(&scratch); + 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]); register_default_text_input_actions(&state.input_map.mode[.Normal]); - for arg in os.args[1:] { - buffer, err := core.new_file_buffer(context.allocator, arg, state.directory); - if err.type != .None { - log.error("Failed to create file buffer:", err); - continue; + core.register_editor_command( + &state.commands, + "nl.spacegirl.editor.core", + "New Scratch Buffer", + "Opens a new scratch buffer", + proc(state: ^State) { + buffer := core.new_virtual_file_buffer(context.allocator); + runtime.append(&state.buffers, buffer); } + ) + core.register_editor_command( + &state.commands, + "nl.spacegirl.editor.core", + "Quit", + "Quits the application", + proc(state: ^State) { + state.should_close = true + } + ) + { + cmds := core.query_editor_commands_by_group(&state.commands, "nl.spacegirl.editor.core", scratch_alloc); + log.info("List of commands:"); + for cmd in cmds { + log.info(cmd.name, ":", cmd.description); + } + } + + if len(os.args) > 1 { + for arg in os.args[1:] { + buffer, err := core.new_file_buffer(context.allocator, arg, state.directory); + if err.type != .None { + log.error("Failed to create file buffer:", err); + continue; + } + + runtime.append(&state.buffers, buffer); + } + } else { + buffer := core.new_virtual_file_buffer(context.allocator); runtime.append(&state.buffers, buffer); } @@ -1314,6 +1355,44 @@ main :: proc() { return 1; } }, + lua.L_Reg { + "query_command_group", + proc "c" (L: ^lua.State) -> i32 { + context = state.ctx; + + group := lua.L_checkstring(L, 1); + cmds := core.query_editor_commands_by_group(&state.commands, string(group), scratch_alloc); + + lua.newtable(L); + { + for cmd, i in cmds { + lua.newtable(L); + { + lua.pushstring(L, strings.clone_to_cstring(cmd.name, scratch_alloc)); + lua.setfield(L, -2, "name"); + + lua.pushstring(L, strings.clone_to_cstring(cmd.description, scratch_alloc)); + lua.setfield(L, -2, "description"); + } + lua.rawseti(L, -2, lua.Integer(i+1)); + } + } + + return 1; + } + }, + lua.L_Reg { + "run_command", + proc "c" (L: ^lua.State) -> i32 { + context = state.ctx; + + group := lua.L_checkstring(L, 1); + name := lua.L_checkstring(L, 2); + core.run_command(&state, string(group), string(name)); + + return 1; + } + } }; bbb = raw_data(editor_lib[:]); diff --git a/todo.md b/todo.md index 8f34283..c8eec23 100644 --- a/todo.md +++ b/todo.md @@ -1,3 +1,5 @@ +- Stack Like Allocator (for cross-frame temp data) + - Undo/Redo - Edit History Tree - Finish selections