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

Reply via email to