diff --git a/plugins/geanyfunctions.h b/plugins/geanyfunctions.h
index a356f8f..b29823c 100644
--- a/plugins/geanyfunctions.h
+++ b/plugins/geanyfunctions.h
@@ -92,6 +92,10 @@
 	geany_functions->p_editor->editor_get_eol_char_mode
 #define editor_goto_pos \
 	geany_functions->p_editor->editor_goto_pos
+#define editor_get_snippets_for_file_type \
+	geany_functions->p_editor->editor_get_snippets_for_file_type
+#define editor_insert_snippet \
+	geany_functions->p_editor->editor_insert_snippet
 #define scintilla_send_message \
 	geany_functions->p_scintilla->scintilla_send_message
 #define scintilla_new \
@@ -200,6 +204,12 @@
 	geany_functions->p_sci->sci_get_line_indentation
 #define sci_get_lexer \
 	geany_functions->p_sci->sci_get_lexer
+#define sci_is_string_style \
+	geany_functions->p_sci->sci_is_string_style
+#define sci_is_comment_style \
+	geany_functions->p_sci->sci_is_comment_style
+#define sci_is_code_style \
+	geany_functions->p_sci->sci_is_code_style
 #define templates_get_template_fileheader \
 	geany_functions->p_templates->templates_get_template_fileheader
 #define utils_str_equal \
@@ -244,6 +254,8 @@
 	geany_functions->p_utils->utils_get_file_list_full
 #define utils_copy_environment \
 	geany_functions->p_utils->utils_copy_environment
+#define utils_find_open_xml_tag \
+	geany_functions->p_utils->utils_find_open_xml_tag
 #define ui_dialog_vbox_new \
 	geany_functions->p_ui->ui_dialog_vbox_new
 #define ui_frame_new_with_alignment \
diff --git a/src/editor.c b/src/editor.c
index 426c48c..ee70d4c 100644
--- a/src/editor.c
+++ b/src/editor.c
@@ -103,8 +103,6 @@ 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 is_code_style(gint lexer, gint style);
-static gboolean is_string_style(gint lexer, gint style);
 static void auto_close_chars(ScintillaObject *sci, gint pos, gchar c);
 static void close_block(GeanyEditor *editor, gint pos);
 static void editor_highlight_braces(GeanyEditor *editor, gint cur_pos);
@@ -195,8 +193,6 @@ static void on_snippet_keybinding_activate(gchar *key)
 	GeanyDocument *doc = document_get_current();
 	const gchar *s;
 	GHashTable *specials;
-	GString *pattern;
-	gint pos, line, indent_width, cursor_pos;
 
 	if (!doc || !GTK_WIDGET_HAS_FOCUS(doc->editor->sci))
 		return;
@@ -214,17 +210,8 @@ static void on_snippet_keybinding_activate(gchar *key)
 		return;
 	}
 
-	pos = sci_get_current_position(doc->editor->sci);
-	line = sci_get_line_from_position(doc->editor->sci, pos);
-	indent_width = sci_get_line_indentation(doc->editor->sci, line);
-
-	pattern = g_string_new(s);
-	cursor_pos = snippets_make_replacements(doc->editor, pattern, indent_width);
-
-	editor_insert_text_block(doc->editor, pattern->str, pos, cursor_pos, indent_width, FALSE);
+	editor_insert_snippet(doc->editor, sci_get_current_position(doc->editor->sci), s);
 	sci_scroll_caret(doc->editor->sci);
-
-	g_string_free(pattern, TRUE);
 }
 
 
@@ -1851,7 +1838,7 @@ gboolean editor_show_calltip(GeanyEditor *editor, gint pos)
 
 	/* the style 1 before the brace (which may be highlighted) */
 	style = sci_get_style_at(sci, pos - 1);
-	if (! is_code_style(lexer, style))
+	if (! sci_is_code_style(lexer, style))
 		return FALSE;
 
 	word[0] = '\0';
@@ -2116,7 +2103,7 @@ gboolean editor_start_auto_complete(GeanyEditor *editor, gint pos, gboolean forc
 	style = sci_get_style_at(sci, pos - 2);
 
 	/* don't autocomplete in comments and strings */
-	if (!force && !is_code_style(lexer, style))
+	if (!force && !sci_is_code_style(lexer, style))
 		return FALSE;
 
 	autocomplete_scope(editor);
@@ -2511,23 +2498,20 @@ static gboolean snippets_complete_constructs(GeanyEditor *editor, gint pos, cons
 {
 	ScintillaObject *sci = editor->sci;
 	gchar *str;
-	GString *pattern;
-	gssize cur_index = -1;
+	const gchar *completion;
 	gint str_len;
 	gint ft_id = editor->document->file_type->id;
 
 	str = g_strdup(word);
 	g_strstrip(str);
-	pattern = g_string_new(snippets_find_completion_by_name(filetypes[ft_id]->name, str));
-	if (pattern == NULL || pattern->len == 0)
+
+	completion = snippets_find_completion_by_name(filetypes[ft_id]->name, str);
+	if (completion == NULL)
 	{
 		g_free(str);
-		g_string_free(pattern, TRUE);
 		return FALSE;
 	}
 
-	read_indent(editor, pos);
-
 	/* remove the typed word, it will be added again by the used auto completion
 	 * (not really necessary but this makes the auto completion more flexible,
 	 *  e.g. with a completion like hi=hello, so typing "hi<TAB>" will result in "hello") */
@@ -2537,14 +2521,10 @@ static gboolean snippets_complete_constructs(GeanyEditor *editor, gint pos, cons
 	sci_replace_sel(sci, "");
 	pos -= str_len; /* pos has changed while deleting */
 
-	cur_index = snippets_make_replacements(editor, pattern, strlen(indent));
-
-	/* finally insert the text and set the cursor */
-	editor_insert_text_block(editor, pattern->str, pos, cur_index, -1, FALSE);
+	editor_insert_snippet(editor, pos, completion);
 	sci_scroll_caret(sci);
 
 	g_free(str);
-	g_string_free(pattern, TRUE);
  	return TRUE;
 }
 
@@ -2666,7 +2646,7 @@ static gboolean handle_xml(GeanyEditor *editor, gint pos, gchar ch)
 
 	/* return if we are inside any embedded script */
 	style = sci_get_style_at(sci, pos);
-	if (style > SCE_H_XCCOMMENT && ! is_string_style(lexer, style))
+	if (style > SCE_H_XCCOMMENT && ! sci_is_string_style(lexer, style))
 		return FALSE;
 
 	/* if ch is /, check for </, else quit */
@@ -3430,293 +3410,6 @@ static void auto_multiline(GeanyEditor *editor, gint cur_line)
 }
 
 
-/* Checks whether the given style is a string for the given lexer.
- * It doesn't handle LEX_HTML, this should be done by the caller.
- * Returns true if the style is a string, FALSE otherwise.
- *
- * Don't forget STRINGEOL, to prevent completion whilst typing a string with no closing char.
- */
-static gboolean is_string_style(gint lexer, gint style)
-{
-	switch (lexer)
-	{
-		case SCLEX_CPP:
-			return (style == SCE_C_CHARACTER ||
-				style == SCE_C_STRING ||
-				style == SCE_C_STRINGEOL);
-
-		case SCLEX_PASCAL:
-			return (style == SCE_PAS_CHARACTER ||
-				style == SCE_PAS_STRING ||
-				style == SCE_PAS_STRINGEOL);
-
-		case SCLEX_D:
-			return (style == SCE_D_STRING ||
-				style == SCE_D_STRINGEOL ||
-				style == SCE_D_CHARACTER ||
-				style == SCE_D_STRINGB ||
-				style == SCE_D_STRINGR);
-
-		case SCLEX_PYTHON:
-			return (style == SCE_P_STRING ||
-				style == SCE_P_TRIPLE ||
-				style == SCE_P_TRIPLEDOUBLE ||
-				style == SCE_P_CHARACTER ||
-				style == SCE_P_STRINGEOL);
-
-		case SCLEX_F77:
-		case SCLEX_FORTRAN:
-			return (style == SCE_F_STRING1 ||
-				style == SCE_F_STRING2 ||
-				style == SCE_F_STRINGEOL);
-
-		case SCLEX_PERL:
-			return (/*style == SCE_PL_STRING ||*/ /* may want variable autocompletion "$(foo)" */
-				style == SCE_PL_CHARACTER ||
-				style == SCE_PL_HERE_DELIM ||
-				style == SCE_PL_HERE_Q ||
-				style == SCE_PL_HERE_QQ ||
-				style == SCE_PL_HERE_QX ||
-				style == SCE_PL_POD ||
-				style == SCE_PL_STRING_Q ||
-				style == SCE_PL_STRING_QQ ||
-				style == SCE_PL_STRING_QX ||
-				style == SCE_PL_STRING_QR ||
-				style == SCE_PL_STRING_QW ||
-				style == SCE_PL_POD_VERB);
-
-		case SCLEX_R:
-			return (style == SCE_R_STRING);
-
-		case SCLEX_RUBY:
-			return (style == SCE_RB_CHARACTER ||
-				style == SCE_RB_STRING ||
-				style == SCE_RB_HERE_DELIM ||
-				style == SCE_RB_HERE_Q ||
-				style == SCE_RB_HERE_QQ ||
-				style == SCE_RB_HERE_QX ||
-				style == SCE_RB_POD);
-
-		case SCLEX_BASH:
-			return (style == SCE_SH_STRING);
-
-		case SCLEX_SQL:
-			return (style == SCE_SQL_STRING);
-
-		case SCLEX_TCL:
-			return (style == SCE_TCL_IN_QUOTE);
-
-		case SCLEX_LUA:
-			return (style == SCE_LUA_LITERALSTRING ||
-				style == SCE_LUA_CHARACTER ||
-				style == SCE_LUA_STRINGEOL ||
-				style == SCE_LUA_STRING);
-
-		case SCLEX_HASKELL:
-			return (style == SCE_HA_CHARACTER ||
-				style == SCE_HA_STRING);
-
-		case SCLEX_FREEBASIC:
-			return (style == SCE_B_STRING ||
-				style == SCE_B_STRINGEOL);
-
-		case SCLEX_OCTAVE:
-			return (style == SCE_MATLAB_STRING ||
-				style == SCE_MATLAB_DOUBLEQUOTESTRING);
-
-		case SCLEX_HTML:
-			return (
-				style == SCE_HBA_STRING ||
-				style == SCE_HBA_STRINGEOL ||
-				style == SCE_HB_STRING ||
-				style == SCE_HB_STRINGEOL ||
-				style == SCE_H_CDATA ||
-				style == SCE_H_DOUBLESTRING ||
-				style == SCE_HJA_DOUBLESTRING ||
-				style == SCE_HJA_SINGLESTRING ||
-				style == SCE_HJA_STRINGEOL ||
-				style == SCE_HJ_DOUBLESTRING ||
-				style == SCE_HJ_SINGLESTRING ||
-				style == SCE_HJ_STRINGEOL ||
-				style == SCE_HPA_CHARACTER ||
-				style == SCE_HPA_STRING ||
-				style == SCE_HPA_TRIPLE ||
-				style == SCE_HPA_TRIPLEDOUBLE ||
-				style == SCE_HP_CHARACTER ||
-				style == SCE_HPHP_HSTRING ||  /* HSTRING is a heredoc */
-				style == SCE_HPHP_HSTRING_VARIABLE ||
-				style == SCE_HPHP_SIMPLESTRING ||
-				style == SCE_HP_STRING ||
-				style == SCE_HP_TRIPLE ||
-				style == SCE_HP_TRIPLEDOUBLE ||
-				style == SCE_H_SGML_DOUBLESTRING ||
-				style == SCE_H_SGML_SIMPLESTRING ||
-				style == SCE_H_SINGLESTRING);
-
-		case SCLEX_CMAKE:
-			return (style == SCE_CMAKE_STRINGDQ ||
-				style == SCE_CMAKE_STRINGLQ ||
-				style == SCE_CMAKE_STRINGRQ ||
-				style == SCE_CMAKE_STRINGVAR);
-
-		case SCLEX_NSIS:
-			return (style == SCE_NSIS_STRINGDQ ||
-				style == SCE_NSIS_STRINGLQ ||
-				style == SCE_NSIS_STRINGRQ ||
-				style == SCE_NSIS_STRINGVAR);
-
-		case SCLEX_ADA:
-			return (style == SCE_ADA_CHARACTER ||
-				style == SCE_ADA_STRING ||
-				style == SCE_ADA_CHARACTEREOL ||
-				style == SCE_ADA_STRINGEOL);
-	}
-	return FALSE;
-}
-
-
-/* Checks whether the given style is a comment for the given lexer.
- * It doesn't handle LEX_HTML, this should be done by the caller.
- * Returns true if the style is a comment, FALSE otherwise.
- */
-static gboolean is_comment_style(gint lexer, gint style)
-{
-	switch (lexer)
-	{
-		case SCLEX_CPP:
-			return (style == SCE_C_COMMENT ||
-				style == SCE_C_COMMENTLINE ||
-				style == SCE_C_COMMENTDOC ||
-				style == SCE_C_COMMENTLINEDOC ||
-				style == SCE_C_COMMENTDOCKEYWORD ||
-				style == SCE_C_COMMENTDOCKEYWORDERROR);
-
-		case SCLEX_PASCAL:
-			return (style == SCE_PAS_COMMENT ||
-				style == SCE_PAS_COMMENT2 ||
-				style == SCE_PAS_COMMENTLINE);
-
-		case SCLEX_D:
-			return (style == SCE_D_COMMENT ||
-				style == SCE_D_COMMENTLINE ||
-				style == SCE_D_COMMENTDOC ||
-				style == SCE_D_COMMENTNESTED ||
-				style == SCE_D_COMMENTLINEDOC ||
-				style == SCE_D_COMMENTDOCKEYWORD ||
-				style == SCE_D_COMMENTDOCKEYWORDERROR);
-
-		case SCLEX_PYTHON:
-			return (style == SCE_P_COMMENTLINE ||
-				style == SCE_P_COMMENTBLOCK);
-
-		case SCLEX_F77:
-		case SCLEX_FORTRAN:
-			return (style == SCE_F_COMMENT);
-
-		case SCLEX_PERL:
-			return (style == SCE_PL_COMMENTLINE);
-
-		case SCLEX_PROPERTIES:
-			return (style == SCE_PROPS_COMMENT);
-
-		case SCLEX_PO:
-			return (style == SCE_PO_COMMENT);
-
-		case SCLEX_LATEX:
-			return (style == SCE_L_COMMENT);
-
-		case SCLEX_MAKEFILE:
-			return (style == SCE_MAKE_COMMENT);
-
-		case SCLEX_RUBY:
-			return (style == SCE_RB_COMMENTLINE);
-
-		case SCLEX_BASH:
-			return (style == SCE_SH_COMMENTLINE);
-
-		case SCLEX_R:
-			return (style == SCE_R_COMMENT);
-
-		case SCLEX_SQL:
-			return (style == SCE_SQL_COMMENT ||
-				style == SCE_SQL_COMMENTLINE ||
-				style == SCE_SQL_COMMENTDOC);
-
-		case SCLEX_TCL:
-			return (style == SCE_TCL_COMMENT ||
-				style == SCE_TCL_COMMENTLINE ||
-				style == SCE_TCL_COMMENT_BOX ||
-				style == SCE_TCL_BLOCK_COMMENT);
-
-		case SCLEX_OCTAVE:
-			return (style == SCE_MATLAB_COMMENT);
-
-		case SCLEX_LUA:
-			return (style == SCE_LUA_COMMENT ||
-				style == SCE_LUA_COMMENTLINE ||
-				style == SCE_LUA_COMMENTDOC);
-
-		case SCLEX_HASKELL:
-			return (style == SCE_HA_COMMENTLINE ||
-				style == SCE_HA_COMMENTBLOCK ||
-				style == SCE_HA_COMMENTBLOCK2 ||
-				style == SCE_HA_COMMENTBLOCK3);
-
-		case SCLEX_FREEBASIC:
-			return (style == SCE_B_COMMENT);
-
-		case SCLEX_YAML:
-			return (style == SCE_YAML_COMMENT);
-
-		case SCLEX_HTML:
-			return (
-				style == SCE_HBA_COMMENTLINE ||
-				style == SCE_HB_COMMENTLINE ||
-				style == SCE_H_COMMENT ||
-				style == SCE_HJA_COMMENT ||
-				style == SCE_HJA_COMMENTDOC ||
-				style == SCE_HJA_COMMENTLINE ||
-				style == SCE_HJ_COMMENT ||
-				style == SCE_HJ_COMMENTDOC ||
-				style == SCE_HJ_COMMENTLINE ||
-				style == SCE_HPA_COMMENTLINE ||
-				style == SCE_HP_COMMENTLINE ||
-				style == SCE_HPHP_COMMENT ||
-				style == SCE_HPHP_COMMENTLINE ||
-				style == SCE_H_SGML_COMMENT);
-
-		case SCLEX_CMAKE:
-			return (style == SCE_CMAKE_COMMENT);
-
-		case SCLEX_NSIS:
-			return (style == SCE_NSIS_COMMENT ||
-				style == SCE_NSIS_COMMENTBOX);
-
-		case SCLEX_ADA:
-			return (style == SCE_ADA_COMMENTLINE ||
-				style == SCE_NSIS_COMMENTBOX);
-	}
-	return FALSE;
-}
-
-
-/* Checks whether the given style is normal code (not string, comment, preprocessor, etc).
- * It doesn't handle LEX_HTML, this should be done by the caller.
- */
-static gboolean is_code_style(gint lexer, gint style)
-{
-	switch (lexer)
-	{
-		case SCLEX_CPP:
-			if (style == SCE_C_PREPROCESSOR)
-				return FALSE;
-			break;
-	}
-	return !(is_comment_style(lexer, style) ||
-		is_string_style(lexer, style));
-}
-
-
 #if 0
 static gboolean editor_lexer_is_c_like(gint lexer)
 {
@@ -5201,3 +4894,33 @@ void editor_indent(GeanyEditor *editor, gboolean increase)
 		sci_set_current_line(sci, lstart);
 	}
 }
+
+
+/** Get snippets specific to a file type
+ *
+ * @param file_type_name File type name (e.g., "HTML")
+ * @return A hash table of snippets or NULL.  The returned hash table must not be freed.
+ */
+GHashTable *editor_get_snippets_for_file_type(const gchar *file_type_name)
+{
+	return g_hash_table_lookup(snippet_hash, file_type_name);
+}
+
+
+/** Replace all special sequences in the @a snippet and insert it at @a pos
+ *
+ * If you insert at the current position, consider calling @c sci_scroll_caret() after this function.
+ */
+void editor_insert_snippet(GeanyEditor *editor, gint pos, const gchar *snippet)
+{
+	gint line, indent_width, cursor_pos;
+	GString *pattern;
+
+	line = sci_get_line_from_position(editor->sci, pos);
+	indent_width = sci_get_line_indentation(editor->sci, line);
+
+	pattern = g_string_new(snippet);
+	cursor_pos = snippets_make_replacements(editor, pattern, indent_width);
+	editor_insert_text_block(editor, pattern->str, pos, cursor_pos, indent_width, FALSE);
+	g_string_free(pattern, TRUE);
+}
diff --git a/src/editor.h b/src/editor.h
index e81a074..2697851 100644
--- a/src/editor.h
+++ b/src/editor.h
@@ -307,4 +307,8 @@ void editor_insert_text_block(GeanyEditor *editor, const gchar *text,
 
 void editor_toggle_fold(GeanyEditor *editor, gint line, gint modifiers);
 
+GHashTable *editor_get_snippets_for_file_type(const gchar *file_type_name);
+
+void editor_insert_snippet(GeanyEditor *editor, gint pos, const gchar *snippet);
+
 #endif
diff --git a/src/plugindata.h b/src/plugindata.h
index d0d10fb..62ed10b 100644
--- a/src/plugindata.h
+++ b/src/plugindata.h
@@ -50,7 +50,7 @@
 enum {
 	/** The Application Programming Interface (API) version, incremented
 	 * whenever any plugin data types are modified or appended to. */
-	GEANY_API_VERSION = 197,
+	GEANY_API_VERSION = 198,
 
 	/** The Application Binary Interface (ABI) version, incremented whenever
 	 * existing fields in the plugin data types have to be changed or reordered. */
@@ -385,6 +385,9 @@ typedef struct SciFuncs
 	void	(*sci_set_line_indentation) (struct _ScintillaObject *sci, gint line, gint indent);
 	gint	(*sci_get_line_indentation) (struct _ScintillaObject *sci, gint line);
 	gint	(*sci_get_lexer) (struct _ScintillaObject *sci);
+	gboolean (*sci_is_string_style) (gint lexer, gint style);
+	gboolean (*sci_is_comment_style) (gint lexer, gint style);
+	gboolean (*sci_is_code_style) (gint lexer, gint style);
 }
 SciFuncs;
 
@@ -431,7 +434,7 @@ typedef struct UtilsFuncs
 	GSList*		(*utils_get_file_list_full)(const gchar *path, gboolean full_path, gboolean sort,
 				GError **error);
 	gchar**		(*utils_copy_environment)(const gchar **exclude_vars, const gchar *first_varname, ...);
-
+	gchar*		(*utils_find_open_xml_tag) (const gchar sel[], gint size);
 }
 UtilsFuncs;
 
@@ -613,6 +616,9 @@ typedef struct EditorFuncs
 
 	gint	(*editor_get_eol_char_mode) (struct GeanyEditor *editor);
 	gboolean (*editor_goto_pos) (struct GeanyEditor *editor, gint pos, gboolean mark);
+
+	GHashTable* (*editor_get_snippets_for_file_type) (const gchar *file_type_name);
+	void (*editor_insert_snippet) (GeanyEditor *editor, gint pos, const gchar *snippet);
 }
 EditorFuncs;
 
diff --git a/src/plugins.c b/src/plugins.c
index f2657b8..232bcc7 100644
--- a/src/plugins.c
+++ b/src/plugins.c
@@ -124,7 +124,9 @@ static EditorFuncs editor_funcs = {
 	&editor_get_eol_char,
 	&editor_insert_text_block,
 	&editor_get_eol_char_mode,
-	&editor_goto_pos
+	&editor_goto_pos,
+	&editor_get_snippets_for_file_type,
+	&editor_insert_snippet
 };
 
 static ScintillaFuncs scintilla_funcs = {
@@ -188,7 +190,10 @@ static SciFuncs sci_funcs = {
 	&sci_find_text,
 	&sci_set_line_indentation,
 	&sci_get_line_indentation,
-	&sci_get_lexer
+	&sci_get_lexer,
+	&sci_is_string_style,
+	&sci_is_comment_style,
+	&sci_is_code_style
 };
 
 static TemplateFuncs template_funcs = {
@@ -216,7 +221,8 @@ static UtilsFuncs utils_funcs = {
 	&utils_str_middle_truncate,
 	&utils_str_remove_chars,
 	&utils_get_file_list_full,
-	&utils_copy_environment
+	&utils_copy_environment,
+	&utils_find_open_xml_tag
 };
 
 static UIUtilsFuncs uiutils_funcs = {
diff --git a/src/sciwrappers.c b/src/sciwrappers.c
index 5371590..9e55f44 100644
--- a/src/sciwrappers.c
+++ b/src/sciwrappers.c
@@ -36,9 +36,9 @@
 #include <string.h>
 
 #include "geany.h"
-
 #include "sciwrappers.h"
 #include "utils.h"
+#include "SciLexer.h"
 
 #define SSM(s, m, w, l) scintilla_send_message(s, m, w, l)
 
@@ -1220,3 +1220,300 @@ gint sci_text_width(ScintillaObject *sci, gint styleNumber, const gchar *text)
 {
 	return SSM(sci, SCI_TEXTWIDTH, styleNumber, (sptr_t) text);
 }
+
+
+/** Checks whether the given style is a string for the given lexer.
+ *
+ * @param lexer Scintilla lexer type (@c SCLEX_*)
+ * @param style Scintilla style (@c SCE_*)
+ *
+ * @return @c TRUE if the style is a string, @c FALSE otherwise
+ */
+gboolean sci_is_string_style(gint lexer, gint style)
+{
+	/* Don't forget STRINGEOL, to prevent completion whilst typing a string with no closing char. */
+
+	switch (lexer)
+	{
+		case SCLEX_CPP:
+			return (style == SCE_C_CHARACTER ||
+				style == SCE_C_STRING ||
+				style == SCE_C_STRINGEOL);
+
+		case SCLEX_PASCAL:
+			return (style == SCE_PAS_CHARACTER ||
+				style == SCE_PAS_STRING ||
+				style == SCE_PAS_STRINGEOL);
+
+		case SCLEX_D:
+			return (style == SCE_D_STRING ||
+				style == SCE_D_STRINGEOL ||
+				style == SCE_D_CHARACTER ||
+				style == SCE_D_STRINGB ||
+				style == SCE_D_STRINGR);
+
+		case SCLEX_PYTHON:
+			return (style == SCE_P_STRING ||
+				style == SCE_P_TRIPLE ||
+				style == SCE_P_TRIPLEDOUBLE ||
+				style == SCE_P_CHARACTER ||
+				style == SCE_P_STRINGEOL);
+
+		case SCLEX_F77:
+		case SCLEX_FORTRAN:
+			return (style == SCE_F_STRING1 ||
+				style == SCE_F_STRING2 ||
+				style == SCE_F_STRINGEOL);
+
+		case SCLEX_PERL:
+			return (/*style == SCE_PL_STRING ||*/ /* may want variable autocompletion "$(foo)" */
+				style == SCE_PL_CHARACTER ||
+				style == SCE_PL_HERE_DELIM ||
+				style == SCE_PL_HERE_Q ||
+				style == SCE_PL_HERE_QQ ||
+				style == SCE_PL_HERE_QX ||
+				style == SCE_PL_POD ||
+				style == SCE_PL_STRING_Q ||
+				style == SCE_PL_STRING_QQ ||
+				style == SCE_PL_STRING_QX ||
+				style == SCE_PL_STRING_QR ||
+				style == SCE_PL_STRING_QW ||
+				style == SCE_PL_POD_VERB);
+
+		case SCLEX_R:
+			return (style == SCE_R_STRING);
+
+		case SCLEX_RUBY:
+			return (style == SCE_RB_CHARACTER ||
+				style == SCE_RB_STRING ||
+				style == SCE_RB_HERE_DELIM ||
+				style == SCE_RB_HERE_Q ||
+				style == SCE_RB_HERE_QQ ||
+				style == SCE_RB_HERE_QX ||
+				style == SCE_RB_POD);
+
+		case SCLEX_BASH:
+			return (style == SCE_SH_STRING);
+
+		case SCLEX_SQL:
+			return (style == SCE_SQL_STRING);
+
+		case SCLEX_TCL:
+			return (style == SCE_TCL_IN_QUOTE);
+
+		case SCLEX_LUA:
+			return (style == SCE_LUA_LITERALSTRING ||
+				style == SCE_LUA_CHARACTER ||
+				style == SCE_LUA_STRINGEOL ||
+				style == SCE_LUA_STRING);
+
+		case SCLEX_HASKELL:
+			return (style == SCE_HA_CHARACTER ||
+				style == SCE_HA_STRING);
+
+		case SCLEX_FREEBASIC:
+			return (style == SCE_B_STRING ||
+				style == SCE_B_STRINGEOL);
+
+		case SCLEX_OCTAVE:
+			return (style == SCE_MATLAB_STRING ||
+				style == SCE_MATLAB_DOUBLEQUOTESTRING);
+
+		case SCLEX_HTML:
+			return (
+				style == SCE_HBA_STRING ||
+				style == SCE_HBA_STRINGEOL ||
+				style == SCE_HB_STRING ||
+				style == SCE_HB_STRINGEOL ||
+				style == SCE_H_CDATA ||
+				style == SCE_H_DOUBLESTRING ||
+				style == SCE_HJA_DOUBLESTRING ||
+				style == SCE_HJA_SINGLESTRING ||
+				style == SCE_HJA_STRINGEOL ||
+				style == SCE_HJ_DOUBLESTRING ||
+				style == SCE_HJ_SINGLESTRING ||
+				style == SCE_HJ_STRINGEOL ||
+				style == SCE_HPA_CHARACTER ||
+				style == SCE_HPA_STRING ||
+				style == SCE_HPA_TRIPLE ||
+				style == SCE_HPA_TRIPLEDOUBLE ||
+				style == SCE_HP_CHARACTER ||
+				style == SCE_HPHP_HSTRING ||  /* HSTRING is a heredoc */
+				style == SCE_HPHP_HSTRING_VARIABLE ||
+				style == SCE_HPHP_SIMPLESTRING ||
+				style == SCE_HP_STRING ||
+				style == SCE_HP_TRIPLE ||
+				style == SCE_HP_TRIPLEDOUBLE ||
+				style == SCE_H_SGML_DOUBLESTRING ||
+				style == SCE_H_SGML_SIMPLESTRING ||
+				style == SCE_H_SINGLESTRING);
+
+		case SCLEX_CMAKE:
+			return (style == SCE_CMAKE_STRINGDQ ||
+				style == SCE_CMAKE_STRINGLQ ||
+				style == SCE_CMAKE_STRINGRQ ||
+				style == SCE_CMAKE_STRINGVAR);
+
+		case SCLEX_NSIS:
+			return (style == SCE_NSIS_STRINGDQ ||
+				style == SCE_NSIS_STRINGLQ ||
+				style == SCE_NSIS_STRINGRQ ||
+				style == SCE_NSIS_STRINGVAR);
+
+		case SCLEX_ADA:
+			return (style == SCE_ADA_CHARACTER ||
+				style == SCE_ADA_STRING ||
+				style == SCE_ADA_CHARACTEREOL ||
+				style == SCE_ADA_STRINGEOL);
+	}
+	return FALSE;
+}
+
+
+/** Checks whether the given style is a comment for the given lexer.
+ *
+ * @param lexer Scintilla lexer type (@c SCLEX_*)
+ * @param style Scintilla style (@c SCE_*)
+ *
+ * @return @c TRUE if the style is a comment, @c FALSE otherwise
+ */
+gboolean sci_is_comment_style(gint lexer, gint style)
+{
+	switch (lexer)
+	{
+		case SCLEX_CPP:
+			return (style == SCE_C_COMMENT ||
+				style == SCE_C_COMMENTLINE ||
+				style == SCE_C_COMMENTDOC ||
+				style == SCE_C_COMMENTLINEDOC ||
+				style == SCE_C_COMMENTDOCKEYWORD ||
+				style == SCE_C_COMMENTDOCKEYWORDERROR);
+
+		case SCLEX_PASCAL:
+			return (style == SCE_PAS_COMMENT ||
+				style == SCE_PAS_COMMENT2 ||
+				style == SCE_PAS_COMMENTLINE);
+
+		case SCLEX_D:
+			return (style == SCE_D_COMMENT ||
+				style == SCE_D_COMMENTLINE ||
+				style == SCE_D_COMMENTDOC ||
+				style == SCE_D_COMMENTNESTED ||
+				style == SCE_D_COMMENTLINEDOC ||
+				style == SCE_D_COMMENTDOCKEYWORD ||
+				style == SCE_D_COMMENTDOCKEYWORDERROR);
+
+		case SCLEX_PYTHON:
+			return (style == SCE_P_COMMENTLINE ||
+				style == SCE_P_COMMENTBLOCK);
+
+		case SCLEX_F77:
+		case SCLEX_FORTRAN:
+			return (style == SCE_F_COMMENT);
+
+		case SCLEX_PERL:
+			return (style == SCE_PL_COMMENTLINE);
+
+		case SCLEX_PROPERTIES:
+			return (style == SCE_PROPS_COMMENT);
+
+		case SCLEX_PO:
+			return (style == SCE_PO_COMMENT);
+
+		case SCLEX_LATEX:
+			return (style == SCE_L_COMMENT);
+
+		case SCLEX_MAKEFILE:
+			return (style == SCE_MAKE_COMMENT);
+
+		case SCLEX_RUBY:
+			return (style == SCE_RB_COMMENTLINE);
+
+		case SCLEX_BASH:
+			return (style == SCE_SH_COMMENTLINE);
+
+		case SCLEX_R:
+			return (style == SCE_R_COMMENT);
+
+		case SCLEX_SQL:
+			return (style == SCE_SQL_COMMENT ||
+				style == SCE_SQL_COMMENTLINE ||
+				style == SCE_SQL_COMMENTDOC);
+
+		case SCLEX_TCL:
+			return (style == SCE_TCL_COMMENT ||
+				style == SCE_TCL_COMMENTLINE ||
+				style == SCE_TCL_COMMENT_BOX ||
+				style == SCE_TCL_BLOCK_COMMENT);
+
+		case SCLEX_OCTAVE:
+			return (style == SCE_MATLAB_COMMENT);
+
+		case SCLEX_LUA:
+			return (style == SCE_LUA_COMMENT ||
+				style == SCE_LUA_COMMENTLINE ||
+				style == SCE_LUA_COMMENTDOC);
+
+		case SCLEX_HASKELL:
+			return (style == SCE_HA_COMMENTLINE ||
+				style == SCE_HA_COMMENTBLOCK ||
+				style == SCE_HA_COMMENTBLOCK2 ||
+				style == SCE_HA_COMMENTBLOCK3);
+
+		case SCLEX_FREEBASIC:
+			return (style == SCE_B_COMMENT);
+
+		case SCLEX_YAML:
+			return (style == SCE_YAML_COMMENT);
+
+		case SCLEX_HTML:
+			return (
+				style == SCE_HBA_COMMENTLINE ||
+				style == SCE_HB_COMMENTLINE ||
+				style == SCE_H_COMMENT ||
+				style == SCE_HJA_COMMENT ||
+				style == SCE_HJA_COMMENTDOC ||
+				style == SCE_HJA_COMMENTLINE ||
+				style == SCE_HJ_COMMENT ||
+				style == SCE_HJ_COMMENTDOC ||
+				style == SCE_HJ_COMMENTLINE ||
+				style == SCE_HPA_COMMENTLINE ||
+				style == SCE_HP_COMMENTLINE ||
+				style == SCE_HPHP_COMMENT ||
+				style == SCE_HPHP_COMMENTLINE ||
+				style == SCE_H_SGML_COMMENT);
+
+		case SCLEX_CMAKE:
+			return (style == SCE_CMAKE_COMMENT);
+
+		case SCLEX_NSIS:
+			return (style == SCE_NSIS_COMMENT ||
+				style == SCE_NSIS_COMMENTBOX);
+
+		case SCLEX_ADA:
+			return (style == SCE_ADA_COMMENTLINE ||
+				style == SCE_NSIS_COMMENTBOX);
+	}
+	return FALSE;
+}
+
+
+/** Checks whether the given style is normal code (not string, comment, preprocessor, etc).
+ *
+ * @param lexer Scintilla lexer type (@c SCLEX_*)
+ * @param style Scintilla style (@c SCE_*)
+ *
+ * @return @c TRUE if the style is code, @c FALSE otherwise
+ */
+gboolean sci_is_code_style(gint lexer, gint style)
+{
+	switch (lexer)
+	{
+		case SCLEX_CPP:
+			if (style == SCE_C_PREPROCESSOR)
+				return FALSE;
+			break;
+	}
+	return !(sci_is_comment_style(lexer, style) ||
+		sci_is_string_style(lexer, style));
+}
\ No newline at end of file
diff --git a/src/sciwrappers.h b/src/sciwrappers.h
index 7aa4d2d..1a04a8c 100644
--- a/src/sciwrappers.h
+++ b/src/sciwrappers.h
@@ -163,7 +163,7 @@ void				sci_indicator_clear			(ScintillaObject *sci, gint pos, gint len);
 void				sci_select_all				(ScintillaObject *sci);
 gint				sci_get_line_indent_position(ScintillaObject *sci, gint line);
 void				sci_set_line_indentation	(ScintillaObject *sci, gint line, gint indent);
-gint					sci_get_line_indentation	(ScintillaObject *sci, gint line);
+gint				sci_get_line_indentation	(ScintillaObject *sci, gint line);
 void				sci_set_autoc_max_height	(ScintillaObject *sci, gint val);
 gint				sci_find_matching_brace		(ScintillaObject *sci, gint pos);
 
@@ -185,4 +185,8 @@ void				sci_lines_split				(ScintillaObject *sci, gint pixelWidth);
 void				sci_lines_join				(ScintillaObject *sci);
 gint				sci_text_width				(ScintillaObject *sci, gint styleNumber, const gchar *text);
 
+gboolean			sci_is_string_style			(gint lexer, gint style);
+gboolean			sci_is_comment_style		(gint lexer, gint style);
+gboolean			sci_is_code_style			(gint lexer, gint style);
+
 #endif
diff --git a/src/utils.c b/src/utils.c
index 137bdc2..eda75e8 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -276,13 +276,13 @@ gint utils_write_file(const gchar *filename, const gchar *text)
 }
 
 
-/*
- * (stolen from anjuta and modified)
- * Search backward through size bytes looking for a '<', then return the tag, if any.
+/** Search backward through @a 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)
 {
+	/* stolen from anjuta and modified */
 	const gchar *begin, *cur;
 
 	if (G_UNLIKELY(size < 3))
