1 /*
2 * automark.c
3 *
4 * Copyright 2014 Pavel Roschin <rpg89(at)post(dot)ru>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 * MA 02110-1301, USA.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h" /* for the gettext domain */
23 #endif
24
25 #include <string.h>
26 #ifdef HAVE_LOCALE_H
27 #include <locale.h>
28 #endif
29
30 #include <gdk/gdkkeysyms.h>
31
32 #include <geanyplugin.h>
33 #include <geany.h>
34
35 #include "Scintilla.h"
36 #include "SciLexer.h"
37
38 #define SSM(s, m, w, l) scintilla_send_message(s, m, w, l)
39
40 GeanyPlugin *geany_plugin;
41 GeanyData *geany_data;
42
43
44 static gint source_id;
45
46 static const gint AUTOMARK_INDICATOR = GEANY_INDICATOR_SEARCH;
47
48 static void
search_mark_in_range(GeanyEditor * editor,gint flags,struct Sci_TextToFind * ttf)49 search_mark_in_range(
50 GeanyEditor *editor,
51 gint flags,
52 struct Sci_TextToFind *ttf)
53 {
54 ScintillaObject *sci = editor->sci;
55
56 while (SSM(sci, SCI_FINDTEXT, flags, (uptr_t)ttf) != -1)
57 {
58 gint start = ttf->chrgText.cpMin;
59 gint end = ttf->chrgText.cpMax;
60
61 if (end > ttf->chrg.cpMax)
62 break;
63
64 ttf->chrg.cpMin = ttf->chrgText.cpMax;
65 if (end == start)
66 continue;
67 if(SSM(sci, SCI_INDICATORVALUEAT, AUTOMARK_INDICATOR, start))
68 continue;
69 SSM(sci, SCI_SETINDICATORCURRENT, AUTOMARK_INDICATOR, 0);
70 SSM(sci, SCI_INDICATORFILLRANGE, start, end - start);
71 }
72 }
73
74 /* based on editor_find_current_word_sciwc from editor.c */
75 static gchar *
get_current_word(ScintillaObject * sci)76 get_current_word(ScintillaObject *sci)
77 {
78 gint pos = sci_get_current_position(sci);
79 gint start = SSM(sci, SCI_WORDSTARTPOSITION, pos, TRUE);
80 gint end = SSM(sci, SCI_WORDENDPOSITION, pos, TRUE);
81
82 if (end == start)
83 return NULL;
84
85 if ((guint)(end - start) >= GEANY_MAX_WORD_LENGTH)
86 end = start + (GEANY_MAX_WORD_LENGTH - 1);
87 return sci_get_contents_range(sci, start, end);
88 }
89
90 static gboolean
automark(gpointer user_data)91 automark(gpointer user_data)
92 {
93 GeanyDocument *doc = (GeanyDocument *)user_data;
94 GeanyEditor *editor;
95 static GeanyEditor *editor_cache = NULL;
96 ScintillaObject *sci;
97 gchar *text;
98 static gchar text_cache[GEANY_MAX_WORD_LENGTH] = {0};
99 gint match_flag = SCFIND_MATCHCASE | SCFIND_WHOLEWORD;
100 struct Sci_TextToFind ttf;
101
102 source_id = 0;
103
104 /* during timeout document could be destroyed so check everything again */
105 if (!DOC_VALID(doc))
106 return FALSE;
107
108 editor = doc->editor;
109 sci = editor->sci;
110
111 /* Do not highlight while selecting text and allow other markers to work */
112 if (sci_has_selection(sci))
113 return FALSE;
114
115 text = get_current_word(editor->sci);
116
117 if (EMPTY(text))
118 {
119 editor_indicator_clear(editor, AUTOMARK_INDICATOR);
120 g_free(text);
121 return FALSE;
122 }
123
124 if (editor_cache != editor || strcmp(text, text_cache) != 0)
125 {
126 editor_indicator_clear(editor, AUTOMARK_INDICATOR);
127 strcpy(text_cache, text);
128 editor_cache = editor;
129 }
130
131 gint vis_first = SSM(sci, SCI_GETFIRSTVISIBLELINE, 0, 0);
132 gint doc_first = SSM(sci, SCI_DOCLINEFROMVISIBLE, vis_first, 0);
133 gint vis_last = SSM(sci, SCI_LINESONSCREEN, 0, 0) + vis_first;
134 gint doc_last = SSM(sci, SCI_DOCLINEFROMVISIBLE, vis_last, 0);
135 gint start = SSM(sci, SCI_POSITIONFROMLINE, doc_first, 0);
136 gint end = SSM(sci, SCI_GETLINEENDPOSITION, doc_last, 0);
137
138 ttf.chrg.cpMin = start;
139 ttf.chrg.cpMax = end;
140 ttf.lpstrText = text;
141
142 search_mark_in_range(editor, match_flag, &ttf);
143
144 g_free(text);
145
146 return FALSE;
147 }
148
149 static gboolean
on_editor_notify(GObject * obj,GeanyEditor * editor,SCNotification * nt,gpointer user_data)150 on_editor_notify(
151 GObject *obj,
152 GeanyEditor *editor,
153 SCNotification *nt,
154 gpointer user_data)
155 {
156 if (SCN_UPDATEUI == nt->nmhdr.code)
157 {
158 /* if events are too intensive - remove old callback */
159 if (source_id)
160 g_source_remove(source_id);
161 source_id = g_idle_add(automark, editor->document);
162 }
163 return FALSE;
164 }
165
166 static PluginCallback plugin_automark_callbacks[] =
167 {
168 { "editor-notify", (GCallback) &on_editor_notify, FALSE, NULL },
169 { NULL, NULL, FALSE, NULL }
170 };
171
172
173 static gboolean
plugin_automark_init(GeanyPlugin * plugin,G_GNUC_UNUSED gpointer pdata)174 plugin_automark_init(GeanyPlugin *plugin, G_GNUC_UNUSED gpointer pdata)
175 {
176 geany_plugin = plugin;
177 geany_data = plugin->geany_data;
178 source_id = 0;
179 return TRUE;
180 }
181
182
183 static void
plugin_automark_cleanup(G_GNUC_UNUSED GeanyPlugin * plugin,G_GNUC_UNUSED gpointer pdata)184 plugin_automark_cleanup(G_GNUC_UNUSED GeanyPlugin *plugin, G_GNUC_UNUSED gpointer pdata)
185 {
186 if (source_id)
187 g_source_remove(source_id);
188 }
189
190
191 static void
plugin_automark_help(G_GNUC_UNUSED GeanyPlugin * plugin,G_GNUC_UNUSED gpointer pdata)192 plugin_automark_help (G_GNUC_UNUSED GeanyPlugin *plugin, G_GNUC_UNUSED gpointer pdata)
193 {
194 utils_open_browser("http://plugins.geany.org/automark.html");
195 }
196
197
198 /* Load module */
199 G_MODULE_EXPORT
geany_load_module(GeanyPlugin * plugin)200 void geany_load_module(GeanyPlugin *plugin)
201 {
202 /* Setup translation */
203 main_locale_init(LOCALEDIR, GETTEXT_PACKAGE);
204
205 /* Set metadata */
206 plugin->info->name = _("Auto-mark");
207 plugin->info->description = _("Auto-mark word under cursor");
208 plugin->info->version = "0.1";
209 plugin->info->author = "Pavel Roschin <rpg89(at)post(dot)ru>";
210
211 /* Set functions */
212 plugin->funcs->init = plugin_automark_init;
213 plugin->funcs->cleanup = plugin_automark_cleanup;
214 plugin->funcs->help = plugin_automark_help;
215 plugin->funcs->callbacks = plugin_automark_callbacks;
216
217 /* Register! */
218 GEANY_PLUGIN_REGISTER(plugin, 226);
219 }
220