Mercurial > octave
changeset 23688:cb36684b7a33
In the GUI editor, automatically add "endif" for "if" etc.
* octave-qscintilla.cc (is_end, octave_qscintilla::keypressEvent):
New function.
* octave-qscintilla.h (octave_qscintilla::keypressEvent): New function.
* file-editor-tab.cc (file_editor_tab::notice_settings):
New config setting, editor/auto_endif
* settings-dialog.cc (settings_dialog::settings_dialog,
settings_dialog::write_changed_settings):
New config setting, editor/auto_endif
* settings-dialog.ui:
New config setting, editor/auto_endif
author | Lachlan Andrew <lachlanbis@gmail.com> |
---|---|
date | Sun, 25 Jun 2017 08:56:45 +0200 |
parents | 315a3dcc229c |
children | b729e97aa8d1 |
files | libgui/src/m-editor/file-editor-tab.cc libgui/src/m-editor/octave-qscintilla.cc libgui/src/m-editor/octave-qscintilla.h libgui/src/settings-dialog.cc libgui/src/settings-dialog.ui |
diffstat | 5 files changed, 249 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- a/libgui/src/m-editor/file-editor-tab.cc Sat Jun 24 16:02:23 2017 -0400 +++ b/libgui/src/m-editor/file-editor-tab.cc Sun Jun 25 08:56:45 2017 +0200 @@ -2348,9 +2348,8 @@ disconnect (_edit_area, SIGNAL (linesChanged ()), 0, 0); } - _edit_area->setAutoIndent - (settings->value ("editor/auto_indent",true).toBool ()); _smart_indent = settings->value ("editor/auto_indent",true).toBool (); + _edit_area->setAutoIndent (_smart_indent); _edit_area->setTabIndents (settings->value ("editor/tab_indents_line",false).toBool ()); _edit_area->setBackspaceUnindents @@ -2365,6 +2364,9 @@ _edit_area->setTabWidth (settings->value ("editor/tab_width",2).toInt ()); + _edit_area->set_auto_endif + (settings->value ("editor/auto_endif",1).toInt ()); + _edit_area->SendScintilla (QsciScintillaBase::SCI_SETHSCROLLBAR, settings->value ("editor/show_hscroll_bar",true).toBool ()); _edit_area->SendScintilla (QsciScintillaBase::SCI_SETSCROLLWIDTH,-1);
--- a/libgui/src/m-editor/octave-qscintilla.cc Sat Jun 24 16:02:23 2017 -0400 +++ b/libgui/src/m-editor/octave-qscintilla.cc Sun Jun 25 08:56:45 2017 +0200 @@ -53,7 +53,7 @@ #include "resource-manager.h" octave_qscintilla::octave_qscintilla (QWidget *p) - : QsciScintilla (p) + : QsciScintilla (p), _auto_endif (1) { connect (this, SIGNAL (textChanged ()), this, SLOT (text_changed ())); @@ -422,6 +422,181 @@ return SendScintilla (QsciScintillaBase::SCI_GETSTYLEAT, position); } +// Return true if CANDIDATE is a "closing" that matches OPENING, +// such as "end" or "endif" for "if", or "catch" for "try". +// Used for testing the last word of an "if" etc. line, +// or the first word of the following line. +static bool +is_end (QString candidate, QString& opening) +{ + bool retval = false; + + if (opening == "do") // The only one that can't be ended by "end" + { + if (candidate == "until") + retval = true; + } + else + { + if (candidate == "end") + retval = true; + else + { + if (opening == "try") + { + if (candidate == "catch" || candidate == "end_try_catch") + retval = true; + } + else if (opening == "unwind_protect") + { + if (candidate == "unwind_protect_cleanup" + || candidate == "end_unwind_protect") + retval = true; + } + else if (candidate == "end" + opening) + retval = true; + else if (opening == "if" && candidate == "else") + retval = true; + } + } + + return retval; +} + +void +octave_qscintilla::keyPressEvent (QKeyEvent *e) +{ + // On receiving Enter, insert and "end" for an "if" etc., if needed. + // (Use of "while" allows "break" to skip the rest. + // It may be clearer to use "if" and "goto", + // but that violates the coding standards.) + while (_auto_endif + && e->type () == QEvent::KeyPress + && (e->key () == Qt::Key_Return || e->key () == Qt::Key_Enter) + && !(e->modifiers () & (Qt::ControlModifier | Qt::MetaModifier + | Qt::AltModifier))) + { + bool autofill_simple_end = (_auto_endif == 2) ; + + // Get line. + QPoint global_pos, local_pos; + get_global_textcursor_pos (&global_pos, &local_pos); + int linenr = lineAt (local_pos); + QString line = text (linenr); // should always exist; + + // Don't autocomplete an empty line. + size_t start = line.toStdString ().find_first_not_of (" \t"); + if (start == std::string::npos) + break; + + // Get the first word of the line + // Keep a compiled regular expression to extract the first word. + QRegExp rx_start, rx_end; + static bool first = true; + if (first) + { + rx_start = QRegExp ("(\\w+)"); + // last word except for comments, assuming no ' or " in comment. + // rx_end = QRegExp ("(\\w+)[ \t;\r\n]*([%#][^\"']*)?$"); + + // last word except for comments, + // allowing % and # in single or double quoted strings + // FIXME This will get confused by transpose. + rx_end = QRegExp ("(?:(?:['\"][^'\"]*['\"])?[^%#]*)*" + "(\\w+)[ \t;\r\n]*([%#].*)?$"); + } + + int tmp = rx_start.indexIn (line, start); + if (tmp == -1) + break; + + QString first_word = rx_start.cap(1); + + // Check if the first word of the line was an opening needing a close + if (! (first_word == "classdef" || first_word == "for" + || first_word == "while" || first_word == "if" + || first_word == "switch" || first_word == "properties" + || first_word == "events" || first_word == "function" + || first_word == "parfor" || first_word == "methods" + || first_word == "try" || first_word == "do" + || first_word == "unwind_protect")) + break; + if (rx_end.indexIn (line, start) != -1 + && is_end (rx_end.cap(1), first_word)) + break; + + // Check if following line has the same or less indentation + // Check if the following line does not start with + // end* (until) (catch) + if (linenr < lines () - 1) + { + int offset = 1; + size_t next_start; + QString next_line; + do // find next non-blank line + { + next_line = text (linenr + offset++); + next_start = next_line.toStdString ().find_first_not_of (" \t\n"); + } + while (linenr + offset < lines () + && next_start == std::string::npos); + + if (next_start == std::string::npos) + next_start = 0; + + if (next_start > start) // more indented => don't add "end" + break; + if (next_start == start) // same => check if already is "end" + { + tmp = rx_start.indexIn (next_line, start); + if (tmp != -1 && is_end (rx_start.cap(1), first_word)) + break; + } + } + + // If all of the above, insert a new line, with matching indent + // and either 'end' or 'end...', depending on a flag. + + // If we insert directly after the last line, the "end" is autoindented, + // so add a dummy line. + if (linenr + 1 == lines ()) + insertAt (QString ("\n"), linenr + 1, 0); + + // For try/catch/end, fill "end" first, so "catch" is top of undo stack + if (first_word == "try") + insertAt (QString (start, ' ') + + (autofill_simple_end ? "end\n" : "end_try_catch\n"), + linenr + 1, 0); + else if (first_word == "unwind_protect") + insertAt (QString (start, ' ') + + (autofill_simple_end ? "end\n" : "end_unwind_protect\n"), + linenr + 1, 0); + + QString next_line; + if (first_word == "do") + next_line = "until\n"; + else if (first_word == "try") + next_line = "catch\n"; + else if (first_word == "unwind_protect") + next_line = "unwind_protect_cleanup\n"; + else if (autofill_simple_end) + next_line = "end\n"; + else + { + if (first_word == "unwind_protect") + first_word = "_" + first_word; + next_line = "end" + first_word + "\n"; + } + + insertAt (QString (start, ' ') + next_line, linenr + 1, 0); + + break; + } + + // Call default processing, even if we did the above. + QsciScintilla::keyPressEvent (e); +} + // Is a specific cursor position in a line or block comment? int octave_qscintilla::is_style_comment (int pos)
--- a/libgui/src/m-editor/octave-qscintilla.h Sat Jun 24 16:02:23 2017 -0400 +++ b/libgui/src/m-editor/octave-qscintilla.h Sun Jun 25 08:56:45 2017 +0200 @@ -57,6 +57,7 @@ QString comment_string (); int get_style (int pos = -1); int is_style_comment (int pos = -1); + void set_auto_endif (int ai) { _auto_endif = ai; } signals: @@ -88,6 +89,10 @@ private: + void keyPressEvent (QKeyEvent *e); + + int _auto_endif; + QString _word_at_cursor; };
--- a/libgui/src/settings-dialog.cc Sat Jun 24 16:02:23 2017 -0400 +++ b/libgui/src/settings-dialog.cc Sun Jun 25 08:56:45 2017 +0200 @@ -424,6 +424,8 @@ ui->editor_highlight_all_occurrences->setChecked ( settings->value ("editor/highlight_all_occurrences",true).toBool ()); + ui->editor_auto_endif->setCurrentIndex ( + settings->value ("editor/auto_endif", 1).toInt () ); ui->editor_codeCompletion->setChecked ( settings->value ("editor/codeCompletion", true).toBool ()); ui->editor_spinbox_ac_threshold->setValue ( @@ -838,6 +840,8 @@ ui->editor_checkbox_ac_case->isChecked ()); settings->setValue ("editor/codeCompletion_replace", ui->editor_checkbox_ac_replace->isChecked ()); + settings->setValue ("editor/auto_endif", + ui->editor_auto_endif->currentIndex ()); settings->setValue ("editor/show_white_space", ui->editor_ws_checkbox->isChecked ()); settings->setValue ("editor/show_white_space_indent",
--- a/libgui/src/settings-dialog.ui Sat Jun 24 16:02:23 2017 -0400 +++ b/libgui/src/settings-dialog.ui Sun Jun 25 08:56:45 2017 +0200 @@ -1303,6 +1303,66 @@ </item> </layout> </item> + + <item> + <layout class="QHBoxLayout" name="horizontalLayout_17"> + <item> + <widget class="QLabel" name="label_auto_endif"> + <property name="text"> + <string>Auto insert after "if" etc.</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="editor_auto_endif"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="sizeAdjustPolicy"> + <enum>QComboBox::AdjustToContents</enum> + </property> + <property name="minimumContentsLength"> + <number>5</number> + </property> + <item> + <property name="text"> + <string>Nothing</string> + </property> + </item> + <item> + <property name="text"> + <string>"endif" etc.</string> + </property> + </item> + <item> + <property name="text"> + <string>"end"</string> + </property> + </item> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_33"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Expanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>10</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> </widget> </item>