From da9f001b8fcf10533d26390f0bb2fb06472ae289 Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 7 Sep 2016 14:36:10 +0200 Subject: [PATCH] Added smart_insertions --- CMakeLists.txt | 2 +- src/config.cc | 3 + src/config.h | 1 + src/files.h | 4 +- src/source.cc | 282 +++++++++++++++++++++++++++++++++++++++++++------ src/source.h | 2 +- 6 files changed, 257 insertions(+), 37 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3edeed3..7923df3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required (VERSION 2.8.8) project(juci) -set(JUCI_VERSION "1.2.1.3") +set(JUCI_VERSION "1.2.1.4") set(CPACK_PACKAGE_NAME "jucipp") set(CPACK_PACKAGE_CONTACT "Ole Christian Eidheim ") diff --git a/src/config.cc b/src/config.cc index 0bb4802..8c388dd 100644 --- a/src/config.cc +++ b/src/config.cc @@ -195,6 +195,9 @@ void Config::get_source() { source.show_whitespace_characters=source_json.get("show_whitespace_characters"); source.smart_brackets=source_json.get("smart_brackets"); + source.smart_insertions=source_json.get("smart_insertions"); + if(source.smart_insertions) + source.smart_brackets=true; source.show_map = source_json.get("show_map"); source.map_font_size = source_json.get("map_font_size"); diff --git a/src/config.h b/src/config.h index 3a7458f..0b232b7 100644 --- a/src/config.h +++ b/src/config.h @@ -61,6 +61,7 @@ public: std::string show_whitespace_characters; bool smart_brackets; + bool smart_insertions; bool show_map; std::string map_font_size; diff --git a/src/files.h b/src/files.h index 8227fa8..61f1041 100644 --- a/src/files.h +++ b/src/files.h @@ -41,8 +41,10 @@ R"RAW( "cleanup_whitespace_characters": false, "show_whitespace_characters_comment": "Determines what kind of whitespaces should be drawn. Use comma-separated list of: space, tab, newline, nbsp, leading, text, trailing or all", "show_whitespace_characters": "", - "smart_brackets_comment": "When inserting an already closed bracket, the cursor might instead be moved, avoiding the need of arrow keys after autocomplete", + "smart_brackets_comment": "If smart_insertions is enabled, this option is automatically enabled. When inserting an already closed bracket, the cursor might instead be moved, avoiding the need of arrow keys after autocomplete", "smart_brackets": true, + "smart_insertions_comment": "When for instance inserting (, () gets inserted. Applies to: (), [], \", '. Also enables pressing ; inside an expression before a final ) to insert ; at the end of line, and deletions of empty insertions", + "smart_insertions": false, "show_map": true, "map_font_size": "1", "show_git_diff": true, diff --git a/src/source.cc b/src/source.cc index 932d5bc..d5002e0 100644 --- a/src/source.cc +++ b/src/source.cc @@ -1016,51 +1016,81 @@ bool Source::View::find_close_curly_bracket_forward(Gtk::TextIter iter, Gtk::Tex return false; } -long Source::View::open_close_bracket_count(Gtk::TextIter iter, unsigned int left_bracket, unsigned int right_bracket) { +long Source::View::symbol_count(Gtk::TextIter iter, unsigned int positive_char, unsigned int negative_char) { auto iter_stored=iter; - long bracket_count=0; + long symbol_count=0; long curly_count=0; + bool break_on_curly=true; + if(positive_char=='{' || negative_char=='}') + break_on_curly=false; + bool check_if_previous_or_next_iter_is_code_iter=false; + if(positive_char=='\'' || negative_char=='\'' || positive_char=='"' || negative_char=='"') + check_if_previous_or_next_iter_is_code_iter=true; Gtk::TextIter previous_iter; do { previous_iter=iter; previous_iter.backward_char(); if(!(*previous_iter=='\'' && is_code_iter(previous_iter))) { - if(*iter==left_bracket && is_code_iter(iter)) - bracket_count++; - else if(*iter==right_bracket && is_code_iter(iter)) - bracket_count--; + if(*iter==positive_char && is_code_iter(iter)) + symbol_count++; + else if(*iter==negative_char && is_code_iter(iter)) + symbol_count--; else if(*iter=='{' && is_code_iter(iter)) curly_count++; else if(*iter=='}' && is_code_iter(iter)) curly_count--; + else if(check_if_previous_or_next_iter_is_code_iter) { + auto next_iter=iter; + next_iter.forward_char(); + if(*iter==positive_char && is_code_iter(previous_iter)) + symbol_count++; + else if(*iter==negative_char && is_code_iter(previous_iter)) + symbol_count--; + else if(*iter==positive_char && is_code_iter(next_iter)) + symbol_count++; + else if(*iter==negative_char && is_code_iter(next_iter)) + symbol_count--; + } - if(curly_count>0) + if(break_on_curly && curly_count>0) break; } } while(iter.backward_char()); iter=iter_stored; if(!iter.forward_char()) { - return bracket_count; + return symbol_count; } curly_count=0; do { - if(*iter==left_bracket && is_code_iter(iter)) - bracket_count++; - else if(*iter==right_bracket && is_code_iter(iter)) - bracket_count--; + if(*iter==positive_char && is_code_iter(iter)) + symbol_count++; + else if(*iter==negative_char && is_code_iter(iter)) + symbol_count--; else if(*iter=='{' && is_code_iter(iter)) curly_count++; else if(*iter=='}' && is_code_iter(iter)) curly_count--; + else if(check_if_previous_or_next_iter_is_code_iter) { + auto next_iter=iter; + next_iter.forward_char(); + if(*iter==positive_char && is_code_iter(previous_iter)) + symbol_count++; + else if(*iter==negative_char && is_code_iter(previous_iter)) + symbol_count--; + else if(*iter==positive_char && is_code_iter(next_iter)) + symbol_count++; + else if(*iter==negative_char && is_code_iter(next_iter)) + symbol_count--; + } - if(curly_count<0) + if(break_on_curly && curly_count<0) break; } while(iter.forward_char()); - return bracket_count; + return symbol_count; } bool Source::View::is_templated_function(Gtk::TextIter iter, Gtk::TextIter &parenthesis_end_iter) { @@ -1210,6 +1240,64 @@ bool Source::View::on_key_press_event(GdkEventKey* key) { return on_key_press_event_basic(key); auto iter=get_buffer()->get_insert()->get_iter(); + + if(is_bracket_language && Config::get().source.smart_insertions) { + auto previous_iter=iter; + previous_iter.backward_char(); + if(key->keyval==GDK_KEY_apostrophe && !is_code_iter(iter)) { + if(*previous_iter!='\\' && *iter=='\'') { + auto next_iter=iter; + next_iter.forward_char(); + get_buffer()->place_cursor(next_iter); + scroll_to(get_buffer()->get_insert()); + return true; + } + } + else if(key->keyval==GDK_KEY_quotedbl && *iter=='\"' && !is_code_iter(iter)) { + bool perform_move=false; + if(*previous_iter!='\\') + perform_move=true; + else { + auto it=previous_iter; + long backslash_count=1; + while(it.backward_char() && *it=='\\') { + ++backslash_count; + } + if(backslash_count%2==0) + perform_move=true; + } + if(perform_move) { + auto next_iter=iter; + next_iter.forward_char(); + get_buffer()->place_cursor(next_iter); + scroll_to(get_buffer()->get_insert()); + return true; + } + } + else if(key->keyval==GDK_KEY_BackSpace || key->keyval==GDK_KEY_Delete) { + auto previous_previous_iter=previous_iter; + previous_previous_iter.backward_char(); + if(*previous_iter=='\'' && *iter=='\'' && is_code_iter(previous_previous_iter)) { + auto next_iter=iter; + next_iter.forward_char(); + get_buffer()->begin_user_action(); + get_buffer()->erase(previous_iter, next_iter); + scroll_to(get_buffer()->get_insert()); + get_buffer()->end_user_action(); + return true; + } + else if(*previous_iter=='"' && *iter=='"' && !is_code_iter(iter) && is_code_iter(previous_previous_iter)) { + auto next_iter=iter; + next_iter.forward_char(); + get_buffer()->begin_user_action(); + get_buffer()->erase(previous_iter, next_iter); + scroll_to(get_buffer()->get_insert()); + get_buffer()->end_user_action(); + return true; + } + } + } + if(!is_code_iter(iter)) return on_key_press_event_basic(key); @@ -1640,23 +1728,25 @@ bool Source::View::on_key_press_event_bracket_language(GdkEventKey* key) { //Indent left when writing } on a new line else if(key->keyval==GDK_KEY_braceright) { std::string line=get_line_before(); - if(line.size()>=tab_size) { + if(line.size()>=tab_size && iter.ends_line()) { + bool indent_left=true; for(auto c: line) { if(c!=tab_char) { - get_buffer()->insert_at_cursor("}"); - get_buffer()->end_user_action(); - return true; + indent_left=false; + break; } } - Gtk::TextIter insert_it = get_buffer()->get_insert()->get_iter(); - Gtk::TextIter line_it = get_buffer()->get_iter_at_line(insert_it.get_line()); - Gtk::TextIter line_plus_it=line_it; - line_plus_it.forward_chars(tab_size); - get_buffer()->erase(line_it, line_plus_it); + if(indent_left) { + Gtk::TextIter insert_it = get_buffer()->get_insert()->get_iter(); + Gtk::TextIter line_it = get_buffer()->get_iter_at_line(insert_it.get_line()); + Gtk::TextIter line_plus_it=line_it; + line_plus_it.forward_chars(tab_size); + get_buffer()->erase(line_it, line_plus_it); + get_buffer()->insert_at_cursor("}"); + get_buffer()->end_user_action(); + return true; + } } - get_buffer()->insert_at_cursor("}"); - get_buffer()->end_user_action(); - return true; } //Indent left when writing { on a new line after for instance if(...)\n... else if(key->keyval==GDK_KEY_braceleft) { @@ -1683,17 +1773,16 @@ bool Source::View::on_key_press_event_bracket_language(GdkEventKey* key) { } } } + if(Config::get().source.smart_brackets) { //Move after ')' if closed expression if(key->keyval==GDK_KEY_parenright) { - if(*iter==')') { - if(open_close_bracket_count(iter, '(', ')')==0) { - iter.forward_char(); - get_buffer()->place_cursor(iter); - scroll_to(get_buffer()->get_insert()); - get_buffer()->end_user_action(); - return true; - } + if(*iter==')' && symbol_count(iter, '(', ')')==0) { + iter.forward_char(); + get_buffer()->place_cursor(iter); + scroll_to(get_buffer()->get_insert()); + get_buffer()->end_user_action(); + return true; } } //Move after '>' if >( and closed expression @@ -1726,6 +1815,131 @@ bool Source::View::on_key_press_event_bracket_language(GdkEventKey* key) { } } + auto previous_iter=iter; + previous_iter.backward_char(); + if(Config::get().source.smart_insertions && *previous_iter!='\'' && *iter!='\'') { + if(key->keyval==GDK_KEY_parenleft) { + if(symbol_count(iter, '(', ')')==0) { + get_buffer()->insert_at_cursor("()"); + auto iter=get_buffer()->get_insert()->get_iter(); + iter.backward_char(); + get_buffer()->place_cursor(iter); + scroll_to(get_buffer()->get_insert()); + get_buffer()->end_user_action(); + return true; + } + } + else if(key->keyval==GDK_KEY_bracketleft) { + if(symbol_count(iter, '[', ']')==0) { + get_buffer()->insert_at_cursor("[]"); + auto iter=get_buffer()->get_insert()->get_iter(); + iter.backward_char(); + get_buffer()->place_cursor(iter); + scroll_to(get_buffer()->get_insert()); + get_buffer()->end_user_action(); + return true; + } + } + else if(key->keyval==GDK_KEY_bracketright) { + if(*iter==']' && symbol_count(iter, '[', ']')==0) { + iter.forward_char(); + get_buffer()->place_cursor(iter); + scroll_to(get_buffer()->get_insert()); + get_buffer()->end_user_action(); + return true; + } + } + // else if(key->keyval==GDK_KEY_braceleft) { + // if(symbol_count(iter, '{', '}')==0) { + // get_buffer()->insert_at_cursor("{}"); + // auto iter=get_buffer()->get_insert()->get_iter(); + // iter.backward_char(); + // get_buffer()->place_cursor(iter); + // scroll_to(get_buffer()->get_insert()); + // get_buffer()->end_user_action(); + // return true; + // } + // } + // else if(key->keyval==GDK_KEY_braceright) { + // if(*iter=='}') { + // if(symbol_count(iter, '{', '}')==0) { + // iter.forward_char(); + // get_buffer()->place_cursor(iter); + // scroll_to(get_buffer()->get_insert()); + // get_buffer()->end_user_action(); + // return true; + // } + // } + // } + + if(key->keyval==GDK_KEY_apostrophe && symbol_count(iter, '\'', '\0')%2==0) { + get_buffer()->insert_at_cursor("''"); + auto iter=get_buffer()->get_insert()->get_iter(); + iter.backward_char(); + get_buffer()->place_cursor(iter); + scroll_to(get_buffer()->get_insert()); + get_buffer()->end_user_action(); + return true; + } + else if(key->keyval==GDK_KEY_quotedbl && symbol_count(iter, '"', '\0')%2==0) { + get_buffer()->insert_at_cursor("\"\""); + auto iter=get_buffer()->get_insert()->get_iter(); + iter.backward_char(); + get_buffer()->place_cursor(iter); + scroll_to(get_buffer()->get_insert()); + get_buffer()->end_user_action(); + return true; + } + else if(key->keyval==GDK_KEY_semicolon) { + if(*iter==')' && symbol_count(iter, '(', ')')==0) { + auto next_iter=iter; + next_iter.forward_char(); + if(next_iter.ends_line()) { + Gtk::TextIter open_non_curly_bracket_iter; + if(find_open_non_curly_bracket_backward(previous_iter, open_non_curly_bracket_iter)) { + open_non_curly_bracket_iter.backward_char(); + if(*open_non_curly_bracket_iter==' ') + open_non_curly_bracket_iter.backward_char(); + if(get_token(open_non_curly_bracket_iter)!="for") { + iter.forward_char(); + get_buffer()->place_cursor(iter); + get_buffer()->insert_at_cursor(";"); + scroll_to(get_buffer()->get_insert()); + get_buffer()->end_user_action(); + return true; + } + } + } + } + } + else if(key->keyval==GDK_KEY_BackSpace || key->keyval==GDK_KEY_Delete) { + if(*previous_iter=='(' && *iter==')' && symbol_count(iter, '(', ')')==0) { + auto next_iter=iter; + next_iter.forward_char(); + get_buffer()->erase(previous_iter, next_iter); + scroll_to(get_buffer()->get_insert()); + get_buffer()->end_user_action(); + return true; + } + else if(*previous_iter=='[' && *iter==']' && symbol_count(iter, '[', ']')==0) { + auto next_iter=iter; + next_iter.forward_char(); + get_buffer()->erase(previous_iter, next_iter); + scroll_to(get_buffer()->get_insert()); + get_buffer()->end_user_action(); + return true; + } + // else if(*previous_iter=='{' && *iter=='}' && symbol_count(iter, '{', '}')==0) { + // auto next_iter=iter; + // next_iter.forward_char(); + // get_buffer()->erase(previous_iter, next_iter); + // scroll_to(get_buffer()->get_insert()); + // get_buffer()->end_user_action(); + // return true; + // } + } + } + get_buffer()->end_user_action(); return on_key_press_event_basic(key); } diff --git a/src/source.h b/src/source.h index 5f76f81..33019c0 100644 --- a/src/source.h +++ b/src/source.h @@ -126,7 +126,7 @@ namespace Source { bool find_open_non_curly_bracket_backward(Gtk::TextIter iter, Gtk::TextIter &found_iter); bool find_open_curly_bracket_backward(Gtk::TextIter iter, Gtk::TextIter &found_iter); bool find_close_curly_bracket_forward(Gtk::TextIter iter, Gtk::TextIter &found_iter); - long open_close_bracket_count(Gtk::TextIter iter, unsigned int left_bracket, unsigned int right_bracket); + long symbol_count(Gtk::TextIter iter, unsigned int positive_char, unsigned int negative_char); bool is_templated_function(Gtk::TextIter iter, Gtk::TextIter &parenthesis_end_iter); std::string get_token(Gtk::TextIter iter);