1/* 2 * Copyright (C) 2008-2010 Abderrahim Kitouni 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18using GLib; 19using Gtk; 20using Anjuta; 21 22public class ValaPlugin : Plugin, IAnjuta.Preferences { 23 internal const string PREF_WIDGET_SPACE = "preferences:completion-space-after-func"; 24 internal const string PREF_WIDGET_BRACE = "preferences:completion-brace-after-func"; 25 internal const string PREF_WIDGET_AUTO = "preferences:completion-enable"; 26 internal const string ICON_FILE = "anjuta-vala.png"; 27 internal static string PREFS_BUILDER = Config.PACKAGE_DATA_DIR + "/glade/anjuta-vala.ui"; 28 29 internal weak IAnjuta.Editor current_editor; 30 internal GLib.Settings settings = new GLib.Settings ("org.gnome.anjuta.plugins.vala"); 31 uint editor_watch_id; 32 ulong project_loaded_id; 33 34 Vala.CodeContext context; 35 Cancellable cancel; 36 BlockLocator locator = new BlockLocator (); 37 38 AnjutaReport report; 39 ValaProvider provider; 40 41 Vala.Parser parser; 42 Vala.Genie.Parser genie_parser; 43 44 public static Gtk.Builder bxml; 45 46 Vala.Set<string> current_sources = new Vala.HashSet<string> (str_hash, str_equal); 47 ValaPlugin () { 48 Object (); 49 } 50 public override bool activate () { 51 debug("Activating ValaPlugin"); 52 report = new AnjutaReport(); 53 report.docman = (IAnjuta.DocumentManager) shell.get_object("IAnjutaDocumentManager"); 54 parser = new Vala.Parser (); 55 genie_parser = new Vala.Genie.Parser (); 56 57 init_context (); 58 59 provider = new ValaProvider(this); 60 editor_watch_id = add_watch("document_manager_current_document", 61 editor_value_added, 62 editor_value_removed); 63 64 return true; 65 } 66 67 public override bool deactivate () { 68 debug("Deactivating ValaPlugin"); 69 remove_watch(editor_watch_id, true); 70 71 cancel.cancel (); 72 lock (context) { 73 context = null; 74 } 75 76 return true; 77 } 78 79 void init_context () { 80 context = new Vala.CodeContext(); 81 context.profile = Vala.Profile.GOBJECT; 82 context.report = report; 83 report.clear_error_indicators (); 84 85 cancel = new Cancellable (); 86 87 /* This doesn't actually parse anything as there are no files yet, 88 it's just to set the context in the parsers */ 89 parser.parse (context); 90 genie_parser.parse (context); 91 92 current_sources = new Vala.HashSet<string> (str_hash, str_equal); 93 94 } 95 96 void parse () { 97 try { 98 Thread.create<void>(() => { 99 lock (context) { 100 Vala.CodeContext.push(context); 101 var report = context.report as AnjutaReport; 102 103 foreach (var src in context.get_source_files ()) { 104 if (src.get_nodes ().size == 0) { 105 debug ("parsing file %s", src.filename); 106 genie_parser.visit_source_file (src); 107 parser.visit_source_file (src); 108 } 109 110 if (cancel.is_cancelled ()) { 111 Vala.CodeContext.pop(); 112 return; 113 } 114 } 115 116 if (report.get_errors () > 0 || cancel.is_cancelled ()) { 117 Vala.CodeContext.pop(); 118 return; 119 } 120 121 context.check (); 122 Vala.CodeContext.pop(); 123 } 124 }, false); 125 } catch (ThreadError err) { 126 warning ("cannot create thread : %s", err.message); 127 } 128 } 129 130 void add_project_files () { 131 var pm = (IAnjuta.ProjectManager) shell.get_object("IAnjutaProjectManager"); 132 var project = pm.get_current_project (); 133 var current_file = (current_editor as IAnjuta.File).get_file (); 134 if (project == null) 135 return; 136 137 Vala.CodeContext.push (context); 138 139 var current_src = project.get_root ().get_source_from_file (current_file); 140 if (current_src == null) 141 return; 142 143 var current_target = current_src.parent_type (Anjuta.ProjectNodeType.TARGET); 144 if (current_target == null) 145 return; 146 147 current_target.foreach (TraverseType.PRE_ORDER, (node) => { 148 if (!(Anjuta.ProjectNodeType.SOURCE in node.get_node_type ())) 149 return; 150 151 if (node.get_file () == null) 152 return; 153 154 var path = node.get_file ().get_path (); 155 if (path == null) 156 return; 157 158 if (path.has_suffix (".vala") || path.has_suffix (".vapi") || path.has_suffix (".gs")) { 159 if (path in current_sources) { 160 debug ("file %s already added", path); 161 } else { 162 context.add_source_filename (path); 163 current_sources.add (path); 164 debug ("file %s added", path); 165 } 166 } else { 167 debug ("file %s skipped", path); 168 } 169 }); 170 171 if (!context.has_package ("gobject-2.0")) { 172 context.add_external_package("glib-2.0"); 173 context.add_external_package("gobject-2.0"); 174 debug ("standard packages added"); 175 } else { 176 debug ("standard packages already added"); 177 } 178 179 string[] flags = {}; 180 unowned Anjuta.ProjectProperty prop = current_target.get_property ("VALAFLAGS"); 181 if (prop != null && prop != prop.info.default_value) { 182 GLib.Shell.parse_argv (prop.value, out flags); 183 } else { 184 /* Fall back to AM_VALAFLAGS */ 185 var current_group = current_target.parent_type (Anjuta.ProjectNodeType.GROUP); 186 prop = current_group.get_property ("VALAFLAGS"); 187 if (prop != null && prop != prop.info.default_value) 188 GLib.Shell.parse_argv (prop.value, out flags); 189 } 190 191 string[] packages = {}; 192 string[] vapidirs = {}; 193 194 for (int i = 0; i < flags.length; i++) { 195 if (flags[i] == "--vapidir") 196 vapidirs += flags[++i]; 197 else if (flags[i].has_prefix ("--vapidir=")) 198 vapidirs += flags[i].substring ("--vapidir=".length); 199 else if (flags[i] == "--pkg") 200 packages += flags[++i]; 201 else if (flags[i].has_prefix ("--pkg=")) 202 packages += flags[i].substring ("--pkg=".length); 203 else 204 debug ("Unknown valac flag %s", flags[i]); 205 } 206 207 var srcdir = current_target.parent_type (Anjuta.ProjectNodeType.GROUP).get_file ().get_path (); 208 var top_srcdir = project.get_root ().get_file ().get_path (); 209 for (int i = 0; i < vapidirs.length; i++) { 210 vapidirs[i] = vapidirs[i].replace ("$(srcdir)", srcdir) 211 .replace ("$(top_srcdir)", top_srcdir); 212 } 213 214 context.vapi_directories = vapidirs; 215 foreach (var pkg in packages) { 216 if (context.has_package (pkg)) { 217 debug ("package %s skipped", pkg); 218 } else if (context.add_external_package(pkg)) { 219 debug ("package %s added", pkg); 220 } else { 221 debug ("package %s not found", pkg); 222 } 223 } 224 Vala.CodeContext.pop(); 225 } 226 227 public void on_project_loaded (IAnjuta.ProjectManager pm, Error? e) { 228 if (context == null) 229 return; 230 add_project_files (); 231 parse (); 232 pm.disconnect (project_loaded_id); 233 project_loaded_id = 0; 234 } 235 236 /* "document_manager_current_document" watch */ 237 public void editor_value_added (Anjuta.Plugin plugin, string name, Value value) { 238 debug("editor value added"); 239 assert (current_editor == null); 240 if (!(value.get_object() is IAnjuta.Editor)) { 241 /* a glade document, for example, isn't an editor */ 242 return; 243 } 244 245 current_editor = value.get_object() as IAnjuta.Editor; 246 var current_file = value.get_object() as IAnjuta.File; 247 248 var pm = (IAnjuta.ProjectManager) shell.get_object("IAnjutaProjectManager"); 249 var project = pm.get_current_project (); 250 251 if (!project.is_loaded()) { 252 if (project_loaded_id == 0) 253 project_loaded_id = pm.project_loaded.connect (on_project_loaded); 254 } else { 255 var cur_gfile = current_file.get_file (); 256 if (cur_gfile == null) { 257 // File hasn't been saved yet 258 return; 259 } 260 261 if (!(cur_gfile.get_path () in current_sources)) { 262 cancel.cancel (); 263 lock (context) { 264 init_context (); 265 add_project_files (); 266 } 267 268 parse (); 269 } 270 } 271 if (current_editor != null) { 272 if (current_editor is IAnjuta.EditorAssist) 273 (current_editor as IAnjuta.EditorAssist).add(provider); 274 if (current_editor is IAnjuta.EditorTip) 275 current_editor.char_added.connect (on_char_added); 276 if (current_editor is IAnjuta.FileSavable) { 277 var file_savable = (IAnjuta.FileSavable) current_editor; 278 file_savable.saved.connect (on_file_saved); 279 } 280 if (current_editor is IAnjuta.EditorGladeSignal) { 281 var gladesig = current_editor as IAnjuta.EditorGladeSignal; 282 gladesig.drop_possible.connect (on_drop_possible); 283 gladesig.drop.connect (on_drop); 284 } 285 current_editor.glade_member_add.connect (insert_member_decl_and_init); 286 } 287 report.update_errors (current_editor); 288 } 289 public void editor_value_removed (Anjuta.Plugin plugin, string name) { 290 debug("editor value removed"); 291 if (current_editor is IAnjuta.EditorAssist) 292 (current_editor as IAnjuta.EditorAssist).remove(provider); 293 if (current_editor is IAnjuta.EditorTip) 294 current_editor.char_added.disconnect (on_char_added); 295 if (current_editor is IAnjuta.FileSavable) { 296 var file_savable = (IAnjuta.FileSavable) current_editor; 297 file_savable.saved.disconnect (on_file_saved); 298 } 299 if (current_editor is IAnjuta.EditorGladeSignal) { 300 var gladesig = current_editor as IAnjuta.EditorGladeSignal; 301 gladesig.drop_possible.disconnect (on_drop_possible); 302 gladesig.drop.disconnect (on_drop); 303 } 304 current_editor.glade_member_add.disconnect (insert_member_decl_and_init); 305 current_editor = null; 306 } 307 308 public void on_file_saved (IAnjuta.FileSavable savable, File file) { 309 foreach (var source_file in context.get_source_files ()) { 310 if (source_file.filename != file.get_path()) 311 continue; 312 313 uint8[] contents; 314 try { 315 file.load_contents (null, out contents, null); 316 source_file.content = (string) contents; 317 update_file (source_file); 318 } catch (Error e) { 319 // ignore 320 } 321 return; 322 } 323 } 324 325 public void on_char_added (IAnjuta.Editor editor, IAnjuta.Iterable position, char ch) { 326 if (!settings.get_boolean (ValaProvider.PREF_CALLTIP_ENABLE)) 327 return; 328 329 var editortip = editor as IAnjuta.EditorTip; 330 if (ch == '(') { 331 provider.show_call_tip (editortip); 332 } else if (ch == ')') { 333 editortip.cancel (); 334 } 335 } 336 337 /* tries to find the opening brace of the scope the current position before calling 338 * get_current_context since the source_reference of a class or namespace only 339 * contain the declaration not the entire "content" */ 340 Vala.Symbol? get_scope (IAnjuta.Editor editor, IAnjuta.Iterable position) { 341 var depth = 0; 342 do { 343 var current_char = (position as IAnjuta.EditorCell).get_character (); 344 if (current_char == "}") { 345 depth++; 346 } else if (current_char == "{") { 347 if (depth > 0) { 348 depth--; 349 } else { 350 // a scope which contains the current position 351 do { 352 position.previous (); 353 current_char = (position as IAnjuta.EditorCell).get_character (); 354 } while (! current_char.get_char ().isalnum ()); 355 return get_current_context (editor, position); 356 } 357 } 358 } while (position.previous ()); 359 return null; 360 } 361 362 public bool on_drop_possible (IAnjuta.EditorGladeSignal editor, IAnjuta.Iterable position) { 363 var line = editor.get_line_from_position (position); 364 var column = editor.get_line_begin_position (line).diff (position); 365 debug ("line %d, column %d", line, column); 366 367 var scope = get_scope (editor, position.clone ()); 368 if (scope != null) 369 debug ("drag is inside %s", scope.get_full_name ()); 370 if (scope == null || scope is Vala.Namespace || scope is Vala.Class) 371 return true; 372 373 return false; 374 } 375 376 public void on_drop (IAnjuta.EditorGladeSignal editor, IAnjuta.Iterable position, string signal_data) { 377 var data = signal_data.split (":"); 378 var widget_name = data[0]; 379 var signal_name = data[1].replace ("-", "_"); 380 var handler_name = data[2]; 381 var swapped = (data[4] == "1"); 382 var scope = get_scope (editor, position.clone ()); 383 var builder = new StringBuilder (); 384 385#if VALA_0_38 386 var handler_cname = ""; 387#else 388 var scope_prefix = ""; 389 if (scope != null) { 390 scope_prefix = Vala.CCodeBaseModule.get_ccode_lower_case_prefix (scope); 391 if (handler_name.has_prefix (scope_prefix)) 392 handler_name = handler_name.substring (scope_prefix.length); 393 } 394 var handler_cname = scope_prefix + handler_name; 395#endif 396 397 if (data[2] != handler_cname && !swapped) { 398 builder.append_printf ("[CCode (cname=\"%s\", instance_pos=-1)]\n", data[2]); 399 } else if (data[2] != handler_cname) { 400 builder.append_printf ("[CCode (cname=\"%s\")]\n", data[2]); 401 } else if (!swapped) { 402 builder.append ("[CCode (instance_pos=-1)]\n"); 403 } 404 405 var widget = lookup_symbol_by_cname (widget_name); 406 var sigs = symbol_lookup_inherited (widget, signal_name, false); 407 if (sigs == null || !(sigs.data is Vala.Signal)) 408 return; 409 Vala.Signal sig = (Vala.Signal) sigs.data; 410 411 builder.append_printf ("public void %s (", handler_name); 412 413 if (swapped) { 414 builder.append_printf ("%s sender", widget.get_full_name ()); 415 416 foreach (var param in sig.get_parameters ()) { 417 builder.append_printf (", %s %s", param.variable_type.data_type.get_full_name (), param.name); 418 } 419 } else { 420 foreach (var param in sig.get_parameters ()) { 421 builder.append_printf ("%s %s, ", param.variable_type.data_type.get_full_name (), param.name); 422 } 423 424 builder.append_printf ("%s sender", widget.get_full_name ()); 425 } 426 427 builder.append_printf (") {\n\n}\n"); 428 429 editor.insert (position, builder.str, -1); 430 431 var indenter = shell.get_object ("IAnjutaIndenter") as IAnjuta.Indenter; 432 if (indenter != null) { 433 var end = position.clone (); 434 /* -1 so we don't count the last newline (as that would indent the line after) */ 435 end.set_position (end.get_position () + builder.str.char_count () - 1); 436 indenter.indent (position, end); 437 } 438 439 var inside = editor.get_line_end_position (editor.get_line_from_position (position) + 2); 440 editor.goto_position (inside); 441 if (indenter != null) 442 indenter.indent (inside, inside); 443 } 444 445 const string DECL_MARK = "/* ANJUTA: Widgets declaration for %s - DO NOT REMOVE */\n"; 446 const string INIT_MARK = "/* ANJUTA: Widgets initialization for %s - DO NOT REMOVE */\n"; 447 448 void insert_member_decl_and_init (IAnjuta.Editor editor, string widget_ctype, string widget_name, string filename) { 449 var widget_type = lookup_symbol_by_cname (widget_ctype).get_full_name (); 450 var basename = Path.get_basename (filename); 451 452 string member_decl = "%s %s;\n".printf (widget_type, widget_name); 453 string member_init = "%s = builder.get_object(\"%s\") as %s;\n".printf (widget_name, widget_name, widget_type); 454 455 insert_after_mark (editor, DECL_MARK.printf (basename), member_decl) 456 && insert_after_mark (editor, INIT_MARK.printf (basename), member_init); 457 } 458 459 bool insert_after_mark (IAnjuta.Editor editor, string mark, string code_to_add) { 460 var search_start = editor.get_start_position () as IAnjuta.EditorCell; 461 var search_end = editor.get_end_position () as IAnjuta.EditorCell; 462 463 IAnjuta.EditorCell result_end; 464 (editor as IAnjuta.EditorSearch).forward (mark, false, search_start, search_end, null, out result_end); 465 466 var mark_position = result_end as IAnjuta.Iterable; 467 if (mark_position == null) 468 return false; 469 470 editor.insert (mark_position, code_to_add, -1); 471 472 var indenter = shell.get_object ("IAnjutaIndenter") as IAnjuta.Indenter; 473 if (indenter != null) { 474 var end = mark_position.clone (); 475 /* -1 so we don't count the last newline (as that would indent the line after) */ 476 end.set_position (end.get_position () + code_to_add.char_count () - 1); 477 indenter.indent (mark_position, end); 478 } 479 480 /* Emit code-added signal, so symbols will be updated */ 481 editor.code_added (mark_position, code_to_add); 482 483 return true; 484 } 485 486 Vala.Symbol? lookup_symbol_by_cname (string cname, Vala.Symbol parent=context.root) { 487 var sym = parent.scope.lookup (cname); 488 if (sym != null) 489 return sym; 490 491 var symtab = parent.scope.get_symbol_table (); 492 foreach (var name in symtab.get_keys ()) { 493 if (cname.has_prefix (name)) { 494 return lookup_symbol_by_cname (cname.substring (name.length), parent.scope.lookup (name)); 495 } 496 } 497 return null; 498 } 499 500 internal Vala.Symbol get_current_context (IAnjuta.Editor editor, IAnjuta.Iterable? position=null) requires (editor is IAnjuta.File) { 501 var file = editor as IAnjuta.File; 502 503 var path = file.get_file().get_path(); 504 lock (context) { 505 Vala.SourceFile source = null; 506 foreach (var src in context.get_source_files()) { 507 if (src.filename == path) { 508 source = src; 509 break; 510 } 511 } 512 if (source == null) { 513 source = new Vala.SourceFile (context, 514 path.has_suffix("vapi") ? Vala.SourceFileType.PACKAGE: 515 Vala.SourceFileType.SOURCE, 516 path); 517 context.add_source_file(source); 518 update_file(source); 519 } 520 int line; int column; 521 if (position == null) { 522 line = editor.get_lineno (); 523 column = editor.get_column (); 524 } else { 525 line = editor.get_line_from_position (position); 526 column = editor.get_line_begin_position (line).diff (position); 527 } 528 return locator.locate(source, line, column); 529 } 530 } 531 532 internal List<Vala.Symbol> lookup_symbol (Vala.Expression? inner, string name, bool prefix_match, 533 Vala.Block? block) { 534 var matching_symbols = new List<Vala.Symbol> (); 535 536 if (block == null) return matching_symbols; 537 538 lock (context) { 539 if (inner == null) { 540 for (var sym = (Vala.Symbol) block; sym != null; sym = sym.parent_symbol) { 541 matching_symbols.concat (symbol_lookup_inherited (sym, name, prefix_match)); 542 } 543 544 foreach (var ns in block.source_reference.file.current_using_directives) { 545 matching_symbols.concat (symbol_lookup_inherited (ns.namespace_symbol, name, prefix_match)); 546 } 547 } else if (inner.symbol_reference != null) { 548 matching_symbols.concat (symbol_lookup_inherited (inner.symbol_reference, name, prefix_match)); 549 } else if (inner is Vala.MemberAccess) { 550 var inner_ma = (Vala.MemberAccess) inner; 551 var matching = lookup_symbol (inner_ma.inner, inner_ma.member_name, false, block); 552 if (matching != null) 553 matching_symbols.concat (symbol_lookup_inherited (matching.data, name, prefix_match)); 554 } else if (inner is Vala.MethodCall) { 555 var inner_inv = (Vala.MethodCall) inner; 556 var inner_ma = inner_inv.call as Vala.MemberAccess; 557 if (inner_ma != null) { 558 var matching = lookup_symbol (inner_ma.inner, inner_ma.member_name, false, block); 559 if (matching != null) 560 matching_symbols.concat (symbol_lookup_inherited (matching.data, name, prefix_match, true)); 561 } 562 } 563 } 564 return matching_symbols; 565 } 566 List<Vala.Symbol> symbol_lookup_inherited (Vala.Symbol? sym, string name, bool prefix_match, bool invocation = false) { 567 List<Vala.Symbol> result = null; 568 569 // This may happen if we cannot find all the needed packages 570 if (sym == null) 571 return result; 572 573 var symbol_table = sym.scope.get_symbol_table (); 574 if (symbol_table != null) { 575 foreach (string key in symbol_table.get_keys()) { 576 if (((prefix_match && key.has_prefix (name)) || key == name)) { 577 result.append (symbol_table[key]); 578 } 579 } 580 } 581 if (invocation && sym is Vala.Method) { 582 var func = (Vala.Method) sym; 583 result.concat (symbol_lookup_inherited (func.return_type.data_type, name, prefix_match)); 584 } else if (sym is Vala.Class) { 585 var cl = (Vala.Class) sym; 586 foreach (var base_type in cl.get_base_types ()) { 587 result.concat (symbol_lookup_inherited (base_type.data_type, name, prefix_match)); 588 } 589 } else if (sym is Vala.Struct) { 590 var st = (Vala.Struct) sym; 591 result.concat (symbol_lookup_inherited (st.base_type.data_type, name, prefix_match)); 592 } else if (sym is Vala.Interface) { 593 var iface = (Vala.Interface) sym; 594 foreach (var prerequisite in iface.get_prerequisites ()) { 595 result.concat (symbol_lookup_inherited (prerequisite.data_type, name, prefix_match)); 596 } 597 } else if (sym is Vala.LocalVariable) { 598 var variable = (Vala.LocalVariable) sym; 599 result.concat (symbol_lookup_inherited (variable.variable_type.data_type, name, prefix_match)); 600 } else if (sym is Vala.Field) { 601 var field = (Vala.Field) sym; 602 result.concat (symbol_lookup_inherited (field.variable_type.data_type, name, prefix_match)); 603 } else if (sym is Vala.Property) { 604 var prop = (Vala.Property) sym; 605 result.concat (symbol_lookup_inherited (prop.property_type.data_type, name, prefix_match)); 606 } else if (sym is Vala.Parameter) { 607 var fp = (Vala.Parameter) sym; 608 result.concat (symbol_lookup_inherited (fp.variable_type.data_type, name, prefix_match)); 609 } 610 611 return result; 612 } 613 void update_file (Vala.SourceFile file) { 614 lock (context) { 615 /* Removing nodes in the same loop causes problems (probably due to ReadOnlyList)*/ 616 var nodes = new Vala.ArrayList<Vala.CodeNode> (); 617 foreach (var node in file.get_nodes()) { 618 nodes.add(node); 619 } 620 foreach (var node in nodes) { 621 file.remove_node (node); 622 if (node is Vala.Symbol) { 623 var sym = (Vala.Symbol) node; 624 if (sym.owner != null) 625 /* we need to remove it from the scope*/ 626 sym.owner.remove(sym.name); 627 if (context.entry_point == sym) 628 context.entry_point = null; 629 } 630 } 631 file.current_using_directives = new Vala.ArrayList<Vala.UsingDirective>(); 632 var ns_ref = new Vala.UsingDirective (new Vala.UnresolvedSymbol (null, "GLib")); 633 file.add_using_directive (ns_ref); 634 context.root.add_using_directive (ns_ref); 635 636 report.clear_error_indicators (file); 637 638 parse (); 639 640 report.update_errors(current_editor); 641 } 642 } 643 644 private void on_autocompletion_toggled (ToggleButton button) { 645 var sensitive = button.get_active(); 646 Gtk.Widget widget = bxml.get_object (PREF_WIDGET_SPACE) as Widget; 647 widget.set_sensitive (sensitive); 648 widget = bxml.get_object (PREF_WIDGET_BRACE) as Widget; 649 widget.set_sensitive (sensitive); 650 } 651 652 public void merge (Anjuta.Preferences prefs) throws GLib.Error { 653 bxml = new Builder(); 654 655 /* Add preferences */ 656 try { 657 bxml.add_from_file (PREFS_BUILDER); 658 } catch (Error err) { 659 warning ("Couldn't load builder file: %s", err.message); 660 } 661 prefs.add_from_builder (bxml, settings, "preferences", _("Auto-complete"), 662 ICON_FILE); 663 var toggle = bxml.get_object (PREF_WIDGET_AUTO) as ToggleButton; 664 toggle.toggled.connect (on_autocompletion_toggled); 665 on_autocompletion_toggled (toggle); 666 } 667 668 public void unmerge (Anjuta.Preferences prefs) throws GLib.Error { 669 prefs.remove_page (_("Auto-complete")); 670 } 671} 672 673[ModuleInit] 674public Type anjuta_glue_register_components (TypeModule module) { 675 return typeof (ValaPlugin); 676} 677