On Thu, 30 Sep 2010 13:23:58 +0100% Nick Treleaven <nick.trelea...@btinternet.com> wrote:
> On Sat, 10 Jul 2010 21:48:35 +0400 > Eugene Arshinov <earshi...@gmail.com> wrote: > > > Hi all. > > > > I attach three patches: > > 1. Keybindings to insert new line after/before current > ... > > 3. Automatically insert additional indentation in XML/HTML files if > > previous line ends with an opening tag > > I finally got round to replying to the other patches, sorry for the > delay. > > > The drawback of the first patch is that I doubt it's so useful. I > > know Vi[m] provides shortcuts for these actions ('o' and 'O' in > > normal mode), but their necessity is questionable for me. I > > implemented this to ease XML editing with tag autocompletion turned > > on. Suppose you need to write to consequent <li>'s. Without this > > patch you need to press End+Enter after writing the first <li> to > > place the cursor where you need to start the second. With my > > patch, you only need to press one shortcut you can assign in > > Preferences > Keybindings :) > > Maybe we could add these. BTW you could use utils_get_eol_char(). > Also we already have sci_get_line_indent_position(). > I missed this function… Updated patch attached. > > The drawback of the third patch is that it's not completed. If user > > likes to leave HTML tags like <br> "unclosed", she would be > > disturbed by automatic indentation caused by my patch, so a check > > box in Preferences is desirable. I'll code it as soon as we decide > > this patch can go to trunk. > > For HTML perhaps we could have a filetype pref for this. > What should it look like? I can't name this pref "autoindent" because it would be confusing if for XML and HTML it only controls the indentation after XML/HTML tags, not after braces in PHP/JS chunks. If I make the pref more specific (e.g., "xml-autoindent"), it won't probably be quite proper to insert such a specific member to GeanyFiletype struct. Maybe it's more appropriate to add a check button near "Preferences > Editor > Indentation > Auto-indent mode" list? AFAIK (never used it), "Match braces" mode works only for braces languages and thus is already somewhat filetype-specific. For the present, I attach an updated patch which doesn't insert indentation after "short" HTML tags. I also modified existing tag autocompletion code so that it doesn't check for HTML tags if current lexer is XML, not HTML. I slightly modified utils_find_open_xml_tag() in order to reuse it in my autoindentation code. Particularly I removed `check_tag' parameter and strange condition else if (! check_tag && *cur == '>') break; I'm not sure why this condition was needed there. Best regards, Eugene.
diff --git a/src/editor.c b/src/editor.c index 7e3f81e..11c7737 100644 --- a/src/editor.c +++ b/src/editor.c @@ -4011,7 +4011,7 @@ void editor_select_paragraph(GeanyEditor *editor) /* simple indentation to indent the current line with the same indent as the previous one */ -static void smart_line_indentation(GeanyEditor *editor, gint first_line, gint last_line) +void editor_smart_indentation_for_lines(GeanyEditor *editor, gint first_line, gint last_line) { gint i, sel_start = 0, sel_end = 0; @@ -4063,12 +4063,12 @@ void editor_smart_line_indentation(GeanyEditor *editor, gint pos) sci_start_undo_action(sci); - smart_line_indentation(editor, first_line, last_line); + editor_smart_indentation_for_lines(editor, first_line, last_line); /* set cursor position if there was no selection */ if (first_sel_start == first_sel_end) { - gint indent_pos = SSM(sci, SCI_GETLINEINDENTPOSITION, first_line, 0); + gint indent_pos = sci_get_line_indent_position(sci, first_line); /* use indent position as user may wish to change indentation afterwards */ sci_set_current_position(sci, indent_pos, FALSE); @@ -4107,7 +4107,7 @@ void editor_indentation_by_one_space(GeanyEditor *editor, gint pos, gboolean dec for (i = first_line; i <= last_line; i++) { - indentation_end = SSM(editor->sci, SCI_GETLINEINDENTPOSITION, i, 0); + indentation_end = sci_get_line_indent_position(editor->sci, i); if (decrease) { line_start = SSM(editor->sci, SCI_POSITIONFROMLINE, i, 0); diff --git a/src/editor.h b/src/editor.h index e81a074..13e5637 100644 --- a/src/editor.h +++ b/src/editor.h @@ -213,6 +213,8 @@ void editor_insert_alternative_whitespace(GeanyEditor *editor); void editor_indent(GeanyEditor *editor, gboolean increase); +void editor_smart_indentation_for_lines(GeanyEditor *editor, gint first_line, gint last_line); + void editor_smart_line_indentation(GeanyEditor *editor, gint pos); void editor_indentation_by_one_space(GeanyEditor *editor, gint pos, gboolean decrease); diff --git a/src/keybindings.c b/src/keybindings.c index b7ad875..e25cadc 100644 --- a/src/keybindings.c +++ b/src/keybindings.c @@ -364,6 +364,10 @@ static void init_default_kb(void) keybindings_set_item(group, GEANY_KEYS_FORMAT_REFLOWPARAGRAPH, NULL, GDK_j, GDK_CONTROL_MASK, "format_reflowparagraph", _("_Reflow Lines/Block"), LW(reflow_lines_block1)); + keybindings_set_item(group, GEANY_KEYS_FORMAT_INSERTLINE_AFTER, NULL, + 0, 0, "format_insertline_after", _("Insert new line after current"), NULL); + keybindings_set_item(group, GEANY_KEYS_FORMAT_INSERTLINE_BEFORE, NULL, + 0, 0, "format_insertline_before", _("Insert new line before current"), NULL); group = ADD_KB_GROUP(INSERT, _("Insert"), cb_func_insert_action); @@ -2359,6 +2363,39 @@ static void reflow_paragraph(GeanyEditor *editor) } +static void insert_line_after(GeanyEditor *editor) +{ + ScintillaObject *sci = editor->sci; + const gchar *eol = utils_get_eol_char(sci_get_eol_mode(sci)); + gint line = sci_get_current_line(sci); + + sci_start_undo_action(sci); + sci_insert_text(sci, sci_get_line_end_position(sci, line), eol); + editor_smart_indentation_for_lines(editor, line+1, line+1); + sci_set_current_position(sci, sci_get_line_end_position(sci, line+1), TRUE); + sci_end_undo_action(sci); +} + + +static void insert_line_before(GeanyEditor *editor) +{ + ScintillaObject *sci = editor->sci; + const gchar *eol = utils_get_eol_char(sci_get_eol_mode(sci)); + gint line = sci_get_current_line(sci); + gint linestart = sci_get_position_from_line(sci, line); + gint indentpos = sci_get_line_indent_position(sci, line); + gchar *indentation = sci_get_contents_range(sci, linestart, indentpos); + + sci_start_undo_action(sci); + sci_insert_text(sci, linestart, eol); + sci_insert_text(sci, linestart, indentation); + sci_set_current_position(sci, indentpos, TRUE); + sci_end_undo_action(sci); + + g_free(indentation); +} + + /* common function for format keybindings, only valid when scintilla has focus. */ static gboolean cb_func_format_action(guint key_id) { @@ -2416,6 +2453,12 @@ static gboolean cb_func_format_action(guint key_id) case GEANY_KEYS_FORMAT_REFLOWPARAGRAPH: reflow_paragraph(doc->editor); break; + case GEANY_KEYS_FORMAT_INSERTLINE_AFTER: + insert_line_after(doc->editor); + break; + case GEANY_KEYS_FORMAT_INSERTLINE_BEFORE: + insert_line_before(doc->editor); + break; } return TRUE; } diff --git a/src/keybindings.h b/src/keybindings.h index f0af6f3..23cb5d4 100644 --- a/src/keybindings.h +++ b/src/keybindings.h @@ -236,6 +236,8 @@ enum GeanyKeyBindingID GEANY_KEYS_FILE_OPENLASTTAB, /**< Keybinding. */ GEANY_KEYS_SEARCH_FINDINFILES, /**< Keybinding. */ GEANY_KEYS_GOTO_NEXTWORDPART, /**< Keybinding. */ + GEANY_KEYS_FORMAT_INSERTLINE_AFTER, /**< Keybinding. */ + GEANY_KEYS_FORMAT_INSERTLINE_BEFORE, /**< Keybinding. */ GEANY_KEYS_COUNT /* must not be used by plugins */ }; @@ -273,4 +275,3 @@ void keybindings_show_shortcuts(void); gboolean keybindings_check_event(GdkEventKey *ev, GeanyKeyBinding *kb); #endif -
diff --git a/src/editor.c b/src/editor.c index 11c7737..2a473d2 100644 --- a/src/editor.c +++ b/src/editor.c @@ -1305,6 +1305,37 @@ static gint get_python_indent(ScintillaObject *sci, gint line) } +static gint get_xml_indent(ScintillaObject *sci, gint line) +{ + gboolean need_close = FALSE; + gint end = sci_get_line_end_position(sci, line) - 1; + + if (sci_get_char_at(sci, end) == '>' && + sci_get_char_at(sci, end - 1) != '/') + { + gint style = sci_get_style_at(sci, end); + + if (style == SCE_H_TAG || style == SCE_H_TAGUNKNOWN) + { + gint start = sci_get_position_from_line(sci, line); + gchar *line_contents = sci_get_contents_range(sci, start, end + 1); + gchar *opened_tag_name = utils_find_open_xml_tag(line_contents, end + 1 - start); + + if (NZV(opened_tag_name)) + { + need_close = TRUE; + if (sci_get_lexer(sci) == SCLEX_HTML && utils_is_short_html_tag(opened_tag_name)) + need_close = FALSE; + } + g_free(line_contents); + g_free(opened_tag_name); + } + } + + return need_close ? 1 : 0; +} + + static gint get_indent_size_after_line(GeanyEditor *editor, gint line) { ScintillaObject *sci = editor->sci; @@ -1317,11 +1348,25 @@ static gint get_indent_size_after_line(GeanyEditor *editor, gint line) if (iprefs->auto_indent_mode > GEANY_AUTOINDENT_BASIC) { + gint additional_indent = 0; + if (lexer_has_braces(sci)) - size += iprefs->width * get_brace_indent(sci, line); + additional_indent = iprefs->width * get_brace_indent(sci, line); else if (FILETYPE_ID(editor->document->file_type) == GEANY_FILETYPES_PYTHON) - size += iprefs->width * get_python_indent(sci, line); + additional_indent = iprefs->width * get_python_indent(sci, line); + + /* HTML lexer "has braces" because of PHP and JavaScript. If get_brace_indent() did not + * recommend us to insert additional indent, we are probably not in PHP/JavaScript chunk and + * should make the XML-related check */ + if (additional_indent == 0 && ( + FILETYPE_ID(editor->document->file_type) == GEANY_FILETYPES_HTML || + FILETYPE_ID(editor->document->file_type) == GEANY_FILETYPES_XML)) + { + size += iprefs->width * get_xml_indent(sci, line); + } + + size += additional_indent; } return size; } @@ -2620,7 +2665,7 @@ static gboolean handle_xml(GeanyEditor *editor, gint pos, gchar ch) { ScintillaObject *sci = editor->sci; gint lexer = sci_get_lexer(sci); - gint min, style; + gint min, size, style; gchar *str_found, sel[512]; gboolean result = FALSE; @@ -2652,19 +2697,12 @@ static gboolean handle_xml(GeanyEditor *editor, gint pos, gchar ch) /* User typed something like "<br/>" */ return FALSE; - str_found = utils_find_open_xml_tag(sel, pos - min, (ch == '/')); - - /* when found string is something like br, img or another short tag, quit */ - if (utils_str_equal(str_found, "br") - || utils_str_equal(str_found, "hr") - || utils_str_equal(str_found, "img") - || utils_str_equal(str_found, "base") - || utils_str_equal(str_found, "basefont") /* < or not < */ - || utils_str_equal(str_found, "frame") - || utils_str_equal(str_found, "input") - || utils_str_equal(str_found, "link") - || utils_str_equal(str_found, "area") - || utils_str_equal(str_found, "meta")) + size = pos - min; + if (ch == '/') + size -= 2; /* skip </ */ + str_found = utils_find_open_xml_tag(sel, size); + + if (lexer == SCLEX_HTML && utils_is_short_html_tag(str_found)) { /* ignore tag */ } diff --git a/src/utils.c b/src/utils.c index 27dbe5d..3c00f3c 100644 --- a/src/utils.c +++ b/src/utils.c @@ -281,7 +281,7 @@ gint utils_write_file(const gchar *filename, const gchar *text) * Search backward through size bytes looking for a '<', then return the tag, if any. * @return The tag name */ -gchar *utils_find_open_xml_tag(const gchar sel[], gint size, gboolean check_tag) +gchar *utils_find_open_xml_tag(const gchar sel[], gint size) { const gchar *begin, *cur; @@ -290,10 +290,7 @@ gchar *utils_find_open_xml_tag(const gchar sel[], gint size, gboolean check_tag) return NULL; } begin = &sel[0]; - if (check_tag) - cur = &sel[size - 3]; - else - cur = &sel[size - 1]; + cur = &sel[size-1]; /* Skip to the character before the closing brace */ while (cur > begin) @@ -312,16 +309,18 @@ gchar *utils_find_open_xml_tag(const gchar sel[], gint size, gboolean check_tag) { if (*cur == '<') break; - else if (! check_tag && *cur == '>') - break; --cur; } if (*cur == '<') { - GString *result = g_string_sized_new(64); + GString *result; cur++; + if (*cur == '/') + return NULL; /* we found a closing tag */ + + result = g_string_sized_new(64); while (strchr(":_-.", *cur) || isalnum(*cur)) { g_string_append_c(result, *cur); @@ -334,6 +333,22 @@ gchar *utils_find_open_xml_tag(const gchar sel[], gint size, gboolean check_tag) } +/* Returns true if specified tag doesn't usually contain any content and can be left unclosed */ +gboolean utils_is_short_html_tag(const gchar *tag_name) +{ + return utils_str_equal(tag_name, "br") + || utils_str_equal(tag_name, "hr") + || utils_str_equal(tag_name, "img") + || utils_str_equal(tag_name, "base") + || utils_str_equal(tag_name, "basefont") /* < or not < */ + || utils_str_equal(tag_name, "frame") + || utils_str_equal(tag_name, "input") + || utils_str_equal(tag_name, "link") + || utils_str_equal(tag_name, "area") + || utils_str_equal(tag_name, "meta"); +} + + const gchar *utils_get_eol_name(gint eol_mode) { switch (eol_mode) @@ -2034,5 +2049,3 @@ gchar **utils_copy_environment(const gchar **exclude_vars, const gchar *first_va return result; } - - diff --git a/src/utils.h b/src/utils.h index 1303fea..5e23f98 100644 --- a/src/utils.h +++ b/src/utils.h @@ -136,7 +136,9 @@ gboolean utils_is_opening_brace(gchar c, gboolean include_angles); gint utils_write_file(const gchar *filename, const gchar *text); -gchar *utils_find_open_xml_tag(const gchar sel[], gint size, gboolean check_tag); +gchar *utils_find_open_xml_tag(const gchar sel[], gint size); + +gboolean utils_is_short_html_tag(const gchar *tag_name); void utils_ensure_same_eol_characters(GString *template, gint target_eol_mode);
_______________________________________________ Geany-devel mailing list Geany-devel@uvena.de http://lists.uvena.de/cgi-bin/mailman/listinfo/geany-devel