1
/* afrodite-provider.vala
3
* Copyright (C) 2010 Nicolas Joseph
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 3 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, see <http://www.gnu.org/licenses/>.
19
* Nicolas Joseph <nicolas.joseph@valaide.org>
24
internal class AfroditeProvider : Fix.SourceCompletionProvider, Object
26
public signal void completion_lock_failed ();
28
private Gdk.Pixbuf icon;
29
private int priority = 1;
30
private List<Gtk.SourceCompletionItem> proposals;
32
private Afrodite.SourceItem sb = null;
34
private uint timeout_id = 0;
35
private uint idle_id = 0;
36
private bool all_doc = false; //this is a hack!!!
38
private int prealloc_index = 0;
40
private bool cache_building = false;
41
private bool filter = false;
42
private uint sb_msg_id = 0;
43
//private uint sb_context_id = 0;
45
private Gtk.SourceCompletionInfo calltip_window = null;
46
private Gtk.Label calltip_window_label = null;
48
private int last_line = -1;
49
private bool doc_changed = false;
51
private Afrodite.CompletionEngine completion = null;
53
public unowned Document document
59
public AfroditeProvider (Document document)
61
Object (document: document);
66
this.icon = this.get_icon ();
68
string name = Vtg.Utils.get_document_name (this.document.buffer);
70
this.sb = new Afrodite.SourceItem ();
72
this.sb.content = this.document.buffer.get_buffer_contents ();
74
this.document.view.key_press_event.connect (this.on_view_key_press);
75
this.document.view.focus_out_event.connect (this.on_view_focus_out);
76
this.document.view.get_completion ().show.connect (this.on_completion_window_hide);
78
this.document.buffer.notify["text"] += this.on_text_changed;
79
this.document.buffer.notify["cursor-position"] += this.on_cursor_position_changed;
80
Signal.connect (this.document, "saved", (Callback)on_document_saved, this);
82
//var status_bar = (Gedit.Statusbar) _symbol_completion.plugin_instance.window.get_statusbar ();
83
//sb_context_id = status_bar.get_context_id ("symbol status");
85
this.cache_building = true;
87
//_symbol_completion.notify["completion-engine"].connect (this.on_completion_engine_changed);
88
this.completion = new Afrodite.CompletionEngine ("Afrodite");
91
~SymbolCompletionProvider ()
93
if (this.timeout_id != 0)
95
GLib.Source.remove (this.timeout_id);
97
if (this.idle_id != 0)
99
GLib.Source.remove (this.idle_id);
102
this.document.view.key_press_event.disconnect (this.on_view_key_press);
103
this.document.view.focus_out_event.disconnect (this.on_view_focus_out);
105
SourceBuffer doc = this.document.buffer;
106
//_symbol_completion.notify["completion-engine"].disconnect (this.on_completion_engine_changed);
107
doc.notify["text"] -= this.on_text_changed;
108
doc.notify["cursor-position"] -= this.on_cursor_position_changed;
109
SignalHandler.disconnect_by_func (doc, (void*)this.on_document_saved, this);
111
if (this.sb_msg_id != 0)
113
var status_bar = (Gedit.Statusbar) _symbol_completion.plugin_instance.window.get_statusbar ();
114
status_bar.remove (_sb_context_id, _sb_msg_id);
119
public string get_name ()
121
return _("Afrodite");
124
public int get_priority ()
126
return this.priority;
129
public bool match (Gtk.SourceCompletionContext context)
131
SourceBuffer src = this.document.buffer;
132
unowned Gtk.TextMark mark = src.get_insert ();
135
src.get_iter_at_mark (out start, mark);
136
Gtk.TextIter pos = start;
137
bool result = !Vtg.Utils.is_inside_comment_or_literal (src, pos);
142
int line = pos.get_line ();
143
unichar ch = pos.get_char ();
144
if (pos.backward_char ())
146
if (pos.get_line () == line)
148
unichar prev_ch = pos.get_char ();
149
if (prev_ch == '(' || ch == '('
150
|| prev_ch == '[' || ch == '['
156
|| prev_ch == '/' || ch == '/'
160
Vtg.Utils.trace ("not match current char: '%s', previous: '%s'", ch.to_string (), prev_ch.to_string ());
164
Vtg.Utils.trace ("match current char: '%s', previous: '%s'", ch.to_string (), prev_ch.to_string ());
173
private void on_completion_window_hide (Gtk.SourceCompletion sender)
178
public void populate (Gtk.SourceCompletionContext context)
180
Vtg.Utils.trace ("populate");
181
unowned Gtk.TextMark mark = (Gtk.TextMark) context.completion.view.get_buffer ().get_insert ();
184
context.completion.view.get_buffer ().get_iter_at_mark (out start, mark);
185
context.completion.view.get_buffer ().get_iter_at_mark (out end, mark);
187
if (!start.starts_line ())
189
start.set_line_offset (0);
192
string text = start.get_text (end);
193
unichar prev_ch = 'a';
194
if (end.backward_char ())
196
prev_ch = end.get_char ();
200
bool symbols_in_scope_mode = false;
204
if (text.has_suffix (".") || (prev_ch != '_' && !prev_ch.isalnum()))
210
bool dummy, is_declaration;
211
Vtg.ParserUtils.parse_line (text, out word, out dummy, out dummy, out is_declaration);
213
if (!is_declaration && word.rstr(".") == null)
215
symbols_in_scope_mode = true;
222
this.proposals = new List<Gtk.SourceCompletionItem> ();
223
if (symbols_in_scope_mode)
225
this.lookup_visible_symbols_in_scope (word, Afrodite.CompareMode.START_WITH);
229
this.complete_current_word ();
232
context.add_proposals (this, this.proposals, true);
236
string[] tmp = word.split (".");
237
string last_part = "";
241
last_part = tmp[tmp.length-1];
244
Vtg.Utils.trace ("filtering with: '%s' - '%s'", word, last_part);
245
if (!Vtg.StringUtils.is_null_or_empty (last_part))
247
List<Gtk.SourceCompletionItem> filtered_proposals = new List<Gtk.SourceCompletionItem> ();
248
foreach (Gtk.SourceCompletionItem proposal in this.proposals)
250
if (proposal.get_label ().has_prefix (last_part))
252
filtered_proposals.append (proposal);
256
if (this.proposals.length () > 0 && filtered_proposals.length () == 0) {
257
// no matching add a dummy one to prevent proposal windows from closing
258
Gtk.SourceCompletionItem dummy_proposal = new Gtk.SourceCompletionItem (_("No matching proposal"), "", null, null);
259
filtered_proposals.append (dummy_proposal);
261
context.add_proposals (this, filtered_proposals, true);
265
// match all optimization
266
context.add_proposals (this, this.proposals, true);
271
public Gdk.Pixbuf get_icon ()
273
if (this.icon == null)
277
Gtk.IconTheme theme = Gtk.IconTheme.get_default ();
278
this.icon = theme.load_icon (Gtk.STOCK_DIALOG_INFO, 16, 0);
282
critical ("error: %s", err.message);
288
public bool activate_proposal (Gtk.SourceCompletionProposal proposal, Gtk.TextIter iter)
294
public Gtk.SourceCompletionActivation get_activation ()
296
return Gtk.SourceCompletionActivation.INTERACTIVE |
297
Gtk.SourceCompletionActivation.USER_REQUESTED;
300
public Gtk.Widget get_info_widget (Gtk.SourceCompletionProposal proposal)
305
public int get_interactive_delay ()
310
public bool get_start_iter (Gtk.SourceCompletionContext context, Gtk.SourceCompletionProposal proposal, Gtk.TextIter iter)
315
public void update_info (Gtk.SourceCompletionProposal proposal, Gtk.SourceCompletionInfo info)
319
private bool on_view_focus_out (Gtk.Widget sender, Gdk.EventFocus event)
321
this.hide_calltip ();
325
[CCode(instance_pos=-1)]
326
private void on_document_saved (Document doc)
328
this.doc_changed = true;
330
this.schedule_reparse ();
333
private void on_completion_engine_changed (Object sender, ParamSpec pspec)
335
this.completion = this.document.completion_engine;
338
private int get_current_line_index (SourceBuffer? doc = null)
342
doc = this.document.view.buffer;
346
unowned Gtk.TextMark mark = doc.get_insert ();
348
doc.get_iter_at_mark (out start, mark);
349
return start.get_line ();
352
private void schedule_reparse ()
354
if (this.timeout_id == 0 && this.doc_changed)
356
this.timeout_id = Timeout.add (250, this.on_timeout_parse);
360
private void on_text_changed (Object sender, ParamSpec pspec)
362
this.doc_changed = true;
363
// parse text only on init or line changes
364
if (this.last_line == -1 || this.last_line != this.get_current_line_index ())
367
this.schedule_reparse ();
371
private void on_cursor_position_changed (Object sender, ParamSpec pspec)
373
// parse text only on init or line changes
374
if (this.last_line == -1 || this.last_line != this.get_current_line_index ())
377
this.schedule_reparse ();
381
private bool on_timeout_parse ()
383
SourceBuffer doc = this.document.buffer;
384
this.parse (this.document);
386
this.last_line = this.get_current_line_index (doc);
390
private bool on_view_key_press (Gtk.Widget sender, Gdk.EventKey evt)
392
unichar ch = Gdk.keyval_to_unicode (evt.keyval);
396
this.show_calltip ();
398
else if (evt.keyval == Gdk.KeySyms.Escape || ch == ')' || ch == ';' || ch == '{' ||
399
(evt.keyval == Gdk.KeySyms.Return && (evt.state & Gdk.ModifierType.SHIFT_MASK) != 0))
401
this.hide_calltip ();
403
if (evt.keyval == Gdk.KeySyms.Return || ch == ';')
405
this.all_doc = true; // new line or eol, reparse all source buffer
407
else if (ch.isprint ()
408
|| evt.keyval == Gdk.KeySyms.Delete
409
|| evt.keyval == Gdk.KeySyms.BackSpace)
411
this.all_doc = false; // a change so reparse the buffer minus the current line
412
this.doc_changed = true;
417
private void show_calltip ()
419
Afrodite.Symbol? completion_result = this.get_current_symbol_item ();
420
if (completion_result != null)
422
this.show_calltip_info (completion_result.info);
426
private void show_calltip_info (string markup_text)
428
if (this.calltip_window == null)
430
this.initialize_calltip_window ();
433
if (markup_text != null)
435
this.calltip_window_label.set_markup (markup_text);
436
this.calltip_window.move_to_iter (this.document.view);
437
this.calltip_window.show_all ();
438
this.calltip_window.show ();
442
private void hide_calltip ()
444
if (this.calltip_window == null)
449
this.calltip_window.hide ();
452
private void initialize_calltip_window ()
454
this.calltip_window = new Gtk.SourceCompletionInfo ();
455
//this.calltip_window.set_transient_for (_symbol_completion.plugin_instance.window);
456
this.calltip_window.set_sizing (800, 400, true, true);
457
this.calltip_window_label = new Gtk.Label ("");
458
this.calltip_window.set_widget (this.calltip_window_label);
461
private void parse (Document doc)
463
// automatically add package if this buffer
464
// belong to the default project
466
var current_project = _symbol_completion.plugin_instance.project_view.current_project;
467
if (current_project.is_default) {
468
if (this.autoadd_packages (doc, current_project) > 0)
470
current_project.project.update ();
475
var buffer = this.get_document_text (doc.buffer, this.all_doc);
476
this.sb.content = buffer;
477
this.completion.queue_source (this.sb);
478
this.doc_changed = false;
481
private int autoadd_packages (Gedit.Document doc, Vtg.ProjectManager project_manager)
487
var text = this.get_document_text (doc, true);
488
GLib.Regex regex = new GLib.Regex ("""^\s*(using)\s+(\w\S*)\s*;.*$""");
490
foreach (string line in text.split ("\n")) {
491
GLib.MatchInfo match;
492
regex.match (line, RegexMatchFlags.NEWLINE_ANY, out match);
493
while (match.matches ()) {
494
string using_name = null;
496
if (match.fetch (2) == "GLib") {
497
// standard GLib are already merged by the completion engine
498
// I'll add gio for the default project
499
if (project_manager.is_default) {
503
using_name = match.fetch (2);
505
string package_name = null;
507
if (using_name != null)
508
package_name = Vbf.Vtg.Utils.guess_package_name (using_name);
510
Vtg.Utils.trace ("guessing name of using clause %s for package %s: %s", match.fetch (2), using_name, package_name);
511
if (package_name != null) {
512
var group = project_manager.project.get_group("Sources");
513
var target = group.get_target_for_id ("Default");
514
if (!target.contains_package (package_name))
516
target.add_package (new Vbf.Package (package_name));
523
} catch (Error err) {
524
critical ("error: %s", err.message);
530
private bool proposal_list_contains_name (string name)
532
foreach (Gtk.SourceCompletionItem proposal in this.proposals)
534
if (proposal.get_label () == name)
543
private void append_symbols (Afrodite.QueryOptions? options, Vala.List<Afrodite.Symbol> symbols, bool include_private_symbols = true)
545
unowned Gtk.SourceCompletionItem[] proposals = Vtg.Utils.get_proposal_cache ();
547
foreach (Afrodite.Symbol symbol in symbols)
549
if ((!include_private_symbols && symbol.access == Afrodite.SymbolAccessibility.PRIVATE)
550
|| symbol.name == "new"
551
|| (options != null && !symbol.check_options (options)))
553
//Vtg.Utils.trace ("not append symbols: %s", symbol.name);
559
if (symbol.type_name == "CreationMethod")
565
name = (symbol.display_name != null ? symbol.display_name : "<null>");
568
if (!symbol.overrides || (symbol.overrides && !this.proposal_list_contains_name (name)))
570
Gtk.SourceCompletionItem proposal;
571
string info = (symbol.info != null ? symbol.info : "");
572
Gdk.Pixbuf icon = Vtg.Utils.get_icon_for_type_name (symbol.type_name);
574
if (this.prealloc_index < Vtg.Utils.prealloc_count)
576
proposal = proposals [this.prealloc_index];
577
this.prealloc_index++;
579
proposal.label = name;
580
proposal.text = name;
581
proposal.info = info;
582
proposal.icon = icon;
586
proposal = new Gtk.SourceCompletionItem (name, name, icon, info);
588
//Vtg.Utils.trace ("append symbols: %s", symbol.name);
589
this.proposals.append (proposal);
593
this.proposals.sort (this.proposal_sort);
596
private static int proposal_sort (void* a, void* b)
598
Gtk.SourceCompletionItem pa = (Gtk.SourceCompletionItem) a;
599
Gtk.SourceCompletionItem pb = (Gtk.SourceCompletionItem) b;
601
return strcmp (pa.get_label (), pb.get_label ());
604
private void transform_result (Afrodite.QueryOptions? options, Afrodite.QueryResult? result)
606
this.prealloc_index = 0;
607
this.proposals = new List<Gtk.SourceCompletionItem> ();
608
Vala.ArrayList<Afrodite.Symbol> visited_interfaces = new Vala.ArrayList<Afrodite.Symbol> ();
610
if (result != null && !result.is_empty)
612
options.dump_settings ();
614
foreach (Afrodite.ResultItem item in result.children)
616
var symbol = item.symbol;
618
if (options == null || symbol.check_options (options))
620
if (symbol.has_children)
622
append_symbols (options, symbol.children);
625
append_base_type_symbols (options, symbol, visited_interfaces);
631
private void append_base_type_symbols (Afrodite.QueryOptions? options, Afrodite.Symbol symbol, Vala.List<Afrodite.Symbol> visited_interfaces)
633
if (symbol.has_base_types
634
&& (symbol.type_name == "Class" || symbol.type_name == "Interface" || symbol.type_name == "Struct"))
636
foreach (Afrodite.DataType type in symbol.base_types)
638
Vtg.Utils.trace ("visiting base type: %s", type.type_name);
640
&& type.symbol.has_children
641
&& (options == null || type.symbol.check_options (options))
642
&& (type.symbol.type_name == "Class" || type.symbol.type_name == "Interface" || type.symbol.type_name == "Struct"))
644
// symbols of base types (classes or interfaces)
645
if (!visited_interfaces.contains (type.symbol))
647
visited_interfaces.add (type.symbol);
648
append_symbols (options, type.symbol.children, false);
649
append_base_type_symbols (options, type.symbol, visited_interfaces);
656
Vtg.Utils.trace ("NO base type for %s-%s", symbol.name, symbol.type_name);
660
private void get_current_line_and_column (out int line, out int column)
662
unowned SourceBuffer doc = this.document.buffer;
663
unowned Gtk.TextMark mark = doc.get_insert ();
666
doc.get_iter_at_mark (out start, mark);
667
line = start.get_line ();
668
column = start.get_line_offset ();
671
private string get_current_line_text (bool align_to_right_word)
673
unowned SourceBuffer doc = this.document.buffer;
674
unowned Gtk.TextMark mark = doc.get_insert ();
679
doc.get_iter_at_mark (out start, mark);
680
int line = start.get_line ();
682
//go to the right word boundary
683
ch = start.get_char ();
684
while (ch.isalnum () || ch == '_')
686
start.forward_char ();
687
int curr_line = start.get_line ();
688
if (line != curr_line) //changed line?
690
start.backward_char ();
693
ch = start.get_char ();
697
start.set_line_offset (0);
698
return start.get_text (end);
701
public Afrodite.Symbol? get_current_symbol_item (int retry_count = 0)
703
string text = this.get_current_line_text (true);
706
bool is_assignment, is_creation, is_declaration;
708
Vtg.ParserUtils.parse_line (text, out word, out is_assignment, out is_creation, out is_declaration);
710
if (word == null || word == "")
715
this.get_current_line_and_column (out line, out col);
717
string[] tmp = word.split (".");
718
string last_part = tmp[tmp.length - 1];
719
string symbol_name = last_part;
721
//don't try to find method signature if is a: for, foreach, if, while etc...
722
if (is_vala_keyword (symbol_name))
728
strip last type part.
729
eg. for demos.demo.demo_method obtains
730
demos.demo + demo_method
733
if (word != last_part)
735
first_part = word.substring (0, word.length - last_part.length - 1);
739
first_part = word; // "this"; //HACK: this won't work for static methods
743
Afrodite.Symbol? symbol = null;
745
if (this.completion.try_acquire_ast (out ast, retry_count))
747
Afrodite.QueryResult? result = null;
748
Afrodite.QueryOptions options = this.get_options_for_line (text, is_assignment, is_creation);
750
if (word == symbol_name)
752
result = this.get_symbol_for_name (options, ast, first_part, null, line, col);
756
result = this.get_symbol_type_for_name (options, ast, first_part, null, line, col);
759
if (result != null && !result.is_empty)
761
var first = result.children.get (0);
762
if (word == symbol_name)
764
symbol = first.symbol;
768
symbol = this.get_symbol_for_name_in_children (symbol_name, first.symbol);
771
symbol =this.get_symbol_for_name_in_base_types (symbol_name, first.symbol);
775
this.completion.release_ast (ast);
780
private Afrodite.Symbol? get_symbol_for_name_in_children (string symbol_name, Afrodite.Symbol parent)
782
if (parent.has_children)
784
foreach (Afrodite.Symbol? symbol in parent.children)
786
if (symbol.name == symbol_name)
795
private Afrodite.Symbol? get_symbol_for_name_in_base_types (string symbol_name, Afrodite.Symbol parent)
797
if (parent.has_base_types)
799
foreach (Afrodite.DataType t in parent.base_types)
801
if (t.symbol != null)
803
var base_symbol = this.get_symbol_for_name_in_children (symbol_name, t.symbol);
804
if (base_symbol == null)
806
base_symbol = this.get_symbol_for_name_in_base_types (symbol_name, t.symbol);
809
if (base_symbol != null)
819
private Afrodite.QueryOptions get_options_for_line (string line, bool is_assignment, bool is_creation)
821
Afrodite.QueryOptions options = null;
825
options = Afrodite.QueryOptions.creation_methods ();
827
else if (is_assignment || (line != null && line.rstr (":") != null))
829
options = Afrodite.QueryOptions.standard ();
830
options.binding |= Afrodite.MemberBinding.STATIC;
832
else if (line != null && (line.str ("throws ") != null || line.str ("throw ") != null))
834
options = Afrodite.QueryOptions.error_domains ();
838
options = Afrodite.QueryOptions.standard ();
841
options.access = Afrodite.SymbolAccessibility.INTERNAL | Afrodite.SymbolAccessibility.PROTECTED | Afrodite.SymbolAccessibility.PUBLIC;
842
options.auto_member_binding_mode = true;
843
options.compare_mode = Afrodite.CompareMode.EXACT;
844
//options.dump_settings ();
848
private void complete_current_word ()
850
//string whole_line, word, last_part;
853
//parse_current_line (false, out word, out last_part, out whole_line, out line, out column);
854
string text = this.get_current_line_text (false);
857
bool is_assignment, is_creation, is_declaration;
859
Vtg.ParserUtils.parse_line (text, out word, out is_assignment, out is_creation, out is_declaration);
861
Afrodite.Ast ast = null;
862
Vtg.Utils.trace ("completing word: '%s'", word);
863
if (!Vtg.StringUtils.is_null_or_empty (word)
864
&& this.completion.try_acquire_ast (out ast))
866
Afrodite.QueryOptions options = this.get_options_for_line (text, is_assignment, is_creation);
867
Afrodite.QueryResult result = null;
870
this.get_current_line_and_column (out line, out col);
872
if (word.has_prefix ("\"") && word.has_suffix ("\""))
876
else if (word.has_prefix ("\'") && word.has_suffix ("\'"))
880
result = this.get_symbol_type_for_name (options, ast, word, text, line, col);
881
this.transform_result (options, result);
882
this.completion.release_ast (ast);
886
if (!Vtg.StringUtils.is_null_or_empty (word))
888
Vtg.Utils.trace ("build_proposal_item_list: couldn't acquire ast lock");
889
this.show_calltip_info (_("<i>source symbol cache is still updating...</i>"));
890
Timeout.add_seconds (2, this.on_hide_calltip_timeout);
891
this.completion_lock_failed ();
893
this.transform_result (null, null);
897
private void lookup_visible_symbols_in_scope (string word, Afrodite.CompareMode mode)
899
Afrodite.Ast ast = null;
900
Vtg.Utils.trace ("lookup_all_symbols_in_scope: mode: %s word:'%s' ",
901
mode == Afrodite.CompareMode.EXACT ? "exact" : "start-with",
903
if (!Vtg.StringUtils.is_null_or_empty (word)
904
&& this.completion.try_acquire_ast (out ast, 0))
906
Vala.List<Afrodite.Symbol> results = new Vala.ArrayList<Afrodite.Symbol> ();
908
weak SourceBuffer doc = this.document.buffer;
909
var source = ast.lookup_source_file (Vtg.Utils.get_document_name (doc));
912
// get the source node at this position
914
get_current_line_and_column (out line, out column);
916
var s = ast.get_symbol_for_source_and_position (source, line, column);
919
results = ast.lookup_visible_symbols_from_symbol (s, word, mode, Afrodite.CaseSensitiveness.CASE_SENSITIVE);
923
if (results.size == 0)
925
Vtg.Utils.trace ("no symbol visible");
926
this.transform_result (null, null);
930
this.proposals = new List<Gtk.SourceCompletionItem> ();
931
append_symbols (null, results);
933
this.completion.release_ast (ast);
937
if (!Vtg.StringUtils.is_null_or_empty (word))
939
Vtg.Utils.trace ("build_proposal_item_list: couldn't acquire ast lock");
940
this.completion_lock_failed ();
942
this.transform_result (null, null);
946
private bool on_hide_calltip_timeout ()
948
this.hide_calltip ();
952
private Afrodite.QueryResult? get_symbol_type_for_name (Afrodite.QueryOptions options, Afrodite.Ast ast, string word, string? whole_line, int line, int column)
954
Afrodite.QueryResult result = null;
955
result = ast.get_symbol_type_for_name_and_path (options, word, this.sb.path, line, column);
956
Vtg.Utils.trace ("symbol matched %d", result.children.size);
960
private Afrodite.QueryResult? get_symbol_for_name (Afrodite.QueryOptions options, Afrodite.Ast ast,string word, string? whole_line, int line, int column)
962
Afrodite.QueryResult result = null;
963
result = ast.get_symbol_for_name_and_path (options, word, this.sb.path, line, column);
968
private bool is_vala_keyword (string keyword)
970
return (keyword == "if"
972
|| keyword == "foreach"
973
|| keyword == "while"
974
|| keyword == "switch");
977
private string get_document_text (SourceBuffer doc, bool all_doc = false)
979
weak Gtk.TextMark mark = doc.get_insert ();
983
doc.get_iter_at_mark (out start, mark);
985
if (all_doc || doc.is_untouched ())
988
start.set_line_offset (0);
989
while (start.backward_line ())
993
while (end.forward_line ())
997
doc_text = start.get_text (end);
1002
end.set_line_offset (0);
1003
while (start.backward_line ())
1007
string text1 = start.get_text (end);
1009
//trick: jump the current edited row (there
1010
//are a lot of probability that this row will
1011
//cause a parser error)
1012
if (end.forward_line ())
1014
end.set_line_offset (0);
1016
while (end.forward_line ())
1020
text2 = start.get_text (end);
1022
doc_text = "%s\n%s".printf (text1, text2);