On Thu, 30 Sep 2010 13:23:58 +0100%
Nick Treleaven <[email protected]> wrote:
> On Sat, 10 Jul 2010 21:48:35 +0400
> Eugene Arshinov <[email protected]> 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
[email protected]
http://lists.uvena.de/cgi-bin/mailman/listinfo/geany-devel