1 #include "ide.h"
2
3 #if 0
4 #define LDUMP(x) DDUMP(x)
5 #define LDUMPC(x) DDUMPC(x)
6 #define LLOG(x) DLOG(x)
7 #else
8 #define LDUMP(x)
9 #define LDUMPC(x)
10 #define LLOG(x)
11 #endif
12
GetIdScope(String & os,const String & scope,const String & id,Index<String> & done)13 bool GetIdScope(String& os, const String& scope, const String& id, Index<String>& done)
14 {
15 CodeBaseLock __;
16 if(done.Find(scope) >= 0)
17 return Null;
18 done.Add(scope);
19 Vector<String> tparam;
20 String n = ParseTemplatedType(scope, tparam);
21 String nn = n + "::" + id;
22 if(CodeBase().Find(nn) >= 0) { // Console -> LineEdit::EditPos
23 os = nn;
24 return true;
25 }
26 int q = CodeBase().Find(n);
27 if(q < 0)
28 return Null;
29 const Array<CppItem>& m = CodeBase()[q];
30 Vector<String> r;
31 if(FindName(m, id) >= 0) {
32 os = n;
33 return true;
34 }
35 for(int i = 0; i < m.GetCount(); i++) {
36 const CppItem& im = m[i];
37 if(im.IsType()) {
38 Vector<String> b = Split(im.qptype, ';');
39 ResolveTParam(b, tparam);
40 for(int i = 0; i < b.GetCount(); i++) {
41 if(GetIdScope(os, b[i], id, done))
42 return true;
43 }
44 }
45 }
46 return false;
47 }
48
GetIdScope(String & os,const String & scope,const String & id)49 bool GetIdScope(String& os, const String& scope, const String& id)
50 {
51 Index<String> done;
52 return GetIdScope(os, scope, id, done);
53 }
54
IsPif(const String & l)55 bool IsPif(const String& l)
56 {
57 return l.Find("#if") >= 0;
58 }
59
IsPelse(const String & l)60 bool IsPelse(const String& l)
61 {
62 return l.Find("#el") >= 0;
63 }
64
IsPendif(const String & l)65 bool IsPendif(const String& l)
66 {
67 return l.Find("#endif") >= 0;
68 }
69
FindId(const String & id)70 void Ide::FindId(const String& id)
71 {
72 int pos = editor.GetCursor();
73 int h = min(editor.GetLength(), pos + 4000);
74 for(;;) {
75 if(pos >= h || editor[pos] == ';')
76 break;
77 if(iscib(editor[pos])) {
78 int p0 = pos;
79 String tid;
80 while(pos < h && iscid(editor[pos])) {
81 tid.Cat(editor[pos]);
82 pos++;
83 }
84 if(tid == id) {
85 editor.SetCursor(p0);
86 return;
87 }
88 }
89 else
90 pos++;
91 }
92 }
93
RemoveTemplateParams(const String & s)94 String RemoveTemplateParams(const String& s)
95 {
96 Vector<String> dummy;
97 return ParseTemplatedType(s, dummy);
98 }
99
OpenLink(const String & s,int pos)100 bool Ide::OpenLink(const String& s, int pos)
101 { // try to find link at cursor, either http, https or file
102 auto IsLinkChar = [](int c) { return findarg(c, '\'', '\"', '\t', ' ', '\0') < 0; };
103 int b = pos;
104 while(b > 0 && IsLinkChar(s[b - 1]))
105 b--;
106 int e = pos;
107 while(IsLinkChar(s[e]))
108 e++;
109 String link = s.Mid(b, e - b);
110 if(link.StartsWith("http://") || link.StartsWith("https://"))
111 LaunchWebBrowser(link);
112 else
113 if(FileExists(link))
114 EditFile(link);
115 else
116 return false;
117 return true;
118 }
119
ContextGoto0(int pos)120 void Ide::ContextGoto0(int pos)
121 {
122 if(designer)
123 return;
124 int lp = pos;
125 int li = editor.GetLinePos(lp);
126 String l = editor.GetUtf8Line(li);
127 if(OpenLink(l, lp))
128 return;
129 if(IsPif(l) || IsPelse(l)) {
130 int lvl = 0;
131 while(li + 1 < editor.GetLineCount()) {
132 l = editor.GetUtf8Line(++li);
133 if(IsPif(l))
134 lvl++;
135 if(IsPelse(l) && lvl == 0)
136 break;
137 if(IsPendif(l)) {
138 if(lvl == 0) break;
139 lvl--;
140 }
141 }
142 AddHistory();
143 editor.SetCursor(editor.GetPos64(li));
144 return;
145 }
146 if(IsPendif(l)) {
147 int lvl = 0;
148 while(li - 1 >= 0) {
149 l = editor.GetUtf8Line(--li);
150 if(IsPif(l)) {
151 if(lvl == 0) break;
152 lvl--;
153 }
154 if(IsPendif(l))
155 lvl++;
156 }
157 AddHistory();
158 editor.SetCursor(editor.GetPos64(li));
159 return;
160 }
161 int cr = editor.Ch(pos);
162 int cl = editor.Ch(pos - 1);
163 if(!IsAlNum(cr)) {
164 if(islbrkt(cr)) {
165 AddHistory();
166 editor.MoveNextBrk(false);
167 return;
168 }
169 if(isrbrkt(cr)) {
170 AddHistory();
171 editor.MovePrevBrk(false);
172 return;
173 }
174 if(islbrkt(cl)) {
175 AddHistory();
176 editor.MoveNextBrk(false);
177 return;
178 }
179 if(isrbrkt(cl)) {
180 AddHistory();
181 editor.MovePrevBrk(false);
182 return;
183 }
184 }
185 try {
186 CParser p(l);
187 if(p.Char('#') && p.Id("include")) {
188 String path = FindIncludeFile(p.GetPtr(), GetFileFolder(editfile), SplitDirs(GetIncludePath()));
189 if(!IsNull(path)) {
190 AddHistory();
191 EditFile(path);
192 editor.SetCursor(0);
193 AddHistory();
194 }
195 return;
196 }
197 }
198 catch(CParser::Error) {}
199 int q = pos;
200 while(iscid(editor.Ch(q - 1)))
201 q--;
202 String tp;
203 Vector<String> xp = editor.ReadBack(q, Index<String>()); // try to load expression like "x[i]." or "ptr->"
204 Index<String> type;
205 ParserContext parser;
206 int ci = pos;
207 for(;;) {
208 int c = editor.Ch(ci);
209 if(c == '{' && editor.Ch(ci + 1)) {
210 ci++;
211 break;
212 }
213 if(c == '}' || c == 0 || c == ';')
214 break;
215 ci++;
216 }
217 editor.Context(parser, ci);
218
219 CodeBaseLock __;
220
221 if(xp.GetCount()) {
222 type = editor.EvaluateExpressionType(parser, xp);
223 if(type.GetCount() == 0)
224 return;
225 }
226
227 String id = editor.GetWord(pos);
228 if(id.GetCount() == 0)
229 return;
230
231 String qual; // Try to load type qualification like Foo::Bar, Vector<String>::Iterator
232 while(editor.Ch(q - 1) == ' ')
233 q--;
234 if(editor.Ch(q - 1) == ':' && editor.Ch(q - 2) == ':') {
235 q -= 3;
236 while(q >= 0) {
237 int c = editor.Ch(q);
238 if(iscid(c) || findarg(c, '<', '>', ':', ',', ' ') >= 0) {
239 if(c != ' ')
240 qual = (char)c + qual;
241 q--;
242 }
243 else
244 break;
245 }
246 if(qual.GetCount() == 0)
247 qual = ":";
248 }
249
250 Vector<String> scope;
251 Vector<bool> istype; // prefer type (e.g. struct Foo) over constructor (Foo::Foo())
252 for(int i = 0; i < type.GetCount(); i++) { // 'x.attr'
253 Index<String> done;
254 String r;
255 if(GetIdScope(r, type[i], id, done)) {
256 Vector<String> todo;
257 todo.Add(r);
258 while(scope.GetCount() < 100 && todo.GetCount()) { // Add base classes
259 String t = todo[0];
260 todo.Remove(0);
261 if(t.EndsWith("::"))
262 t.Trim(t.GetCount() - 2);
263 if(t.GetCount()) {
264 scope.Add(t);
265 istype.Add(false);
266 ScopeInfo f(CodeBase(), t); // Try base classes too!
267 todo.Append(f.GetBases());
268 }
269 }
270 }
271 }
272
273 Vector<String> ns = parser.GetNamespaces();
274
275 if(qual.GetCount() > 2 && qual.StartsWith("::"))
276 qual = qual.Mid(2);
277 if(qual.GetCount()) { // Ctrl::MOUSELEFT, Vector<String>::Iterator
278 Vector<String> todo;
279 String qa = Qualify(CodeBase(), parser.current_scope, *qual == ':' ? id : qual + "::" + id,
280 parser.context.namespace_using);
281 qa = RemoveTemplateParams(qa);
282 if(CodeBase().Find(qa) < 0) { // Upp::FileTabs::RenameFile
283 int q = qa.ReverseFind("::");
284 if(q > 0) {
285 String h = qa.Mid(0, q);
286 if(CodeBase().Find(h) >= 0) {
287 scope.Add(h);
288 istype.Add(false);
289 }
290 }
291 else {
292 scope.Add(Null); // Add global namespace
293 istype.Add(false);
294 }
295 }
296 todo.Add(qa);
297 while(scope.GetCount() < 100 && todo.GetCount()) {
298 String t = todo[0];
299 if(t.EndsWith("::"))
300 t.Trim(t.GetCount() - 2);
301 todo.Remove(0);
302 if(CodeBase().Find(t) >= 0) { // Ctrl::MOUSELEFT
303 scope.Add(t);
304 istype.Add(true);
305 }
306 String tt = t;
307 tt << "::" << id;
308 if(CodeBase().Find(tt) >= 0) { // Vector<String>::Iterator
309 scope.Add(tt);
310 istype.Add(true);
311 }
312 ScopeInfo f(CodeBase(), t); // Try base classes too!
313 todo.Append(f.GetBases());
314 }
315 }
316 else {
317 Vector<String> todo;
318 todo.Add(parser.current_scope);
319 while(scope.GetCount() < 100 && todo.GetCount()) { // Add base classes
320 String t = todo[0];
321 todo.Remove(0);
322 t.TrimEnd("::");
323 if(t.GetCount()) {
324 scope.Add(t);
325 istype.Add(false);
326 ScopeInfo f(CodeBase(), t); // Try base classes too!
327 todo.Append(f.GetBases());
328 }
329 }
330 if(xp.GetCount() == 0) {
331 q = parser.local.Find(id);
332 if(q >= 0) { // Try locals
333 AddHistory();
334 editor.SetCursor(editor.GetPos64(parser.local[q].line - 1));
335 FindId(id);
336 return;
337 }
338 }
339 // Can be unqualified type name like 'String'
340 String t = RemoveTemplateParams(Qualify(CodeBase(), parser.current_scope, id, parser.context.namespace_using));
341 for(int i = 0; i < ns.GetCount(); i++) {
342 String tt = Merge("::", ns[i], t);
343 if(CodeBase().Find(tt) >= 0) {
344 scope.Add(tt);
345 istype.Add(true);
346 }
347 }
348 }
349
350 Vector<String> usings = Split(parser.context.namespace_using, ';');
351 usings.Add(""); // Add global namespace too
352
353 Index<String> done;
354 for(int i = 0; i < ns.GetCount(); i++) {
355 String r;
356 if(GetIdScope(r, ns[i], id, done)) {
357 scope.Add(r);
358 istype.Add(false);
359 }
360 }
361
362 for(int j = 0; j < scope.GetCount(); j++) {
363 q = CodeBase().Find(scope[j]);
364 if(q >= 0) {
365 int ii = -1;
366 const Array<CppItem>& n = CodeBase()[q];
367 for(int i = 0; i < n.GetCount(); i++) {
368 const CppItem& m = n[i];
369 if(m.name == id) {
370 if(ii < 0)
371 ii = i;
372 else {
373 const CppItem& mm = n[ii];
374 if(CombineCompare(findarg(mm.kind, CONSTRUCTOR) < 0, findarg(m.kind, CONSTRUCTOR) < 0)
375 (!istype[j] || mm.IsType(), !istype[j] || m.IsType())
376 (findarg(mm.filetype, FILE_CPP, FILE_C) >= 0,
377 findarg(m.filetype, FILE_CPP, FILE_C) >= 0)
378 (mm.line, m.line) < 0)
379 ii = i;
380 }
381 }
382 }
383 if(ii >= 0) {
384 JumpToDefinition(n, ii, scope[j]);
385 FindId(id);
386 return;
387 }
388 }
389 }
390 }
391
ContextGoto()392 void Ide::ContextGoto()
393 {
394 ContextGoto0(editor.GetCursor());
395 }
396
CtrlClick(int64 pos)397 void Ide::CtrlClick(int64 pos)
398 {
399 if(pos < INT_MAX)
400 ContextGoto0((int)pos);
401 }
402
GotoDesignerFile(const String & path,const String & scope,const String & name,int line)403 bool Ide::GotoDesignerFile(const String& path, const String& scope, const String& name, int line)
404 {
405 if(ToLower(GetFileExt(path)) == ".lay") {
406 AddHistory();
407 EditFile(path);
408 LayDesigner *l = dynamic_cast<LayDesigner *>(~designer);
409 if(l) {
410 if(scope.StartsWith("With"))
411 l->FindLayout(scope.Mid(4), name);
412 else
413 if(name.StartsWith("SetLayout_"))
414 l->FindLayout(name.Mid(10), Null);
415 }
416 else {
417 editor.SetCursor(editor.GetPos64(line - 1));
418 editor.TopCursor(4);
419 editor.SetFocus();
420 }
421 AddHistory();
422 return true;
423 }
424 else
425 if(ToLower(GetFileExt(path)) == ".iml") {
426 AddHistory();
427 EditFile(path);
428 IdeIconDes *l = dynamic_cast<IdeIconDes *>(~designer);
429 if(l)
430 l->FindId(name);
431 else
432 editor.SetFocus();
433 AddHistory();
434 return true;
435 }
436 return false;
437 }
438
JumpToDefinition(const Array<CppItem> & n,int q,const String & scope)439 void Ide::JumpToDefinition(const Array<CppItem>& n, int q, const String& scope)
440 {
441 String qitem = n[q].qitem;
442 int i = q;
443 int qml = 0;
444 int qcpp = -1;
445 int qcppml = 0;
446 int qimpl = -1;
447 int qimplml = 0;
448 String currentfile = editfile;
449 while(i < n.GetCount() && n[i].qitem == qitem) {
450 const CppItem& m = n[i];
451 int ml = GetMatchLen(editfile, GetSourceFilePath(m.file));
452 if(m.impl && ml > qimplml) {
453 qimplml = ml;
454 qimpl = i;
455 }
456 if((m.filetype == FILE_CPP || m.filetype == FILE_C) && ml > qcppml) {
457 qcpp = i;
458 qcppml = ml;
459 }
460 if(ml > qml) {
461 q = i;
462 qml = ml;
463 }
464 i++;
465 }
466 CppItem pos = n[qimpl >= 0 ? qimpl : qcpp >= 0 ? qcpp : q];
467 String path = GetSourceFilePath(pos.file);
468 editastext.RemoveKey(path);
469 editashex.RemoveKey(path);
470 UnlockCodeBaseAll();
471 if(!GotoDesignerFile(path, scope, pos.name, pos.line))
472 GotoCpp(pos);
473 }
474
GotoFileAndId(const String & path,const String & id)475 void Ide::GotoFileAndId(const String& path, const String& id)
476 {
477 AddHistory();
478 EditFile(path);
479 WString wid = id.ToWString();
480 if(editor.GetLength64() < 100000) {
481 for(int i = 0; i < editor.GetLineCount(); i++) {
482 WString ln = editor.GetWLine(i);
483 int q = ln.Find(wid);
484 while(q >= 0) {
485 if(q == 0 || !iscid(ln[q - 1]) && !iscid(ln[q + wid.GetCount()])) {
486 editor.SetCursor(editor.GetPos64(i, q));
487 editor.CenterCursor();
488 return;
489 }
490 if(q + 1 >= ln.GetCount())
491 break;
492 q = ln.Find(wid, q + 1);
493 }
494 }
495 }
496 AddHistory();
497 }
498