4coder/code/custom/4coder_yeet.cpp

1049 lines
38 KiB
C++

/*
// YEET SHEET. By Luke Perkin. luke@locogame.co.uk
// Header comment last edited 21st 03/2021
//
// "yeet" regions of text into a seperate buffer called a "yeet sheet".
// The yeet sheet will be kept in sync with any text changes.
// This allows you to have multiple portals into various files in your codebase.
//
// == IMPLEMENTATION ==
// @include "4coder_loco_yeets.cpp" in your custom layer.
//
// > loco_render_buffer(Application_Links *app, View_ID view_id, Face_ID face_id, Buffer_ID buffer, Text_Layout_ID text_layout_id, Rect_f32 rect, Frame_Info frame_info)
// Call this is your custom layer's "render" hook.
//
// > loco_on_buffer_edit(Application_Links *app, Buffer_ID buffer_id, Range_i64 old_range, Range_i64 new_range)
// Call this in your custom layer's "on buffer edit" hook.
//
// > loco_yeet_on_buffer_end(Application_Links *app, Buffer_ID buffer_id)
// Call this in your custom layer's "on buffer end" hook.
//
// == COMMANDS ==
// The main command you will want to bind to a key is the yeet range command:
// > loco_yeet_selected_range_or_jump
// That will yeet the selected range, or if the cursor is already inside a yeet range, jump to the location
// in the yeet buffer, or vice versa.
//
// > loco_yeet_surrounding_function
// Selects the surrounding function then yeets it.
//
// > loco_yeet_tag
// Queries user for a string then searches all open buffers for that string
// as a comment tag (i.e. "// @tag") and yeets the scope it precedes.
//
// > loco_yeet_clear
// Clears all current yeets.
//
// > loco_yeet_reset_all
// Clears all yeets in all slots, also remove the markers from the original buffers.
//
// > loco_yeet_remove_marker_pair
// Removes a single 'yeet', whatever one the cursor is currently inside.
//
// > loco_save_snapshot_1
// > loco_save_snapshot_2
// > loco_save_snapshot_3
// Saves the current collection of yeets to a slot.
// Note that these do not persist if you close the editor.
//
// > loco_load_yeet_snapshot_1
// > loco_load_yeet_snapshot_2
// > loco_load_yeet_snapshot_3
// Loads a yeet snapshot into the yeet buffer.
//
// > loco_jump_between_yeet
// Will attempt to jump to the corresponding location in the linked buffer.
//
// == CONFIG ==
// There are currently a few global variables below, their variable names are self-explanatory.
//
*/
CUSTOM_ID(attachment, loco_marker_handle);
CUSTOM_ID(attachment, loco_marker_pair_handle);
//--TYPES
// @yeettype
struct Loco_Marker_Pair
{
i32 start_marker_idx;
i32 end_marker_idx;
i32 yeet_start_marker_idx;
i32 yeet_end_marker_idx;
Buffer_ID buffer;
};
// @yeettype
struct Loco_Yeets
{
Loco_Marker_Pair pairs[1024];
i32 pairs_count;
};
// @yeettype
struct Loco_Yeets_Snapshots
{
Loco_Yeets snapshots[3];
i32 snapshots_count;
};
//--GLOBALS
global bool loco_yeet_make_yeet_buffer_active_on_yeet = false;
global bool loco_yeet_show_highlight_ranges = true;
global bool loco_yeet_show_source_comment = true;
global FColor loco_yeet_source_comment_color = fcolor_change_alpha(f_green, 0.35f);
global FColor loco_yeet_highlight_start_color = fcolor_argb(0.f, 1.f, 0.f, 0.06f);
global FColor loco_yeet_highlight_end_color = fcolor_argb(0.f, 0.f, 1.f, 0.05f);
global Loco_Yeets_Snapshots yeets_snapshots = {};
// This is set to true when editing either buffer so you don't get
// infinite echoes of syncronization.
global bool lock_yeet_buffer = false;
// Set this to true to also delete the markers in the original buffer.
// I've left this at false because we might want to store different
// 'collections' of yeeted functions, switch between them would
// be as simple as saving a snapshot of the Loco_Yeets structure.
global bool loco_yeets_delete_og_markers = false;
//--IMPLEMENTATIONS
//~
static Buffer_ID
loco_get_yeet_buffer(Application_Links *app)
{
String_Const_u8 yeet_name = string_u8_litexpr("*yeet*");
Buffer_ID yeet_buffer = get_buffer_by_name(app, yeet_name, Access_Always);
if (!buffer_exists(app, yeet_buffer))
{
yeet_buffer = create_buffer(app, yeet_name, BufferCreate_AlwaysNew);
buffer_set_setting(app, yeet_buffer, BufferSetting_Unimportant, true);
}
return yeet_buffer;
}
//~ @marker @buffer
static Marker*
loco_get_buffer_markers(Application_Links *app, Arena *arena, Buffer_ID buffer_id, i32* count)
{
// Adds a certain thing.
Managed_Scope scope = buffer_get_managed_scope(app, buffer_id);
Managed_Object* markers_obj = scope_attachment(app, scope, loco_marker_handle, Managed_Object);
*count = managed_object_get_item_count(app, *markers_obj);
Marker* markers = push_array(arena, Marker, *count);
managed_object_load_data(app, *markers_obj, 0, *count, markers);
return markers;
}
//~ @marker @buffer @overwrite
static void
loco_overwrite_buffer_markers(Application_Links *app, Arena *arena, Buffer_ID buffer_id, Marker* markers, i32 count)
{
Managed_Scope scope = buffer_get_managed_scope(app, buffer_id);
Managed_Object* markers_obj = scope_attachment(app, scope, loco_marker_handle, Managed_Object);
managed_object_free(app, *markers_obj);
*markers_obj = alloc_buffer_markers_on_buffer(
app,
buffer_id,
count,
&scope
);
managed_object_store_data(app, *markers_obj, 0, count, markers);
}
//~ @overwrite
static void
loco_overwrite_yeets(Application_Links *app, Buffer_ID yeet_buffer, Loco_Yeets* yeets)
{
Managed_Scope yeet_scope = buffer_get_managed_scope(app, yeet_buffer);
Managed_Object* pair_obj = scope_attachment(app, yeet_scope, loco_marker_pair_handle, Managed_Object);
i32 count = managed_object_get_item_count(app, *pair_obj);
if (count == 0)
{
*pair_obj = alloc_managed_memory_in_scope(
app, yeet_scope, sizeof(Loco_Yeets), 1);
}
managed_object_store_data(app, *pair_obj, 0, 1, yeets);
}
//~ @buffer
static Loco_Yeets
loco_get_buffer_yeets(Application_Links *app, Buffer_ID buffer_id)
{
Managed_Scope scope = buffer_get_managed_scope(app, buffer_id);
Managed_Object* man_obj = scope_attachment(app, scope, loco_marker_pair_handle, Managed_Object);
Loco_Yeets yeets = {};
managed_object_load_data(app, *man_obj, 0, 1, &yeets);
return yeets;
}
//~ @marker @append
// Append an array of markers to the buffer's managed objects.
static i32
loco_append_markers(Application_Links *app, Buffer_ID buffer_id, Marker* new_markers, i32 count)
{
Managed_Scope scope;
scope = buffer_get_managed_scope(app, buffer_id);
Scratch_Block scratch(app);
Temp_Memory marker_temp = begin_temp(scratch);
Managed_Object* markers_obj = scope_attachment(app, scope, loco_marker_handle, Managed_Object);
i32 marker_count = managed_object_get_item_count(app, *markers_obj);
Marker* old_markers = push_array(scratch, Marker, marker_count);
managed_object_load_data(app, *markers_obj, 0, marker_count, old_markers);
managed_object_free(app, *markers_obj);
*markers_obj = alloc_buffer_markers_on_buffer(
app,
buffer_id,
marker_count + count,
&scope
);
managed_object_store_data(app, *markers_obj, 0, marker_count, old_markers);
managed_object_store_data(app, *markers_obj, marker_count, count, new_markers);
end_temp(marker_temp);
return marker_count;
}
//~ @marker @range
// Pass in a buffer and a range of indices into a Marker array, this will retrieve the Markers
// array from its scope. Use loco_make_range_from_markers if you already have the Markers.
static Range_i64
loco_get_marker_range(Application_Links *app, Buffer_ID buffer_id, i32 start_idx, i32 end_idx)
{
Scratch_Block scratch(app);
i32 count = 0;
Marker* markers = loco_get_buffer_markers(app, scratch, buffer_id, &count);
if (markers != 0)
{
i64 start = markers[start_idx].pos;
i64 end = markers[end_idx].pos;
return Ii64(start, end);
}
return Ii64(0, 0);
}
//~ @marker @range
// Pass in an array of Markers and a range of indices to that array and this outputs
// a Range of character positions.
static Range_i64
loco_make_range_from_markers(Marker* markers, i32 start_idx, i32 end_idx)
{
i64 start = markers[start_idx].pos;
i64 end = markers[end_idx].pos;
return Ii64(start, end);
}
//~
static void
loco_delete_marker_pair(Application_Links *app, Buffer_ID yeet_buffer, Loco_Yeets *yeets, i32 i)
{
Scratch_Block scratch(app);
// Swap delete pairs.
Loco_Marker_Pair pair = yeets->pairs[i];
yeets->pairs[i] = yeets->pairs[yeets->pairs_count-1];
yeets->pairs_count -= 1;
Loco_Marker_Pair &new_pair = yeets->pairs[i];
// Need to swap out the indices too
// becuase we've swapped deleted the markers so their index has changed.
if (loco_yeets_delete_og_markers && new_pair.buffer == pair.buffer)
{
new_pair.start_marker_idx = pair.start_marker_idx;
new_pair.end_marker_idx = pair.end_marker_idx;
}
// Always need to set the yeet_marker indices.
new_pair.yeet_start_marker_idx = pair.yeet_start_marker_idx;
new_pair.yeet_end_marker_idx = pair.yeet_end_marker_idx;
// Swap delete yeet markers.
i32 yeet_marker_count = 0;
Marker* yeet_markers = loco_get_buffer_markers(app, scratch, yeet_buffer, &yeet_marker_count);
yeet_markers[pair.yeet_start_marker_idx] = yeet_markers[yeet_marker_count - 2];
yeet_markers[pair.yeet_end_marker_idx] = yeet_markers[yeet_marker_count - 1];
yeet_marker_count -= 2;
// Swap delete og markers.
if (loco_yeets_delete_og_markers)
{
i32 og_marker_count = 0;
Marker* og_markers = loco_get_buffer_markers(app, scratch, pair.buffer, &og_marker_count);
og_markers[pair.start_marker_idx] = og_markers[og_marker_count - 2];
og_markers[pair.end_marker_idx] = og_markers[og_marker_count - 1];
og_marker_count -= 2;
loco_overwrite_buffer_markers(app, scratch, pair.buffer, og_markers, og_marker_count);
}
// Store markers in memory.
loco_overwrite_buffer_markers(app, scratch, yeet_buffer, yeet_markers, yeet_marker_count);
loco_overwrite_yeets(app, yeet_buffer, yeets);
Range_i64 yeet_range = loco_make_range_from_markers(yeet_markers, pair.yeet_start_marker_idx, pair.yeet_end_marker_idx);
String_Const_u8 empty_str = string_u8_litexpr("");
buffer_replace_range(app, yeet_buffer, yeet_range, empty_str);
}
//~ @buffer @edit
static void
loco_on_yeet_buffer_edit(Application_Links *app, Buffer_ID buffer_id, Range_i64 old_range, Range_i64 new_range)
{
Scratch_Block scratch(app);
Loco_Yeets yeets = loco_get_buffer_yeets(app, buffer_id);
i32 yeet_markers_count = 0;
Marker* yeet_markers = loco_get_buffer_markers(app, scratch, buffer_id, &yeet_markers_count);
for (size_t i = 0; i < yeets.pairs_count; i++)
{
Loco_Marker_Pair& pair = yeets.pairs[i];
if (!buffer_exists(app, pair.buffer)) continue;
Range_i64 yeet_range = loco_make_range_from_markers(yeet_markers, pair.yeet_start_marker_idx, pair.yeet_end_marker_idx);
if (old_range.min > yeet_range.min && new_range.max < yeet_range.max)
{
// User edited inside a yeet block.
Scratch_Block og_scratch(app);
i32 og_markers_count = 0;
Marker* og_markers = loco_get_buffer_markers(app, og_scratch, pair.buffer, &og_markers_count);
Range_i64 og_range = loco_make_range_from_markers(og_markers, pair.start_marker_idx, pair.end_marker_idx);
String_Const_u8 string = push_buffer_range(app, og_scratch, buffer_id, yeet_range);
buffer_replace_range(
app,
pair.buffer,
og_range,
string
);
}
}
}
//~ @buffer @edit
static void
loco_on_original_buffer_edit(Application_Links *app, Buffer_ID buffer_id, Range_i64 old_range, Range_i64 new_range)
{
// Get the yeet buffer.
Buffer_ID yeet_buffer = loco_get_yeet_buffer(app);
if (!buffer_exists(app, yeet_buffer) || buffer_id == yeet_buffer) return;
// Check if this buffer exists in the yeet list.
Loco_Yeets yeets = loco_get_buffer_yeets(app, yeet_buffer);
bool exists_in_yeets = false;
for (i32 i = 0; i < yeets.pairs_count; i++)
{
if (yeets.pairs[i].buffer == buffer_id)
{
exists_in_yeets = true;
break;
}
}
if (!exists_in_yeets) return;
// Get yeet list and markers for both buffers.
Scratch_Block scratch(app);
i32 yeet_markers_count = 0;
Marker* yeet_markers = loco_get_buffer_markers(app, scratch, yeet_buffer, &yeet_markers_count);
i32 og_markers_count = 0;
Marker* og_markers = loco_get_buffer_markers(app, scratch, buffer_id, &og_markers_count);
for (i32 i = 0; i < yeets.pairs_count; i++)
{
Loco_Marker_Pair pair = yeets.pairs[i];
if (pair.buffer != buffer_id) continue;
Range_i64 og_range = loco_make_range_from_markers(
og_markers, pair.start_marker_idx, pair.end_marker_idx);
if (old_range.min > og_range.min && new_range.max < og_range.max)
{
// User edited inside an original buffer block.
Range_i64 yeet_range = loco_make_range_from_markers(
yeet_markers, pair.yeet_start_marker_idx, pair.yeet_end_marker_idx);
String_Const_u8 string = push_buffer_range(app, scratch, buffer_id, og_range);
buffer_replace_range(
app,
yeet_buffer,
yeet_range,
string
);
}
}
}
//~ @api @buffer @edit
api(LOCO) void
loco_on_buffer_edit(Application_Links *app, Buffer_ID buffer_id, Range_i64 old_range, Range_i64 new_range)
{
Buffer_ID yeet_buffer = get_buffer_by_name(app, string_u8_litexpr("*yeet*"), Access_Always);
if (buffer_id == yeet_buffer)
{
if (!lock_yeet_buffer)
{
lock_yeet_buffer = true;
loco_on_yeet_buffer_edit(app, buffer_id, old_range, new_range);
lock_yeet_buffer = false;
}
}
else if (!lock_yeet_buffer)
{
lock_yeet_buffer = true;
loco_on_original_buffer_edit(app, buffer_id, old_range, new_range);
lock_yeet_buffer = false;
}
}
//~ @api @buffer @render
api(LOCO) void
loco_render_buffer(Application_Links *app, View_ID view_id, Face_ID face_id,
Buffer_ID buffer, Text_Layout_ID text_layout_id,
Rect_f32 rect, Frame_Info frame_info)
{
String_Const_u8 name = string_u8_litexpr("*yeet*");
Buffer_ID yeet_buffer = get_buffer_by_name(app, name, Access_Always);
if (!buffer_exists(app, yeet_buffer)) return;
Loco_Yeets yeets = loco_get_buffer_yeets(app, yeet_buffer);
if (buffer == yeet_buffer && loco_yeet_show_source_comment)
{
Scratch_Block scratch(app);
i32 markers_count = 0;
Marker* markers = loco_get_buffer_markers(app, scratch, yeet_buffer, &markers_count);
f32 line_height = get_view_line_height(app, view_id);
FColor comment_color = loco_yeet_source_comment_color;
for (i32 i = 0; i < yeets.pairs_count; i++)
{
Loco_Marker_Pair pair = yeets.pairs[i];
if (!buffer_exists(app, pair.buffer)) continue;
Range_i64 og_range = loco_get_marker_range(
app, pair.buffer, pair.start_marker_idx, pair.end_marker_idx);
i64 start_line = get_line_number_from_pos(app, pair.buffer, og_range.min);
i64 end_line = get_line_number_from_pos(app, pair.buffer, og_range.max);
Fancy_Line line = {};
String_Const_u8 unique_name = push_buffer_unique_name(app, scratch, pair.buffer);
push_fancy_string(scratch, &line, fcolor_zero(), unique_name);
push_fancy_stringf(scratch, &line, fcolor_zero(), " - Lines: %3.lld - %3.lld", start_line, end_line);
i64 start_pos = markers[pair.yeet_start_marker_idx].pos;
Rect_f32 start_rect = text_layout_character_on_screen(app, text_layout_id, start_pos);
Vec2_f32 comment_pos = { start_rect.x0 + 0, start_rect.y0 - line_height };
draw_fancy_line(app, face_id, comment_color, &line, comment_pos);
}
}
if (loco_yeet_show_highlight_ranges)
{
FColor start_color = loco_yeet_highlight_start_color;
FColor end_color = loco_yeet_highlight_end_color;
for (i32 i = 0; i < yeets.pairs_count; i++)
{
Loco_Marker_Pair pair = yeets.pairs[i];
Range_i64 range = {};
if (pair.buffer == buffer || buffer == yeet_buffer)
{
if (buffer == yeet_buffer)
{
range = loco_get_marker_range(app, buffer, pair.yeet_start_marker_idx, pair.yeet_end_marker_idx);
}
else
{
range = loco_get_marker_range(app, buffer, pair.start_marker_idx, pair.end_marker_idx);
}
i64 start_line_number = get_line_number_from_pos(app, buffer, range.min);
draw_line_highlight(
app,
text_layout_id,
start_line_number,
start_color);
i64 end_line_number = get_line_number_from_pos(app, buffer, range.max);
draw_line_highlight(
app,
text_layout_id,
end_line_number,
end_color);
}
}
}
}
//~ @api @buffer
api(LOCO) void
loco_on_buffer_end(Application_Links *app, Buffer_ID buffer_id)
{
Buffer_ID yeet_buffer = loco_get_yeet_buffer(app);
Loco_Yeets yeets = loco_get_buffer_yeets(app, yeet_buffer);
for (i32 i = yeets.pairs_count - 1; i >= 0; i--)
{
if (yeets.pairs[i].buffer == buffer_id)
{
loco_delete_marker_pair(app, yeet_buffer, &yeets, i);
}
}
}
//~
static bool
loco_is_cursor_inside_yeet(Application_Links *app, i64 cursor_pos, i64 *out_dst_cursor_pos, Buffer_ID *out_dst_buffer)
{
View_ID view = get_active_view(app, Access_Always);
Buffer_ID buffer = view_get_buffer(app, view, Access_Always);
Buffer_ID yeet_buffer = loco_get_yeet_buffer(app);
bool is_yeet_buffer = (buffer == yeet_buffer);
Loco_Yeets yeets = loco_get_buffer_yeets(app, yeet_buffer);
for (i32 i = 0; i < yeets.pairs_count; i++)
{
Loco_Marker_Pair pair = yeets.pairs[i];
Buffer_ID src_buf = is_yeet_buffer ? yeet_buffer : pair.buffer;
i32 src_start = is_yeet_buffer ? pair.yeet_start_marker_idx : pair.start_marker_idx;
i32 src_end = is_yeet_buffer ? pair.yeet_end_marker_idx : pair.end_marker_idx;
Range_i64 src_range = loco_get_marker_range(app, src_buf, src_start, src_end);
bool is_cursor_inside = (cursor_pos >= src_range.min && cursor_pos <= src_range.max);
if (is_cursor_inside)
{
Buffer_ID dst_buf = !is_yeet_buffer ? yeet_buffer : pair.buffer;
i32 dst_start = !is_yeet_buffer ? pair.yeet_start_marker_idx : pair.start_marker_idx;
i32 dst_end = !is_yeet_buffer ? pair.yeet_end_marker_idx : pair.end_marker_idx;
Range_i64 dst_range = loco_get_marker_range(app, dst_buf, dst_start, dst_end);
if (out_dst_cursor_pos != 0)
{
*out_dst_cursor_pos = dst_range.min + (cursor_pos - src_range.min);
}
if (out_dst_buffer != 0)
{
*out_dst_buffer = dst_buf;
}
return true;
}
}
return false;
}
//~ @jump @buffer
static void
loco_jump_to_buffer(Application_Links *app, Buffer_ID dst_buffer, i64 dst_cursor)
{
View_ID view = get_next_view_after_active(app, Access_Always);
view_set_buffer(app, view, dst_buffer, 0);
view_set_active(app, view);
view_set_cursor_and_preferred_x(app, view, seek_pos(dst_cursor));
if (auto_center_after_jumps)
{
center_view(app);
}
}
//~ @jump
static bool
loco_try_jump_between_yeet_pair(Application_Links *app)
{
View_ID view = get_active_view(app, Access_Always);
i64 cursor_pos = view_get_cursor_pos(app, view);
i64 dst_cursor_pos = 0;
Buffer_ID dst_buffer = 0;
bool success = loco_is_cursor_inside_yeet(app, cursor_pos, &dst_cursor_pos, &dst_buffer);
if (success)
{
loco_jump_to_buffer(app, dst_buffer, dst_cursor_pos);
}
return success;
}
//~ @buffer
// Copies text region from one buffer to another buffer (appends to end)
// Returns the insertion region in the dest buffer.
static Range_i64
loco_copy_buffer_text_to_buffer(Application_Links *app,
Arena * arena,
Buffer_ID src_buffer,
Buffer_ID dst_buffer,
Range_i64 src_range)
{
// Copy range string from original buffer.
String_Const_u8 copy_string = push_buffer_range(app, arena, src_buffer, src_range);
// Find dest buffer pos to start insertion.
i64 dst_insert_start = (i64)buffer_get_size(app, dst_buffer);
// Insert range to yeet buffer.
lock_yeet_buffer = true;
Buffer_Insertion insert = begin_buffer_insertion_at_buffered(app, dst_buffer, dst_insert_start, arena, KB(16));
insertc(&insert, '\n');
insert_string(&insert, copy_string);
insertc(&insert, '\n');
insertc(&insert, '\n');
end_buffer_insertion(&insert);
lock_yeet_buffer = false;
// Find dest end buffer pos.
i64 dst_insert_end = (i64)buffer_get_size(app, dst_buffer);
// +1 to ignore start newline.
// -2 to ignore the two end newlines.
return Ii64(dst_insert_start + 1, dst_insert_end - 2);
}
//~ @marker @append
// Append two markers that sit and the start and end of a range.
static i32
loco_append_marker_range(Application_Links *app, Buffer_ID buffer, Range_i64 range)
{
Marker markers[2];
markers[0].pos = range.min;
markers[0].lean_right = false;
markers[1].pos = range.max;
markers[1].lean_right = true;
return loco_append_markers(app, buffer, markers, 2);
}
//~ @snapshot
static void
loco_save_yeet_snapshot_to_slot(Application_Links *app, i32 slot)
{
Buffer_ID yeet_buffer = loco_get_yeet_buffer(app);
Loco_Yeets yeets = loco_get_buffer_yeets(app, yeet_buffer);
yeets_snapshots.snapshots[slot] = yeets;
}
//~ @snapshot
static void
loco_load_yeet_snapshot_from_slot(Application_Links *app, i32 slot)
{
Buffer_ID yeet_buffer = loco_get_yeet_buffer(app);
loco_yeet_clear(app);
Loco_Yeets unsorted_yeets = yeets_snapshots.snapshots[slot];
Scratch_Block scratch(app);
// Sort the pairs by the yeet_buffer marker index.
Sort_Pair_i32* yeet_sorter = push_array(scratch, Sort_Pair_i32, unsorted_yeets.pairs_count);
for (i32 i = 0; i < unsorted_yeets.pairs_count; i += 1){
yeet_sorter[i].index = i;
yeet_sorter[i].key = unsorted_yeets.pairs[i].yeet_start_marker_idx;
}
sort_pairs_by_key(yeet_sorter, unsorted_yeets.pairs_count);
Loco_Yeets yeets = {};
yeets.pairs_count = unsorted_yeets.pairs_count;
for (i32 i = 0; i < unsorted_yeets.pairs_count; i += 1) {
yeets.pairs[i] = unsorted_yeets.pairs[yeet_sorter[i].index];
}
// Delete all mentions of buffers that no longer exist.
for (i32 i = yeets.pairs_count - 1; i >= 0; i--)
{
if (!buffer_exists(app, yeets.pairs[i].buffer))
{
// Swap delete.
yeets.pairs[i] = yeets.pairs[yeets.pairs_count-1];
yeets.pairs_count -= 1;
}
}
// Insert the text into the yeet_buffer.
for (i32 i = 0; i < yeets.pairs_count; i++)
{
Loco_Marker_Pair pair = yeets.pairs[i];
Range_i64 og_range = loco_get_marker_range(app, pair.buffer, pair.start_marker_idx, pair.end_marker_idx);
Range_i64 insertion_range = loco_copy_buffer_text_to_buffer(app, scratch, pair.buffer, yeet_buffer, og_range);
loco_append_marker_range(app, yeet_buffer, insertion_range);
}
loco_overwrite_yeets(app, yeet_buffer, &yeets);
// Show the yeet buffer in opposite view if not in yeet view already.
View_ID view = get_active_view(app, Access_Always);
Buffer_ID buffer = view_get_buffer(app, view, Access_Always);
if (buffer != yeet_buffer)
{
View_ID yeet_view = get_next_view_after_active(app, Access_Always);
view_set_buffer(app, yeet_view, yeet_buffer, 0);
view_set_cursor_and_preferred_x(app, yeet_view, seek_pos(0));
}
}
//~ @buffer
static void
loco_yeet_buffer_range(Application_Links *app, Buffer_ID buffer, Range_i64 range)
{
Buffer_ID yeet_buffer = loco_get_yeet_buffer(app);
if (buffer == yeet_buffer || loco_is_cursor_inside_yeet(app, range.min, 0, 0))
{
return;
}
i32 old_marker_idx = loco_append_marker_range(app, buffer, range);
// Copy range string from original buffer.
Scratch_Block scratch(app);
Range_i64 insertion_range = loco_copy_buffer_text_to_buffer(app, scratch, buffer, yeet_buffer, range);
i32 old_yeet_marker_idx = loco_append_marker_range(app, yeet_buffer, insertion_range);
// Show the yeet buffer in opposite view.
View_ID yeet_view = get_next_view_after_active(app, Access_Always);
view_set_buffer(app, yeet_view, yeet_buffer, 0);
view_set_cursor_and_preferred_x(app, yeet_view, seek_pos(insertion_range.min));
if (loco_yeet_make_yeet_buffer_active_on_yeet)
{
view_set_active(app, yeet_view);
}
// add marker pair to yeet table.
Managed_Scope yeet_scope = buffer_get_managed_scope(app, yeet_buffer);
Managed_Object* pair_obj = scope_attachment(app, yeet_scope, loco_marker_pair_handle, Managed_Object);
Loco_Yeets yeets = {};
if (!managed_object_load_data(app, *pair_obj, 0, 1, &yeets))
{
yeets.pairs_count = 0;
*pair_obj = alloc_managed_memory_in_scope(app, yeet_scope, sizeof(Loco_Yeets), 1);
}
Loco_Marker_Pair& pair = yeets.pairs[yeets.pairs_count++];
pair.buffer = buffer;
pair.start_marker_idx = old_marker_idx;
pair.end_marker_idx = old_marker_idx + 1;
pair.yeet_start_marker_idx = old_yeet_marker_idx;
pair.yeet_end_marker_idx = old_yeet_marker_idx + 1;
managed_object_store_data(app, *pair_obj, 0, 1, &yeets);
}
//~ @command
CUSTOM_COMMAND_SIG(loco_jump_between_yeet)
CUSTOM_DOC("Jumps from the yeet sheet to the original buffer or vice versa.")
{
loco_try_jump_between_yeet_pair(app);
}
//~ @command
CUSTOM_COMMAND_SIG(loco_yeet_selected_range_or_jump)
CUSTOM_DOC("Yeets some code into a yeet buffer.")
{
View_ID view = get_active_view(app, Access_Always);
Buffer_ID buffer = view_get_buffer(app, view, Access_Always);
Range_i64 range = get_view_range(app, view);
// The "or jump" part.
if (loco_try_jump_between_yeet_pair(app))
return;
loco_yeet_buffer_range(app, buffer, range);
}
//~ @command
CUSTOM_COMMAND_SIG(loco_yeet_surrounding_function)
CUSTOM_DOC("Selects the surrounding function scope and yeets it.")
{
View_ID view = get_active_view(app, Access_ReadVisible);
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadVisible);
// Select the surrounding {} braces.
i64 pos = view_get_cursor_pos(app, view);
Range_i64 range = {};
if (find_surrounding_nest(app, buffer, pos, FindNest_Scope, &range)){
for (;;){
pos = range.min;
if (!find_surrounding_nest(app, buffer, pos, FindNest_Scope, &range)){
break;
}
}
i64 start_line = get_line_number_from_pos(app, buffer, range.min);
start_line -= 2;
if (start_line < 1) start_line = 1;
range = Ii64(get_line_start_pos(app, buffer, start_line), range.max);
select_scope(app, view, range);
}
// yeet it.
loco_yeet_selected_range_or_jump(app);
}
//~ @command
CUSTOM_COMMAND_SIG(loco_yeet_clear)
CUSTOM_DOC("Clears all yeets.")
{
Buffer_ID yeet_buffer = loco_get_yeet_buffer(app);
Loco_Yeets yeets = loco_get_buffer_yeets(app, yeet_buffer);
if (loco_yeets_delete_og_markers)
{
for (i32 i = 0; i < yeets.pairs_count; i++)
{
Managed_Scope scope = buffer_get_managed_scope(app, yeets.pairs[i].buffer);
Managed_Object* markers_obj = scope_attachment(
app, scope, loco_marker_handle, Managed_Object);
managed_object_free(app, *markers_obj);
}
}
{
Managed_Scope scope = buffer_get_managed_scope(app, yeet_buffer);
Managed_Object* markers_obj = scope_attachment(app, scope, loco_marker_handle, Managed_Object);
managed_object_free(app, *markers_obj);
Managed_Object* pair_obj = scope_attachment(app, scope, loco_marker_pair_handle, Managed_Object);
managed_object_free(app, *pair_obj);
}
clear_buffer(app, yeet_buffer);
}
//~ @command
CUSTOM_COMMAND_SIG(loco_yeet_reset_all)
CUSTOM_DOC("Clears all yeets in all snapshots, also clears all the markers.")
{
bool cache_delete_og_markers = loco_yeets_delete_og_markers;
loco_yeets_delete_og_markers = true;
loco_load_yeet_snapshot_from_slot(app, 0);
loco_yeet_clear(app);
loco_load_yeet_snapshot_from_slot(app, 1);
loco_yeet_clear(app);
loco_load_yeet_snapshot_from_slot(app, 2);
loco_yeet_clear(app);
loco_yeets_delete_og_markers = cache_delete_og_markers;
loco_load_yeet_snapshot_from_slot(app, 0);
yeets_snapshots = {};
}
//~ @command
CUSTOM_COMMAND_SIG(loco_yeet_remove_marker_pair)
CUSTOM_DOC("Removes the marker pair the cursor is currently inside.")
{
Buffer_ID yeet_buffer = loco_get_yeet_buffer(app);
View_ID view = get_active_view(app, Access_Always);
Buffer_ID buffer = view_get_buffer(app, view, Access_Always);
i64 cursor_pos = view_get_cursor_pos(app, view);
if (!loco_is_cursor_inside_yeet(app, cursor_pos, 0, 0)) return;
// If we're in the original buffer,
// try to jump to the relative location in the yeet before.
// That way I just keep the one branch of logic for deleting
// from the yeet buffer only.
View_ID cached_view = view;
if (buffer != yeet_buffer)
{
loco_try_jump_between_yeet_pair(app);
view = get_active_view(app, Access_Always);
buffer = view_get_buffer(app, view, Access_Always);
}
if (buffer == yeet_buffer)
{
Scratch_Block scratch(app);
Range_i64 range = get_view_range(app, view);
Loco_Yeets yeets = loco_get_buffer_yeets(app, yeet_buffer);
i32 yeet_marker_count = 0;
Marker* yeet_markers = loco_get_buffer_markers(app, scratch, yeet_buffer, &yeet_marker_count);
for (i32 i = yeets.pairs_count - 1; i >= 0; i--)
{
Loco_Marker_Pair pair = yeets.pairs[i];
Range_i64 yeet_range = loco_make_range_from_markers(
yeet_markers, pair.yeet_start_marker_idx, pair.yeet_end_marker_idx);
if (range.max > yeet_range.min && range.max < yeet_range.max)
{
loco_delete_marker_pair(app, yeet_buffer, &yeets, i);
break;
}
}
view_set_active(app, cached_view);
}
}
//--SNAPSHOTS
//~ @command @snapshot
CUSTOM_COMMAND_SIG(loco_save_yeet_snapshot_1)
CUSTOM_DOC("Save yeets snapshot to slot 1.")
{
loco_save_yeet_snapshot_to_slot(app, 0);
}
//~ @command @snapshot
CUSTOM_COMMAND_SIG(loco_save_yeet_snapshot_2)
CUSTOM_DOC("Save yeets snapshot to slot 2.")
{
loco_save_yeet_snapshot_to_slot(app, 1);
}
//~ @command @snapshot
CUSTOM_COMMAND_SIG(loco_save_yeet_snapshot_3)
CUSTOM_DOC("Save yeets snapshot to slot 3.")
{
loco_save_yeet_snapshot_to_slot(app, 2);
}
//~ @command @snapshot
CUSTOM_COMMAND_SIG(loco_load_yeet_snapshot_1)
CUSTOM_DOC("Load yeets snapshot from slot 1.")
{
loco_load_yeet_snapshot_from_slot(app, 0);
}
//~ @command @snapshot
CUSTOM_COMMAND_SIG(loco_load_yeet_snapshot_2)
CUSTOM_DOC("Load yeets snapshot from slot 2.")
{
loco_load_yeet_snapshot_from_slot(app, 1);
}
//~ @command @snapshot
CUSTOM_COMMAND_SIG(loco_load_yeet_snapshot_3)
CUSTOM_DOC("Load yeets snapshot from slot 3.")
{
loco_load_yeet_snapshot_from_slot(app, 2);
}
//--CATEGORIES
// @yeettags @yeettype
enum Loco_Yeet_Tags_Parse_State
{
Loco_Tag_PState_Looking_For_Tag,
Loco_Tag_PState_Reading_Word,
Loco_Tag_PState_End_Of_Word,
Loco_Tag_PState_Looking_For_Comment,
Loco_Tag_PState_Looking_For_Scope_Start,
Loco_Tag_PState_Looking_For_Scope_End
};
// @yeettags @yeettype
struct Loco_Yeet_Tag_Range
{
Range_i64 range;
};
//~ @yeettags
static void
loco_yeet_all_scopes_with_tag(Application_Links *app, Buffer_ID buffer, String_Const_u8 tag_name)
{
Token_Array token_arr = get_token_array_from_buffer(app, buffer);
if (token_arr.tokens == 0) return;
Scratch_Block scratch(app);
Token_Iterator_Array it = token_iterator_index(buffer, &token_arr, 0);
Loco_Yeet_Tag_Range tag_ranges[1024];
u64 tag_ranges_count = 0;
Loco_Yeet_Tags_Parse_State root_parse_state = Loco_Tag_PState_Looking_For_Comment;
u64 scope_enter_count = 0;
for(Token* tok = token_it_read(&it);
tok != 0;
tok = token_it_read(&it))
{
if (HasFlag(tok->flags, TokenBaseFlag_PreprocessorBody))
{
if (!token_it_inc_non_whitespace(&it)) break;
continue;
}
if (tok->sub_kind == TokenCppKind_LineComment && root_parse_state == Loco_Tag_PState_Looking_For_Comment)
{
String_Const_u8 tok_str = push_buffer_range(app, scratch, buffer, Ii64(tok));
Loco_Yeet_Tags_Parse_State parse_state = Loco_Tag_PState_Looking_For_Tag;
i64 tag_start = 0;
i64 tag_end = 0;
bool found_tag = false;
for (u64 i = 0; i <= tok_str.size; i++)
{
u8 c = (i < tok_str.size) ? tok_str.str[i] : 0;
switch(parse_state)
{
case Loco_Tag_PState_Looking_For_Tag: {
if (c == '@')
{
parse_state = Loco_Tag_PState_Reading_Word;
tag_start = i+1;
}
break;
}
case Loco_Tag_PState_Reading_Word: {
bool is_lower_alpha = (c >= 'a' && c <= 'z');
bool is_upper_alpha = (c >= 'A' && c <= 'Z');
bool is_numeric = (c >= '0' && c <= '9');
bool is_alphanumeric = (is_lower_alpha || is_upper_alpha || is_numeric);
if (!is_alphanumeric)
{
parse_state = Loco_Tag_PState_End_Of_Word;
tag_end = i;
}
break;
}
}
if (parse_state == Loco_Tag_PState_End_Of_Word)
{
parse_state = Loco_Tag_PState_Looking_For_Tag;
String_Const_u8 sub = string_substring(tok_str, Ii64(tag_start, tag_end));
if (string_match(sub, tag_name))
{
found_tag = true;
break;
}
}
}
if (found_tag)
{
root_parse_state = Loco_Tag_PState_Looking_For_Scope_Start;
tag_ranges[tag_ranges_count].range.min = tok->pos;
}
}
if (tok->sub_kind == TokenCppKind_BraceOp)
{
if (root_parse_state == Loco_Tag_PState_Looking_For_Scope_Start)
{
scope_enter_count = 1;
root_parse_state = Loco_Tag_PState_Looking_For_Scope_End;
}
else if (root_parse_state == Loco_Tag_PState_Looking_For_Scope_End)
{
scope_enter_count += 1;
}
}
if (tok->sub_kind == TokenCppKind_BraceCl && root_parse_state == Loco_Tag_PState_Looking_For_Scope_End)
{
scope_enter_count -= 1;
if (scope_enter_count == 0)
{
root_parse_state = Loco_Tag_PState_Looking_For_Comment;
tag_ranges[tag_ranges_count].range.max = tok->pos+1;
tag_ranges_count += 1;
}
}
if (!token_it_inc_non_whitespace(&it)) break;
}
for (u64 i = 0; i < tag_ranges_count; i++)
{
loco_yeet_buffer_range(app, buffer, tag_ranges[i].range);
}
}
//--TAG-COMMANDS
// @command @yeettags
CUSTOM_COMMAND_SIG(loco_yeet_tag)
CUSTOM_DOC("Find all locations of a comment tag (//@tag) in all buffers and yeet the scope they precede.")
{
Scratch_Block scratch(app);
u8 *space = push_array(scratch, u8, KB(1));
String_Const_u8 tag_name = get_query_string(app, "Yeet Tag: ", space, KB(1));
Buffer_ID yeet_buffer = loco_get_yeet_buffer(app);
for (Buffer_ID buffer = get_buffer_next(app, 0, Access_ReadWriteVisible);
buffer != 0;
buffer = get_buffer_next(app, buffer, Access_ReadWriteVisible))
{
if (buffer == yeet_buffer) continue;
loco_yeet_all_scopes_with_tag(app, buffer, tag_name);
}
}