Fix multiple snippet cursor positions for Tabs + Spaces mode.

Simplify editor_insert_snippet() code now we use cursor marker
strings.



git-svn-id: https://geany.svn.sourceforge.net/svnroot/geany/trunk@5729 ea778897-0a13-0410-b9d1-a72fbfd435f5
This commit is contained in:
Nick Treleaven 2011-04-19 16:04:31 +00:00
parent b8f8a36815
commit 06f9d7e068
2 changed files with 93 additions and 112 deletions

View File

@ -1,10 +1,18 @@
2011-04-19 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
* src/editor.c:
Fix multiple snippet cursor positions for Tabs + Spaces mode.
Simplify editor_insert_snippet() code now we use cursor marker
strings.
2011-04-17 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de> 2011-04-17 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
* src/plugindata.h, src/document.c, src/plugins.c, src/document.h, * src/plugindata.h, src/document.c, src/plugins.c, src/document.h,
plugins/geanyfunctions.h: plugins/geanyfunctions.h:
Add document_compare_by_tab_order() and Add document_compare_by_tab_order() and
document_compare_by_tab_order_reverse() to the plugin API. document_compare_by_tab_order_reverse() to the plugin API.
* src/ui_uitls.c: * src/ui_utils.c:
Use document_compare_by_tab_order() as default compare function Use document_compare_by_tab_order() as default compare function
to sort the document list in the document notebook tab menu, this to sort the document list in the document notebook tab menu, this
fixes the currently broken default ordering. fixes the currently broken default ordering.

View File

@ -117,8 +117,8 @@ static void read_current_word(GeanyEditor *editor, gint pos, gchar *word, size_t
const gchar *wc, gboolean stem); const gchar *wc, gboolean stem);
static gsize count_indent_size(GeanyEditor *editor, const gchar *base_indent); static gsize count_indent_size(GeanyEditor *editor, const gchar *base_indent);
static const gchar *snippets_find_completion_by_name(const gchar *type, const gchar *name); static const gchar *snippets_find_completion_by_name(const gchar *type, const gchar *name);
static gssize snippets_make_replacements(GeanyEditor *editor, GString *pattern, static void snippets_make_replacements(GeanyEditor *editor, GString *pattern);
gsize indent_size); static gssize replace_cursor_markers(GeanyEditor *editor, GString *pattern);
void editor_snippets_free(void) void editor_snippets_free(void)
@ -2309,56 +2309,85 @@ static void snippets_replace_specials(gpointer key, gpointer value, gpointer use
} }
/* this only works with spaces only indentation on the lines */ static gboolean utils_regex_find(regex_t *regex, const gchar *haystack, gsize start,
static void fix_line_indents(GeanyEditor *editor, gint line_start, gint line_end) gsize nmatches, regmatch_t *matches)
{ {
ScintillaObject *sci = editor->sci; gint eflags = 0;
gint line, cur_line, cur_col, pos;
/* get the line, col position as fixing indentation will move cursor to start of line */ if (start > 0)
pos = sci_get_current_position(sci);
cur_col = sci_get_col_from_position(sci, pos);
cur_line = sci_get_current_line(sci);
for (line = line_start; line <= line_end; line++)
{ {
gint size = sci_get_line_indentation(sci, line); gchar c = haystack[start - 1];
/* set to 0 first to trigger proper indent creation */ if (c == '\n' || c == '\r')
sci_set_line_indentation(sci, line, 0); eflags = REG_NOTBOL;
sci_set_line_indentation(sci, line, size);
} }
pos = scintilla_send_message(sci, SCI_FINDCOLUMN, cur_line, cur_col); return regexec(regex, haystack + start, nmatches, matches, eflags) == 0;
sci_set_current_position(sci, pos, FALSE);
} }
static void replace_leading_tabs(GString *str, const gchar *whitespace) /* match_index: which match to replace, 0 for whole regex.
* note: this doesn't support backreferences in replacements */
static guint utils_string_regex_replace_all(GString *haystack,
regex_t *regex, guint match_index, const gchar *replace)
{ {
regex_t regex;
gssize pos; gssize pos;
regmatch_t matches[2]; regmatch_t matches[10];
gchar *ptr; guint ret = 0;
if (regcomp(&regex, "^ *(\t)", 0) != 0) g_return_val_if_fail(match_index < 10, 0);
{
g_return_if_fail(FALSE);
}
ptr = str->str;
while (ptr)
{
if (regexec(&regex, ptr,
G_N_ELEMENTS(matches), matches, 0) != 0)
break;
pos = matches[1].rm_so; /* ensure haystack->str is not null */
g_return_if_fail(pos >= 0); if (haystack->len == 0)
pos += ptr - str->str; return 0;
g_string_erase(str, pos, 1);
g_string_insert(str, pos, whitespace); pos = 0;
ptr = str->str + pos + strlen(whitespace); while (utils_regex_find(regex, haystack->str, pos, G_N_ELEMENTS(matches), matches))
{
regmatch_t *match = &matches[match_index];
g_return_val_if_fail(match->rm_so >= 0, FALSE);
pos += match->rm_so;
g_string_erase(haystack, pos, match->rm_eo - match->rm_so);
g_string_insert(haystack, pos, replace);
pos += strlen(replace);
ret++;
} }
return ret;
}
static void fix_indentation(GeanyEditor *editor, GString *buf)
{
const GeanyIndentPrefs *iprefs = editor_get_indent_prefs(editor);
gchar *whitespace;
regex_t regex;
gint cflags = REG_EXTENDED | REG_NEWLINE;
/* transform leading tabs into indent widths (in spaces) */
whitespace = g_strnfill(iprefs->width, ' ');
regcomp(&regex, "^ *(\t)", cflags);
while (utils_string_regex_replace_all(buf, &regex, 1, whitespace));
regfree(&regex); regfree(&regex);
/* remaining tabs are for alignment */
if (iprefs->type != GEANY_INDENT_TYPE_TABS)
utils_string_replace_all(buf, "\t", whitespace);
/* use leading tabs */
if (iprefs->type != GEANY_INDENT_TYPE_SPACES)
{
gchar *str;
/* for tabs+spaces mode we want the real tab width, not indent width */
setptr(whitespace, g_strnfill(sci_get_tab_width(editor->sci), ' '));
str = g_strdup_printf("^\t*(%s)", whitespace);
regcomp(&regex, str, cflags);
while (utils_string_regex_replace_all(buf, &regex, 1, "\t"));
regfree(&regex);
g_free(str);
}
g_free(whitespace);
} }
@ -2385,11 +2414,10 @@ void editor_insert_text_block(GeanyEditor *editor, const gchar *text, gint inser
{ {
ScintillaObject *sci = editor->sci; ScintillaObject *sci = editor->sci;
gint line_start = sci_get_line_from_position(sci, insert_pos); gint line_start = sci_get_line_from_position(sci, insert_pos);
gint line_end;
gchar *whitespace; gchar *whitespace;
GString *buf; GString *buf;
const gchar *eol = editor_get_eol_char(editor); const gchar *eol = editor_get_eol_char(editor);
const GeanyIndentPrefs *iprefs = editor_get_indent_prefs(editor); gint idx;
g_return_if_fail(text); g_return_if_fail(text);
g_return_if_fail(editor != NULL); g_return_if_fail(editor != NULL);
@ -2405,7 +2433,8 @@ void editor_insert_text_block(GeanyEditor *editor, const gchar *text, gint inser
/* count indent size up to insert_pos instead of asking sci /* count indent size up to insert_pos instead of asking sci
* because there may be spaces after it */ * because there may be spaces after it */
gchar *tmp = sci_get_line(sci, line_start); gchar *tmp = sci_get_line(sci, line_start);
gint idx = insert_pos - sci_get_position_from_line(sci, line_start);
idx = insert_pos - sci_get_position_from_line(sci, line_start);
tmp[idx] = '\0'; tmp[idx] = '\0';
newline_indent_size = count_indent_size(editor, tmp); newline_indent_size = count_indent_size(editor, tmp);
g_free(tmp); g_free(tmp);
@ -2424,32 +2453,19 @@ void editor_insert_text_block(GeanyEditor *editor, const gchar *text, gint inser
if (replace_newlines) if (replace_newlines)
utils_string_replace_all(buf, "\n", eol); utils_string_replace_all(buf, "\n", eol);
/* transform leading tabs into indent widths (in spaces) */ fix_indentation(editor, buf);
whitespace = g_strnfill(iprefs->width, ' ');
replace_leading_tabs(buf, whitespace);
/* remaining tabs are for alignment */
if (iprefs->type != GEANY_INDENT_TYPE_TABS)
utils_string_replace_all(buf, "\t", whitespace);
g_free(whitespace);
sci_start_undo_action(sci); idx = replace_cursor_markers(editor, buf);
if (idx >= 0)
if (cursor_index >= 0)
{ {
gint idx = utils_string_replace(buf, 0, -1, geany_cursor_marker, NULL);
sci_insert_text(sci, insert_pos, buf->str); sci_insert_text(sci, insert_pos, buf->str);
sci_set_current_position(sci, insert_pos + idx, FALSE); sci_set_current_position(sci, insert_pos + idx, FALSE);
} }
else else
sci_insert_text(sci, insert_pos, buf->str); sci_insert_text(sci, insert_pos, buf->str);
/* fixup indentation (very useful for Tabs & Spaces indent type) */
line_end = sci_get_line_from_position(sci, insert_pos + buf->len);
fix_line_indents(editor, line_start, line_end);
snippet_cursor_insert_pos = sci_get_current_position(sci); snippet_cursor_insert_pos = sci_get_current_position(sci);
sci_end_undo_action(sci);
g_string_free(buf, TRUE); g_string_free(buf, TRUE);
} }
@ -2480,13 +2496,9 @@ void editor_goto_next_snippet_cursor(GeanyEditor *editor)
} }
static gssize replace_cursor_markers(GeanyEditor *editor, GString *pattern, gsize indent_size); static void snippets_make_replacements(GeanyEditor *editor, GString *pattern)
static gssize snippets_make_replacements(GeanyEditor *editor, GString *pattern, gsize indent_size)
{ {
gchar *whitespace;
GHashTable *specials; GHashTable *specials;
const GeanyIndentPrefs *iprefs = editor_get_indent_prefs(editor);
/* replace 'special' completions */ /* replace 'special' completions */
specials = g_hash_table_lookup(snippet_hash, "Special"); specials = g_hash_table_lookup(snippet_hash, "Special");
@ -2498,19 +2510,8 @@ static gssize snippets_make_replacements(GeanyEditor *editor, GString *pattern,
} }
/* now transform other wildcards */ /* now transform other wildcards */
utils_string_replace_all(pattern, "%newline%", "\n"); utils_string_replace_all(pattern, "%newline%", "\n");
utils_string_replace_all(pattern, "%ws%", "\t");
/* if spaces are used, replace tabs with spaces
* otherwise replace all %ws% by \t */
if (iprefs->type == GEANY_INDENT_TYPE_SPACES)
utils_string_replace_all(pattern, "\t", "%ws%");
else
utils_string_replace_all(pattern, "%ws%", "\t");
whitespace = g_strnfill(iprefs->width, ' '); /* use spaces for indentation, will be fixed up */
utils_string_replace_all(pattern, "%ws%", whitespace);
g_free(whitespace);
/* replace %cursor% by a very unlikely string marker */ /* replace %cursor% by a very unlikely string marker */
utils_string_replace_all(pattern, "%cursor%", geany_cursor_marker); utils_string_replace_all(pattern, "%cursor%", geany_cursor_marker);
@ -2520,61 +2521,35 @@ static gssize snippets_make_replacements(GeanyEditor *editor, GString *pattern,
/* replace any template {foo} wildcards */ /* replace any template {foo} wildcards */
templates_replace_common(pattern, editor->document->file_name, editor->document->file_type, NULL); templates_replace_common(pattern, editor->document->file_name, editor->document->file_type, NULL);
return replace_cursor_markers(editor, pattern, indent_size);
} }
static gssize replace_cursor_markers(GeanyEditor *editor, GString *pattern, gsize indent_size) static gssize replace_cursor_markers(GeanyEditor *editor, GString *pattern)
{ {
gssize cur_index = -1; gssize cur_index = -1;
gint i, idx, nl_count = 0; gint i;
GList *temp_list = NULL; GList *temp_list = NULL;
gint cursor_steps, old_cursor = 0; gint cursor_steps = 0, old_cursor = 0;
i = 0; i = 0;
idx = 0; while (1)
while ((cursor_steps = utils_strpos(pattern->str, geany_cursor_marker)) >= 0)
{ {
/* replace every newline (up to next cursor) with EOL, cursor_steps = utils_string_replace(pattern, cursor_steps, -1, geany_cursor_marker, NULL);
* count newlines and update cursor_steps after */ if (cursor_steps == -1)
while (1) break;
{
idx = utils_string_replace(pattern, idx, cursor_steps, "\n", editor_get_eol_char(editor));
if (idx == -1)
break;
nl_count++;
idx += editor_get_eol_char_len(editor);
cursor_steps = utils_strpos(pattern->str, geany_cursor_marker);
}
utils_string_replace_first(pattern, geany_cursor_marker, "");
idx = cursor_steps;
if (i++ > 0) if (i++ > 0)
{ {
/* save the relative offset to each cursor position */ /* save the relative offset to each cursor position */
cursor_steps += (nl_count * indent_size);
temp_list = g_list_prepend(temp_list, GINT_TO_POINTER(cursor_steps - old_cursor)); temp_list = g_list_prepend(temp_list, GINT_TO_POINTER(cursor_steps - old_cursor));
} }
else else
{ {
/* first cursor already includes newline positions */ /* first cursor already includes newline positions */
nl_count = 0;
cur_index = cursor_steps; cur_index = cursor_steps;
} }
old_cursor = cursor_steps; old_cursor = cursor_steps;
} }
/* replace remaining \n which may occur after the last cursor */
while (1)
{
idx = utils_string_replace(pattern, idx, -1, "\n", editor_get_eol_char(editor));
if (idx == -1)
break;
idx += editor_get_eol_char_len(editor);
}
/* put the cursor positions for the most recent /* put the cursor positions for the most recent
* parsed snippet first, followed by any remaining positions */ * parsed snippet first, followed by any remaining positions */
@ -5110,12 +5085,10 @@ const gchar *editor_find_snippet(GeanyEditor *editor, const gchar *snippet_name)
*/ */
void editor_insert_snippet(GeanyEditor *editor, gint pos, const gchar *snippet) void editor_insert_snippet(GeanyEditor *editor, gint pos, const gchar *snippet)
{ {
gint cursor_pos;
GString *pattern; GString *pattern;
pattern = g_string_new(snippet); pattern = g_string_new(snippet);
read_indent(editor, pos); snippets_make_replacements(editor, pattern);
cursor_pos = snippets_make_replacements(editor, pattern, strlen(indent)); editor_insert_text_block(editor, pattern->str, pos, -1, -1, FALSE);
editor_insert_text_block(editor, pattern->str, pos, cursor_pos, -1, FALSE);
g_string_free(pattern, TRUE); g_string_free(pattern, TRUE);
} }