From 24c8370f75294d71777897b957337a441b6da582 Mon Sep 17 00:00:00 2001 From: PS Date: Sat, 10 Feb 2024 20:12:36 -0800 Subject: [PATCH] Indent next line only on hitting enter --- code/custom/4coder_auto_indent.cpp | 120 +++++++++++++++++++++-- code/custom/generated/command_metadata.h | 2 +- 2 files changed, 112 insertions(+), 10 deletions(-) diff --git a/code/custom/4coder_auto_indent.cpp b/code/custom/4coder_auto_indent.cpp index a2b17025..df474255 100644 --- a/code/custom/4coder_auto_indent.cpp +++ b/code/custom/4coder_auto_indent.cpp @@ -437,14 +437,84 @@ CUSTOM_DOC("Auto-indents the range between the cursor and the mark.") move_past_lead_whitespace(app, view, buffer); } +function i64 +get_line_indent_level(Application_Links *app, View_ID view, Buffer_ID buffer, i64 line) +{ + Scratch_Block scratch(app); + + String_Const_u8 line_string = push_buffer_line(app, scratch, buffer, line); + i64 line_start_pos = get_line_start_pos(app, buffer, line); + + Range_i64 line_indent_range = Ii64(0, 0); + i64 tabs_at_beginning = 0; + i64 spaces_at_beginning = 0; + for(u64 i = 0; i < line_string.size; i += 1) + { + if(line_string.str[i] == '\t') + { + tabs_at_beginning += 1; + } + else if(character_is_whitespace(line_string.str[i])) + { + spaces_at_beginning += 1; + } + else if(!character_is_whitespace(line_string.str[i])) + { + line_indent_range.max = (i64)i; + break; + } + } + + // NOTE(PS): This is in the event that we are unindenting a line that + // is JUST tabs or spaces - rather than unindenting nothing + // and then reindenting the proper amount, this should cause + // the removal of all leading tabs and spaces on an otherwise + // empty line + bool place_cursor_at_end = false; + if (line_indent_range.max == 0 && line_string.size > 0) + { + line_indent_range.max = line_string.size; + place_cursor_at_end = true; + } + + Range_i64 indent_range = + { + line_indent_range.min + line_start_pos, + line_indent_range.max + line_start_pos, + }; + + i64 indent_width = (i64)def_get_config_u64(app, vars_save_string_lit("indent_width")); + i64 spaces_per_indent_level = indent_width; + i64 indent_level = spaces_at_beginning / spaces_per_indent_level + tabs_at_beginning; + + return indent_level; +} + +function String_Const_u8 +get_indent_string(Application_Links* app, Arena* scratch) +{ + i64 indent_width = (i64)def_get_config_u64(app, vars_save_string_lit("indent_width")); + b32 indent_with_tabs = def_get_config_b32(vars_save_string_lit("indent_with_tabs")); + String_Const_u8 result; + if (indent_with_tabs) { + result = string_u8_litexpr("\t"); + } else { + result = push_stringf(scratch, "%.*s", Min(indent_width, 16), " "); + } + return result; +} + CUSTOM_COMMAND_SIG(write_text_and_auto_indent) CUSTOM_DOC("Inserts text and auto-indents the line on which the cursor sits if any of the text contains 'layout punctuation' such as ;:{}()[]# and new lines.") { ProfileScope(app, "write and auto indent"); + Scratch_Block scratch(app); User_Input in = get_current_input(app); String_Const_u8 insert = to_writable(&in); if (insert.str != 0 && insert.size > 0){ b32 do_auto_indent = false; + b32 only_indent_next_line = true; + b32 is_newline = false; for (u64 i = 0; !do_auto_indent && i < insert.size; i += 1){ switch (insert.str[i]){ case ';': case ':': @@ -452,16 +522,30 @@ CUSTOM_DOC("Inserts text and auto-indents the line on which the cursor sits if a case '(': case ')': case '[': case ']': case '#': - case '\n': case '\t': { do_auto_indent = true; }break; + case '\n': case '\t': + { + do_auto_indent = true; + is_newline = true; + }break; } } + + View_ID view = get_active_view(app, Access_ReadWriteVisible); + Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible); + + String_Const_u8 file_name = push_buffer_file_name(app, scratch, buffer); + String_Const_u8 ext = string_file_extension(file_name); + if (string_match(ext, string_u8_litexpr("js")) || + string_match(ext, string_u8_litexpr("css"))) + { + only_indent_next_line = do_auto_indent; + } + if (do_auto_indent){ - View_ID view = get_active_view(app, Access_ReadWriteVisible); - Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible); - + History_Group group = history_group_begin(app, buffer); Range_i64 pos = {}; if (view_has_highlighted_range(app, view)){ pos = get_view_range(app, view); @@ -473,11 +557,29 @@ CUSTOM_DOC("Inserts text and auto-indents the line on which the cursor sits if a write_text_input(app); i64 end_pos = view_get_cursor_pos(app, view); - pos.min = Min(pos.min, end_pos); - pos.max = Max(pos.max, end_pos); - - auto_indent_buffer(app, buffer, pos, 0); - move_past_lead_whitespace(app, view, buffer); + if (!only_indent_next_line) { + pos.min = Min(pos.min, end_pos); + pos.max = Max(pos.max, end_pos); + auto_indent_buffer(app, buffer, pos, 0); + move_past_lead_whitespace(app, view, buffer); + } else if (only_indent_next_line && is_newline) { + String_Const_u8 indent_string = get_indent_string(app, scratch); + + // getting the indent from the PREVIOUS line, not the line + // the cursor is about to be on - since that line is new, + // and therefore, empty + i64 line = get_line_number_from_pos(app, buffer, pos.min); + i64 indent_level = get_line_indent_level(app, view, buffer, line); + + pos.min = pos.max = end_pos; + + for(i64 i = 0; i < indent_level; i += 1) + { + buffer_replace_range(app, buffer, Ii64(pos.max), indent_string); + } + move_past_lead_whitespace(app, view, buffer); + } + history_group_end(group); } else{ write_text_input(app); diff --git a/code/custom/generated/command_metadata.h b/code/custom/generated/command_metadata.h index 039da088..a00bf392 100644 --- a/code/custom/generated/command_metadata.h +++ b/code/custom/generated/command_metadata.h @@ -555,7 +555,7 @@ static Command_Metadata fcoder_metacmd_table[269] = { { PROC_LINKS(write_hack, 0), false, "write_hack", 10, "At the cursor, insert a '// HACK' comment, includes user name if it was specified in config.4coder.", 99, "C:\\projects\\4coder_gs\\code\\custom\\4coder_combined_write_commands.cpp", 68, 82 }, { PROC_LINKS(write_note, 0), false, "write_note", 10, "At the cursor, insert a '// NOTE' comment, includes user name if it was specified in config.4coder.", 99, "C:\\projects\\4coder_gs\\code\\custom\\4coder_combined_write_commands.cpp", 68, 88 }, { PROC_LINKS(write_space, 0), false, "write_space", 11, "Inserts a space.", 16, "C:\\projects\\4coder_gs\\code\\custom\\4coder_base_commands.cpp", 58, 67 }, -{ PROC_LINKS(write_text_and_auto_indent, 0), false, "write_text_and_auto_indent", 26, "Inserts text and auto-indents the line on which the cursor sits if any of the text contains 'layout punctuation' such as ;:{}()[]# and new lines.", 145, "C:\\projects\\4coder_gs\\code\\custom\\4coder_auto_indent.cpp", 56, 440 }, +{ PROC_LINKS(write_text_and_auto_indent, 0), false, "write_text_and_auto_indent", 26, "Inserts text and auto-indents the line on which the cursor sits if any of the text contains 'layout punctuation' such as ;:{}()[]# and new lines.", 145, "C:\\projects\\4coder_gs\\code\\custom\\4coder_auto_indent.cpp", 56, 507 }, { PROC_LINKS(write_text_input, 0), false, "write_text_input", 16, "Inserts whatever text was used to trigger this command.", 55, "C:\\projects\\4coder_gs\\code\\custom\\4coder_base_commands.cpp", 58, 59 }, { PROC_LINKS(write_todo, 0), false, "write_todo", 10, "At the cursor, insert a '// TODO' comment, includes user name if it was specified in config.4coder.", 99, "C:\\projects\\4coder_gs\\code\\custom\\4coder_combined_write_commands.cpp", 68, 76 }, { PROC_LINKS(write_underscore, 0), false, "write_underscore", 16, "Inserts an underscore.", 22, "C:\\projects\\4coder_gs\\code\\custom\\4coder_base_commands.cpp", 58, 73 },