woo, copy and paste
parent
5559441180
commit
8c352355de
|
@ -41,6 +41,9 @@ State :: struct {
|
||||||
current_buffer: int,
|
current_buffer: int,
|
||||||
buffers: [dynamic]FileBuffer,
|
buffers: [dynamic]FileBuffer,
|
||||||
|
|
||||||
|
// TODO: make more than one register to plop stuff into
|
||||||
|
yank_register: Register,
|
||||||
|
|
||||||
log_buffer: FileBuffer,
|
log_buffer: FileBuffer,
|
||||||
|
|
||||||
current_input_map: ^InputActions,
|
current_input_map: ^InputActions,
|
||||||
|
@ -53,6 +56,11 @@ State :: struct {
|
||||||
panels: util.StaticList(Panel),
|
panels: util.StaticList(Panel),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Register :: struct {
|
||||||
|
whole_line: bool,
|
||||||
|
data: []u8,
|
||||||
|
}
|
||||||
|
|
||||||
EditorCommand :: struct {
|
EditorCommand :: struct {
|
||||||
name: string,
|
name: string,
|
||||||
description: string,
|
description: string,
|
||||||
|
@ -127,6 +135,63 @@ current_buffer :: proc(state: ^State) -> ^FileBuffer {
|
||||||
return &state.buffers[state.current_buffer];
|
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_state_mode :: proc(state: ^State) {
|
||||||
reset_input_map_from_mode(state, state.mode)
|
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) {
|
swap_selections :: proc(selection: Selection) -> (swapped: Selection) {
|
||||||
swapped = selection
|
swapped = selection
|
||||||
|
|
||||||
if selection.start.index.slice_index > selection.end.index.slice_index ||
|
if is_selection_inverted(selection) {
|
||||||
(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.start = selection.end
|
||||||
swapped.end = selection.start
|
swapped.end = selection.start
|
||||||
}
|
}
|
||||||
|
@ -660,6 +670,28 @@ swap_selections :: proc(selection: Selection) -> (swapped: Selection) {
|
||||||
return swapped
|
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 {
|
new_virtual_file_buffer :: proc(allocator: mem.Allocator) -> FileBuffer {
|
||||||
context.allocator = allocator;
|
context.allocator = allocator;
|
||||||
width := 256;
|
width := 256;
|
||||||
|
|
|
@ -179,6 +179,31 @@ register_default_visual_actions :: proc(input_map: ^core.InputActions) {
|
||||||
sdl2.StartTextInput();
|
sdl2.StartTextInput();
|
||||||
}, "change selection");
|
}, "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) {
|
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();
|
sdl2.StartTextInput();
|
||||||
}, "insert mode on newline");
|
}, "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.open_element(s, nil, { kind = {ui.Grow{}, ui.Grow{}}})
|
||||||
ui.close_element(s)
|
ui.close_element(s)
|
||||||
|
|
||||||
|
it := core.new_file_buffer_iter_with_cursor(buffer, buffer.cursor)
|
||||||
ui.open_element(
|
ui.open_element(
|
||||||
s,
|
s,
|
||||||
fmt.tprintf(
|
fmt.tprintf(
|
||||||
"%v:%v - Slice %v:%v",
|
"%v:%v - Slice %v:%v - Char: %v",
|
||||||
buffer.cursor.line + 1,
|
buffer.cursor.line + 1,
|
||||||
buffer.cursor.col + 1,
|
buffer.cursor.col + 1,
|
||||||
buffer.cursor.index.slice_index,
|
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
|
- Finish selections
|
||||||
- [x] Guarantee that start and end are always ordered
|
- [x] Guarantee that start and end are always ordered
|
||||||
- Add in text actions
|
- Add in text actions
|
||||||
- [ ] Yank
|
- [x] Yank
|
||||||
- [x] Delete
|
- [x] Delete
|
||||||
- [x] Change
|
- [ ] Change
|
||||||
|
- [ ] Change
|
||||||
|
- [ ] Change word
|
||||||
|
- [ ] Change inside delimiter
|
||||||
- Virtual Whitespace
|
- Virtual Whitespace
|
||||||
- Allow any-sized tabs
|
- Allow any-sized tabs
|
||||||
- Modify input system to allow for keybinds that take input
|
- Modify input system to allow for keybinds that take input
|
||||||
|
|
Loading…
Reference in New Issue