108
110
static void auto_close_chars(ScintillaObject *sci, gint pos, gchar c);
109
111
static void close_block(GeanyEditor *editor, gint pos);
110
112
static void editor_highlight_braces(GeanyEditor *editor, gint cur_pos);
111
static void read_current_word(GeanyEditor *editor, gint pos, gchar *word, size_t wordlen,
113
static void read_current_word(GeanyEditor *editor, gint pos, gchar *word, gsize wordlen,
112
114
const gchar *wc, gboolean stem);
113
115
static gsize count_indent_size(GeanyEditor *editor, const gchar *base_indent);
114
116
static const gchar *snippets_find_completion_by_name(const gchar *type, const gchar *name);
115
static gssize snippets_make_replacements(GeanyEditor *editor, GString *pattern,
117
static void snippets_make_replacements(GeanyEditor *editor, GString *pattern);
118
static gssize replace_cursor_markers(GeanyEditor *editor, GString *pattern);
119
121
void editor_snippets_free(void)
1604
/* checks whether @p c is an ASCII character (e.g. < 0x80) */
1605
#define IS_ASCII(c) (((unsigned char)(c)) < 0x80)
1588
1608
/* Reads the word at given cursor position and writes it into the given buffer. The buffer will be
1589
1609
* NULL terminated in any case, even when the word is truncated because wordlen is too small.
1590
1610
* position can be -1, then the current position is used.
1591
1611
* wc are the wordchars to use, if NULL, GEANY_WORDCHARS will be used */
1592
static void read_current_word(GeanyEditor *editor, gint pos, gchar *word, size_t wordlen,
1612
static void read_current_word(GeanyEditor *editor, gint pos, gchar *word, gsize wordlen,
1593
1613
const gchar *wc, gboolean stem)
1595
1615
gint line, line_start, startword, endword;
1641
1661
* NULL terminated in any case, even when the word is truncated because wordlen is too small.
1642
1662
* position can be -1, then the current position is used.
1643
1663
* wc are the wordchars to use, if NULL, GEANY_WORDCHARS will be used */
1644
void editor_find_current_word(GeanyEditor *editor, gint pos, gchar *word, size_t wordlen,
1664
void editor_find_current_word(GeanyEditor *editor, gint pos, gchar *word, gsize wordlen,
1645
1665
const gchar *wc)
1647
1667
read_current_word(editor, pos, word, wordlen, wc, FALSE);
1671
/* Same as editor_find_current_word() but uses editor's word boundaries to decide what the word
1672
* is. This should be used e.g. to get the word to search for */
1673
void editor_find_current_word_sciwc(GeanyEditor *editor, gint pos, gchar *word, gsize wordlen)
1678
g_return_if_fail(editor != NULL);
1681
pos = sci_get_current_position(editor->sci);
1683
start = SSM(editor->sci, SCI_WORDSTARTPOSITION, pos, TRUE);
1684
end = SSM(editor->sci, SCI_WORDENDPOSITION, pos, TRUE);
1686
if (start == end) /* caret in whitespaces sequence */
1690
if ((guint)(end - start) >= wordlen)
1691
end = start + (wordlen - 1);
1692
sci_get_text_range(editor->sci, start, end, word);
1652
1698
* Finds the word at the position specified by @a pos. If any word is found, it is returned.
1653
1699
* Otherwise NULL is returned.
2037
2080
word_end = pos_find + rootlen;
2038
2081
if (pos_find != current)
2040
while (word_end < len && strchr(GEANY_WORDCHARS, sci_get_char_at(sci, word_end)) != NULL)
2083
word_end = SSM(sci, SCI_WORDENDPOSITION, word_end, TRUE);
2043
2085
word_length = word_end - pos_find;
2044
2086
if (word_length > rootlen)
2046
word = g_malloc0(word_length + 3);
2047
sci_get_text_range(sci, pos_find, word_end, word + 1);
2049
word[word_length + 1] = ' ';
2050
/* search the words string whether we already have the word in, otherwise add it */
2051
if (strstr(words->str, word) == NULL)
2088
word = sci_get_contents_range(sci, pos_find, word_end);
2089
/* search whether we already have the word in, otherwise add it */
2090
if (g_slist_find_custom(words, word, (GCompareFunc)utils_str_casecmp) != NULL)
2053
g_string_append(words, word + 1);
2094
words = g_slist_prepend(words, word);
2058
2098
if (nmatches == editor_prefs.autocompletion_max_entries)
2092
/* words are unsorted, make list of words */
2093
foreach_str(ptr, words->str)
2097
list = g_slist_prepend(list, ptr + 1);
2098
/* terminate previous string in list */
2103
list = g_slist_sort(list, (GCompareFunc)utils_str_casecmp);
2105
str = g_string_sized_new(words->len);
2106
foreach_slist(node, list)
2124
str = g_string_sized_new(editor_prefs.autocompletion_max_entries * (rootlen + 1));
2125
foreach_slist(node, words)
2108
2127
g_string_append(str, node->data);
2109
2129
if (node->next)
2110
2130
g_string_append_c(str, '\n');
2112
if (g_slist_length(list) >= editor_prefs.autocompletion_max_entries)
2133
if (n_words >= editor_prefs.autocompletion_max_entries)
2113
2134
g_string_append(str, "\n...");
2116
g_string_free(words, TRUE);
2136
g_slist_free(words);
2118
2138
show_autocomplete(sci, rootlen, str->str);
2119
2139
g_string_free(str, TRUE);
2245
/* This is very ugly but passing the pattern to ac_replace_specials() doesn't work because it is
2246
* modified when replacing a completion but the foreach function still passes the old pointer
2247
* to ac_replace_specials, so we use a global pointer outside of ac_replace_specials and
2248
* ac_complete_constructs. Any hints to improve this are welcome. */
2249
static GString *snippets_global_pattern = NULL;
2251
2260
static void snippets_replace_specials(gpointer key, gpointer value, gpointer user_data)
2263
GString *pattern = user_data;
2255
2265
g_return_if_fail(key != NULL);
2256
2266
g_return_if_fail(value != NULL);
2258
2268
needle = g_strconcat("%", (gchar*) key, "%", NULL);
2260
utils_string_replace_all(snippets_global_pattern, needle, (gchar*) value);
2270
utils_string_replace_all(pattern, needle, (gchar*) value);
2261
2271
g_free(needle);
2265
/* this only works with spaces only indentation on the lines */
2266
static void fix_line_indents(GeanyEditor *editor, gint line_start, gint line_end)
2268
ScintillaObject *sci = editor->sci;
2269
gint line, cur_line, cur_col, pos;
2271
/* get the line, col position as fixing indentation will move cursor to start of line */
2272
pos = sci_get_current_position(sci);
2273
cur_col = sci_get_col_from_position(sci, pos);
2274
cur_line = sci_get_current_line(sci);
2276
for (line = line_start; line <= line_end; line++)
2278
gint size = sci_get_line_indentation(sci, line);
2280
/* set to 0 first to trigger proper indent creation */
2281
sci_set_line_indentation(sci, line, 0);
2282
sci_set_line_indentation(sci, line, size);
2284
pos = scintilla_send_message(sci, SCI_FINDCOLUMN, cur_line, cur_col);
2285
sci_set_current_position(sci, pos, FALSE);
2289
static void replace_leading_tabs(GString *str, const gchar *whitespace)
2275
static gboolean utils_regex_find(regex_t *regex, const gchar *haystack, gsize start,
2276
gsize nmatches, regmatch_t *matches)
2282
gchar c = haystack[start - 1];
2284
if (c == '\n' || c == '\r')
2285
eflags = REG_NOTBOL;
2287
return regexec(regex, haystack + start, nmatches, matches, eflags) == 0;
2291
/* match_index: which match to replace, 0 for whole regex.
2292
* note: this doesn't support backreferences in replacements */
2293
static guint utils_string_regex_replace_all(GString *haystack,
2294
regex_t *regex, guint match_index, const gchar *replace)
2297
regmatch_t matches[10];
2300
g_return_val_if_fail(match_index < 10, 0);
2302
/* ensure haystack->str is not null */
2303
if (haystack->len == 0)
2307
while (utils_regex_find(regex, haystack->str, pos, G_N_ELEMENTS(matches), matches))
2309
regmatch_t *match = &matches[match_index];
2311
g_return_val_if_fail(match->rm_so >= 0, FALSE);
2312
pos += match->rm_so;
2313
pos = utils_string_replace(haystack, pos, match->rm_eo - match->rm_so, replace);
2320
static void fix_indentation(GeanyEditor *editor, GString *buf)
2322
const GeanyIndentPrefs *iprefs = editor_get_indent_prefs(editor);
2293
regmatch_t matches[2];
2296
if (regcomp(®ex, "^ *(\t)", 0) != 0)
2298
g_return_if_fail(FALSE);
2303
if (regexec(®ex, ptr,
2304
G_N_ELEMENTS(matches), matches, 0) != 0)
2307
pos = matches[1].rm_so;
2308
g_return_if_fail(pos >= 0);
2309
pos += ptr - str->str;
2310
g_string_erase(str, pos, 1);
2311
g_string_insert(str, pos, whitespace);
2312
ptr = str->str + pos + strlen(whitespace);
2325
gint cflags = REG_EXTENDED | REG_NEWLINE;
2327
/* transform leading tabs into indent widths (in spaces) */
2328
whitespace = g_strnfill(iprefs->width, ' ');
2329
regcomp(®ex, "^ *(\t)", cflags);
2330
while (utils_string_regex_replace_all(buf, ®ex, 1, whitespace));
2314
2331
regfree(®ex);
2333
/* remaining tabs are for alignment */
2334
if (iprefs->type != GEANY_INDENT_TYPE_TABS)
2335
utils_string_replace_all(buf, "\t", whitespace);
2337
/* use leading tabs */
2338
if (iprefs->type != GEANY_INDENT_TYPE_SPACES)
2342
/* for tabs+spaces mode we want the real tab width, not indent width */
2343
setptr(whitespace, g_strnfill(sci_get_tab_width(editor->sci), ' '));
2344
str = g_strdup_printf("^\t*(%s)", whitespace);
2346
regcomp(®ex, str, cflags);
2347
while (utils_string_regex_replace_all(buf, ®ex, 1, "\t"));
2378
2416
if (replace_newlines)
2379
2417
utils_string_replace_all(buf, "\n", eol);
2381
/* transform leading tabs into indent widths (in spaces) */
2382
whitespace = g_strnfill(iprefs->width, ' ');
2383
replace_leading_tabs(buf, whitespace);
2384
/* remaining tabs are for alignment */
2385
if (iprefs->type != GEANY_INDENT_TYPE_TABS)
2386
utils_string_replace_all(buf, "\t", whitespace);
2389
sci_start_undo_action(sci);
2391
if (cursor_index >= 0)
2419
fix_indentation(editor, buf);
2421
idx = replace_cursor_markers(editor, buf);
2393
gint idx = utils_strpos(buf->str, cur_marker);
2395
g_string_erase(buf, idx, strlen(cur_marker));
2397
2424
sci_insert_text(sci, insert_pos, buf->str);
2398
2425
sci_set_current_position(sci, insert_pos + idx, FALSE);
2401
2428
sci_insert_text(sci, insert_pos, buf->str);
2403
/* fixup indentation (very useful for Tabs & Spaces indent type) */
2404
line_end = sci_get_line_from_position(sci, insert_pos + buf->len);
2405
fix_line_indents(editor, line_start, line_end);
2406
2430
snippet_cursor_insert_pos = sci_get_current_position(sci);
2408
sci_end_undo_action(sci);
2409
2432
g_string_free(buf, TRUE);
2439
static gssize snippets_make_replacements(GeanyEditor *editor, GString *pattern,
2462
static void snippets_make_replacements(GeanyEditor *editor, GString *pattern)
2442
gssize cur_index = -1;
2444
gint i, tmp_pos, whitespace_len, nl_count = 0;
2445
2464
GHashTable *specials;
2446
GList *temp_list = NULL;
2447
const GeanyIndentPrefs *iprefs = editor_get_indent_prefs(editor);
2448
gint cursor_steps, old_cursor = 0;
2450
2466
/* replace 'special' completions */
2451
2467
specials = g_hash_table_lookup(snippet_hash, "Special");
2452
2468
if (G_LIKELY(specials != NULL))
2454
/* ugly hack using global_pattern */
2455
snippets_global_pattern = pattern;
2456
g_hash_table_foreach(specials, snippets_replace_specials, NULL);
2470
g_hash_table_foreach(specials, snippets_replace_specials, pattern);
2473
/* now transform other wildcards */
2474
utils_string_replace_all(pattern, "%newline%", "\n");
2475
utils_string_replace_all(pattern, "%ws%", "\t");
2477
/* replace %cursor% by a very unlikely string marker */
2478
utils_string_replace_all(pattern, "%cursor%", geany_cursor_marker);
2480
/* unescape '%' after all %wildcards% */
2481
templates_replace_valist(pattern, "{pc}", "%", NULL);
2459
2483
/* replace any template {foo} wildcards */
2460
2484
templates_replace_common(pattern, editor->document->file_name, editor->document->file_type, NULL);
2462
/* transform other wildcards */
2463
/* convert to %newlines%, else we get endless loops */
2464
utils_string_replace_all(pattern, "\n", "%newline%");
2466
/* if spaces are used, replaces all tabs with %ws%, which is later replaced
2467
* by real whitespace characters
2468
* otherwise replace all %ws% by \t, which will be replaced later by tab
2470
* this makes seperating between tab and spaces intentation pretty easy */
2471
if (iprefs->type == GEANY_INDENT_TYPE_SPACES)
2472
utils_string_replace_all(pattern, "\t", "%ws%");
2474
utils_string_replace_all(pattern, "%ws%", "\t");
2476
whitespace = g_strnfill(iprefs->width, ' '); /* use spaces for indentation, will be fixed up */
2477
whitespace_len = strlen(whitespace);
2488
static gssize replace_cursor_markers(GeanyEditor *editor, GString *pattern)
2490
gssize cur_index = -1;
2492
GList *temp_list = NULL;
2493
gint cursor_steps = 0, old_cursor = 0;
2479
while ((cursor_steps = utils_strpos(pattern->str, "%cursor%")) >= 0)
2481
/* replace every %newline% (up to next %cursor%) with EOL,
2482
* and update cursor_steps after */
2483
while ((tmp_pos = utils_strpos(pattern->str, "%newline%")) < cursor_steps && tmp_pos != -1)
2486
utils_string_replace_first(pattern, "%newline%", editor_get_eol_char(editor));
2487
cursor_steps = utils_strpos(pattern->str, "%cursor%");
2489
/* replace every %ws% (up to next %cursor%) with whitespaces,
2490
* and update cursor_steps after */
2491
while ((tmp_pos = utils_strpos(pattern->str, "%ws%")) < cursor_steps && tmp_pos != -1)
2493
utils_string_replace_first(pattern, "%ws%", whitespace);
2494
cursor_steps = utils_strpos(pattern->str, "%cursor%");
2496
/* finally replace the next %cursor% */
2497
utils_string_replace_first(pattern, "%cursor%", "");
2499
/* modify cursor_steps to take indentation count and type into account */
2501
/* We're saving the relative offset to each cursor position in a simple
2502
* linked list, including indentations between them. */
2498
cursor_steps = utils_string_find(pattern, cursor_steps, -1, geany_cursor_marker);
2499
if (cursor_steps == -1)
2502
g_string_erase(pattern, cursor_steps, strlen(geany_cursor_marker));
2505
cursor_steps += (nl_count * indent_size);
2506
temp_list = g_list_append(temp_list, GINT_TO_POINTER(cursor_steps - old_cursor));
2506
/* save the relative offset to each cursor position */
2507
temp_list = g_list_prepend(temp_list, GINT_TO_POINTER(cursor_steps - old_cursor));
2511
/* first cursor already includes newline positions */
2511
2512
cur_index = cursor_steps;
2513
2514
old_cursor = cursor_steps;
2515
/* replace remaining %ws% and %newline% which may occur after the last %cursor% */
2516
utils_string_replace_all(pattern, "%newline%", editor_get_eol_char(editor));
2517
utils_string_replace_all(pattern, "%ws%", whitespace);
2521
/* Bug: {ob}pc{cb} will be replaced by % too */
2522
templates_replace_valist(pattern, "{pc}", "%", NULL);
2524
/* We put the cursor positions for the most recent
2517
/* put the cursor positions for the most recent
2525
2518
* parsed snippet first, followed by any remaining positions */
2524
temp_list = g_list_reverse(temp_list);
2531
2525
foreach_list(node, temp_list)
2532
2526
g_queue_push_nth(snippet_offsets, node->data, i++);