2 * Copyright (C) 2008-2010 Abderrahim Kitouni
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.
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.
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/>.
18 public class ValaProvider
: Object
, IAnjuta
.Provider
{
19 IAnjuta
.Iterable start_pos
;
20 weak ValaPlugin plugin
;
22 static Regex member_access
;
23 static Regex member_access_split
;
24 static Regex function_call
;
26 const string PREF_AUTOCOMPLETE_ENABLE
= "completion-enable";
27 const string PREF_SPACE_AFTER_FUNC
= "completion-space-after-func";
28 const string PREF_BRACE_AFTER_FUNC
= "completion-brace-after-func";
29 internal const string PREF_CALLTIP_ENABLE
= "calltip-enable";
33 member_access
= new
Regex("""((?:\w+(?:\s*\([^()]*\))?\.)*)(\w*)$""");
34 member_access_split
= new
Regex ("""(\s*\([^()]*\))?\.""");
35 function_call
= new
Regex("""(new )?((?:\w+(?:\s*\([^()]*\))?\.)*)(\w+)\s*\(([^(,)]+,)*([^(,)]*)$""");
36 } catch(RegexError err
) {
37 critical("Regular expressions failed to compile : %s", err
.message
);
41 public ValaProvider(ValaPlugin plugin
) {
44 public unowned
string get_name () throws GLib
.Error
{
47 public void populate (IAnjuta
.Iterable iter
) throws GLib
.Error
{
48 if (!plugin
.settings
.get_boolean (PREF_AUTOCOMPLETE_ENABLE
))
51 var editor
= plugin
.current_editor as IAnjuta
.EditorAssist
;
52 var line_start
= editor
.get_line_begin_position(editor
.get_lineno());
53 var current_text
= editor
.get_text(line_start
, iter
);
56 if (! member_access
.match(current_text
, 0, out match_info
))
58 if (match_info
.fetch(0).length
< 2)
61 start_pos
= iter
.clone();
62 start_pos
.set_position(iter
.get_position() - (int) match_info
.fetch(2).length
);
64 var names
= member_access_split
.split (match_info
.fetch(1));
66 var syms
= plugin
.lookup_symbol (construct_member_access (names
), match_info
.fetch(2),
67 true, plugin
.get_current_context (editor
) as Vala
.Block
);
69 var proposals
= new GLib
.List
<IAnjuta
.EditorAssistProposal?
>();
70 foreach (var symbol
in syms
) {
71 if (symbol is Vala
.LocalVariable
72 && symbol
.source_reference
.begin
.line
> editor
.get_lineno())
75 var prop
= IAnjuta
.EditorAssistProposal();
77 prop
.label
= symbol
.name
;
78 proposals
.prepend(prop
);
81 editor
.proposals(this
, proposals
, null, true);
83 public unowned IAnjuta
.Iterable
get_start_iter () throws GLib
.Error
{
86 public void activate (IAnjuta
.Iterable iter
, void* data
) {
87 var sym
= data as Vala
.Symbol
;
88 var editor
= plugin
.current_editor as IAnjuta
.EditorAssist
;
89 var assist
= sym
.name
;
93 if (sym is Vala
.Method
|| sym is Vala
.Signal
) {
95 } else if (sym is Vala
.Variable
) {
96 if (((Vala
.Variable
) sym
).variable_type is Vala
.DelegateType
) {
102 if (plugin
.settings
.get_boolean (PREF_SPACE_AFTER_FUNC
)) {
105 if (plugin
.settings
.get_boolean (PREF_BRACE_AFTER_FUNC
)) {
107 if (plugin
.settings
.get_boolean (PREF_CALLTIP_ENABLE
)) {
113 (editor as IAnjuta
.Document
).begin_undo_action();
114 editor
.erase(start_pos
, iter
);
115 editor
.insert(start_pos
, assist
, -1);
116 (editor as IAnjuta
.Document
).end_undo_action();
118 if (calltip
&& editor is IAnjuta
.EditorTip
) {
119 show_call_tip ((IAnjuta
.EditorTip
) editor
);
123 public void show_call_tip (IAnjuta
.EditorTip editor
) {
124 var current_position
= editor
.get_position ();
125 var line_start
= editor
.get_line_begin_position(editor
.get_lineno());
126 var to_complete
= editor
.get_text(line_start
, current_position
);
128 List
<string> tips
= null;
130 MatchInfo match_info
;
131 if (! function_call
.match(to_complete
, 0, out match_info
))
134 var creation_method
= (match_info
.fetch(1) != "");
135 var names
= member_access_split
.split (match_info
.fetch(2));
136 var syms
= plugin
.lookup_symbol (construct_member_access (names
), match_info
.fetch(3),
137 false, plugin
.get_current_context (editor
) as Vala
.Block
);
138 foreach (var sym
in syms
) {
139 var calltip
= new
StringBuilder ();
140 Vala
.List
<Vala
.Parameter
> parameters
= null;
141 if (sym is Vala
.Method
) {
142 parameters
= ((Vala
.Method
) sym
).get_parameters ();
143 calltip
.append (((Vala
.Method
) sym
).return_type
.to_qualified_string() + " ");
144 } else if (sym is Vala
.Signal
) {
145 parameters
= ((Vala
.Signal
) sym
).get_parameters ();
146 } else if (creation_method
&& sym is Vala
.Class
) {
147 parameters
= ((Vala
.Class
) sym
).default_construction_method
.get_parameters ();
148 } else if (sym is Vala
.Variable
) {
149 var var_type
= ((Vala
.Variable
) sym
).variable_type
;
150 if (var_type is Vala
.DelegateType
) {
151 var delegate_sym
= ((Vala
.DelegateType
) var_type
).delegate_symbol
;
152 parameters
= delegate_sym
.get_parameters ();
153 calltip
.append (delegate_sym
.return_type
.to_qualified_string() + " ");
161 calltip
.append (sym
.get_full_name ());
162 calltip
.append(" (");
163 var prestring
= "".nfill (calltip
.len
, ' ');
165 foreach (var p
in parameters
) {
169 calltip
.append (",\n");
170 calltip
.append (prestring
);
173 calltip
.append ("...");
175 calltip
.append (p
.variable_type
.to_qualified_string());
176 calltip
.append (" ").append (p
.name
);
179 calltip
.append (")");
180 tips
.append (calltip
.str
);
182 editor
.show (tips
, editor
.get_position ());
184 Vala
.Expression
construct_member_access (string[] names
) {
185 Vala
.Expression expr
= null;
187 for (var i
= 0; names
[i
] != null; i
++) {
188 if (names
[i
] != "") {
189 expr
= new Vala
.MemberAccess (expr
, names
[i
]);
190 if (names
[i
+1] != null && names
[i
+1].chug ().has_prefix ("(")) {
191 expr
= new Vala
.MethodCall (expr
);