woo, copy and paste
parent
5559441180
commit
8c352355de
|
@ -41,6 +41,9 @@ State :: struct {
|
|||
current_buffer: int,
|
||||
buffers: [dynamic]FileBuffer,
|
||||
|
||||
// TODO: make more than one register to plop stuff into
|
||||
yank_register: Register,
|
||||
|
||||
log_buffer: FileBuffer,
|
||||
|
||||
current_input_map: ^InputActions,
|
||||
|
@ -53,6 +56,11 @@ State :: struct {
|
|||
panels: util.StaticList(Panel),
|
||||
}
|
||||
|
||||
Register :: struct {
|
||||
whole_line: bool,
|
||||
data: []u8,
|
||||
}
|
||||
|
||||
EditorCommand :: struct {
|
||||
name: string,
|
||||
description: string,
|
||||
|
@ -127,6 +135,63 @@ current_buffer :: proc(state: ^State) -> ^FileBuffer {
|
|||
return &state.buffers[state.current_buffer];
|
||||
}
|
||||
|
||||
yank_whole_line :: proc(state: ^State) {
|
||||
if state.yank_register.data != nil {
|
||||
delete(state.yank_register.data)
|
||||
state.yank_register.data = nil
|
||||
}
|
||||
|
||||
if buffer := current_buffer(state); buffer != nil {
|
||||
selection := new_selection(buffer, buffer.cursor)
|
||||
length := selection_length(buffer, selection)
|
||||
|
||||
state.yank_register.whole_line = true
|
||||
state.yank_register.data = make([]u8, length)
|
||||
|
||||
it := new_file_buffer_iter_with_cursor(buffer, selection.start)
|
||||
|
||||
index := 0
|
||||
for !it.hit_end && index < length {
|
||||
state.yank_register.data[index] = get_character_at_iter(it)
|
||||
|
||||
iterate_file_buffer(&it)
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
yank_selection :: proc(state: ^State) {
|
||||
if state.yank_register.data != nil {
|
||||
delete(state.yank_register.data)
|
||||
state.yank_register.data = nil
|
||||
}
|
||||
|
||||
if buffer := current_buffer(state); buffer != nil && buffer.selection != nil {
|
||||
selection := swap_selections(buffer.selection.?)
|
||||
length := selection_length(buffer, selection)
|
||||
|
||||
state.yank_register.whole_line = false
|
||||
state.yank_register.data = make([]u8, length)
|
||||
|
||||
it := new_file_buffer_iter_with_cursor(buffer, selection.start)
|
||||
|
||||
index := 0
|
||||
for !it.hit_end && index < length {
|
||||
state.yank_register.data[index] = get_character_at_iter(it)
|
||||
|
||||
iterate_file_buffer(&it)
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
paste_register :: proc(state: ^State, register: Register) {
|
||||
if buffer := current_buffer(state); buffer != nil && register.data != nil {
|
||||
insert_content(buffer, register.data)
|
||||
move_cursor_left(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
reset_input_map_from_state_mode :: proc(state: ^State) {
|
||||
reset_input_map_from_mode(state, state.mode)
|
||||
}
|
||||
|
|
|
@ -644,15 +644,25 @@ new_selection_span :: proc(start: Cursor, end: Cursor) -> Selection {
|
|||
};
|
||||
}
|
||||
|
||||
new_selection :: proc{new_selection_zero_length, new_selection_span};
|
||||
new_selection_current_line :: proc(buffer: ^FileBuffer, cursor: Cursor) -> Selection {
|
||||
start := cursor
|
||||
end := cursor
|
||||
|
||||
move_cursor_start_of_line(buffer, &start)
|
||||
move_cursor_end_of_line(buffer, true, &end)
|
||||
|
||||
return {
|
||||
start = start,
|
||||
end = end,
|
||||
}
|
||||
}
|
||||
|
||||
new_selection :: proc{new_selection_zero_length, new_selection_span, new_selection_current_line};
|
||||
|
||||
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)
|
||||
{
|
||||
if is_selection_inverted(selection) {
|
||||
swapped.start = selection.end
|
||||
swapped.end = selection.start
|
||||
}
|
||||
|
@ -660,6 +670,28 @@ swap_selections :: proc(selection: Selection) -> (swapped: Selection) {
|
|||
return swapped
|
||||
}
|
||||
|
||||
is_selection_inverted :: proc(selection: Selection) -> bool {
|
||||
return 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)
|
||||
}
|
||||
|
||||
selection_length :: proc(buffer: ^FileBuffer, selection: Selection) -> int {
|
||||
selection := selection
|
||||
it := new_file_buffer_iter_with_cursor(buffer, selection.start)
|
||||
|
||||
length := 0
|
||||
|
||||
for !it.hit_end && !is_selection_inverted(selection) {
|
||||
iterate_file_buffer(&it);
|
||||
|
||||
selection.start = it.cursor
|
||||
length += 1
|
||||
}
|
||||
|
||||
return length
|
||||
}
|
||||
|
||||
new_virtual_file_buffer :: proc(allocator: mem.Allocator) -> FileBuffer {
|
||||
context.allocator = allocator;
|
||||
width := 256;
|
||||
|
|
|
@ -179,6 +179,31 @@ register_default_visual_actions :: proc(input_map: ^core.InputActions) {
|
|||
sdl2.StartTextInput();
|
||||
}, "change selection");
|
||||
}
|
||||
|
||||
// Copy-Paste
|
||||
{
|
||||
core.register_key_action(input_map, .Y, proc(state: ^State) {
|
||||
core.yank_selection(state)
|
||||
|
||||
state.mode = .Normal;
|
||||
core.reset_input_map(state)
|
||||
|
||||
core.current_buffer(state).selection = nil;
|
||||
core.update_file_buffer_scroll(core.current_buffer(state))
|
||||
}, "Yank Line");
|
||||
|
||||
core.register_key_action(input_map, .P, proc(state: ^State) {
|
||||
if state.yank_register.whole_line {
|
||||
core.insert_content(core.current_buffer(state), []u8{'\n'});
|
||||
core.paste_register(state, state.yank_register)
|
||||
core.insert_content(core.current_buffer(state), []u8{'\n'});
|
||||
} else {
|
||||
core.paste_register(state, state.yank_register)
|
||||
}
|
||||
|
||||
core.reset_input_map(state)
|
||||
}, "Paste");
|
||||
}
|
||||
}
|
||||
|
||||
register_default_text_input_actions :: proc(input_map: ^core.InputActions) {
|
||||
|
@ -201,4 +226,31 @@ register_default_text_input_actions :: proc(input_map: ^core.InputActions) {
|
|||
|
||||
sdl2.StartTextInput();
|
||||
}, "insert mode on newline");
|
||||
|
||||
// Copy-Paste
|
||||
{
|
||||
{
|
||||
yank_actions := core.new_input_actions()
|
||||
defer core.register_key_action(input_map, .Y, yank_actions)
|
||||
|
||||
core.register_key_action(&yank_actions, .Y, proc(state: ^State) {
|
||||
core.yank_whole_line(state)
|
||||
|
||||
core.reset_input_map(state)
|
||||
}, "Yank Line");
|
||||
}
|
||||
|
||||
core.register_key_action(input_map, .P, proc(state: ^State) {
|
||||
if state.yank_register.whole_line {
|
||||
core.move_cursor_end_of_line(core.current_buffer(state), false);
|
||||
core.insert_content(core.current_buffer(state), []u8{'\n'});
|
||||
} else {
|
||||
core.move_cursor_right(core.current_buffer(state))
|
||||
}
|
||||
core.paste_register(state, state.yank_register)
|
||||
|
||||
core.reset_input_map(state)
|
||||
}, "Paste");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -181,14 +181,16 @@ render_file_buffer :: proc(state: ^core.State, s: ^ui.State, buffer: ^core.FileB
|
|||
ui.open_element(s, nil, { kind = {ui.Grow{}, ui.Grow{}}})
|
||||
ui.close_element(s)
|
||||
|
||||
it := core.new_file_buffer_iter_with_cursor(buffer, buffer.cursor)
|
||||
ui.open_element(
|
||||
s,
|
||||
fmt.tprintf(
|
||||
"%v:%v - Slice %v:%v",
|
||||
"%v:%v - Slice %v:%v - Char: %v",
|
||||
buffer.cursor.line + 1,
|
||||
buffer.cursor.col + 1,
|
||||
buffer.cursor.index.slice_index,
|
||||
buffer.cursor.index.content_index
|
||||
buffer.cursor.index.content_index,
|
||||
core.get_character_at_iter(it)
|
||||
),
|
||||
{}
|
||||
)
|
||||
|
|
7
todo.md
7
todo.md
|
@ -37,9 +37,12 @@
|
|||
- Finish selections
|
||||
- [x] Guarantee that start and end are always ordered
|
||||
- Add in text actions
|
||||
- [ ] Yank
|
||||
- [x] Yank
|
||||
- [x] Delete
|
||||
- [x] Change
|
||||
- [ ] Change
|
||||
- [ ] Change
|
||||
- [ ] Change word
|
||||
- [ ] Change inside delimiter
|
||||
- Virtual Whitespace
|
||||
- Allow any-sized tabs
|
||||
- Modify input system to allow for keybinds that take input
|
||||
|
|
Loading…
Reference in New Issue