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