libanjuta, language-support-*: Introspection from bgo#680466
[anjuta.git] / plugins / language-support-js / plugin.c
blob0ea97e9e435577a1bf553b2415e72b702292d659
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 Copyright (C) 2009 Maxim Ermilov <zaspire@rambler.ru>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 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, MA 02110-1301 USA
20 #include <config.h>
21 #include <libanjuta/anjuta-shell.h>
22 #include <libanjuta/anjuta-session.h>
23 #include <libanjuta/anjuta-debug.h>
24 #include <libanjuta/anjuta-language-provider.h>
25 #include <libanjuta/interfaces/ianjuta-document-manager.h>
26 #include <libanjuta/interfaces/ianjuta-project-manager.h>
27 #include <libanjuta/interfaces/ianjuta-editor-assist.h>
28 #include <libanjuta/interfaces/ianjuta-editor-tip.h>
29 #include <libanjuta/interfaces/ianjuta-language.h>
30 #include <libanjuta/interfaces/ianjuta-language-provider.h>
31 #include <libanjuta/interfaces/ianjuta-preferences.h>
32 #include <ctype.h>
34 #include <glib.h>
35 #include "util.h"
36 #include "plugin.h"
37 #include "prefs.h"
38 #include "code-completion.h"
40 #include "gi-symbol.h"
42 #define PREFS_BUILDER ANJUTA_GLADE_DIR"/anjuta-language-javascript.ui"
43 #define ICON_FILE "anjuta-language-cpp-java-plugin.png"
44 #define UI_FILE ANJUTA_UI_DIR"/anjuta-language-javascript.xml"
46 #define JSDIRS_LISTSTORE "jsdirs_liststore"
47 #define JSDIRS_TREEVIEW "jsdirs_treeview"
49 static gpointer parent_class;
51 static void
52 on_value_added_current_editor (AnjutaPlugin *plugin, const gchar *name,
53 const GValue *value, gpointer data);
54 static void
55 on_value_removed_current_editor (AnjutaPlugin *plugin, const gchar *name,
56 gpointer data);
58 /*Export for GtkBuilder*/
59 G_MODULE_EXPORT void
60 on_jsdirs_rm_button_clicked (GtkButton *button, gpointer user_data);
61 G_MODULE_EXPORT void
62 on_jsdirs_add_button_clicked (GtkButton *button, gpointer user_data);
64 static gboolean
65 js_support_plugin_activate (AnjutaPlugin *plugin)
67 JSLang *js_support_plugin;
69 DEBUG_PRINT ("%s", "JSLang: Activating JSLang plugin ...");
70 js_support_plugin = (JSLang*) plugin;
71 js_support_plugin->prefs = g_settings_new (JS_SUPPORT_SCHEMA);
72 js_support_plugin->editor_watch_id =
73 anjuta_plugin_add_watch (plugin, IANJUTA_DOCUMENT_MANAGER_CURRENT_DOCUMENT,
74 on_value_added_current_editor,
75 on_value_removed_current_editor,
76 plugin);
77 return TRUE;
80 static gboolean
81 js_support_plugin_deactivate (AnjutaPlugin *plugin)
83 JSLang *js_support_plugin;
85 DEBUG_PRINT ("%s", "JSLang: Dectivating JSLang plugin ...");
86 js_support_plugin = (JSLang*) plugin;
87 anjuta_plugin_remove_watch (plugin, js_support_plugin->editor_watch_id, TRUE);
88 return TRUE;
91 static void
92 js_support_plugin_finalize (GObject *obj)
94 JSLang *self = (JSLang*)obj;
96 g_object_unref (self->symbol);
97 self->symbol = NULL;
99 G_OBJECT_CLASS (parent_class)->finalize (obj);
102 static void
103 js_support_plugin_dispose (GObject *obj)
105 JSLang *self = (JSLang*)obj;
107 g_assert (self != NULL);
109 g_object_unref (self->symbol);
110 self->symbol = NULL;
112 G_OBJECT_CLASS (parent_class)->dispose (obj);
115 static void
116 js_support_plugin_instance_init (GObject *obj)
118 JSLang *plugin = (JSLang*)obj;
119 plugin->prefs = NULL;
120 plugin->symbol = NULL;
123 static void
124 js_support_plugin_class_init (GObjectClass *klass)
126 AnjutaPluginClass *plugin_class = ANJUTA_PLUGIN_CLASS (klass);
128 parent_class = g_type_class_peek_parent (klass);
130 plugin_class->activate = js_support_plugin_activate;
131 plugin_class->deactivate = js_support_plugin_deactivate;
132 klass->finalize = js_support_plugin_finalize;
133 klass->dispose = js_support_plugin_dispose;
136 static void
137 install_support (JSLang *plugin)
139 const gchar *lang;
140 IAnjutaLanguage* lang_manager;
142 setPlugin (plugin);
144 if (!IANJUTA_IS_EDITOR (plugin->current_editor))
145 return;
146 lang_manager = anjuta_shell_get_interface (ANJUTA_PLUGIN (plugin)->shell,
147 IAnjutaLanguage, NULL);
148 if (!lang_manager)
149 return;
150 lang = ianjuta_language_get_name_from_editor (lang_manager,
151 IANJUTA_EDITOR_LANGUAGE (plugin->current_editor), NULL);
152 if (!lang || !g_str_equal (lang, "JavaScript"))
153 return;
155 plugin->lang_prov = g_object_new (ANJUTA_TYPE_LANGUAGE_PROVIDER, NULL);
156 anjuta_language_provider_install (plugin->lang_prov,
157 IANJUTA_EDITOR (plugin->current_editor),
158 plugin->prefs);
160 DEBUG_PRINT ("%s", "JSLang: Install support");
162 ianjuta_editor_assist_add (IANJUTA_EDITOR_ASSIST(plugin->current_editor),
163 IANJUTA_PROVIDER(plugin), NULL);
166 static void
167 uninstall_support (JSLang *plugin)
169 if (plugin->lang_prov)
171 g_object_unref (plugin->lang_prov);
172 plugin->lang_prov = NULL;
175 DEBUG_PRINT ("%s", "JSLang: Uninstall support");
177 ianjuta_editor_assist_remove (IANJUTA_EDITOR_ASSIST(plugin->current_editor),
178 IANJUTA_PROVIDER(plugin), NULL);
182 static void
183 on_value_added_current_editor (AnjutaPlugin *plugin, const gchar *name,
184 const GValue *value, gpointer data)
186 JSLang *js_support_plugin;
187 IAnjutaDocument* doc = IANJUTA_DOCUMENT(g_value_get_object (value));
189 DEBUG_PRINT ("%s", "JSLang: Add editor");
191 js_support_plugin = (JSLang*) plugin;
192 if (IANJUTA_IS_EDITOR(doc))
193 js_support_plugin->current_editor = G_OBJECT(doc);
194 else
196 js_support_plugin->current_editor = NULL;
197 return;
199 install_support (js_support_plugin);
202 static void
203 on_value_removed_current_editor (AnjutaPlugin *plugin, const gchar *name,
204 gpointer data)
206 JSLang *js_support_plugin;
208 DEBUG_PRINT ("%s", "JSLang: Remove editor");
210 js_support_plugin = (JSLang*) plugin;
211 if (IANJUTA_IS_EDITOR(js_support_plugin->current_editor))
212 uninstall_support (js_support_plugin);
213 js_support_plugin->current_editor = NULL;
216 static void
217 jsdirs_save (GtkTreeModel *list_store)
219 GtkTreeIter iter;
220 const gchar *project_root = NULL;
221 anjuta_shell_get (ANJUTA_PLUGIN (getPlugin ())->shell,
222 IANJUTA_PROJECT_MANAGER_PROJECT_ROOT_URI,
223 G_TYPE_STRING, &project_root, NULL);
225 GFile *tmp = g_file_new_for_uri (project_root);
226 AnjutaSession *session = anjuta_session_new (g_file_get_path (tmp));
227 g_object_unref (tmp);
229 GList *dirs = NULL;
230 if (!gtk_tree_model_iter_children (list_store, &iter, NULL))
231 return;
234 gchar *dir;
235 gtk_tree_model_get (list_store, &iter, 0, &dir, -1);
237 g_assert (dir != NULL);
239 dirs = g_list_append (dirs, dir);
240 } while (gtk_tree_model_iter_next (list_store, &iter));
241 anjuta_session_set_string_list (session, "options", "js_dirs", dirs);
242 anjuta_session_sync (session);
245 G_MODULE_EXPORT void
246 on_jsdirs_rm_button_clicked (GtkButton *button, gpointer user_data)
248 GtkTreeIter iter;
249 GtkTreeView *tree = GTK_TREE_VIEW (user_data);
250 GtkTreeModel *list_store = gtk_tree_view_get_model (tree);
251 GtkTreeSelection *selection = gtk_tree_view_get_selection (tree);
253 if (!gtk_tree_selection_get_selected (selection, &list_store, &iter))
254 return;
255 gtk_list_store_remove (GTK_LIST_STORE (list_store), &iter);
256 jsdirs_save (list_store);
259 G_MODULE_EXPORT void
260 on_jsdirs_add_button_clicked (GtkButton *button, gpointer user_data)
262 GtkWidget *dialog;
264 g_assert (user_data != NULL);
266 GtkTreeView *tree = GTK_TREE_VIEW (user_data);
267 GtkListStore *list_store = GTK_LIST_STORE (gtk_tree_view_get_model (tree));
269 g_assert (list_store != NULL);
271 dialog = gtk_file_chooser_dialog_new ("Choose directory",
272 NULL,
273 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
274 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
275 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
276 NULL);
277 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
279 char *filename;
280 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
281 if (filename)
283 GtkTreeIter iter;
284 gtk_list_store_append (list_store, &iter);
285 gtk_list_store_set (list_store, &iter, 0, filename, -1);
286 g_free (filename);
288 jsdirs_save (GTK_TREE_MODEL (list_store));
290 gtk_widget_destroy (dialog);
293 static void
294 jsdirs_init_treeview (JSLang* plugin)
296 const gchar *project_root = NULL;
297 GtkTreeIter iter;
298 GtkListStore *list_store = GTK_LIST_STORE (gtk_builder_get_object (
299 plugin->bxml, JSDIRS_LISTSTORE));
300 if (!list_store)
301 return;
303 anjuta_shell_get (ANJUTA_PLUGIN (plugin)->shell,
304 IANJUTA_PROJECT_MANAGER_PROJECT_ROOT_URI,
305 G_TYPE_STRING, &project_root, NULL);
307 GFile *tmp = g_file_new_for_uri (project_root);
308 AnjutaSession *session = anjuta_session_new (g_file_get_path (tmp));
309 g_object_unref (tmp);
310 GList* dir_list = anjuta_session_get_string_list (session, "options", "js_dirs");
311 GList *i;
312 gtk_list_store_clear (list_store);
314 for (i = dir_list; i; i = g_list_next (i))
316 gtk_list_store_append (list_store, &iter);
317 gtk_list_store_set (list_store, &iter, 0, i->data, -1);
319 if (!dir_list)
321 gtk_list_store_append (list_store, &iter);
322 gtk_list_store_set (list_store, &iter, 0, ".", -1);
326 #define PREF_WIDGET_SPACE "preferences:completion-space-after-func"
327 #define PREF_WIDGET_BRACE "preferences:completion-brace-after-func"
328 #define PREF_WIDGET_CLOSEBRACE "preferences:completion-closebrace-after-func"
329 #define PREF_WIDGET_AUTO "preferences:completion-enable"
331 static void
332 on_autocompletion_toggled (GtkToggleButton* button,
333 JSLang* plugin)
335 GtkWidget* widget;
336 gboolean sensitive = gtk_toggle_button_get_active (button);
338 widget = GTK_WIDGET (gtk_builder_get_object (plugin->bxml, PREF_WIDGET_SPACE));
339 gtk_widget_set_sensitive (widget, sensitive);
340 widget = GTK_WIDGET (gtk_builder_get_object (plugin->bxml, PREF_WIDGET_BRACE));
341 gtk_widget_set_sensitive (widget, sensitive);
342 widget = GTK_WIDGET (gtk_builder_get_object (plugin->bxml, PREF_WIDGET_CLOSEBRACE));
343 gtk_widget_set_sensitive (widget, sensitive);
346 static void
347 ipreferences_merge (IAnjutaPreferences* ipref, AnjutaPreferences* prefs,
348 GError** e)
350 /* Add preferences */
351 GError* error = NULL;
352 JSLang* plugin = (JSLang*) ipref;
353 plugin->bxml = gtk_builder_new ();
354 GtkWidget* toggle;
356 if (!gtk_builder_add_from_file (plugin->bxml, PREFS_BUILDER, &error))
358 g_warning ("Couldn't load builder file: %s", error->message);
359 g_error_free (error);
362 GtkTreeView *tree = GTK_TREE_VIEW (gtk_builder_get_object (plugin->bxml, JSDIRS_TREEVIEW));
364 gtk_builder_connect_signals (plugin->bxml, tree);
365 jsdirs_init_treeview (plugin);
367 anjuta_preferences_add_from_builder (prefs,
368 plugin->bxml,
369 plugin->prefs,
370 "preferences", _("JavaScript"),
371 ICON_FILE);
372 toggle = GTK_WIDGET (gtk_builder_get_object (plugin->bxml, PREF_WIDGET_AUTO));
373 g_signal_connect (toggle, "toggled", G_CALLBACK (on_autocompletion_toggled),
374 plugin);
375 on_autocompletion_toggled (GTK_TOGGLE_BUTTON (toggle), plugin);
378 static void
379 ipreferences_unmerge (IAnjutaPreferences* ipref, AnjutaPreferences* prefs,
380 GError** e)
382 JSLang* plugin = (JSLang*) ipref;
383 anjuta_preferences_remove_page(prefs, _("JavaScript"));
384 g_object_unref (plugin->bxml);
387 static void
388 ipreferences_iface_init (IAnjutaPreferencesIface* iface)
390 iface->merge = ipreferences_merge;
391 iface->unmerge = ipreferences_unmerge;
394 static void
395 iprovider_activate (IAnjutaProvider* self,
396 IAnjutaIterable* iter,
397 gpointer data,
398 GError** e)
400 JSLang *plugin = (JSLang*) self;
401 anjuta_language_provider_activate (plugin->lang_prov, self, iter, data);
404 static void
405 iprovider_populate (IAnjutaProvider* self,
406 IAnjutaIterable* cursor,
407 GError** e)
409 JSLang *plugin = (JSLang*) self;
410 anjuta_language_provider_populate (plugin->lang_prov, self, cursor);
413 static const gchar*
414 iprovider_get_name (IAnjutaProvider* self,
415 GError** e)
417 return _("JavaScript");
420 static IAnjutaIterable*
421 iprovider_get_start_iter (IAnjutaProvider* self,
422 GError** e)
424 JSLang *plugin = (JSLang*) self;
425 return anjuta_language_provider_get_start_iter (plugin->lang_prov);
428 static void
429 iprovider_iface_init (IAnjutaProviderIface* iface)
431 iface->activate = iprovider_activate;
432 iface->populate = iprovider_populate;
433 iface->get_name = iprovider_get_name;
434 iface->get_start_iter = iprovider_get_start_iter;
437 static GList*
438 ilanguage_provider_get_calltip_cache (IAnjutaLanguageProvider *obj,
439 gchar* call_context,
440 GError** err)
442 /* TODO: Not implemented yet */
443 return NULL;
446 static gchar*
447 ilanguage_provider_get_calltip_context (IAnjutaLanguageProvider *obj,
448 IAnjutaIterable *iter,
449 GError** err)
451 /* TODO: Not implemented yet
452 GList *t = NULL;
453 gchar *args = code_completion_get_func_tooltip (plugin, sym);
454 t = g_list_append (t, args);
455 if (args)
457 ianjuta_editor_tip_show (IANJUTA_EDITOR_TIP(plugin->current_editor), t,
458 position, NULL);
459 g_free (args);
462 return NULL;
465 static void
466 ilanguage_provider_new_calltip (IAnjutaLanguageProvider* obj,
467 gchar* call_context,
468 IAnjutaIterable* cursor,
469 GError** err)
471 /* TODO: Not implemented yet */
472 return;
475 static IAnjutaIterable*
476 ilanguage_provider_populate (IAnjutaLanguageProvider* obj,
477 IAnjutaIterable* iter,
478 GError **err)
480 JSLang *plugin = (JSLang*)obj;
481 IAnjutaIterable* start_iter;
482 GList *suggestions;
484 start_iter = ianjuta_iterable_clone (iter, NULL);
486 if (!plugin->current_editor)
487 return start_iter;
488 gint depth;
489 gchar *str = code_completion_get_str (IANJUTA_EDITOR (plugin->current_editor), FALSE);
491 if (!str)
492 return start_iter;
494 g_assert (plugin->prefs);
495 gchar *file = file_completion (IANJUTA_EDITOR (plugin->current_editor), &depth);
497 if (strlen (str) < g_settings_get_int (plugin->prefs, MIN_CODECOMPLETE))
499 ianjuta_editor_assist_proposals (IANJUTA_EDITOR_ASSIST (plugin->current_editor),
500 IANJUTA_PROVIDER(obj), NULL, NULL, TRUE, NULL);
501 /* Highlight missed semicolon */
502 code_completion_get_list (plugin, file, NULL, depth);
503 return start_iter;
506 gint i;
507 DEBUG_PRINT ("JSLang: Auto complete for %s (TMFILE=%s)", str, file);
508 for (i = strlen (str) - 1; i; i--)
510 if (str[i] == '.')
511 break;
513 /* TODO: Use anjuta_language_provider_get_pre_word in the future */
514 if (i > 0)
515 suggestions = code_completion_get_list (plugin, file, g_strndup (str, i), depth);
516 else
517 suggestions = code_completion_get_list (plugin, file, NULL, depth);
518 if (suggestions)
520 GList *nsuggest = NULL;
521 gint k;
522 if (i > 0)
524 suggestions = filter_list (suggestions, str + i + 1);
525 k = strlen (str + i + 1);
526 } else
528 suggestions = filter_list (suggestions, str);
529 k = strlen (str);
531 GList *i;
532 for (; k > 0; k--)
533 ianjuta_iterable_previous (start_iter, NULL);
535 for (i = suggestions; i; i = g_list_next(i)) {
536 IAnjutaEditorAssistProposal* proposal;
537 AnjutaLanguageProposalData* prop_data;
539 proposal = g_new0(IAnjutaEditorAssistProposal, 1);
541 if (!i->data)
542 continue;
544 proposal->label = i->data;
545 prop_data = anjuta_language_proposal_data_new (i->data);
546 prop_data->is_func = code_completion_is_symbol_func (plugin, str);
547 /* TODO: Not implemented yet */
548 prop_data->has_para = TRUE;
549 prop_data->info = i->data;
550 proposal->data = prop_data;
551 nsuggest = g_list_prepend (nsuggest, proposal);
553 ianjuta_editor_assist_proposals (IANJUTA_EDITOR_ASSIST (plugin->current_editor),
554 IANJUTA_PROVIDER(obj), nsuggest, NULL, TRUE, NULL);
555 g_list_free (nsuggest);
556 return start_iter;
558 ianjuta_editor_assist_proposals (IANJUTA_EDITOR_ASSIST (plugin->current_editor),
559 IANJUTA_PROVIDER(obj), NULL, NULL, TRUE, NULL);
561 return start_iter;
564 static void
565 ilanguage_provider_iface_init (IAnjutaLanguageProviderIface* iface)
567 iface->get_calltip_cache = ilanguage_provider_get_calltip_cache;
568 iface->get_calltip_context = ilanguage_provider_get_calltip_context;
569 iface->new_calltip = ilanguage_provider_new_calltip;
570 iface->populate_completions = ilanguage_provider_populate;
573 ANJUTA_PLUGIN_BEGIN (JSLang, js_support_plugin);
574 ANJUTA_PLUGIN_ADD_INTERFACE(ipreferences, IANJUTA_TYPE_PREFERENCES);
575 ANJUTA_PLUGIN_ADD_INTERFACE(iprovider, IANJUTA_TYPE_PROVIDER);
576 ANJUTA_PLUGIN_ADD_INTERFACE(ilanguage_provider, IANJUTA_TYPE_LANGUAGE_PROVIDER)
577 ANJUTA_PLUGIN_END;
579 ANJUTA_SIMPLE_PLUGIN (JSLang, js_support_plugin);