From d246870dbb081a6888f0a7a7dd8d3c1305567da3 Mon Sep 17 00:00:00 2001 From: Jeremias Kleer Date: Thu, 14 Oct 2021 14:43:32 +0200 Subject: [PATCH 1/2] Consume closing brackets, braces, parenthesis, single and double quotes For a flowing writing and coding I love to not change my hand position to use the arrow keys! I realy like auto-closing. If I activates the auto closing brackets, braces, parenthesis and quotes feature, the closing quotes are inserted, but to continue after these, the arrow keys are necessary. The modification I made: if autoclosing is activated in options, if a closing character is typed and the following character matches this, not both characters stay - one deleted - curr pos afterwards. Example: the | marks the curr position: "|" "hallo|" [Then the " is hit] "hallo"| The " as well as the other affected characters are reachable without changing hand pos. I saw this in other IDEs and I want it in my Geany. --- doc/geany.txt | 5 +++ src/editor.c | 77 ++++++++++++++++++++++++++++++++++++++--------- src/editor.h | 1 + src/sciwrappers.c | 4 +++ src/sciwrappers.h | 2 ++ 5 files changed, 74 insertions(+), 15 deletions(-) diff --git a/doc/geany.txt b/doc/geany.txt index 16dcf9c42a..576f6ca700 100644 --- a/doc/geany.txt +++ b/doc/geany.txt @@ -2193,6 +2193,10 @@ Geany can automatically insert a closing bracket and quote characters when you open them. For instance, you type a ``(`` and Geany will automatically insert ``)``. With the following options, you can define for which characters this should work. +For a more flowing writing (not move the typing hand to arrow keys) Geany +checks if the character written is the closing quote or bracket that was +previously inserted. In that case only one quote or bracket of the one written +and the one previously inserted remains. Parenthesis ( ) Auto-close parenthesis when typing an opening one @@ -2210,6 +2214,7 @@ Double quotes " " Auto-close double quotes when typing an opening one + Editor Display preferences ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/editor.c b/src/editor.c index c60a897e1e..5668d9812d 100644 --- a/src/editor.c +++ b/src/editor.c @@ -96,6 +96,7 @@ static gboolean handle_xml(GeanyEditor *editor, gint pos, gchar ch); static void insert_indent_after_line(GeanyEditor *editor, gint line); static void auto_multiline(GeanyEditor *editor, gint pos); static void auto_close_chars(ScintillaObject *sci, gint pos, gchar c); +static gboolean auto_close_chars_consume(ScintillaObject *sci, gint pos, gchar c); static void close_block(GeanyEditor *editor, gint pos); static void editor_highlight_braces(GeanyEditor *editor, gint cur_pos); static void read_current_word(GeanyEditor *editor, gint pos, gchar *word, gsize wordlen, @@ -831,8 +832,15 @@ static void on_char_added(GeanyEditor *editor, SCNotification *nt) editor_show_calltip(editor, --pos); break; } + case '{': + case '[': + { + auto_close_chars(sci, pos, nt->ch); + break; + } case ')': - { /* hide calltips */ + { + /* hide calltips */ if (SSM(sci, SCI_CALLTIPACTIVE, 0, 0)) { SSM(sci, SCI_CALLTIPCANCEL, 0, 0); @@ -842,22 +850,28 @@ static void on_char_added(GeanyEditor *editor, SCNotification *nt) calltip.pos = 0; calltip.sci = NULL; calltip.set = FALSE; - break; } - case '{': - case '[': - case '"': - case '\'': + case ']': { - auto_close_chars(sci, pos, nt->ch); + auto_close_chars_consume(sci, pos, nt->ch); break; } case '}': - { /* closing bracket handling */ + { + auto_close_chars_consume(sci, pos, nt->ch); + /* closing bracket handling */ if (editor->auto_indent) close_block(editor, pos - 1); break; } + case '"': + case '\'': + { + if( ! auto_close_chars_consume(sci, pos, nt->ch) ) { + auto_close_chars(sci, pos, nt->ch); + } + break; + } /* scope autocompletion */ case '.': case ':': /* C/C++ class:: syntax */ @@ -1299,6 +1313,8 @@ static void on_new_line_added(GeanyEditor *editor) if (editor->auto_indent) { insert_indent_after_line(editor, line - 1); + + /* Nice bracket handling */ } if (get_project_pref(auto_continue_multiline)) @@ -1523,27 +1539,58 @@ static void insert_indent_after_line(GeanyEditor *editor, gint line) g_free(text); } +static gboolean auto_close_chars_consume(ScintillaObject *sci, gint pos, gchar c) { + gboolean isAutoClosed = FALSE; + switch (c) + { + case ')': + if (editor_prefs.autoclose_chars & GEANY_AC_PARENTHESIS) + isAutoClosed = TRUE; + break; + case '}': + if (editor_prefs.autoclose_chars & GEANY_AC_CBRACKET) + isAutoClosed = TRUE; + break; + case ']': + if (editor_prefs.autoclose_chars & GEANY_AC_SBRACKET) + isAutoClosed = TRUE; + break; + case '\'': + if (editor_prefs.autoclose_chars & GEANY_AC_SQUOTE) + isAutoClosed = TRUE; + break; + case '"': + if (editor_prefs.autoclose_chars & GEANY_AC_DQUOTE) + isAutoClosed = TRUE; + break; + } + + gchar cNext = sci_get_char_at( sci, pos); + + if( isAutoClosed && cNext == c ) { + sci_delete_range(sci, pos, 1); + sci_colourise(sci, 0, -1); + return TRUE; + } + return FALSE; +} static void auto_close_chars(ScintillaObject *sci, gint pos, gchar c) { const gchar *closing_char = NULL; - gint end_pos = -1; - - if (utils_isbrace(c, 0)) - end_pos = sci_find_matching_brace(sci, pos - 1); switch (c) { case '(': - if ((editor_prefs.autoclose_chars & GEANY_AC_PARENTHESIS) && end_pos == -1) + if (editor_prefs.autoclose_chars & GEANY_AC_PARENTHESIS) closing_char = ")"; break; case '{': - if ((editor_prefs.autoclose_chars & GEANY_AC_CBRACKET) && end_pos == -1) + if (editor_prefs.autoclose_chars & GEANY_AC_CBRACKET) closing_char = "}"; break; case '[': - if ((editor_prefs.autoclose_chars & GEANY_AC_SBRACKET) && end_pos == -1) + if (editor_prefs.autoclose_chars & GEANY_AC_SBRACKET) closing_char = "]"; break; case '\'': diff --git a/src/editor.h b/src/editor.h index 347f6d0f2a..3aab81a6e7 100644 --- a/src/editor.h +++ b/src/editor.h @@ -138,6 +138,7 @@ typedef struct GeanyEditorPrefs gint autocompletion_update_freq; gint scroll_lines_around_cursor; gint ime_interaction; /* input method editor's candidate window behaviour */ + guint autoclose_chars_consume; } GeanyEditorPrefs; diff --git a/src/sciwrappers.c b/src/sciwrappers.c index 804c0d91a7..a48950234e 100644 --- a/src/sciwrappers.c +++ b/src/sciwrappers.c @@ -285,6 +285,10 @@ void sci_add_text(ScintillaObject *sci, const gchar *text) } } +void sci_delete_range(ScintillaObject *sci, const gint pos, const gint length) +{ + SSM(sci, SCI_DELETERANGE, pos, length); +} /** Sets all text. * @param sci Scintilla widget. diff --git a/src/sciwrappers.h b/src/sciwrappers.h index f74e23221b..042cecd722 100644 --- a/src/sciwrappers.h +++ b/src/sciwrappers.h @@ -98,6 +98,8 @@ gint sci_get_current_line (ScintillaObject *sci); void sci_indicator_set (ScintillaObject *sci, gint indic); void sci_indicator_clear (ScintillaObject *sci, gint pos, gint len); +void sci_delete_range (ScintillaObject *sci, gint pos, gint len); + void sci_set_line_indentation (ScintillaObject *sci, gint line, gint indent); gint sci_get_line_indentation (ScintillaObject *sci, gint line); gint sci_find_matching_brace (ScintillaObject *sci, gint pos); From b3d0cdd1de364c253d18a36bb225a928feb10eff Mon Sep 17 00:00:00 2001 From: Jeremias Kleer Date: Thu, 14 Oct 2021 15:39:53 +0200 Subject: [PATCH 2/2] Nice on-new-line bracket handling When using auto closing brackets, the cursor is between the opening and closing bracket. When hitting enter for new-line, the closing bracket is still behind the cursor. If the cursor is between an opening and closing bracket and enter is hit, in my opinion it, it is desired to get a situation like this (where | is the cursor) { | } or void main() { if( true ) { | } } --- src/editor.c | 95 ++++++++++++++++++++++++++++++++++++++++------- src/sciwrappers.c | 6 +++ src/sciwrappers.h | 1 + 3 files changed, 89 insertions(+), 13 deletions(-) diff --git a/src/editor.c b/src/editor.c index 5668d9812d..163e06b0a9 100644 --- a/src/editor.c +++ b/src/editor.c @@ -94,7 +94,7 @@ static gchar indent[100]; static void on_new_line_added(GeanyEditor *editor); static gboolean handle_xml(GeanyEditor *editor, gint pos, gchar ch); static void insert_indent_after_line(GeanyEditor *editor, gint line); -static void auto_multiline(GeanyEditor *editor, gint pos); +static gboolean auto_multiline(GeanyEditor *editor, gint pos, gboolean forceIgnoreLexer); static void auto_close_chars(ScintillaObject *sci, gint pos, gchar c); static gboolean auto_close_chars_consume(ScintillaObject *sci, gint pos, gchar c); static void close_block(GeanyEditor *editor, gint pos); @@ -573,7 +573,7 @@ static void check_line_breaking(GeanyEditor *editor, gint pos) /* break the line after the space */ sci_set_current_position(sci, pos + 1, FALSE); sci_cancel(sci); /* don't select from completion list */ - sci_send_command(sci, SCI_NEWLINE); + sci_new_line(sci); line++; /* correct cursor position (might not be at line end) */ @@ -1307,25 +1307,92 @@ editor_get_indent_prefs(GeanyEditor *editor) static void on_new_line_added(GeanyEditor *editor) { ScintillaObject *sci = editor->sci; - gint line = sci_get_current_line(sci); + const gint line = sci_get_current_line(sci); + + gboolean doHandleBracket = FALSE; + gboolean bracketHandled = FALSE; + gint posStart; + gint posBracketStart; /* simple indentation */ - if (editor->auto_indent) - { + if (editor->auto_indent) { + + posStart = sci_get_current_position(sci); + posBracketStart = posStart-(sci_get_eol_mode(sci) == SC_EOL_CRLF ? 3 : 2); + gchar cPrev = sci_get_char_at( sci, posBracketStart); + gchar cNext = sci_get_char_at( sci, posStart); + insert_indent_after_line(editor, line - 1); /* Nice bracket handling */ + if (cPrev == '{' && cNext == '}') { + posStart = sci_get_current_position(sci); + doHandleBracket = TRUE; + } } if (get_project_pref(auto_continue_multiline)) { /* " * " auto completion in multiline C/C++/D/Java comments */ - auto_multiline(editor, line); + if (auto_multiline(editor, line, FALSE) && doHandleBracket) { + const gint posBaseMultilineComment = sci_get_current_position(sci); + const gint colBaseMultilineComment = sci_get_col_from_position(sci, posBaseMultilineComment); + + const gint linePrev = line-1; + + struct Sci_TextToFind ttf; + + ttf.lpstrText = (gchar*) "\\s*"; + ttf.chrg.cpMin = sci_get_position_from_col(sci, linePrev, colBaseMultilineComment ); + ttf.chrg.cpMax = sci_get_line_end_position(sci, linePrev); + ttf.chrgText.cpMin = 0; + ttf.chrgText.cpMax = 0; + const gint flags = SCFIND_REGEXP; + + /* search the whole document for the word root and collect results */ + gboolean foundWhitespaces = sci_find_text(sci, flags, &ttf ) != -1; + const gint sizeWhitespaces = ttf.chrgText.cpMax - ttf.chrgText.cpMin; + gchar * whitespaceText = 0; + gboolean insertWhitespaceText = foundWhitespaces && sizeWhitespaces>0; + if (insertWhitespaceText) { + whitespaceText = sci_get_contents_range(sci, ttf.chrgText.cpMin, ttf.chrgText.cpMax); + sci_add_text(sci, whitespaceText); + } + + // Add one "tab" + const GeanyIndentPrefs *iprefs = editor_get_indent_prefs(editor); + sci_add_text(sci, get_whitespace(iprefs, get_tab_width(iprefs))); + + // Insert newline before the closing } and apply multiline comment + const gint posBackAfterNewline = sci_get_current_position(sci); + sci_add_text(sci, editor_get_eol_char(editor)); + insert_indent_after_line(editor, line); // editor->auto_indent == true if doHandleBracket == true + auto_multiline(editor, line+1, TRUE); + + if (insertWhitespaceText) { + sci_add_text(sci, whitespaceText); + } + if (whitespaceText != 0) { + g_free(whitespaceText); + } + sci_set_current_position(sci, posBackAfterNewline, TRUE); + bracketHandled = TRUE; + } } - if (editor_prefs.newline_strip) - { /* strip the trailing spaces on the previous line */ + if (editor_prefs.newline_strip && !doHandleBracket) + { /* strip the trailing spaces on the previous line + * if doHandleBracket == true, the previous line ends with { + */ editor_strip_line_trailing_spaces(editor, line - 1); } + + if (doHandleBracket && !bracketHandled) { + sci_add_text(sci, editor_get_eol_char(editor)); + insert_indent_after_line(editor, line); // editor->auto_indent == true if doHandleBracket == true + close_block(editor, sci_get_current_position(sci)); + sci_set_current_position(sci, posStart, TRUE); + bracketHandled = TRUE; + } } @@ -3569,7 +3636,7 @@ static gboolean is_comment_char(gchar c, gint lexer) } -static void auto_multiline(GeanyEditor *editor, gint cur_line) +static gboolean auto_multiline(GeanyEditor *editor, gint cur_line, gboolean forceIgnoreLexer) { ScintillaObject *sci = editor->sci; gint indent_pos, style; @@ -3578,12 +3645,12 @@ static void auto_multiline(GeanyEditor *editor, gint cur_line) /* Use the start of the line enter was pressed on, to avoid any doc keyword styles */ indent_pos = sci_get_line_indent_position(sci, cur_line - 1); style = sci_get_style_at(sci, indent_pos); - if (!in_block_comment(lexer, style)) - return; + if (!forceIgnoreLexer && !in_block_comment(lexer, style)) + return FALSE; /* Check whether the comment block continues on this line */ indent_pos = sci_get_line_indent_position(sci, cur_line); - if (sci_get_style_at(sci, indent_pos) == style || indent_pos >= sci_get_length(sci)) + if ((forceIgnoreLexer || sci_get_style_at(sci, indent_pos) == style) || indent_pos >= sci_get_length(sci)) { gchar *previous_line = sci_get_line(sci, cur_line - 1); /* the type of comment, '*' (C/C++/Java), '+' and the others (D) */ @@ -3609,7 +3676,7 @@ static void auto_multiline(GeanyEditor *editor, gint cur_line) if (indent_len % indent_width == 1) SSM(sci, SCI_DELETEBACKNOTLINE, 0, 0); /* remove whitespace indent */ g_free(previous_line); - return; + return TRUE; } /* check whether we are on the second line of multi line comment */ i = 0; @@ -3629,7 +3696,9 @@ static void auto_multiline(GeanyEditor *editor, gint cur_line) g_free(result); g_free(previous_line); + return TRUE; } + return FALSE; } diff --git a/src/sciwrappers.c b/src/sciwrappers.c index a48950234e..cf6ffb178d 100644 --- a/src/sciwrappers.c +++ b/src/sciwrappers.c @@ -1129,6 +1129,12 @@ void sci_selection_duplicate(ScintillaObject *sci) } +void sci_new_line(ScintillaObject *sci) +{ + SSM(sci, SCI_NEWLINE, 0, 0); +} + + /** Inserts text. * @param sci Scintilla widget. * @param pos Position, or -1 for current. diff --git a/src/sciwrappers.h b/src/sciwrappers.h index 042cecd722..1a5d7c9fa3 100644 --- a/src/sciwrappers.h +++ b/src/sciwrappers.h @@ -182,6 +182,7 @@ void sci_clear_cmdkey (ScintillaObject *sci, gint key); void sci_assign_cmdkey (ScintillaObject *sci, gint key, gint command); void sci_selection_duplicate (ScintillaObject *sci); void sci_line_duplicate (ScintillaObject *sci); +void sci_new_line (ScintillaObject *sci); void sci_set_keywords (ScintillaObject *sci, guint k, const gchar *text); void sci_set_lexer (ScintillaObject *sci, guint lexer_id);