diff --git a/src/source.cpp b/src/source.cpp index 2553f06..70296a9 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -2289,11 +2289,11 @@ bool Source::View::on_key_press_event_bracket_language(GdkEventKey *key) { iter = get_buffer()->get_insert()->get_iter(); auto start_iter = get_tabs_end_iter(iter.get_line()); - auto end_iter = start_iter; - end_iter.forward_chars(2); - auto start_of_sentence = get_buffer()->get_text(start_iter, end_iter); - if(!start_of_sentence.empty()) { - if(start_of_sentence == "/*" || start_of_sentence[0] == '*') { + if(!is_code_iter(start_iter)) { + auto end_iter = start_iter; + end_iter.forward_chars(2); + auto start_of_sentence = get_buffer()->get_text(start_iter, end_iter); + if(!start_of_sentence.empty() && (start_of_sentence == "/*" || start_of_sentence[0] == '*')) { auto tabs = get_line_before(start_iter); auto insert_str = '\n' + tabs; if(start_of_sentence[0] == '/') @@ -2349,6 +2349,90 @@ bool Source::View::on_key_press_event_bracket_language(GdkEventKey *key) { } } + // Indent enter inside HTML or JSX elements, for instance: + //
CURSOR
+ // to: + //
+ // CURSOR + //
+ // and + //
CURSORtest + // to + //
+ // CURSORtest + if(*condition_iter == '>' && language && (language->get_id() == "js" || language->get_id() == "html")) { + auto prev = condition_iter; + Gtk::TextIter open_element_iter; + if(prev.backward_char() && backward_to_code(prev) && *prev != '/' && + find_open_symbol_backward(prev, open_element_iter, '<', '>') && + open_element_iter.forward_char() && forward_to_code(open_element_iter) && *open_element_iter != '/') { + auto get_element = [this](Gtk::TextIter iter) { + auto start = iter; + auto end = iter; + while(iter.backward_char() && (is_token_char(*iter) || *iter == '.')) + start = iter; + while((is_token_char(*end) || *end == '.') && end.forward_char()) { + } + return get_buffer()->get_text(start, end); + }; + auto open_element_token = get_element(open_element_iter); + std::vector void_elements = {"area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr"}; + if(std::none_of(void_elements.begin(), void_elements.end(), [&open_element_token](const std::string &e) { return e == open_element_token; })) { + auto close_element_iter = iter; + // If cursor is placed between open and close tag + if(*close_element_iter == '<' && close_element_iter.forward_char() && forward_to_code(close_element_iter) && *close_element_iter == '/' && + close_element_iter.forward_char() && forward_to_code(close_element_iter) && + open_element_token == get_element(close_element_iter)) { + get_buffer()->insert_at_cursor('\n' + tabs + tab + '\n' + tabs); + auto insert_it = get_buffer()->get_insert()->get_iter(); + if(insert_it.backward_chars(tabs.size() + 1)) { + scroll_to(get_buffer()->get_insert()); + get_buffer()->place_cursor(insert_it); + } + return true; + } + close_element_iter = iter; + // Add closing tag if missing + if(close_element_iter.ends_line()) { + forward_to_code(close_element_iter); + auto close_element_tabs_size = static_cast(get_tabs_end_iter(close_element_iter).get_line_offset()); + if(!close_element_iter || close_element_tabs_size < tabs.size() || + (close_element_tabs_size == tabs.size() && *close_element_iter == '<' && close_element_iter.forward_char() && forward_to_code(close_element_iter) && + !(*close_element_iter == '/' && close_element_iter.forward_char() && forward_to_code(close_element_iter) && get_element(close_element_iter) == open_element_token))) { + get_buffer()->insert_at_cursor('\n' + tabs + tab + '\n' + tabs + "'); + auto insert_it = get_buffer()->get_insert()->get_iter(); + if(insert_it.backward_chars(tabs.size() + 1 + open_element_token.size() + 3)) { + scroll_to(get_buffer()->get_insert()); + get_buffer()->place_cursor(insert_it); + } + return true; + } + } + // If line ends with closing element, move content after open to new line, and closing tag on the next line thereafter + close_element_iter = iter; + if(!close_element_iter.ends_line()) + close_element_iter.forward_to_line_end(); + if(close_element_iter.backward_char() && *close_element_iter == '>' && close_element_iter.backward_char() && + find_open_symbol_backward(close_element_iter, close_element_iter, '<', '>')) { + auto start_close_element_iter = close_element_iter; + auto content_size = start_close_element_iter.get_offset() - iter.get_offset(); + if(close_element_iter.forward_char() && forward_to_code(close_element_iter) && + *close_element_iter == '/' && close_element_iter.forward_char() && forward_to_code(close_element_iter) && get_element(close_element_iter) == open_element_token) { + get_buffer()->insert_at_cursor('\n' + tabs + tab); + auto close_element_start_iter = get_buffer()->get_insert()->get_iter(); + close_element_start_iter.forward_chars(content_size); + get_buffer()->insert(close_element_start_iter, '\n' + tabs); + scroll_to(get_buffer()->get_insert()); + return true; + } + } + get_buffer()->insert_at_cursor('\n' + tabs + tab); + scroll_to(get_buffer()->get_insert()); + return true; + } + } + } + // Special indentation of {, [ and ( for for instance JavaScript, JSON, Rust if(use_fixed_continuation_indenting) { unsigned int open_symbol = 0, close_symbol = 0; @@ -2622,6 +2706,7 @@ bool Source::View::on_key_press_event_bracket_language(GdkEventKey *key) { } } } + // Indent as in current or next line int line_nr = iter.get_line(); if(iter.ends_line() && (line_nr + 1) < get_buffer()->get_line_count()) { diff --git a/tests/source_key_test.cpp b/tests/source_key_test.cpp index 552809d..06cbd90 100644 --- a/tests/source_key_test.cpp +++ b/tests/source_key_test.cpp @@ -1291,6 +1291,17 @@ int main() { " * "); g_assert(buffer->get_insert()->get_iter() == buffer->end()); } + { + buffer->set_text(" /*\n" + " *"); + while(Gtk::Main::events_pending()) + Gtk::Main::iteration(); + view.on_key_press_event(&event); + g_assert(buffer->get_text() == " /*\n" + " *\n" + " * "); + g_assert(buffer->get_insert()->get_iter() == buffer->end()); + } { buffer->set_text(" /*\n" " */"); @@ -2312,33 +2323,65 @@ int main() { g_assert(buffer->get_insert()->get_iter().get_line_offset() == 4); } { - buffer->set_text("
\n" - "
"); + buffer->set_text(" test\n" + " test"); + auto iter = buffer->end(); + iter.backward_chars(9); + buffer->place_cursor(iter); + view.on_key_press_event(&event); + g_assert(buffer->get_text() == " test\n" + " \n" + " test"); + iter = buffer->end(); + iter.backward_chars(9); + g_assert(buffer->get_insert()->get_iter() == iter); + } + { + buffer->set_text(" test(test\n" + " test"); auto iter = buffer->end(); iter.backward_chars(9); buffer->place_cursor(iter); view.on_key_press_event(&event); - g_assert(buffer->get_text() == "
\n" + g_assert(buffer->get_text() == " test(test\n" " \n" - "
"); + " test"); iter = buffer->end(); iter.backward_chars(9); g_assert(buffer->get_insert()->get_iter() == iter); } { - buffer->set_text(" test(
\n" - "
"); + buffer->set_text(" test(\n" + " test\n" + " test"); auto iter = buffer->end(); iter.backward_chars(9); buffer->place_cursor(iter); view.on_key_press_event(&event); - g_assert(buffer->get_text() == " test(
\n" + g_assert(buffer->get_text() == " test(\n" + " test\n" " \n" - "
"); + " test"); iter = buffer->end(); iter.backward_chars(9); g_assert(buffer->get_insert()->get_iter() == iter); } + { + buffer->set_text(" test(\n" + " test\n" + " test"); + auto iter = buffer->end(); + iter.backward_chars(11); + buffer->place_cursor(iter); + view.on_key_press_event(&event); + g_assert(buffer->get_text() == " test(\n" + " test\n" + " \n" + " test"); + iter = buffer->end(); + iter.backward_chars(11); + g_assert(buffer->get_insert()->get_iter() == iter); + } { buffer->set_text(" test(\n" "
\n" @@ -2357,18 +2400,102 @@ int main() { } { buffer->set_text(" test(\n" - "
\n" "
"); auto iter = buffer->end(); - iter.backward_chars(9); buffer->place_cursor(iter); view.on_key_press_event(&event); g_assert(buffer->get_text() == " test(\n" "
\n" - " \n" - "
"); + " "); iter = buffer->end(); - iter.backward_chars(9); + g_assert(buffer->get_insert()->get_iter() == iter); + } + { + buffer->set_text(" test(\n" + "
"); + auto iter = buffer->end(); + buffer->place_cursor(iter); + view.on_key_press_event(&event); + g_assert(buffer->get_text() == " test(\n" + "
\n" + " \n" + "
"); + iter = buffer->end(); + iter.backward_chars(11); + g_assert(buffer->get_insert()->get_iter() == iter); + } + { + buffer->set_text(" test(\n" + "
\n"); + auto iter = buffer->end(); + iter.backward_char(); + buffer->place_cursor(iter); + view.on_key_press_event(&event); + g_assert(buffer->get_text() == " test(\n" + "
\n" + " \n" + "
\n"); + iter = buffer->end(); + iter.backward_chars(12); + g_assert(buffer->get_insert()->get_iter() == iter); + } + { + buffer->set_text(" test(\n" + "
\n" + "
\n"); + auto iter = buffer->end(); + iter.backward_chars(12); + buffer->place_cursor(iter); + view.on_key_press_event(&event); + g_assert(buffer->get_text() == " test(\n" + "
\n" + " \n" + "
\n"); + iter = buffer->end(); + iter.backward_chars(12); + g_assert(buffer->get_insert()->get_iter() == iter); + } + { + buffer->set_text(" test(\n" + "
"); + auto iter = buffer->end(); + iter.backward_chars(6); + buffer->place_cursor(iter); + view.on_key_press_event(&event); + g_assert(buffer->get_text() == " test(\n" + "
\n" + " \n" + "
"); + iter = buffer->end(); + iter.backward_chars(11); + g_assert(buffer->get_insert()->get_iter() == iter); + } + { + buffer->set_text(" test(\n" + "
"); + auto iter = buffer->end(); + iter.backward_chars(6); + buffer->place_cursor(iter); + view.on_key_press_event(&event); + g_assert(buffer->get_text() == " test(\n" + "
\n" + "
"); + iter = buffer->end(); + iter.backward_chars(6); + g_assert(buffer->get_insert()->get_iter() == iter); + } + { + buffer->set_text(" test(\n" + "
"); + auto iter = buffer->end(); + iter.backward_chars(5); + buffer->place_cursor(iter); + view.on_key_press_event(&event); + g_assert(buffer->get_text() == " test(\n" + "
\n" + "
"); + iter = buffer->end(); + iter.backward_chars(5); g_assert(buffer->get_insert()->get_iter() == iter); } {