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 
13 #define LTIMING(x) // DTIMING(x)
14 
15 class IndexSeparatorFrameCls : public CtrlFrame {
FrameLayout(Rect & r)16 	virtual void FrameLayout(Rect& r)                   { r.right -= 1; }
FramePaint(Draw & w,const Rect & r)17 	virtual void FramePaint(Draw& w, const Rect& r) {
18 		w.DrawRect(r.right - 1, r.top, 1, r.Height(), SColorShadow);
19 	}
FrameAddSize(Size & sz)20 	virtual void FrameAddSize(Size& sz) { sz.cx += 2; }
21 };
22 
Format(const Value & q) const23 Value AssistEditor::AssistItemConvert::Format(const Value& q) const
24 {
25 	int ii = q;
26 	if(ii >= 0 && ii < editor->assist_item_ndx.GetCount()) {
27 		ii = editor->assist_item_ndx[ii];
28 		if(ii < editor->assist_item.GetCount())
29 			return RawToValue(editor->assist_item[ii]);
30 	}
31 	CppItemInfo empty;
32 	return RawToValue(empty);
33 }
34 
SyncNavigatorPlacement()35 void AssistEditor::SyncNavigatorPlacement()
36 {
37 	int sz = navigatorframe.GetSize();
38 	if(navigator_right)
39 		navigatorframe.Right(navigatorpane, sz);
40 	else
41 		navigatorframe.Left(navigatorpane, sz);
42 }
43 
AssistEditor()44 AssistEditor::AssistEditor()
45 {
46 	assist_convert.editor = this;
47 	assist.NoHeader();
48 	assist.NoGrid();
49 	assist.AddRowNumColumn().Margin(0).SetConvert(assist_convert).SetDisplay(Single<CppItemInfoDisplay>());
50 	assist.NoWantFocus();
51 	assist.WhenLeftClick = THISBACK(AssistInsert);
52 	type.NoHeader();
53 	type.NoGrid();
54 	type.AddColumn();
55 	type.WhenCursor = THISBACK(SyncAssist);
56 	type.NoWantFocus();
57 	popup.Horz(type, assist);
58 	popup.SetPos(2000);
59 	auto_assist = auto_check = true;
60 	commentdp = false;
61 
62 	SyncNavigatorPlacement();
63 	navigatorframe.Left(navigatorpane, HorzLayoutZoom(140));
64 	navigating = false;
65 
66 	int cy = search.GetMinSize().cy;
67 	navigatorpane.Add(search.TopPos(0, cy).HSizePos(0, cy + 4));
68 	navigatorpane.Add(sortitems.TopPos(0, cy).RightPos(0, cy));
69 	navigatorpane.Add(navigator_splitter.VSizePos(cy, 0).HSizePos());
70 	navigator_splitter.Vert() << scope << list << navlines;
71 	navigator_splitter.SetPos(1500, 0);
72 	navigator_splitter.SetPos(9500, 1);
73 
74 	navigator = true;
75 
76 	WhenAnnotationMove = THISBACK(SyncAnnotationPopup);
77 	WhenAnnotationClick = THISBACK1(EditAnnotation, true);
78 	WhenAnnotationRightClick = THISBACK1(EditAnnotation, false);
79 	Annotations(Zx(12));
80 	annotation_popup.Background(SColorPaper());
81 	annotation_popup.SetFrame(BlackFrame());
82 	annotation_popup.Margins(6);
83 	annotation_popup.NoSb();
84 
85 	thisback = false;
86 
87 	cachedpos = INT_MAX;
88 	cachedln = -1;
89 
90 	parami = 0;
91 
92 	param_info.Margins(2);
93 	param_info.Background(SColorPaper());
94 	param_info.SetFrame(BlackFrame());
95 	param_info.BackPaint();
96 	param_info.NoSb();
97 
98 	include_assist = false;
99 
100 	NoFindReplace();
101 }
102 
CppItemInfoOrder(const Value & va,const Value & vb)103 int CppItemInfoOrder(const Value& va, const Value& vb) {
104 	const CppItemInfo& a = ValueTo<CppItemInfo>(va);
105 	const CppItemInfo& b = ValueTo<CppItemInfo>(vb);
106 	return CombineCompare(a.name, b.name)(a.natural, b.natural);
107 }
108 
CloseAssist()109 void AssistEditor::CloseAssist()
110 {
111 	if(popup.IsOpen())
112 		popup.Close();
113 	if(annotation_popup.IsOpen())
114 		annotation_popup.Close();
115 	assist_item.Clear();
116 	CloseTip();
117 }
118 
isincludefnchar(int c)119 bool isincludefnchar(int c)
120 {
121 	return c && c != '<' && c != '>' && c != '?' &&
122 	       c != ' ' && c != '\"' && c != '/' && c != '\\' && c >= 32 && c < 65536;
123 }
124 
ReadIdBackPos(int & pos,bool include)125 String AssistEditor::ReadIdBackPos(int& pos, bool include)
126 {
127 	String id;
128 	bool (*test)(int c) = include ? isincludefnchar : iscid;
129 	while(pos > 0 && (*test)(GetChar(pos - 1)))
130 		pos--;
131 	int q = pos;
132 	while(q < GetLength64() && (*test)(GetChar(q)))
133 		id << (char)GetChar(q++);
134 	return id;
135 }
136 
ReadIdBack(int q,bool include,bool * destructor)137 String AssistEditor::ReadIdBack(int q, bool include, bool *destructor)
138 {
139 	String id = ReadIdBackPos(q, include);
140 	if(destructor) {
141 		int n = 0;
142 		while(q > 0 && isspace(GetChar(q - 1)) && n < 100) {
143 			q--;
144 			n++;
145 		}
146 		*destructor = q > 0 && GetChar(q - 1) == '~';
147 	}
148 	return id;
149 }
150 
DirtyFrom(int line)151 void AssistEditor::DirtyFrom(int line)
152 {
153 	if(line >= cachedln) {
154 		cachedpos = INT_MAX;
155 		cachedline.Clear();
156 		cachedln = -1;
157 	}
158 	CodeEditor::DirtyFrom(line);
159 }
160 
Ch(int i)161 int AssistEditor::Ch(int i)
162 {
163 	if(i >= 0 && i < GetLength64()) {
164 		if(i < cachedpos || i - cachedpos > cachedline.GetCount()) {
165 			cachedln = GetLine(i);
166 			cachedline = GetWLine(cachedln);
167 			cachedpos = GetPos32(cachedln);
168 		}
169 		i -= cachedpos;
170 		return i < cachedline.GetCount() ? cachedline[i] : '\n';
171 	}
172 	return 0;
173 }
174 
ParsBack(int q)175 int AssistEditor::ParsBack(int q)
176 {
177 	int level = 1;
178 	--q;
179 	while(q > 0) {
180 		if(isrbrkt(Ch(q)))
181 			level++;
182 		if(islbrkt(Ch(q)))
183 			if(--level <= 0)
184 				break;
185 		--q;
186 	}
187 	return max(0, q);
188 }
189 
IsSpc(int c)190 bool IsSpc(int c)
191 {
192 	return c > 0 && c <= 32;
193 }
194 
SkipSpcBack(int & q)195 void AssistEditor::SkipSpcBack(int& q)
196 {
197 	while(q > 0 && IsSpc(Ch(q - 1)))
198 		q--;
199 }
200 
IdBack(int & qq)201 String AssistEditor::IdBack(int& qq)
202 {
203 	String r;
204 	if(iscid(Ch(qq - 1))) {
205 		int q = qq;
206 		while(iscid(Ch(q - 1)))
207 			q--;
208 		if(iscib(Ch(q))) {
209 			qq = q;
210 			while(q < GetLength64() && iscid(Ch(q)))
211 				r.Cat(Ch(q++));
212 		}
213 	}
214 	return r;
215 }
216 
CompleteIdBack(int & q,const Index<String> & locals)217 String AssistEditor::CompleteIdBack(int& q, const Index<String>& locals)
218 {
219 	String id;
220 	for(;;) {
221 		SkipSpcBack(q);
222 		if(Ch(q - 1) == ',') {
223 			q--;
224 			id = ',' + id;
225 		}
226 		else
227 		if(Ch(q - 1) == '>') {
228 			q--;
229 			id = '>' + id;
230 		}
231 		else
232 		if(Ch(q - 1) == '<') {
233 			q--;
234 			id = '<' + id;
235 		}
236 		else
237 		if(Ch(q - 1) == ':' && Ch(q - 2) == ':') {
238 			q -= 2;
239 			id = "::" + id;
240 		}
241 		else {
242 			if(iscib(*id))
243 				break;
244 			String nid = IdBack(q);
245 			if(IsNull(nid))
246 				break;
247 			if(locals.Find(nid) >= 0 && findarg(*id, '<', '>') >= 0)
248 				return id.Mid(1);
249 			id = nid + id;
250 		}
251 	}
252 	return id;
253 }
254 
ReadBack(int q,const Index<String> & locals)255 Vector<String> AssistEditor::ReadBack(int q, const Index<String>& locals)
256 {
257 	Vector<String> r;
258 	type.Clear();
259 	bool wasid = true;
260 	for(;;) {
261 		if(r.GetCount() > 200) {
262 			r.Clear();
263 			type.Clear();
264 			break;
265 		}
266 		SkipSpcBack(q);
267 		int c = Ch(q - 1);
268 		if(c == '>' && !wasid) {
269 			q--;
270 			r.Add() = CompleteIdBack(q, locals) + ">";
271 			wasid = true;
272 			continue;
273 		}
274 		if(iscid(c)) {
275 			if(wasid)
276 				break;
277 			String id;
278 			for(;;) {
279 				id = IdBack(q) + id;
280 				SkipSpcBack(q);
281 				if(!(Ch(q - 1) == ':' && Ch(q - 2) == ':'))
282 					break;
283 				q -= 2;
284 				id = "::" + id;
285 				SkipSpcBack(q);
286 			}
287 			r.Add() = id;
288 			wasid = true;
289 			continue;
290 		}
291 		else {
292 //			if(findarg(c, '(', '[', '{') >= 0)
293 //				break;
294 			if(c == ']') {
295 				if(wasid)
296 					break;
297 				r.Add("[]");
298 				q = ParsBack(q - 1);
299 				wasid = false;
300 				continue;
301 			}
302 			else
303 			if(c == ')') {
304 				if(wasid)
305 					break;
306 				r.Add("()");
307 				q = ParsBack(q - 1);
308 				wasid = false;
309 				continue;
310 			}
311 			wasid = false;
312 			c = Ch(q - 1);
313 			if(c == '>' && Ch(q - 2) == '-') {
314 				r.Add("->");
315 				q -= 2;
316 				continue;
317 			}
318 			if(c == '.') {
319 				r.Add(".");
320 				q--;
321 				continue;
322 			}
323 		}
324 		break;
325 	}
326 	Reverse(r);
327 	return r;
328 }
329 
SyncAssist()330 void AssistEditor::SyncAssist()
331 {
332 	LTIMING("SyncAssist");
333 	bool destructor;
334 	String name = ReadIdBack(GetCursor32(), include_assist, &destructor);
335 	String uname = ToUpper(name);
336 	assist_item_ndx.Clear();
337 	int typei = type.GetCursor() - 1;
338 	Buffer<bool> found(assist_item.GetCount(), false);
339 	for(int pass = 0; pass < 2; pass++) {
340 		VectorMap<String, int> over;
341 		for(int i = 0; i < assist_item.GetCount(); i++) {
342 			const CppItemInfo& m = assist_item[i];
343 			if(!found[i] &&
344 			   (typei < 0 || m.typei == typei) &&
345 			   (pass ? m.uname.StartsWith(uname) : m.name.StartsWith(name)) &&
346 			   (!destructor || m.kind == DESTRUCTOR && m.scope == current_type + "::")) {
347 					int q = include_assist ? -1 : over.Find(m.qitem);
348 					if(q < 0 || over[q] == m.typei && m.scope.GetCount()) {
349 						found[i] = true;
350 						assist_item_ndx.Add(i);
351 						if(q < 0)
352 							over.Add(m.qitem, m.typei);
353 					}
354 			}
355 		}
356 	}
357 	assist.Clear();
358 	assist.SetVirtualCount(assist_item_ndx.GetCount());
359 }
360 
IncludeAssist()361 bool AssistEditor::IncludeAssist()
362 {
363 	Vector<String> include;
364 	String ln = GetUtf8Line(GetCursorLine());
365 	CParser p(ln);
366 	try {
367 		if(!p.Char('#') || !p.Id("include"))
368 			return false;
369 		if(p.Char('\"')) {
370 			include.Add(GetFileFolder(theide->editfile));
371 			include_local = true;
372 		}
373 		else {
374 			p.Char('<');
375 			include = SplitDirs(theide->GetIncludePath());
376 			include_local = false;
377 		}
378 		include_path.Clear();
379 		include_back = 0;
380 		if(include_local)
381 			while(p.Char3('.', '.', '/') || p.Char3('.', '.', '\\')) {
382 				include.Top() = GetFileFolder(include.Top());
383 				include_back++;
384 			}
385 		for(;;) {
386 			String dir;
387 			while(isincludefnchar(p.PeekChar()))
388 				dir.Cat(p.GetChar());
389 			if(dir.GetCount() && (p.Char('/') || p.Char('\\'))) {
390 				if(include_path.GetCount())
391 					include_path << '/';
392 				include_path << dir;
393 			}
394 			else
395 				break;
396 		}
397 	}
398 	catch(CParser::Error) {
399 		return false;
400 	}
401 	Vector<String> folder, upper_folder, file, upper_file;
402 	for(int i = 0; i < include.GetCount(); i++) {
403 		FindFile ff(AppendFileName(AppendFileName(include[i], include_path), "*.*"));
404 		while(ff) {
405 			String fn = ff.GetName();
406 			if(!ff.IsHidden()) {
407 				if(ff.IsFolder()) {
408 					folder.Add(fn);
409 					upper_folder.Add(ToUpper(fn));
410 				}
411 				else {
412 					static Index<String> ext(Split(".h;.hpp;.hh;.hxx;.i;.lay;.iml;.t;.dli", ';'));
413 					String fext = GetFileExt(fn);
414 					if(fext.GetCount() == 0 || ext.Find(ToLower(fext)) >= 0) {
415 						file.Add(fn);
416 						upper_file.Add(ToUpper(fn));
417 					}
418 				}
419 			}
420 			ff.Next();
421 		}
422 	}
423 	IndexSort(upper_folder, folder);
424 	Index<String> fnset;
425 	for(int i = 0; i < folder.GetCount(); i++) {
426 		String fn = folder[i];
427 		if(fnset.Find(fn) < 0) {
428 			fnset.Add(fn);
429 			CppItemInfo& f = assist_item.Add();
430 			f.name = f.natural = fn;
431 			f.access = 0;
432 			f.kind = KIND_INCLUDEFOLDER;
433 		}
434 	}
435 	IndexSort(upper_file, file);
436 	for(int i = 0; i < file.GetCount(); i++) {
437 		String fn = file[i];
438 		CppItemInfo& f = assist_item.Add();
439 		f.name = f.natural = fn;
440 		f.access = 0;
441 		static Index<String> hdr(Split(".h;.hpp;.hh;.hxx", ';'));
442 		String fext = GetFileExt(fn);
443 		f.kind = hdr.Find(ToLower(GetFileExt(fn))) >= 0 || fext.GetCount() == 0 ? KIND_INCLUDEFILE
444 		                                                                        : KIND_INCLUDEFILE_ANY;
445 	}
446 	include_assist = true;
447 	if(include_path.GetCount())
448 		include_path << "/";
449 	PopUpAssist();
450 	return true;
451 }
452 
Assist()453 void AssistEditor::Assist()
454 {
455 	LTIMING("Assist");
456 	if(!assist_active)
457 		return;
458 	CloseAssist();
459 	int q = GetCursor32();
460 	assist_cursor = q;
461 	assist_type.Clear();
462 	assist_item.Clear();
463 	include_assist = false;
464 	if(IncludeAssist())
465 		return;
466 	ParserContext parser;
467 	Context(parser, GetCursor32());
468 	Index<String> in_types;
469 	while(iscid(Ch(q - 1)) || Ch(q - 1) == '~')
470 		q--;
471 	SkipSpcBack(q);
472 	thisback = false;
473 	current_type.Clear();
474 	LTIMING("Assist2");
475 	if(Ch(q - 1) == '(') {
476 		int qq = q - 1;
477 		String id = IdBack(qq);
478 		int tn = findarg(id, "THISBACK", "THISBACK1", "THISBACK2", "THISBACK3", "THISBACK4");
479 		if(tn >= 0) {
480 			thisback = true;
481 			thisbackn = tn > 0;
482 			GatherItems(parser.current_scope, false, in_types, false);
483 			RemoveDuplicates();
484 			PopUpAssist();
485 			return;
486 		}
487 	}
488 	if(Ch(q - 1) == ':' && Ch(q - 2) == ':') {
489 		q -= 2;
490 		Vector<String> tparam;
491 		String scope = ParseTemplatedType(Qualify(parser.current_scope,
492 		                                          CompleteIdBack(q, parser.local.GetIndex()),
493 		                                          parser.context.namespace_using),
494 		                                  tparam);
495 		GatherItems(scope, false, in_types, true);
496 		current_type = scope;
497 	}
498 	else {
499 		String tp;
500 		Vector<String> xp = ReadBack(q, parser.local.GetIndex());
501 		bool isok = false;
502 		if(xp.GetCount() && xp[0].StartsWith("::")) // ::Foo().
503 			xp[0] = xp[0].Mid(2);
504 		for(int i = 0; i < xp.GetCount(); i++)
505 			if(iscib(*xp[i])) {
506 				isok = true;
507 				break;
508 			}
509 		if(xp.GetCount()) {
510 			if(isok) { // Do nothing on pressing '.' when there is no identifier before
511 				Index<String> typeset = EvaluateExpressionType(parser, xp);
512 				for(int i = 0; i < typeset.GetCount(); i++)
513 					if(typeset[i].GetCount())
514 						GatherItems(typeset[i], xp[0] != "this", in_types, false);
515 			}
516 		}
517 		else {
518 			GatherItems(parser.current_scope, false, in_types, true);
519 			Vector<String> ns = parser.GetNamespaces();
520 			for(int i = 0; i < ns.GetCount(); i++)
521 				if(parser.current_scope != ns[i]) // Do not scan namespace already scanned
522 					GatherItems(ns[i], false, in_types, true);
523 		}
524 	}
525 	LTIMING("Assist3");
526 	RemoveDuplicates();
527 	PopUpAssist();
528 }
529 
530 Ptr<Ctrl> AssistEditor::assist_ptr;
531 
WheelHook(Ctrl *,bool inframe,int event,Point p,int zdelta,dword keyflags)532 bool AssistEditor::WheelHook(Ctrl *, bool inframe, int event, Point p, int zdelta, dword keyflags)
533 {
534 	if(!inframe && event == MOUSEWHEEL && assist_ptr && assist_ptr->IsOpen()) {
535 		assist_ptr->MouseWheel(p, zdelta, keyflags);
536 		return true;
537 	}
538 	return false;
539 }
540 
PopUpAssist(bool auto_insert)541 void AssistEditor::PopUpAssist(bool auto_insert)
542 {
543 	LTIMING("PopUpAssist");
544 	if(assist_item.GetCount() == 0)
545 		return;
546 	int lcy = max(16, BrowserFont().Info().GetHeight());
547 	type.Clear();
548 	type.Add(AttrText("<all>").Ink(SColorHighlight()));
549 	if(assist_type.GetCount() == 0)
550 		popup.Zoom(1);
551 	else {
552 		for(int i = 0; i < assist_type.GetCount(); i++) {
553 			String s = assist_type[i];
554 			if(s[0] == ':' && s[1] == ':')
555 				s = s.Mid(2);
556 			s = Nvl(s, "<globals>");
557 			if(s[0] == '<')
558 				type.Add(AttrText(s).Ink(SColorMark()));
559 			else
560 				type.Add(Nvl(s, "<globals>"));
561 		}
562 		popup.NoZoom();
563 	}
564 	type.SetCursor(0);
565 	if(!assist.GetCount())
566 		return;
567 	LTIMING("PopUpAssist2");
568 	int cy = VertLayoutZoom(304);
569 	cy += HeaderCtrl::GetStdHeight();
570 	assist.SetLineCy(lcy);
571 	Point p = GetCaretPoint() + GetScreenView().TopLeft();
572 	Rect wa = GetWorkArea();
573 	int cx = min(wa.Width() - 100, Zx(800));
574 	if(p.x + cx > wa.right)
575 		p.x = wa.right - cx;
576 	popup.SetRect(RectC(p.x, p.y + cy + GetFontSize().cy < wa.bottom ? p.y + GetFontSize().cy : p.y - cy, cx, cy) & wa);
577 	popup.BackPaint();
578 	if(auto_insert && assist.GetCount() == 1) {
579 		assist.GoBegin();
580 		AssistInsert();
581 	}
582 	else {
583 		popup.Ctrl::PopUp(this, false, false, true);
584 		assist_ptr = &assist;
585 		ONCELOCK {
586 			InstallMouseHook(AssistEditor::WheelHook);
587 		}
588 	}
589 }
590 
sILess(const String & a,const String & b)591 bool sILess(const String& a, const String& b)
592 {
593 	return ToUpper(a) < ToUpper(b);
594 }
595 
Complete()596 void AssistEditor::Complete()
597 {
598 	CloseAssist();
599 
600 	int c = GetCursor32();
601 	String q = IdBack(c);
602 
603 	Index<String> ids;
604 	int64 len = 0;
605 	for(int i = 0; i < GetLineCount() && len < 1000000; i++) {
606 		String x = GetUtf8Line(i);
607 		len += x.GetLength();
608 		CParser p(x);
609 		try {
610 			while(!p.IsEof())
611 				if(p.IsId()) {
612 					String h = p.ReadId();
613 					if(h != q)
614 						ids.FindAdd(h);
615 				}
616 				else
617 					p.SkipTerm();
618 		}
619 		catch(CParser::Error) {
620 			return;
621 		}
622 	}
623 
624 	Vector<String> id = ids.PickKeys();
625 	Upp::Sort(id, sILess);
626 
627 	if(q.GetCount()) {
628 		String h;
629 		for(int i = 0; i < id.GetCount(); i++) {
630 			String s = id[i];
631 			if(s.StartsWith(q)) {
632 				if(IsNull(h))
633 					h = s;
634 				else {
635 					s.Trim(min(s.GetCount(), h.GetCount()));
636 					for(int j = 0; j < s.GetCount(); j++)
637 						if(s[j] != h[j]) {
638 							h.Trim(j);
639 							break;
640 						}
641 				}
642 			}
643 		}
644 		if(h.GetCount() > q.GetCount())
645 			Paste(h.Mid(q.GetCount()).ToWString());
646 	}
647 	for(int i = 0; i < id.GetCount(); i++) {
648 		CppItemInfo& f = assist_item.Add();
649 		f.name = f.natural = f.qitem = id[i];
650 		f.access = 0;
651 		f.kind = 100;
652 	}
653 	assist_type.Clear();
654 	PopUpAssist(true);
655 }
656 
Abbr()657 void AssistEditor::Abbr()
658 {
659 	CloseAssist();
660 	int c = GetCursor32();
661 	int ch;
662 	String s;
663 	while(IsAlpha(ch = Ch(c - 1))) {
664 		s.Insert(0, ch);
665 		--c;
666 	}
667 	int len = s.GetCount();
668 	s = theide->abbr.Get(s, String());
669 	if(IsNull(s))
670 		return;
671 	NextUndo();
672 	SetCursor(c);
673 	Remove(c, len);
674 	int linepos = c;
675 	int line = GetLinePos32(linepos);
676 	WString h = GetWLine(line).Mid(0, linepos);
677 	for(int i = 0; i < s.GetCount(); i++) {
678 		ch = s[i];
679 		switch(ch) {
680 		case '@':
681 			c = GetCursor32();
682 			break;
683 		case '\n':
684 			InsertChar('\n');
685 			for(int j = 0; j < h.GetCount(); j++)
686 				InsertChar(h[j]);
687 			break;
688 		default:
689 			if((byte)s[i] >= ' ' || s[i] == '\t')
690 				InsertChar(s[i]);
691 			break;
692 		}
693 	}
694 	SetCursor(c);
695 }
696 
AssistInsert()697 void AssistEditor::AssistInsert()
698 {
699 	if(assist.IsCursor()) {
700 		int ii = assist.GetCursor();
701 		if(ii < 0 || ii >= assist_item_ndx.GetCount())
702 			return;
703 		ii = assist_item_ndx[ii];
704 		if(ii >= assist_item.GetCount()) {
705 			CloseAssist();
706 			IgnoreMouseUp();
707 			return;
708 		}
709 		const CppItemInfo& f = assist_item[ii];
710 		if(include_assist) {
711 			int ln = GetLine(GetCursor32());
712 			int pos = GetPos32(ln);
713 			Remove(pos, GetLineLength(ln));
714 			SetCursor(pos);
715 			String h;
716 			for(int i = 0; i < include_back; i++)
717 				h << "../";
718 			Paste(ToUnicode(String().Cat() << "#include "
719 			                << (include_local ? "\"" : "<")
720 			                << h << include_path
721 			                << f.name
722 			                << (f.kind == KIND_INCLUDEFOLDER ? "/" :
723 			                       include_local ? "\"" : ">")
724 			                , CHARSET_WIN1250));
725 			if(f.kind == KIND_INCLUDEFOLDER) {
726 				Assist();
727 				IgnoreMouseUp();
728 				return;
729 			}
730 			else {
731 				String pkg = include_path.Left(include_path.GetCount() - 1);
732 				Vector<String> nests = SplitDirs(GetVar("UPP"));
733 				for(int i = 0; i < nests.GetCount(); i++){
734 					if(FileExists(nests[i] + "/" + include_path + GetFileName(pkg) + ".upp")) {
735 						Ide *ide = dynamic_cast<Ide *>(TheIde());
736 						if(ide)
737 							ide->AddPackage(pkg);
738 						break;
739 					}
740 				}
741 			}
742 		}
743 		else {
744 			String txt = f.name;
745 			int l = txt.GetCount();
746 			int pl = txt.GetCount();
747 			if(!thisback && f.kind >= FUNCTION && f.kind <= INLINEFRIEND)
748 				txt << "()";
749 			int cl = GetCursor32();
750 			int ch = cl;
751 			while(iscid(Ch(cl - 1)))
752 				cl--;
753 			while(iscid(Ch(ch)))
754 				ch++;
755 			Remove(cl, ch - cl);
756 			SetCursor(cl);
757 			if(thisback)
758 				for(;;) {
759 					int c = Ch(cl++);
760 					if(!c || Ch(cl) == ',' || Ch(cl) == ')')
761 						break;
762 					if(c != ' ') {
763 						if(thisbackn)
764 							txt << ", ";
765 						txt << ')';
766 						break;
767 					}
768 				}
769 			if(findarg(f.kind, CONSTRUCTOR, DESTRUCTOR) >= 0)
770 				txt << "()";
771 			int n = Paste(ToUnicode(txt, CHARSET_WIN1250));
772 			if(!thisback && f.kind >= FUNCTION && f.kind <= INLINEFRIEND) {
773 				SetCursor(GetCursor32() - 1);
774 				StartParamInfo(f, cl);
775 				if(f.natural.EndsWith("()"))
776 					SetCursor(GetCursor32() + 1);
777 			}
778 			else
779 			if(thisback) {
780 				if(thisbackn)
781 					SetCursor(GetCursor32() - 1);
782 			}
783 			else
784 			if(!inbody)
785 				SetCursor(cl + n - thisbackn);
786 			else
787 			if(pl > l)
788 				SetSelection(cl + l, cl + pl);
789 			else
790 				SetCursor(cl + l);
791 		}
792 	}
793 	CloseAssist();
794 	IgnoreMouseUp();
795 }
796 
InCode()797 bool AssistEditor::InCode()
798 {
799 	int pos = GetCursor32();
800 	int line = GetLinePos32(pos);
801 	One<EditorSyntax> st = GetSyntax(line);
802 	WString l = GetWLine(line);
803 	st->ScanSyntax(l, ~l + pos, line, GetTabSize());
804 	return st->CanAssist();
805 }
806 
isaid(int c)807 bool isaid(int c)
808 {
809 	return c == '~' || iscid(c);
810 }
811 
Key(dword key,int count)812 bool AssistEditor::Key(dword key, int count)
813 {
814 	if(popup.IsOpen()) {
815 		int k = key & ~K_CTRL;
816 		ArrayCtrl& kt = key & K_CTRL ? type : assist;
817 		if(k == K_UP || k == K_PAGEUP || k == K_CTRL_PAGEUP || k == K_CTRL_END) {
818 			if(kt.IsCursor())
819 				return kt.Key(k, count);
820 			else {
821 				kt.SetCursor(kt.GetCount() - 1);
822 				return true;
823 			}
824 		}
825 		if(k == K_DOWN || k == K_PAGEDOWN || k == K_CTRL_PAGEDOWN || k == K_CTRL_HOME) {
826 			if(kt.IsCursor())
827 				return kt.Key(k, count);
828 			else {
829 				kt.SetCursor(0);
830 				return true;
831 			}
832 		}
833 		if(key == K_ENTER && assist.IsCursor()) {
834 			AssistInsert();
835 			return true;
836 		}
837 		if(key == K_TAB && !assist.IsCursor() && assist.GetCount()) {
838 			assist.GoBegin();
839 			AssistInsert();
840 			return true;
841 		}
842 	}
843 	int c = GetCursor32();
844 	int cc = GetChar(c);
845 	int bcc = c > 0 ? GetChar(c - 1) : 0;
846 	bool b = CodeEditor::Key(key, count);
847 	if(b && search.HasFocus())
848 		SetFocus();
849 	if(IsReadOnly())
850 		return b;
851 	if(assist.IsOpen()) {
852 		bool (*test)(int c) = include_assist ? isincludefnchar : isaid;
853 		if(!(*test)(key) &&
854 		   !((*test)(cc) && (key == K_DELETE || key == K_RIGHT)) &&
855 		   !((*test)(bcc) && (key == K_LEFT || key == K_BACKSPACE))) {
856 			if(b) {
857 				CloseAssist();
858 				if(include_assist ? (key == '/' || key == '\\') : key == '.')
859 					Assist();
860 			}
861 		}
862 		else
863 			SyncAssist();
864 	}
865 	else
866 	if(auto_assist) {
867 		if(InCode()) {
868 			if(key == '.' || key == '>' && Ch(GetCursor32() - 2) == '-' ||
869 			   key == ':' && Ch(GetCursor32() - 2) == ':')
870 				Assist();
871 			else
872 			if(key == '(') {
873 				int q = GetCursor32() - 1;
874 				String id = IdBack(q);
875 				if(id == "THISBACK" || id == "THISBACK1" || id == "THISBACK2" || id == "THISBACK3" || id == "THISBACK4")
876 					Assist();
877 			}
878 		}
879 		if((key == '\"' || key == '<' || key == '/' || key == '\\') && GetUtf8Line(GetCursorLine()).StartsWith("#include"))
880 			Assist();
881 	}
882 	return b;
883 }
884 
MouseWheel(Point p,int zdelta,dword keyflags)885 void AssistEditor::MouseWheel(Point p, int zdelta, dword keyflags)
886 {
887 	if(keyflags & K_CTRL)
888 		WhenFontScroll(sgn(zdelta));
889 	else
890 		if(assist.IsOpen())
891 			assist.MouseWheel(p, zdelta, keyflags);
892 		else
893 			CodeEditor::MouseWheel(p, zdelta, keyflags);
894 }
895 
LeftDown(Point p,dword keyflags)896 void AssistEditor::LeftDown(Point p, dword keyflags)
897 {
898 	CloseAssist();
899 	CodeEditor::LeftDown(p, keyflags);
900 }
901 
LostFocus()902 void AssistEditor::LostFocus()
903 {
904 	CloseAssist();
905 }
906 
RemoveDefPar(const char * s)907 String AssistEditor::RemoveDefPar(const char *s)
908 {
909 	String r;
910 	int lvl = 0;
911 	bool dp = true;
912 	while(*s) {
913 		byte c = *s++;
914 		if(c == '(')
915 			lvl++;
916 		if(lvl == 0) {
917 			if(c == '=') {
918 				dp = false;
919 				if(commentdp)
920 					r.Cat("/* ");
921 				else
922 					while(r.GetCount() && *r.Last() == ' ')
923 						r.Trim(r.GetCount() - 1);
924 			}
925 			if(c == ')') {
926 				if(!dp && commentdp)
927 					r.Cat("*/");
928 				r.Cat(')');
929 				try {
930 					if(CParser(s).Char('='))
931 						break;
932 				}
933 				catch(CParser::Error) {}
934 				r.Cat(s);
935 				break;
936 			}
937 			if(c == ',') {
938 				if(!dp && commentdp)
939 					r.Cat("*/");
940 				dp = true;
941 			}
942 		}
943 		else
944 		if(c == ')')
945 			lvl--;
946 		if(dp || commentdp)
947 			r.Cat(c);
948 	}
949 	return r;
950 }
951 
MakeDefinition(const String & cls,const String & _n)952 String AssistEditor::MakeDefinition(const String& cls, const String& _n)
953 {
954 	String n = TrimLeft(_n);
955 	auto RemoveId = [&](const char *s) {
956 		int len = strlen(s);
957 		int q = n.Find(s);
958 		if(q >= 0 && (q == 0 || !iscid(n[q - 1])) && (q + len >= n.GetCount() || !iscid(n[q + len])))
959 			n.Remove(q, len);
960 	};
961 	RemoveId("override");
962 	RemoveId("final");
963 	CParser p(n);
964 	try {
965 		bool dest = false;
966 		const char *beg = n;
967 		while(!p.IsEof()) {
968 			const char *b = p.GetPtr();
969 			if(p.Id("operator"))
970 				return cls.GetCount() ? NormalizeSpaces(String(beg, b) + ' ' + cls + "::" + b)
971 				                      : NormalizeSpaces(String(beg, b) + ' ' + b);
972 			if(p.Char('~')) {
973 				beg = p.GetPtr();
974 				dest = true;
975 			}
976 			else
977 			if(p.IsId()) {
978 				String id = p.ReadId();
979 				if(p.Char('(')) {
980 					String rp = RemoveDefPar(p.GetPtr());
981 					auto merge = [](String a, String b) {
982 						return IsAlNum(*a.Last()) && IsAlNum(*b) ? a + ' ' + b : a + b;
983 					};
984 					if(cls.GetCount() == 0)
985 						return NormalizeSpaces(merge(String(beg, b), id) + '(' + rp);
986 					if(dest)
987 						return NormalizeSpaces(String(beg, b) + cls + "::~" + id + '(' + rp);
988 					else
989 						return NormalizeSpaces(merge(String(beg, b), cls) + "::" + id + '(' + rp);
990 				}
991 			}
992 			else
993 				p.SkipTerm();
994 		}
995 	}
996 	catch(CParser::Error) {}
997 	return n;
998 }
999 
IdeGotoCodeRef(String coderef)1000 void Ide::IdeGotoCodeRef(String coderef)
1001 {
1002 	LLOG("IdeGotoLink " << coderef);
1003 	CodeBaseLock __;
1004 	if(IsNull(coderef)) return;
1005 	String scope, item;
1006 	SplitCodeRef(coderef, scope, item);
1007 	int q = CodeBase().Find(scope);
1008 	if(q < 0)
1009 		return;
1010 	const Array<CppItem>& n = CodeBase()[q];
1011 	q = FindItem(n, item);
1012 	if(q >= 0)
1013 		JumpToDefinition(n, q, scope);
1014 }
1015 
Esc()1016 bool AssistEditor::Esc()
1017 {
1018 	bool r = false;
1019 	if(assist.IsOpen()) {
1020 		CloseAssist();
1021 		r = true;
1022 	}
1023 	if(param_info.IsOpen()) {
1024 		for(int i = 0; i < PARAMN; i++)
1025 			param[i].line = -1;
1026 		param_info.Close();
1027 		r = true;
1028 	}
1029 	return r;
1030 }
1031 
SyncNavigatorShow()1032 void AssistEditor::SyncNavigatorShow()
1033 {
1034 	navigatorframe.Show(navigator && theide && !theide->designer && !theide->IsEditorMode());
1035 }
1036 
Navigator(bool nav)1037 void AssistEditor::Navigator(bool nav)
1038 {
1039 	navigator = nav;
1040 	SyncNavigatorShow();
1041 	if(IsNavigator())
1042 		SetFocus();
1043 	SyncNavigator();
1044 	SyncCursor();
1045 }
1046 
SyncNavigator()1047 void AssistEditor::SyncNavigator()
1048 {
1049 	if(navigating)
1050 		return;
1051 	if(IsNavigator()) {
1052 		Search();
1053 		SyncCursor();
1054 	}
1055 	SyncNavigatorShow();
1056 }
1057 
SelectionChanged()1058 void AssistEditor::SelectionChanged()
1059 {
1060 	CodeEditor::SelectionChanged();
1061 	SyncCursor();
1062 	SyncParamInfo();
1063 }
1064 
SerializeNavigator(Stream & s)1065 void AssistEditor::SerializeNavigator(Stream& s)
1066 {
1067 	int version = 5;
1068 	s / version;
1069 	s % navigatorframe;
1070 	s % navigator;
1071 	if(version == 1 || version == 3) {
1072 		Splitter dummy;
1073 		s % dummy;
1074 	}
1075 	if(version >= 4)
1076 		s % navigator_splitter;
1077 	if(version >= 5)
1078 		s % navigator_right;
1079 	Navigator(navigator);
1080 
1081 	if(s.IsLoading())
1082 		SyncNavigatorPlacement();
1083 }
1084