editor commands + text deletion
parent
f0eae25439
commit
402205a2e3
|
@ -25,8 +25,8 @@ local MovingTabInBetween = false
|
|||
local LastMouseX = 0
|
||||
local LastMouseY = 0
|
||||
|
||||
function buffer_list_iter()
|
||||
local idx = 0
|
||||
function buffer_list_iter(start)
|
||||
local idx = start
|
||||
return function ()
|
||||
buffer_info = Editor.buffer_info_from_index(idx)
|
||||
idx = idx + 1
|
||||
|
@ -49,6 +49,30 @@ function centered(ctx, label, axis, width, height, body)
|
|||
UI.pop_parent(ctx)
|
||||
end
|
||||
|
||||
function list_iter(start, list)
|
||||
local idx = start
|
||||
|
||||
return function()
|
||||
local value = list[idx]
|
||||
idx = idx + 1
|
||||
return value, idx-1
|
||||
end
|
||||
end
|
||||
|
||||
function list(ctx, label, selection_index, list, render_func)
|
||||
list_with_iter(ctx, label, selection_index, list_iter(selection_index, list), render_func)
|
||||
end
|
||||
|
||||
function list_with_iter(ctx, label, selection_index, list_iter, render_func)
|
||||
local num_items = 10
|
||||
|
||||
UI.push_parent(ctx, UI.push_rect(ctx, label, true, true, UI.Vertical, UI.Fill, UI.Fill))
|
||||
for data, i in list_iter do
|
||||
render_func(ctx, data, i == selection_index)
|
||||
end
|
||||
UI.pop_parent(ctx)
|
||||
end
|
||||
|
||||
function lerp(from, to, rate)
|
||||
return (1 - rate) * from + rate*to
|
||||
end
|
||||
|
@ -97,7 +121,7 @@ function ui_sidemenu(ctx)
|
|||
UI.pop_parent(ctx)
|
||||
UI.push_rect(ctx, "padded bottom open files", false, false, UI.Horizontal, UI.Fill, UI.Exact(8))
|
||||
|
||||
for buffer_info, i in buffer_list_iter() do
|
||||
for buffer_info, i in buffer_list_iter(0) do
|
||||
button_container = UI.push_rect(ctx, "button container"..i, false, false, UI.Horizontal, UI.Fill, UI.ChildrenSum)
|
||||
UI.push_parent(ctx, button_container)
|
||||
flags = {"Clickable", "Hoverable", "DrawText"}
|
||||
|
@ -309,19 +333,32 @@ function render_buffer_search(ctx)
|
|||
UI.push_parent(ctx, UI.push_floating(ctx, "buffer search canvas", 0, 0))
|
||||
centered(ctx, "buffer search 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))
|
||||
UI.push_parent(ctx, UI.push_rect(ctx, "buffer list", false, false, UI.Vertical, UI.Fill, UI.Fill))
|
||||
for buffer_info, i in buffer_list_iter() do
|
||||
list_with_iter(ctx, "buffer list", BufferSearchIndex, buffer_list_iter(BufferSearchIndex),
|
||||
function(ctx, buffer_info, is_selected)
|
||||
flags = {"DrawText"}
|
||||
|
||||
if i == BufferSearchIndex then
|
||||
if is_selected then
|
||||
table.insert(flags, 1, "DrawBorder")
|
||||
end
|
||||
|
||||
interaction = UI.advanced_button(ctx, " "..buffer_info.file_path.." ", flags, UI.Fill, UI.FitText)
|
||||
end
|
||||
UI.pop_parent(ctx)
|
||||
)
|
||||
UI.buffer(ctx, BufferSearchIndex)
|
||||
UI.pop_parent(ctx)
|
||||
|
||||
-- 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, "buffer list", false, false, UI.Vertical, UI.Fill, UI.Fill))
|
||||
-- for buffer_info, i in buffer_list_iter() do
|
||||
-- flags = {"DrawText"}
|
||||
--
|
||||
-- if i == BufferSearchIndex then
|
||||
-- table.insert(flags, 1, "DrawBorder")
|
||||
-- end
|
||||
-- interaction = UI.advanced_button(ctx, " "..buffer_info.file_path.." ", flags, UI.Fill, UI.FitText)
|
||||
-- end
|
||||
-- UI.pop_parent(ctx)
|
||||
-- UI.buffer(ctx, BufferSearchIndex)
|
||||
-- UI.pop_parent(ctx)
|
||||
end
|
||||
))
|
||||
UI.pop_parent(ctx)
|
||||
|
@ -346,23 +383,21 @@ function render_command_search(ctx)
|
|||
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), (
|
||||
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
|
||||
list(ctx, "command list", CommandSearchIndex, CommandList,
|
||||
function(ctx, cmd, is_selected)
|
||||
flags = {"DrawText"}
|
||||
|
||||
if i == CommandSearchIndex then
|
||||
if is_selected 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
|
||||
|
@ -416,7 +451,7 @@ function OnInit()
|
|||
end
|
||||
end)},
|
||||
{Editor.Key.Space, "", {
|
||||
{Editor.Key.Backtick, "Command Palette",
|
||||
{Editor.Key.P, "Command Palette",
|
||||
(function ()
|
||||
CommandSearchOpen = true
|
||||
CommandSearchIndex = 1
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package core
|
||||
|
||||
import "base:runtime"
|
||||
import "base:intrinsics"
|
||||
import "core:reflect"
|
||||
import "core:fmt"
|
||||
import "core:log"
|
||||
|
@ -78,6 +79,8 @@ State :: struct {
|
|||
current_input_map: ^InputActions,
|
||||
|
||||
commands: EditorCommandList,
|
||||
command_arena: runtime.Allocator,
|
||||
command_args: [dynamic]EditorCommandArgument,
|
||||
|
||||
plugins: [dynamic]plugin.Interface,
|
||||
plugin_vtable: plugin.Plugin,
|
||||
|
@ -92,6 +95,16 @@ EditorCommand :: struct {
|
|||
action: EditorAction,
|
||||
}
|
||||
|
||||
EditorCommandExec :: struct {
|
||||
num_args: int,
|
||||
args: [dynamic]EditorCommandArgument,
|
||||
}
|
||||
|
||||
EditorCommandArgument :: union {
|
||||
string,
|
||||
i32
|
||||
}
|
||||
|
||||
current_buffer :: proc(state: ^State) -> ^FileBuffer {
|
||||
if state.current_buffer == -2 {
|
||||
return &state.log_buffer;
|
||||
|
@ -123,6 +136,7 @@ add_hook :: proc(state: ^State, hook: plugin.Hook, hook_proc: plugin.OnHookProc)
|
|||
add_lua_hook :: proc(state: ^State, hook: plugin.Hook, hook_ref: LuaHookRef) {
|
||||
if _, exists := state.lua_hooks[hook]; !exists {
|
||||
state.lua_hooks[hook] = make([dynamic]LuaHookRef);
|
||||
log.info("added lua hook", hook)
|
||||
}
|
||||
|
||||
runtime.append(&state.lua_hooks[hook], hook_ref);
|
||||
|
@ -289,7 +303,26 @@ query_editor_commands_by_group :: proc(command_list: ^EditorCommandList, name: s
|
|||
return commands[:];
|
||||
}
|
||||
|
||||
push_command_arg :: proc(state: ^State, command_arg: EditorCommandArgument) {
|
||||
context.allocator = state.command_arena;
|
||||
|
||||
if state.command_args == nil {
|
||||
state.command_args = make([dynamic]EditorCommandArgument);
|
||||
}
|
||||
|
||||
append(&state.command_args, command_arg)
|
||||
}
|
||||
|
||||
run_command :: proc(state: ^State, group: string, name: string) {
|
||||
if state.command_args == nil {
|
||||
state.command_args = make([dynamic]EditorCommandArgument);
|
||||
}
|
||||
|
||||
defer {
|
||||
state.command_args = nil
|
||||
runtime.free_all(state.command_arena)
|
||||
}
|
||||
|
||||
if cmds, ok := state.commands[group]; ok {
|
||||
for cmd in cmds {
|
||||
if cmd.name == name {
|
||||
|
@ -302,3 +335,55 @@ run_command :: proc(state: ^State, group: string, name: string) {
|
|||
|
||||
log.error("no command", group, name);
|
||||
}
|
||||
|
||||
attempt_read_command_args :: proc($T: typeid, args: []EditorCommandArgument) -> (value: T, ok: bool)
|
||||
where intrinsics.type_is_struct(T) {
|
||||
ti := runtime.type_info_base(type_info_of(T));
|
||||
|
||||
#partial switch v in ti.variant {
|
||||
case runtime.Type_Info_Struct:
|
||||
{
|
||||
if int(v.field_count) != len(args) {
|
||||
ok = false
|
||||
log.error("invalid number of arguments", len(args), ", expected", v.field_count);
|
||||
return
|
||||
}
|
||||
|
||||
for arg, i in args {
|
||||
switch varg in arg {
|
||||
case string:
|
||||
{
|
||||
if _, is_string := v.types[i].variant.(runtime.Type_Info_String); !is_string {
|
||||
ok = false
|
||||
log.error("invalid argument #", i, "given to command, found string, expected", v.types[i].variant)
|
||||
return
|
||||
}
|
||||
|
||||
value_string: ^string = transmute(^string)(uintptr(&value) + v.offsets[i])
|
||||
value_string^ = varg
|
||||
}
|
||||
case i32:
|
||||
{
|
||||
|
||||
if _, is_integer := v.types[i].variant.(runtime.Type_Info_Integer); !is_integer {
|
||||
ok = false
|
||||
log.error("invalid argument #", i, "given to command, unexpected integer, expected", v.types[i].variant)
|
||||
return
|
||||
}
|
||||
|
||||
value_i32: ^i32 = transmute(^i32)(uintptr(&value) + v.offsets[i])
|
||||
value_i32^ = varg
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case:
|
||||
{
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ok = true
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package core;
|
||||
|
||||
import "core:os"
|
||||
import "core:log"
|
||||
import "core:path/filepath"
|
||||
import "core:mem"
|
||||
import "core:fmt"
|
||||
|
@ -644,6 +645,20 @@ new_selection_span :: proc(start: Cursor, end: Cursor) -> Selection {
|
|||
|
||||
new_selection :: proc{new_selection_zero_length, new_selection_span};
|
||||
|
||||
swap_selections :: proc(selection: Selection) -> (swapped: Selection) {
|
||||
swapped = selection
|
||||
|
||||
if selection.start.index.slice_index > selection.end.index.slice_index ||
|
||||
(selection.start.index.slice_index == selection.end.index.slice_index
|
||||
&& selection.start.index.content_index > selection.end.index.content_index)
|
||||
{
|
||||
swapped.start = selection.end
|
||||
swapped.end = selection.start
|
||||
}
|
||||
|
||||
return swapped
|
||||
}
|
||||
|
||||
new_virtual_file_buffer :: proc(allocator: mem.Allocator) -> FileBuffer {
|
||||
context.allocator = allocator;
|
||||
width := 256;
|
||||
|
@ -937,16 +952,22 @@ 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 && current_buffer(state) == buffer {
|
||||
selection := swap_selections(buffer.selection.?)
|
||||
// selection := buffer.selection.?
|
||||
|
||||
sel_x := x + padding;
|
||||
width: int
|
||||
|
||||
if begin+j >= buffer.selection.?.start.line && begin+j <= buffer.selection.?.end.line {
|
||||
if begin+j == buffer.selection.?.end.line {
|
||||
width = buffer.selection.?.end.col * state.source_font_width;
|
||||
if begin+j >= selection.start.line && begin+j <= selection.end.line {
|
||||
if begin+j == selection.start.line && selection.start.line == selection.end.line {
|
||||
width = (selection.end.col - selection.start.col) * state.source_font_width;
|
||||
sel_x += selection.start.col * state.source_font_width;
|
||||
} else if begin+j == selection.end.line {
|
||||
width = selection.end.col * state.source_font_width;
|
||||
} else {
|
||||
if begin+j == buffer.selection.?.start.line {
|
||||
width = (line_length - buffer.selection.?.start.col) * state.source_font_width;
|
||||
sel_x += buffer.selection.?.start.col * state.source_font_width;
|
||||
if begin+j == selection.start.line {
|
||||
width = (line_length - selection.start.col) * state.source_font_width;
|
||||
sel_x += selection.start.col * state.source_font_width;
|
||||
} else {
|
||||
width = line_length * state.source_font_width;
|
||||
}
|
||||
|
@ -1027,22 +1048,44 @@ insert_content :: proc(buffer: ^FileBuffer, to_be_inserted: []u8, append_to_end:
|
|||
}
|
||||
|
||||
// TODO: potentially add FileBufferIndex as parameter
|
||||
split_content_slice :: proc(buffer: ^FileBuffer) {
|
||||
if buffer.cursor.index.content_index == 0 {
|
||||
split_content_slice_from_cursor :: proc(buffer: ^FileBuffer, cursor: ^Cursor) -> (did_split: bool) {
|
||||
if cursor.index.content_index == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
end_slice := buffer.content_slices[buffer.cursor.index.slice_index][buffer.cursor.index.content_index:];
|
||||
buffer.content_slices[buffer.cursor.index.slice_index] = buffer.content_slices[buffer.cursor.index.slice_index][:buffer.cursor.index.content_index];
|
||||
end_slice := buffer.content_slices[cursor.index.slice_index][cursor.index.content_index:];
|
||||
buffer.content_slices[cursor.index.slice_index] = buffer.content_slices[cursor.index.slice_index][:cursor.index.content_index];
|
||||
|
||||
inject_at(&buffer.content_slices, buffer.cursor.index.slice_index+1, end_slice);
|
||||
inject_at(&buffer.content_slices, cursor.index.slice_index+1, end_slice);
|
||||
|
||||
// TODO: maybe move this out of this function
|
||||
buffer.cursor.index.slice_index += 1;
|
||||
buffer.cursor.index.content_index = 0;
|
||||
cursor.index.slice_index += 1;
|
||||
cursor.index.content_index = 0;
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
delete_content :: proc(buffer: ^FileBuffer, amount: int) {
|
||||
split_content_slice_from_selection :: proc(buffer: ^FileBuffer, selection: ^Selection) {
|
||||
// TODO: swap selections
|
||||
|
||||
log.info("start:", selection.start, "- end:", selection.end);
|
||||
|
||||
// move the end cursor forward one (we want the splitting to be exclusive, not inclusive)
|
||||
it := new_file_buffer_iter_with_cursor(buffer, selection.end);
|
||||
iterate_file_buffer(&it);
|
||||
selection.end = it.cursor;
|
||||
|
||||
split_content_slice_from_cursor(buffer, &selection.end);
|
||||
if split_content_slice_from_cursor(buffer, &selection.start) {
|
||||
selection.end.index.slice_index += 1;
|
||||
}
|
||||
|
||||
log.info("start:", selection.start, "- end:", selection.end);
|
||||
}
|
||||
|
||||
split_content_slice :: proc{split_content_slice_from_cursor, split_content_slice_from_selection};
|
||||
|
||||
delete_content_from_buffer_cursor :: proc(buffer: ^FileBuffer, amount: int) {
|
||||
if amount <= len(buffer.input_buffer) {
|
||||
runtime.resize(&buffer.input_buffer, len(buffer.input_buffer)-amount);
|
||||
} else {
|
||||
|
@ -1053,7 +1096,7 @@ delete_content :: proc(buffer: ^FileBuffer, amount: int) {
|
|||
return;
|
||||
}
|
||||
|
||||
split_content_slice(buffer);
|
||||
split_content_slice(buffer, &buffer.cursor);
|
||||
|
||||
it := new_file_buffer_iter_with_cursor(buffer, buffer.cursor);
|
||||
|
||||
|
@ -1086,3 +1129,27 @@ delete_content :: proc(buffer: ^FileBuffer, amount: int) {
|
|||
}
|
||||
}
|
||||
|
||||
delete_content_from_selection :: proc(buffer: ^FileBuffer, selection: ^Selection) {
|
||||
assert(len(buffer.content_slices) >= 1);
|
||||
|
||||
selection^ = swap_selections(selection^)
|
||||
|
||||
split_content_slice(buffer, selection);
|
||||
|
||||
it := new_file_buffer_iter_with_cursor(buffer, selection.start);
|
||||
|
||||
// go back one (to be at the end of the content slice)
|
||||
iterate_file_buffer_reverse(&it);
|
||||
|
||||
for _ in selection.start.index.slice_index..<selection.end.index.slice_index {
|
||||
runtime.ordered_remove(&buffer.content_slices, selection.start.index.slice_index);
|
||||
}
|
||||
|
||||
if !it.hit_end {
|
||||
iterate_file_buffer(&it);
|
||||
}
|
||||
buffer.cursor = it.cursor;
|
||||
}
|
||||
|
||||
delete_content :: proc{delete_content_from_buffer_cursor, delete_content_from_selection};
|
||||
|
||||
|
|
|
@ -182,12 +182,13 @@ register_default_visual_actions :: proc(input_map: ^core.InputActions) {
|
|||
state.current_input_map = &state.input_map.mode[.Normal];
|
||||
|
||||
core.current_buffer(state).selection = nil;
|
||||
core.update_file_buffer_scroll(core.current_buffer(state))
|
||||
}, "exit visual mode");
|
||||
|
||||
// Cursor Movement
|
||||
{
|
||||
core.register_key_action(input_map, .W, proc(state: ^State) {
|
||||
sel_cur := core.current_buffer(state).selection.?;
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.move_cursor_forward_start_of_word(core.current_buffer(state), cursor = &sel_cur.end);
|
||||
}, "move forward one word");
|
||||
|
@ -235,6 +236,20 @@ register_default_visual_actions :: proc(input_map: ^core.InputActions) {
|
|||
core.scroll_file_buffer(core.current_buffer(state), .Down, cursor = &sel_cur.end);
|
||||
}, "scroll buffer up");
|
||||
}
|
||||
|
||||
// Text Modification
|
||||
{
|
||||
core.register_key_action(input_map, .D, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.delete_content(core.current_buffer(state), sel_cur);
|
||||
|
||||
state.mode = .Normal
|
||||
state.current_input_map = &state.input_map.mode[.Normal];
|
||||
core.current_buffer(state).selection = nil;
|
||||
core.update_file_buffer_scroll(core.current_buffer(state))
|
||||
}, "delete selection");
|
||||
}
|
||||
}
|
||||
|
||||
register_default_text_input_actions :: proc(input_map: ^core.InputActions) {
|
||||
|
@ -253,7 +268,6 @@ register_default_text_input_actions :: proc(input_map: ^core.InputActions) {
|
|||
core.register_key_action(input_map, .O, proc(state: ^State) {
|
||||
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();
|
||||
|
@ -1023,6 +1037,9 @@ lua_ui_flags :: proc(L: ^lua.State, index: i32) -> (bit_set[ui.Flag], bool) {
|
|||
}
|
||||
|
||||
main :: proc() {
|
||||
_command_arena: mem.Arena
|
||||
mem.arena_init(&_command_arena, make([]u8, 1024*1024));
|
||||
|
||||
state = State {
|
||||
ctx = context,
|
||||
screen_width = 640,
|
||||
|
@ -1031,6 +1048,7 @@ main :: proc() {
|
|||
source_font_height = 16,
|
||||
input_map = core.new_input_map(),
|
||||
commands = make(core.EditorCommandList),
|
||||
command_arena = mem.arena_allocator(&_command_arena),
|
||||
|
||||
window = nil,
|
||||
directory = os.get_current_directory(),
|
||||
|
@ -1042,9 +1060,28 @@ main :: proc() {
|
|||
log_buffer = core.new_virtual_file_buffer(context.allocator),
|
||||
};
|
||||
|
||||
// TODO: please move somewhere else
|
||||
{
|
||||
ti := runtime.type_info_base(type_info_of(plugin.Hook));
|
||||
if v, ok := ti.variant.(runtime.Type_Info_Enum); ok {
|
||||
for i in &v.values {
|
||||
state.hooks[cast(plugin.Hook)i] = make([dynamic]plugin.OnHookProc);
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
ti := runtime.type_info_base(type_info_of(plugin.Hook));
|
||||
if v, ok := ti.variant.(runtime.Type_Info_Enum); ok {
|
||||
for i in &v.values {
|
||||
state.lua_hooks[cast(plugin.Hook)i] = make([dynamic]core.LuaHookRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.logger = core.new_logger(&state.log_buffer);
|
||||
state.ctx = context;
|
||||
|
||||
// TODO: don't use this
|
||||
mem.scratch_allocator_init(&scratch, 1024*1024);
|
||||
scratch_alloc = mem.scratch_allocator(&scratch);
|
||||
|
||||
|
@ -1064,6 +1101,31 @@ main :: proc() {
|
|||
runtime.append(&state.buffers, buffer);
|
||||
}
|
||||
)
|
||||
core.register_editor_command(
|
||||
&state.commands,
|
||||
"nl.spacegirl.editor.core",
|
||||
"Open File",
|
||||
"Opens a file in a new buffer",
|
||||
proc(state: ^State) {
|
||||
log.info("open file args:");
|
||||
|
||||
Args :: struct {
|
||||
file_path: string
|
||||
}
|
||||
|
||||
if args, ok := core.attempt_read_command_args(Args, state.command_args[:]); ok {
|
||||
log.info("attempting to open file", args.file_path)
|
||||
|
||||
buffer, err := core.new_file_buffer(context.allocator, args.file_path, state.directory);
|
||||
if err.type != .None {
|
||||
log.error("Failed to create file buffer:", err);
|
||||
return;
|
||||
}
|
||||
|
||||
runtime.append(&state.buffers, buffer);
|
||||
}
|
||||
}
|
||||
)
|
||||
core.register_editor_command(
|
||||
&state.commands,
|
||||
"nl.spacegirl.editor.core",
|
||||
|
@ -1074,14 +1136,6 @@ main :: proc() {
|
|||
}
|
||||
)
|
||||
|
||||
{
|
||||
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);
|
||||
|
@ -2156,6 +2210,27 @@ main :: proc() {
|
|||
|
||||
sdl2.StopTextInput();
|
||||
}
|
||||
case .TAB: {
|
||||
// TODO: change this to insert a tab character
|
||||
for _ in 0..<4 {
|
||||
append(&buffer.input_buffer, ' ');
|
||||
|
||||
for hook_proc in state.hooks[plugin.Hook.BufferInput] {
|
||||
hook_proc(state.plugin_vtable, buffer);
|
||||
}
|
||||
for hook_ref in state.lua_hooks[plugin.Hook.BufferInput] {
|
||||
lua.rawgeti(state.L, lua.REGISTRYINDEX, lua.Integer(hook_ref));
|
||||
if lua.pcall(state.L, 0, 0, 0) != i32(lua.OK) {
|
||||
err := lua.tostring(state.L, lua.gettop(state.L));
|
||||
lua.pop(state.L, lua.gettop(state.L));
|
||||
|
||||
log.error(err);
|
||||
} else {
|
||||
lua.pop(state.L, lua.gettop(state.L));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case .BACKSPACE: {
|
||||
core.delete_content(buffer, 1);
|
||||
|
||||
|
|
10
todo.md
10
todo.md
|
@ -1,20 +1,20 @@
|
|||
- Stack Like Allocator (for cross-frame temp data)
|
||||
|
||||
- Undo/Redo
|
||||
- Edit History Tree
|
||||
- Finish selections
|
||||
- Guarantee that start and end are always ordered
|
||||
- Add in text actions
|
||||
- Yank
|
||||
- Delete
|
||||
- [x] Delete
|
||||
- Change
|
||||
- Virtual Whitespace
|
||||
- Allow any-sized tabs
|
||||
- Modify input system to allow for keybinds that take input
|
||||
- Vim's f and F movement commands
|
||||
- Vim's r command
|
||||
- Command Search and Execution
|
||||
- Palette based UI?
|
||||
- Registering Plugin Commands that can be run in palette and via other plugins
|
||||
- A way to query these commands by-plugin
|
||||
- [ ] Registering Plugin Commands that can be run in palette and via other plugins
|
||||
- [x] A way to query these commands by-plugin
|
||||
- Re-write the UI (again)
|
||||
- Re-do plugin system
|
||||
- Potentially have a C# plugins system? Use it instead of Lua? (probably not)
|
||||
|
|
Loading…
Reference in New Issue