CE Source GUI.cpp 68 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. namespace Edit{
  5. /******************************************************************************/
  6. static Bool Overwrite;
  7. /******************************************************************************/
  8. // OPERATIONS
  9. /******************************************************************************/
  10. void Line::setRect(Int i)
  11. {
  12. Flt lh=CE.ts.lineHeight();
  13. super::rect(Rect_LU(0, -lh*i + CE.fontSpaceOffset(), CE.ts.textWidth(T), lh));
  14. }
  15. void Line::setGui(Int i, GuiObj &parent)
  16. {
  17. text_valid=false;
  18. if(T.parent()!=&parent)parent+=create(S, &CE.ts).visible(!CE.view_mode());
  19. setRect(i);
  20. }
  21. /******************************************************************************/
  22. void Source::setOffset () {lc_offset.set(slidebar[0].wantedOffset()/CE.ts.colWidth(), slidebar[1].wantedOffset()/CE.ts.lineHeight());}
  23. void Source::setRegionSize()
  24. {
  25. Vec2 size=0; REP(childNum())if(GuiObj *c=child(i))if(c->visible() && c->type()==GO_TEXT){MAX(size.x, GuiMaxX(c->rect())); MAX(size.y, GuiMaxY(c->rect()));}
  26. size.x+=CE.ts.colWidth()+CE.lineNumberSize(); size.y+=clientHeight()/2;
  27. virtualSize(&size);
  28. slidebar[0].offset(lc_offset.x*CE.ts. colWidth ());
  29. slidebar[1].offset(lc_offset.y*CE.ts.lineHeight());
  30. }
  31. void Source::resize()
  32. {
  33. REPAO( lines).setRect(i);
  34. REPAO(view_lines).setRect(i);
  35. rect(CE.sourceRect());
  36. setRegionSize();
  37. setScroll();
  38. suggestions_list.elmHeight(CE.ts.lineHeight()).textSize(CE.ts.size.y).columnWidth(0, CE.ts.lineHeight());
  39. suggestionsSetRect();
  40. }
  41. void Source::themeChanged()
  42. {
  43. REPAO( lines).text_valid=false;
  44. REPAO(view_lines).text_valid=false;
  45. }
  46. void Source::setGui()
  47. {
  48. REPAO(lines).setGui(i, T);
  49. setRegionSize();
  50. suggestions_region .moveToTop();
  51. suggestions_textline.moveToTop();
  52. }
  53. /******************************************************************************/
  54. void Source::prepareForDraw()
  55. {
  56. resize ();
  57. validateView();
  58. }
  59. void Source::validateView()
  60. {
  61. Flt const_view_y=(hasMsFocus() ? posCur(Ms.pos()).y-lc_offset.y : 0), // cursor position relative to zero which should remain constant
  62. const_full_y=lc_offset.y+const_view_y;
  63. Int const_line=Trunc(const_full_y); Flt frac=const_full_y-const_line;
  64. if(view_lines.elms() && view_lines[0].visible())const_line=view_lines[Mid(const_line, 0, view_lines.elms()-1)].line(); // top visible line
  65. if(CE.view_mode())
  66. if((lines.elms() && !view_lines.elms()) || view_comments!=CE.view_comments || view_funcs!=CE.view_funcs || view_func_bodies!=CE.view_func_bodies || view_private_members!=CE.view_private_members)setView(CE.view_comments, CE.view_funcs, CE.view_func_bodies, CE.view_private_members);
  67. REPAO( lines).visible(!CE.view_mode());
  68. REPAO(view_lines).visible( CE.view_mode());
  69. if(CE.view_mode())const_line=realToView(const_line);
  70. lc_offset.y=const_line-const_view_y+frac;
  71. setRegionSize();
  72. }
  73. void Source::setView(Bool comments, Bool funcs, Bool func_bodies, Bool private_members)
  74. {
  75. view_lines.del();
  76. view_comments =comments;
  77. view_funcs =funcs;
  78. view_func_bodies =func_bodies;
  79. view_private_members=private_members;
  80. // copy to 'view_lines' hiding function bodies if necessary
  81. Int func_level=0;
  82. Bool last_was_bracket=false; // if last encountered char was ')'
  83. Int bracket_line=-1, bracket_col=-1;
  84. FREPAD(y, lines)
  85. {
  86. Line &src=lines[y];
  87. if(!(func_level && !src.length())) // don't copy if we're inside function and the line is empty
  88. {
  89. ViewLine &dest=view_lines.New(); dest.includeLine(y); dest.source=this;
  90. FREPA(src)
  91. {
  92. if(!func_bodies && !func_level && src.Type(i)==TOKEN_OPERATOR && src[i]==')'){last_was_bracket=true; bracket_line=y; bracket_col=i;}
  93. if((func_level || last_was_bracket) && src.Type(i)==TOKEN_OPERATOR && src[i]=='{')
  94. {
  95. if(!func_level)
  96. if(CodeLine *cl=FindLine(view_lines, bracket_line))
  97. {
  98. if(lines[bracket_line][bracket_col+1]==' ' && bracket_line==y)cl->remove(bracket_col+1);
  99. cl->insert(bracket_col+1, ";", TOKEN_OPERATOR);
  100. }
  101. func_level++;
  102. }
  103. if(!func_level)dest.cols.New().set(src[i], i, y, -1, src.Type(i));else dest.cols.New().remove();
  104. if( func_level && src.Type(i)==TOKEN_OPERATOR && src[i]=='}')func_level--;
  105. if(TokenType(src.Type(i)) && src[i]!=')')
  106. {
  107. if((src[i]=='C' && src.Type(i)==TOKEN_CODE && src.Type(i+1)!=TOKEN_CODE && src.Type(i-1)!=TOKEN_CODE) // C macro for const
  108. || (src.Type(i)==TOKEN_KEYWORD )) // const keyword (for simplicity just check if it's a keyword)
  109. {
  110. bracket_line=y; bracket_col=i;
  111. }else
  112. {
  113. last_was_bracket=false;
  114. }
  115. }
  116. }
  117. }
  118. }
  119. // remove "private" until "}" or "public" or "protected" found
  120. if(!private_members)
  121. {
  122. Int private_level=0;
  123. FREPA(view_lines)
  124. {
  125. ViewLine &line=view_lines[i];
  126. if(private_level && !line.cols.elms()) // if it's an empty line and we're inside 'private_level' then remove fully
  127. {
  128. view_lines.remove(i--, true); // decrease 'i' because it will get increased in FREPA
  129. }else
  130. FREPA(line)
  131. {
  132. if(!private_level && !lines[line.line()].preproc && line.isKeyword(i, "private") && line[i+7]==':')private_level=1; // check for ':' to overcome "struct A : private B" issues
  133. if( private_level)
  134. {
  135. if(!lines[line.line()].preproc)
  136. {
  137. if(line.CodeLine::type(i)==TOKEN_OPERATOR)if(line[i]=='{')private_level++;else if(line[i]=='}')private_level--;
  138. if(private_level==1 && (line.isKeyword(i, "public") || line.isKeyword(i, "protected")))private_level=0;
  139. }
  140. if(private_level)line.cols[i].remove();else
  141. { // if we stopped removing private
  142. REPD(j, i)if(ValidType(line.cols[j].type))goto at_least_one_valid; // check if there's at least one valid character in current line before current position
  143. REPD(j, i) line.cols[j].type=TOKEN_NONE; // if not, then force all previous characters as spaces
  144. at_least_one_valid:;
  145. }
  146. }
  147. }
  148. }
  149. }
  150. if(!comments)
  151. {
  152. REPA(view_lines)
  153. {
  154. ViewLine &line=view_lines[i];
  155. FREPA(line.cols)if(line.cols[i].type==TOKEN_COMMENT)if(line.starts(i, SEP_LINE))i+=Length(SEP_LINE)-1;else line.cols[i].remove();
  156. }
  157. }
  158. // clean
  159. if(!comments || !funcs || !func_bodies || !private_members)Clean(view_lines);
  160. // set gui text
  161. FREPA(view_lines){ViewLine &line=view_lines[i]; T+=line.create(S, &CE.ts); line.setRect(i);}
  162. }
  163. /******************************************************************************/
  164. // UPDATE
  165. /******************************************************************************/
  166. GuiObj* Source::test(C GuiPC &gpc, C Vec2 &pos, GuiObj* &mouse_wheel)
  167. {
  168. GuiObj *prev=mouse_wheel, *go=super::test(gpc, pos, mouse_wheel);
  169. if(go && Kb.ctrlCmd() && prev==mouse_wheel)mouse_wheel=this; // if didn't change mouse wheel focus and has general focus then set wheel focus as well (needed for zooming)
  170. return go;
  171. }
  172. void Source::update(C GuiPC &gpc)
  173. {
  174. if(Kb.ctrlCmd() && contains(Gui.wheel())){CE.zoom(Ms.wheelI()); Ms.eatWheel();}
  175. super::update(gpc);
  176. if(slidebar[1].button[1]()
  177. || slidebar[1].button[2]())
  178. if(!isCurVisible())
  179. {
  180. forceCreateNextUndo(); clearSuggestions(); sel=-1;
  181. Clamp(cur.y, viewBeginPos(), viewEndPos());
  182. }
  183. highlight_time-=Time.ad();
  184. if(hasKbFocus())
  185. {
  186. Kb.requestTextInput();
  187. if(Kb.ctrlCmd() && !Kb.alt() && Kb.b(KB_UP ))slidebar[1].button[1].push(); // scroll up
  188. if(Kb.ctrlCmd() && !Kb.alt() && Kb.b(KB_DOWN))slidebar[1].button[2].push(); // scroll down
  189. for(; Kb.k.any(); Kb.nextKey())
  190. {
  191. if(Kb.k(KB_BACK ) && !Kb.k.alt()){if(Kb.k.ctrlCmd())delWordBack ();else delBack ();}else
  192. if(Kb.k(KB_DEL ) && !Kb.k.shift() && !Kb.k.alt()){if(Kb.k.ctrlCmd())delWordForward();else delForward();}else
  193. if(Kb.k(KB_LEFT ) && !Kb.k.alt()){if(Kb.k.ctrlCmd())curPrevWord ();else curLeft ();}else
  194. if(Kb.k(KB_RIGHT) && !Kb.k.alt()){if(Kb.k.ctrlCmd())curNextWord ();else curRight ();}else
  195. if(Kb.k(KB_HOME ) && !Kb.k.alt()){if(Kb.k.ctrlCmd())curViewBegin ();else /*if(suggestions_region.visible())setSuggestion(0 );else */curLineBegin();}else
  196. if(Kb.k(KB_END ) && !Kb.k.alt()){if(Kb.k.ctrlCmd())curViewEnd ();else /*if(suggestions_region.visible())setSuggestion(suggestions_list.elms());else */curLineEnd ();}else
  197. if(Kb.k(KB_PGUP ) && !Kb.k.alt()){if(Kb.k.ctrlCmd())curDocBegin ();else if(suggestions_region.visible())setSuggestion(suggestions_list.cur-Trunc(suggestions_region.slidebar[1].length()/CE.ts.lineHeight()));else curPageUp ();}else
  198. if(Kb.k(KB_PGDN ) && !Kb.k.alt()){if(Kb.k.ctrlCmd())curDocEnd ();else if(suggestions_region.visible())setSuggestion(suggestions_list.cur+Trunc(suggestions_region.slidebar[1].length()/CE.ts.lineHeight()));else curPageDown();}else
  199. if( Kb.k.shift() && Kb.k(KB_DEL ))cut ();else
  200. if( Kb.k.shift() && Kb.k(KB_INS ))paste(null, !Kb.k.ctrlCmd());else
  201. if( Kb.k.ctrlCmd() && Kb.k(KB_INS ))copy ();else
  202. if( Kb.k.ctrlCmd() && Kb.k.alt() && Kb.k(KB_UP ))curPrevLevelBracket();else
  203. if( Kb.k.ctrlCmd() && Kb.k.alt() && Kb.k(KB_DOWN))curNextLevelBracket();else
  204. if(!Kb.k.ctrlCmd() && Kb.k.alt() && Kb.k(KB_UP ))curPrevBracket ();else
  205. if(!Kb.k.ctrlCmd() && Kb.k.alt() && Kb.k(KB_DOWN))curNextBracket ();else
  206. if(!Kb.k.ctrlCmd() && !Kb.k.alt() && Kb.k(KB_UP )){if(suggestions_region.visible())setSuggestion(suggestions_list.cur-1);else curUp ();}else
  207. if(!Kb.k.ctrlCmd() && !Kb.k.alt() && Kb.k(KB_DOWN)){if(suggestions_region.visible())setSuggestion(suggestions_list.cur+1);else curDown();}else
  208. // overwrite
  209. if(Kb.kf(KB_INS) && !Kb.k.ctrlCmd() && !Kb.k.shift() && !Kb.k.alt())Overwrite^=1;else
  210. // escape
  211. if(KbSc(KB_ESC).pd() || Kb.k(KB_NAV_BACK))
  212. {
  213. if(suggestions_region.visible())clearSuggestions();else // hide suggestions
  214. if(CE.visibleOutput() || CE.visibleAndroidDevLog())
  215. {
  216. CE.visibleOutput (false);
  217. CE.visibleAndroidDevLog(false);
  218. }else break; // don't eat the key when not processed
  219. }else
  220. // enter
  221. if(Kb.k(KB_ENTER) && Kb.k.ctrlCmd() && !Kb.k.shift())
  222. {
  223. if(Kb.k.alt())findAllReferences();
  224. else jumpToCur();
  225. }else
  226. if(Kb.k(KB_ENTER) && !Const)
  227. {
  228. if(Kb.k.ctrlCmd() && Kb.k.shift())separator();else
  229. {
  230. delSel(true, false); // sets undo
  231. if(cur.y>=lines.elms())lines.setNum(cur.y+1);
  232. if(!Kb.k.ctrlCmd() && Kb.k.alt() && Kb.k.shift()){curNextBracket(); curRight();} // on Shift+Alt+Enter, used for writing new methods
  233. if( Kb.k.ctrlCmd() && !Kb.k.alt() ){cur.x=lines[cur.y].length(); clearSuggestions();} // Ctrl+Enter = move to end of line and press enter
  234. Int pos;
  235. Line &prev=lines[cur.y];
  236. if(suggestions_list()) // use suggestion
  237. {
  238. autoComplete(true, false);
  239. }else
  240. if(CE.options.guided() &&
  241. (
  242. (prev.chrNext (cur.x, &pos)=='{' && prev.chrBefore(pos)) // detect if we're before '{' and there's something before '{'
  243. ||prev.chrBefore(cur.x, &pos)=='{' // or after '{'
  244. )
  245. )
  246. {
  247. if(prev.chrBefore(pos)) // if there's something before '{'
  248. {
  249. // move '{' to next line
  250. Line &next =lines.NewAt(cur.y+1); REP(AlignToTab(prev.start()) )next +=' '; next+='{'; // set "{"
  251. Line &next2=lines.NewAt(cur.y+2); REP(AlignToTab(prev.start())+TabLength)next2+=' '; Int pos2; if(prev.chrNext(pos+1, &pos2))for(Int i=pos2; i<prev.length(); i++)next2.append(prev[i], prev.Type(i)); // set " remains"
  252. cur.x=AlignToTab(prev.start())+TabLength; // set cursor to " |remains"
  253. Int level=0; FREPAD(end, next2) // find any loose '}' in remainings, then move it to next line
  254. {
  255. if(next2[end]=='{' && next2.Type(end)==TOKEN_OPERATOR)level++;
  256. if(next2[end]=='}' && next2.Type(end)==TOKEN_OPERATOR)level--;
  257. if(level<0)
  258. {
  259. Line &next3=lines.NewAt(cur.y+3); REP(AlignToTab(prev.start()))next3+=' '; for(Int i=end; i<next2.length(); i++)next3+=next2[i]; // set "}"
  260. next2.clip(end);
  261. exist(0, cur.y+4); // make sure that there is at least 1 line after newly added '}'
  262. break;
  263. }
  264. }
  265. cur.y+=2;
  266. prev.clip(pos); prev.clipSpaces();
  267. }else
  268. {
  269. Line &next=lines.NewAt(cur.y+1); REP(AlignToTab(prev.start())+TabLength)next+=' '; Int pos2; if(prev.chrNext(pos+1, &pos2))for(Int i=pos2; i<prev.length(); i++)next.append(prev[i], prev.Type(i)); // set " remains"
  270. cur.x=AlignToTab(prev.start())+TabLength; // set cursor to " |remains"
  271. Int level=0; FREPAD(end, next) // find any loose '}' in remainings, then move it to next line
  272. {
  273. if(next[end]=='{' && next.Type(end)==TOKEN_OPERATOR)level++;
  274. if(next[end]=='}' && next.Type(end)==TOKEN_OPERATOR)level--;
  275. if(level<0)
  276. {
  277. Line &next2=lines.NewAt(cur.y+2); REP(AlignToTab(prev.start()))next2+=' '; for(Int i=end; i<next.length(); i++)next2+=next[i]; // set "}"
  278. next.clip(end);
  279. exist(0, cur.y+3); // make sure that there is at least 1 line after newly added '}'
  280. break;
  281. }
  282. }
  283. cur.y++;
  284. prev.clip(pos+1); prev.clipSpaces();
  285. }
  286. changed(cur.y-4, 5);
  287. }else
  288. {
  289. Line &next=lines.NewAt(cur.y+1);
  290. Int start=(prev.empty() ? prev.length() : prev.start()); MIN(start, cur.x);
  291. FREP(start)next+=' '; for(Int i=cur.x; i<prev.length(); i++)next+=prev[i];
  292. prev.clip(cur.x);
  293. cur.x=start;
  294. cur.y++;
  295. changed(cur.y-1, 2);
  296. }
  297. clearSuggestions();
  298. makeCurVisible();
  299. }
  300. }else
  301. // tab
  302. if(Kb.k(KB_TAB) && !Kb.k.ctrlCmd())
  303. {
  304. if(!Const)
  305. if(!CE.options.ac_on_enter() && suggestions_list())autoComplete();else
  306. {
  307. setUndo();
  308. if(sel.y>=0) // move whole selection
  309. {
  310. VecI2 min, max; curSel(min, max);
  311. for(Int y=min.y; y<Min(max.y+(max.x!=0), lines.elms()); y++)
  312. {
  313. if(Kb.k.shift())
  314. {
  315. Int processed=0; REP(TabLength)if(lines[y].first()!=' ')break;else{lines[y].remove(0); processed++;}
  316. if(sel.y==y)MAX(sel.x-=processed, 0);
  317. if(cur.y==y)MAX(cur.x-=processed, 0);
  318. }else
  319. {
  320. if(lines[y].length())REP(TabLength)lines[y].insert(0, ' ');
  321. }
  322. }
  323. if(!Kb.k.shift())
  324. {
  325. if(sel.x)sel.x+=TabLength;
  326. if(cur.x)cur.x+=TabLength;
  327. }
  328. if(sel==cur)sel=-1;
  329. changed(min.y, max.y-min.y+1);
  330. clearSuggestions();
  331. }else // in single line only (at cursor)
  332. {
  333. if(Kb.k.shift()) // backwards
  334. {
  335. if(!InRange(cur.y, lines) )cur.x=0;else
  336. if(cur.x>lines[cur.y].length())cur.x=lines[cur.y].length();else
  337. {
  338. Int dest_x=Max(0, (cur.x-1)/TabLength*TabLength);
  339. for(; cur.x>dest_x; cur.x--)
  340. {
  341. if(lines[cur.y][cur.x-1]!=' ')break;
  342. lines[cur.y].remove(cur.x-1);
  343. }
  344. }
  345. }else // forward
  346. {
  347. exist(cur.x, cur.y);
  348. Int dest_x=(cur.x+TabLength)/TabLength*TabLength,
  349. num =dest_x-cur.x;
  350. if (Overwrite)lines[cur.y].remove(cur.x, num);
  351. REP(num )lines[cur.y].insert(cur.x, ' ');
  352. cur.x=dest_x;
  353. }
  354. changed(cur.y);
  355. clearSuggestions();
  356. makeCurVisible();
  357. }
  358. }
  359. }else
  360. // insert single character
  361. if(Kb.k.c && !Kb.k.ctrlCmd() && !Kb.k.lalt() && !Const)
  362. {
  363. CE.markCurPos();
  364. if(Suggestion *sugg=suggestions_list())
  365. if(CodeCharType(Kb.k.c)!=CHART_CHAR)
  366. {
  367. Bool ac=!CE.options.ac_on_enter(); // if want to auto-complete
  368. if( ac)
  369. {
  370. // check if we typed a label (for which auto-complete should be disabled)
  371. if(Kb.k(':'))
  372. {
  373. Int i; if(Token *token=findPrevToken(cur, i))if(token->type==TOKEN_CODE && !(token->parent && token->parent->type==Symbol::CLASS) && InRange(i-1, tokens)) // labels can't be declared inside class (there private/protected/public can be used)
  374. {
  375. Token &token=*tokens[i-1];
  376. if(token==';' || token=='{' || token=='}')ac=false;
  377. }
  378. }
  379. }
  380. if(ac)
  381. {
  382. autoComplete(false, true, !Kb.k('('), false, Kb.k('.') || Kb.k('?'));
  383. }else
  384. {
  385. if(InRange(suggestions_pos.y, lines))
  386. {
  387. Str &src=lines[suggestions_pos.y];
  388. if(InRange(suggestions_pos.x, src))
  389. {
  390. Str s=src()+suggestions_pos.x; s.clip(cur.x-suggestions_pos.x);
  391. if(Equal(s, sugg->text, true))SuggestionsUsed(s);
  392. }
  393. }
  394. }
  395. }
  396. Bool space=false;
  397. char_loop:
  398. {
  399. space|=Kb.k(' ');
  400. Bool has_sel=(sel.x>=0);
  401. if(has_sel)delSel(); // already calls undo
  402. else setUndo((Kb.k('{') || Kb.k('(') || Kb.k('[') || Kb.k(';') || Kb.k(',') || Kb.k('"') || Kb.k('\'') || Kb.k('?') || Kb.k(':')) ? INS_CHRS : INS_CHR);
  403. exist(cur.x, cur.y);
  404. Line &prev=lines[cur.y];
  405. // add to suggestions a word we typed before
  406. if(CodeCharType(Kb.k.c)!=CHART_CHAR && !Overwrite)
  407. {
  408. if(CodeCharType(prev[cur.x-1])==CHART_CHAR)
  409. {
  410. Str w; Int s=prev.wordStart(cur.x-1); for(; s<cur.x; s++)w+=prev[s];
  411. SuggestionsUsed(w);
  412. }
  413. }
  414. if(CE.options.guided())
  415. {
  416. if(Kb.k('{')) // automatically place cursor at the end of the line when adding new bracket
  417. {
  418. if(!prev.empty() && !TextType(prev.Type(cur.x)))
  419. {
  420. if(prev.chrBefore(cur.x)==')' && prev.chrNext(cur.x)!=')') // we're in "if(x)|y=2;" situation -> change to "if(x){|y=2;}", support also "if(x)|if(x)x;else x;" -> "if(x){|if(x)x;else x;}"
  421. {
  422. for(Int i=cur.x, brackets=0, level=0; i<prev.length(); i++)//if(prev[i]==';' && prev.Type(i)==TOKEN_OPERATOR) // find first ';'
  423. {
  424. TOKEN_TYPE type=prev.Type(i); if(type!=TOKEN_COMMENT && !TextType(type))
  425. {
  426. Char c=prev[i];
  427. if(c=='(')brackets++;else
  428. if(c==')')brackets--;else
  429. if(!brackets)
  430. {
  431. if(c=='{')level++;else
  432. if(c=='}')level--;
  433. if(!level)
  434. {
  435. if(c=='}' || c==';')
  436. {
  437. Bool next_else=false; // check if we're followed by else
  438. for(Int j=i+1; j<prev.length(); j++)
  439. {
  440. TOKEN_TYPE type=prev.Type(j); if(type!=TOKEN_COMMENT && type!=TOKEN_NONE)
  441. {
  442. if(type==TOKEN_KEYWORD && prev[j]=='e' && prev[j+1]=='l' && prev[j+2]=='s' && prev[j+3]=='e')next_else=true;
  443. break;
  444. }
  445. }
  446. if(!next_else)
  447. {
  448. prev.insert(i+1, '}', TOKEN_OPERATOR); // insert } after ; -> ";}"
  449. break;
  450. }
  451. }
  452. }
  453. }
  454. }
  455. }
  456. }else // we're in "void func(masndas|)" situation -> move to "void func(masndas)|"
  457. if(insideRSTBrackets(cur))
  458. {
  459. cur.x=prev.end()+1;
  460. exist(cur.x, cur.y);
  461. }
  462. }
  463. }else
  464. if(Kb.k(';')) // automatically place cursor at the end of params "|..)" -> "..)|"
  465. {
  466. Int bottom_level=0, level=0;
  467. if(!TextType(prev.Type(cur.x)))
  468. if(!Contains(prev, "for", true, true)) // 'for' is the only keyword that allows ';' inside "()" brackets
  469. for(Int i=cur.x; i<prev.length(); i++)
  470. {
  471. if((prev[i]=='(' || prev[i]=='[') && prev.Type(i)==TOKEN_OPERATOR)level++;
  472. if((prev[i]==')' || prev[i]==']') && prev.Type(i)==TOKEN_OPERATOR)level--;
  473. if(level<bottom_level)
  474. {
  475. bottom_level=level;
  476. cur.x=i+1;
  477. }
  478. }
  479. }
  480. }
  481. if(Overwrite && !has_sel)prev.remove(cur.x); // don't use overwrite if we have selection
  482. prev.insert(cur.x, Kb.k.c);
  483. if(CE.options.guided() && !Overwrite)
  484. {
  485. prev.resetType(); // this will help detect the TOKEN_TYPE of inserted character
  486. if(!Kb.k(':') && !Kb.k(' ') && !Kb.k(';') && prev[cur.x-1]==':' && prev.Type(cur.x-1)==TOKEN_OPERATOR && prev[cur.x-2]!=':') // convert X:|X -> X : |X (';' because of labels "label:;")
  487. {
  488. if(prev[cur.x-2]!=' ')prev.insert(cur.x++ -1, ' ');
  489. if(prev[cur.x+1]!=' ')prev.insert(cur.x++ , ' ');
  490. }
  491. if(Kb.k('{'))
  492. {
  493. if(prev.start()==cur.x && prev.end()==cur.x+1) // if line consists only of '{'
  494. {
  495. if(!(InRange(cur.y+1, lines) && (lines[cur.y+1][cur.x]=='}' || lines[cur.y+1].start()>cur.x)))
  496. {
  497. lines.NewAt( cur.y+1);
  498. lines.NewAt( cur.y+2);
  499. exist(cur.x, cur.y+2);
  500. lines[cur.y+2].insert(cur.x, '}');
  501. exist(0, cur.y+3); // make sure that there is at least 1 line after newly added '}'
  502. cur.y++;
  503. cur.x+=TabLength-1;
  504. REP(cur.x+1)lines[cur.y].insert(0, ' ');
  505. }
  506. }else
  507. {
  508. if(prev.end()==cur.x+1)prev.append('}');
  509. }
  510. }else
  511. if(Kb.k('}'))
  512. {
  513. // align to previous '{'
  514. Int level=0;
  515. for(VecI2 p=cur-VecI2(1, 0); lineValid(p); dec(p))
  516. {
  517. if(T[p]=='{' && Type(p)==TOKEN_OPERATOR)level++;
  518. if(T[p]=='}' && Type(p)==TOKEN_OPERATOR)level--;
  519. if(level>0)
  520. {
  521. for(; cur.x>p.x && prev[cur.x-1]==' '; )prev.remove(--cur.x);
  522. // overwrite any existing '}' that shouldn't be there
  523. if(prev[cur.x+1]=='}')prev.remove(cur.x);
  524. break;
  525. }
  526. }
  527. }else
  528. if(Kb.k('('))
  529. {
  530. prev.insert(cur.x+1, ')');
  531. //if(prev.Type(cur.x+1)==TOKEN_NONE || prev[cur.x+1]==';' || prev[cur.x+1]==',')prev.insert(cur.x+1, ')');
  532. }else
  533. if(Kb.k(')'))
  534. {
  535. if(prev[cur.x+1]==')') // if there's another ')' after the one we just wrote to, then check if we can remove it
  536. {
  537. Int left =0; for(VecI2 p=cur ; lineValid(p); dec(p))if(Type(p)==TOKEN_OPERATOR){Char c=T[p]; if(c=='{' || c=='}' || c==';')break; if(c=='(')left ++; if(c==')')left --;}
  538. Int right=0; for(VecI2 p=cur+VecI2(1, 0); lineValid(p); inc(p))if(Type(p)==TOKEN_OPERATOR){Char c=T[p]; if(c=='{' || c=='}' || c==';')break; if(c=='(')right++; if(c==')')right--;}
  539. if(left<=-right)prev.remove(cur.x); // right side already has enough brackets, we can remove this one
  540. }
  541. }else
  542. if(Kb.k('['))
  543. {
  544. prev.insert(cur.x+1, ']');
  545. }else
  546. if(Kb.k(']'))
  547. {
  548. if(prev[cur.x+1]==']') // if there's another ']' after the one we just wrote to, then check if we can remove it
  549. {
  550. Int left =0; for(VecI2 p=cur ; lineValid(p); dec(p))if(Type(p)==TOKEN_OPERATOR){Char c=T[p]; if(c=='{' || c=='}' || c==';')break; if(c=='[')left ++; if(c==']')left --;}
  551. Int right=0; for(VecI2 p=cur+VecI2(1, 0); lineValid(p); inc(p))if(Type(p)==TOKEN_OPERATOR){Char c=T[p]; if(c=='{' || c=='}' || c==';')break; if(c=='[')right++; if(c==']')right--;}
  552. if(left<=-right)prev.remove(cur.x); // right side already has enough brackets, we can remove this one
  553. }
  554. }else
  555. if(Kb.k('"'))
  556. {
  557. if(prev[cur.x+1]=='"')prev.remove(cur.x);else // if we're already followed by '"'
  558. {TOKEN_TYPE t=prev.Type(cur.x-1); if(!TextType(t))prev.insert(cur.x+1, '"');}
  559. }else
  560. if(Kb.k('\''))
  561. {
  562. if(prev[cur.x+1]=='\'')prev.remove(cur.x);else // if we're already followed by '\''
  563. {TOKEN_TYPE t=prev.Type(cur.x-1); if(!TextType(t) && t!=TOKEN_COMMENT)prev.insert(cur.x+1, '\'');}
  564. }else
  565. if(Kb.k('.'))
  566. {
  567. // overwrite any existing '.' that shouldn't be there
  568. TOKEN_TYPE t=prev.Type(cur.x-1); if(!TextType(t))if(prev[cur.x+1]=='.')prev.remove(cur.x);
  569. }else
  570. if(Kb.k(','))
  571. {
  572. if(prev.Type(cur.x)==TOKEN_OPERATOR) // if this became an operator
  573. {
  574. if(prev.Type(cur.x+1)!=TOKEN_NONE)prev.insert(cur.x+1, ' '); cur.x++; // after ','
  575. }
  576. }else
  577. if(Kb.k('?'))
  578. {
  579. if(prev.Type(cur.x)==TOKEN_OPERATOR) // if this became an operator
  580. {
  581. if(prev.Type(cur.x-1)!=TOKEN_NONE)prev.insert(cur.x++, ' '); // before '?'
  582. if(prev.Type(cur.x+1)!=TOKEN_NONE)prev.insert(cur.x+1, ' '); cur.x++; // after '?'
  583. }
  584. }else
  585. if(Kb.k(':')) // "case X:", "label:", "a ? b : c"
  586. {
  587. if(prev.Type(cur.x)==TOKEN_OPERATOR) // if this became an operator
  588. for(VecI2 p=cur-VecI2(1, 0); lineValid(p); dec(p))
  589. {
  590. if(Type(p)==TOKEN_OPERATOR)
  591. {
  592. Char c=T[p];
  593. if(c=='?')
  594. {
  595. if(prev.Type(cur.x-1)!=TOKEN_NONE)prev.insert(cur.x++, ' '); // before ':'
  596. if(prev.Type(cur.x+1)!=TOKEN_NONE)prev.insert(cur.x+1, ' '); cur.x++; // after ':'
  597. break;
  598. }
  599. if(c=='{' || c=='}' || c==';')break;
  600. }else
  601. if(Type(p)==TOKEN_KEYWORD)
  602. {
  603. CChar *s=lines[p.y]()+p.x;
  604. if(Starts(s, "case" , true, true)
  605. || Starts(s, "default", true, true))
  606. {
  607. if(prev.Type(cur.x+1)!=TOKEN_NONE)prev.insert(cur.x+1, ' '); cur.x++; // after ':'
  608. break;
  609. }
  610. }
  611. }
  612. }else
  613. if(Kb.k('&')) // check for '&&'
  614. {
  615. if(prev[cur.x-1]=='&' && prev.Type(cur.x-1)==TOKEN_OPERATOR)
  616. {
  617. if(prev.Type(cur.x-2)!=TOKEN_NONE)prev.insert(cur.x++ -1, ' '); // before "&&"
  618. if(prev.Type(cur.x+1)!=TOKEN_NONE)prev.insert(cur.x +1, ' '); cur.x++; // after "&&"
  619. }
  620. }else
  621. if(Kb.k('|')) // check for '||'
  622. {
  623. if(prev[cur.x-1]=='|' && prev.Type(cur.x-1)==TOKEN_OPERATOR)
  624. {
  625. if(prev.Type(cur.x-2)!=TOKEN_NONE)prev.insert(cur.x++ -1, ' '); // before "||"
  626. if(prev.Type(cur.x+1)!=TOKEN_NONE)prev.insert(cur.x +1, ' '); cur.x++; // after "||"
  627. }
  628. }
  629. }
  630. cur.x++;
  631. CE.markCurPos();
  632. if(CE.options.guided())
  633. {
  634. if(Kb.k(';') && prev.Type(cur.x )!=TOKEN_NONE && prev[cur.x]!='}' && prev.Type(cur.x-1)==TOKEN_OPERATOR)prev.insert(cur.x++ , ' ');else // if we typed ';' which is followed by something then add space
  635. if(prev[cur.x-2]==',' && prev.Type(cur.x-1)!=TOKEN_NONE && prev.Type(cur.x-2)==TOKEN_OPERATOR)prev.insert(cur.x++ -1, ' ');else // if we typed something after ',' then insert space between that
  636. if(prev[cur.x-2]==';' && !Kb.k(' ') && !Kb.k('}') )prev.insert(cur.x++ -1, ' '); // if we typed something after ';' then insert space between that
  637. if(Kb.k(':')) // if we typed ':' after private,protected,public then convert " private:|" to "private:\n |"
  638. {
  639. Int start=prev .start();
  640. CChar *s =prev()+start ;
  641. if(Starts(s, "public" , true, true)
  642. || Starts(s, "protected", true, true)
  643. || Starts(s, "private" , true, true))
  644. {
  645. if(prev[cur.x-2]==' '){prev.remove(cur.x-2); cur.x--;} // remove any space between keyword and ':' ("private :|" -> "private:|")
  646. Int rem=Min(start, TabLength); prev.remove(0, rem); start-=rem; start+=TabLength;
  647. Line &l=lines.NewAt(cur.y+1); REP(start)l.insert(0, ' ');
  648. cur.y++; cur.x=start;
  649. }
  650. }
  651. if( Kb.k('/') && prev[cur.x-2]=='/' && prev[cur.x-3]!='/' && prev.Type(cur.x-3)!=TOKEN_NONE && !TextType(prev.Type(cur.x-2))){prev.insert(cur.x-2, ' '); cur.x++;} // if we started typing a comment then make sure it's separated with at least one space
  652. if(!Kb.k('/') && !Kb.k(' ') && prev[cur.x-2]=='/' && prev[cur.x-3]=='/' && !TextType(prev.Type(cur.x-2))){prev.insert(cur.x-1, ' '); cur.x++;} // if we started typing after comment then make sure it's separated with at least one space
  653. }
  654. Keyboard::Key *k=Kb.nextKeyPtr(); if(k && k->c && !k->ctrlCmd() && !k->lalt()){Kb.nextKey(); goto char_loop;}
  655. }
  656. changed(cur.y-1, 3);
  657. makeCurVisible();
  658. listSuggestions(space ? -1 : 0);
  659. CE.markCurPos();
  660. }else
  661. break; // stop and don't eat using 'nextKey' in the 'for' loop
  662. }
  663. }else
  664. if(Gui.kb()==&suggestions_textline)
  665. {
  666. if(Kb.ctrlCmd() && Kb.b(KB_UP ))slidebar[1].button[1].push();else // scroll up
  667. if(Kb.ctrlCmd() && Kb.b(KB_DOWN))slidebar[1].button[2].push();else // scroll down
  668. if(Kb.k(KB_ESC) || Kb.k(KB_NAV_BACK)){clearSuggestions(); Kb.eatKey();}else // hide suggestions
  669. if(Kb.k(KB_ENTER)){autoComplete(); Kb.eatKey();}else
  670. if(Kb.k(KB_PGUP))setSuggestion(suggestions_list.cur-Trunc(suggestions_region.slidebar[1].length()/CE.ts.lineHeight()));else
  671. if(Kb.k(KB_PGDN))setSuggestion(suggestions_list.cur+Trunc(suggestions_region.slidebar[1].length()/CE.ts.lineHeight()));else
  672. if(Kb.k(KB_UP ))setSuggestion(suggestions_list.cur-1);else
  673. if(Kb.k(KB_DOWN))setSuggestion(suggestions_list.cur+1);
  674. }
  675. REPA(MT)if(hasFocus(MT.guiObj(i)))
  676. {
  677. if(MT.bp(i, 1))if(Kb.ctrlCmd())CE.nextCurPos();else CE.prevCurPos();
  678. if(CE.view_mode())
  679. {
  680. if(MT.bp(i, 0) || MT.bp(i, 2))
  681. {
  682. VecI2 view=Trunc(posCur(MT.pos(i))), real;
  683. if(viewToReal(view, real))
  684. {
  685. SymbolPtr symbol;
  686. Macro *macro=null;
  687. UID id;
  688. if(getSymbolMacroID(real, symbol, macro, id))
  689. {
  690. cur=real;
  691. if(Kb.ctrlCmd())
  692. {
  693. if(symbol )CE.findAllReferences(symbol() );else
  694. if(macro )CE.findAllReferences(macro->name);else
  695. if(id.valid())CE.findAllReferences(id );
  696. }else
  697. {
  698. if(symbol )CE.jumpTo(symbol());else
  699. if(macro )CE.jumpTo(macro );else
  700. if(id.valid())CE.cei().elmOpen(id);
  701. }
  702. }
  703. }
  704. }
  705. }else
  706. {
  707. Vec2 c=posCur(MT.pos(i)); MAX(c.x, 0); MAX(c.y, 0);
  708. if(MT.bp(i, 2) || (Kb.ctrlCmd() && MT.bp(i, 0)))
  709. {
  710. sel=sel_temp=-1;
  711. cur.set(Trunc(c.x), Trunc(c.y));
  712. if(Kb.ctrlCmd() && MT.bp(i, 2))findAllReferences();
  713. else jumpToCur();
  714. }else
  715. if(MT.bd(i, 0))
  716. {
  717. sel_temp=-1;
  718. cur.set(Trunc(c.x), Trunc(c.y));
  719. curClip();
  720. selWord();
  721. }else
  722. if(MT.bp(i, 0))
  723. {
  724. forceCreateNextUndo(); clearSuggestions();
  725. if(Kb.shift() && (sel.x<0 || sel.y<0))sel=cur;
  726. cur.set(Round(c.x), Trunc(c.y));
  727. curClip();
  728. if(!Kb.shift()){sel=-1; sel_temp=cur;}
  729. makeCurVisible();
  730. }else
  731. if(MT.b(i, 0) && sel_temp.x>=0)
  732. {
  733. if(MT.pos(i).y>=_crect.max.y)slidebar[1].button[1].push();else
  734. if(MT.pos(i).y<=_crect.min.y)slidebar[1].button[2].push();
  735. cur.set(Round(c.x), Trunc(c.y));
  736. curClip();
  737. sel=sel_temp;
  738. if(sel==cur)sel=-1;
  739. }else
  740. if(MT.bp(i, 4))delBack();
  741. }
  742. }
  743. if(Gui.ms()==&suggestions_list)
  744. {
  745. if(Ms.bp(0))
  746. {
  747. if(suggestions_list.lit>=0)suggestions_list.cur=suggestions_list.lit;
  748. autoComplete();
  749. }else
  750. if(Ms.b(1))
  751. {
  752. if(suggestions_list.lit>=0)suggestions_list.cur=suggestions_list.lit;
  753. }else
  754. if(Ms.bp(2))
  755. {
  756. clearSuggestions();
  757. }else
  758. if(Ms.bp(4))
  759. {
  760. delBack();
  761. }
  762. }
  763. REPA(Touches)if(Touches[i].guiObj()==&suggestions_list && Touches[i].rs())
  764. {
  765. autoComplete();
  766. }
  767. if(CE.view_mode() && contains(Gui.kb()))
  768. {
  769. if(Kb.b(KB_LEFT ))slidebar[0].button[1].push();
  770. if(Kb.b(KB_RIGHT))slidebar[0].button[2].push();
  771. if(Kb.b(KB_UP ))slidebar[1].button[1].push();
  772. if(Kb.b(KB_DOWN ))slidebar[1].button[2].push();
  773. if(Kb.k(KB_PGUP ))scrollY (-slidebar[1].length());
  774. if(Kb.k(KB_PGDN ))scrollY ( slidebar[1].length());
  775. if(Kb.k(KB_HOME ))scrollToY (0);
  776. if(Kb.k(KB_END ))scrollEndY( );
  777. }
  778. setOffset();
  779. }
  780. /******************************************************************************/
  781. // DRAW
  782. /******************************************************************************/
  783. static void HighlightFind(C Str &str, C Rect &rect, C GuiPC &gpc)
  784. {
  785. for(Int offset=0; ; )
  786. {
  787. Int match_length, i=TextPosSkipSpaceI(str()+offset, CE.find.text(), match_length, CE.find.case_sensitive(), CE.find.whole_words()); if(i<0)break; offset+=i;
  788. Rect_LU(rect.lu()+gpc.offset+Vec2(offset*CE.ts.colWidth(), 0), match_length*CE.ts.colWidth(), CE.ts.lineHeight()).draw(ColorAlpha(YELLOW, 0.5f)); offset+=match_length;
  789. }
  790. }
  791. static void ShowElmName(C UID &id, C Rect &rect, C GuiPC &gpc, C VecI2 &range)
  792. {
  793. Bool valid;
  794. Str name=CE.cei().idToText(id, &valid);
  795. if( name.is())
  796. {
  797. TextStyleParams ts=CE.ts_small; ts.align=0; ts.color=Theme.colors[TOKEN_ELM_NAME];
  798. Rect_LU r(rect.lu()+gpc.offset, Max((name.length()+0.5f)*ts.colWidth(), (range.y-range.x+1)*CE.ts.colWidth()), CE.ts.lineHeight()); r+=Vec2(range.x*CE.ts.colWidth(), 0);
  799. r.draw(valid ? Theme.colors[TOKEN_ELM_BACKGROUND] : Color(148, 0, 0, 233));
  800. D.text(ts, r.center(), name);
  801. }
  802. }
  803. static void ShowElmNames(C Str &str, C Rect &rect, C GuiPC &gpc)
  804. {
  805. UID id; VecI2 range; FREPA(str)if(TextToIDAt(str, i, id, range))ShowElmName(id, rect, gpc, range);
  806. }
  807. /******************************************************************************/
  808. void Line::draw(C GuiPC &gpc)
  809. {
  810. if(visible() && gpc.visible)
  811. if(rect().max.y+gpc.offset.y>=gpc.clip.min.y
  812. && rect().min.y+gpc.offset.y<=gpc.clip.max.y)
  813. {
  814. if(!text_valid){text_valid=true; code(textCode());}
  815. GuiPC gpc2=gpc ; gpc2.offset.x+=CE. lineNumberSize();
  816. // if spacing between elements is a fixed number of pixels then flickering can occur if positions will be at 0.5 pixels (0.5, 1.5, 2.5, ..), to prevent that from happening align the vertical position
  817. gpc2.offset+=D.alignScreenToPixelOffset(Vec2(gpc2.offset.x, CE.fontSpaceOffset()+CE.ts.posY(gpc2.offset.y)));
  818. GuiPC gpc3=gpc2; gpc3.offset.y-=CE.fontSpaceOffset();
  819. // highlight symbol
  820. if(source)if(Symbol *lit=source->lit_symbol)
  821. {
  822. Memc<Token> &tokens=Tokens();
  823. REPA(tokens)
  824. {
  825. Token &token=tokens[i];
  826. if(token.symbol==lit)goto highlight;
  827. if(token==source->lit_symbol_cpp_name)
  828. {
  829. if(!token.symbol){source->parseFunc(token); if(!token.symbol)token.symbol=source->finalSymbol(token.source_index);}
  830. if( token.symbol && token.symbol->type==Symbol::FUNC_LIST)if(Symbol *token_symbol=source->finalSymbol(token.source_index))token.symbol=token_symbol;
  831. if( token.symbol)if(lit->sameSymbol(*token.symbol))
  832. {
  833. highlight:;
  834. D.clip(gpc3.clip);
  835. Rect_LU(rect().lu()+gpc3.offset+Vec2(tokens[i].col*CE.ts.colWidth(), 0), tokens[i].length()*CE.ts.colWidth(), CE.ts.lineHeight()).draw(Theme.colors[TOKEN_SYMBOL_HIGHLIGHT]);
  836. }
  837. }
  838. }
  839. }
  840. super::draw(gpc2);
  841. if(CE.find.visible() && CE.find.text().is())HighlightFind(T, rect(), gpc3);
  842. if(CE.view_elm_names )ShowElmNames (T, rect(), gpc3);
  843. }
  844. }
  845. void Source::ViewLine::draw(C GuiPC &gpc)
  846. {
  847. if(visible() && gpc.visible)
  848. if(rect().max.y+gpc.offset.y>=gpc.clip.min.y
  849. && rect().min.y+gpc.offset.y<=gpc.clip.max.y)
  850. {
  851. if(!text_valid){text_valid=true; code(textCode());}
  852. GuiPC gpc2=gpc; gpc2.offset.x+=CE. lineNumberSize();
  853. // if spacing between elements is a fixed number of pixels then flickering can occur if positions will be at 0.5 pixels (0.5, 1.5, 2.5, ..), to prevent that from happening align the vertical position
  854. gpc2.offset+=D.alignScreenToPixelOffset(Vec2(gpc2.offset.x, CE.fontSpaceOffset()+CE.ts.posY(gpc2.offset.y)));
  855. super::draw(gpc2); gpc2.offset.y-=CE.fontSpaceOffset();
  856. if(CE.find.visible() && CE.find.text().is())HighlightFind(asStr(), rect(), gpc2);
  857. if(CE.view_elm_names )ShowElmNames (asStr(), rect(), gpc2);
  858. }
  859. }
  860. /******************************************************************************/
  861. void Source::drawSelection(C Color &color, Int y, Int min_x, Int max_x)
  862. {
  863. Vec2 pos=rect().lu()+offset(); D.alignScreenToPixel(pos);
  864. pos+=D.alignScreenToPixelOffset(Vec2(pos.x, CE.fontSpaceOffset()+CE.ts.posY(pos.y)));
  865. pos+=posVisual(VecI2(min_x, y));
  866. Rect_LU(pos, (max_x>=min_x) ? CE.ts.colWidth()*(max_x-min_x) : D.w()-pos.x, CE.ts.lineHeight()).draw(color);
  867. }
  868. /******************************************************************************/
  869. void Source::drawSelection(C Color &color, C VecI2 &a, C VecI2 &b, Bool including)
  870. {
  871. VecI2 min=a, max=b;
  872. if(max.y<min.y || (max.y==min.y && max.x<min.x))Swap(min, max);
  873. if(min.y==max.y)drawSelection(color, min.y, min.x, max.x+including);else
  874. {
  875. drawSelection(color, min.y, min.x, -1);
  876. for(Int y=min.y+1; y<max.y; y++)drawSelection(color, y, 0, -1);
  877. drawSelection(color, max.y, 0, max.x+including);
  878. }
  879. }
  880. /******************************************************************************/
  881. void Source::draw(C GuiPC &gpc)
  882. {
  883. if(gpc.visible && visible())
  884. {
  885. D.clip(_crect);
  886. Vec2 offset=rect().lu()+T.offset(); D.alignScreenToPixel(offset);
  887. offset+=D.alignScreenToPixelOffset(Vec2(offset.x, CE.fontSpaceOffset()+CE.ts.posY(offset.y)));
  888. if(!CE.view_mode())
  889. {
  890. // draw selection
  891. if(sel.x>=0 && sel.y>=0)drawSelection(Theme.colors[TOKEN_SELECT], sel, cur);
  892. // draw braces highlight
  893. if(InRange(cur.y, lines))
  894. {
  895. VecI2 test=cur; Char c=lines[test.y][test.x];
  896. if(c!='{' && c!='}' && c!='(' && c!=')' && c!='[' && c!=']')c=lines[test.y][--test.x]; // try the previous char
  897. if(c=='{' || c=='}' || c=='(' || c==')' || c=='[' || c==']')
  898. {
  899. TOKEN_TYPE type=MainType(lines[test.y].Type(test.x));
  900. VecI2 pos =test; Int level=0;
  901. if(c=='{'){for(inc(pos); posValid(pos); inc(pos)){Char c=lines[pos.y][pos.x]; if(type==MainType(lines[pos.y].Type(pos.x)))if(c=='{')level++;else if(c=='}')level--; if(level<0)break;}}else
  902. if(c=='}'){for(dec(pos); posValid(pos); dec(pos)){Char c=lines[pos.y][pos.x]; if(type==MainType(lines[pos.y].Type(pos.x)))if(c=='{')level++;else if(c=='}')level--; if(level>0)break;}}else
  903. if(c=='('){for(inc(pos); posValid(pos); inc(pos)){Char c=lines[pos.y][pos.x]; if(type==MainType(lines[pos.y].Type(pos.x)))if(c=='(')level++;else if(c==')')level--; if(level<0)break;}}else
  904. if(c==')'){for(dec(pos); posValid(pos); dec(pos)){Char c=lines[pos.y][pos.x]; if(type==MainType(lines[pos.y].Type(pos.x)))if(c=='(')level++;else if(c==')')level--; if(level>0)break;}}else
  905. if(c=='['){for(inc(pos); posValid(pos); inc(pos)){Char c=lines[pos.y][pos.x]; if(type==MainType(lines[pos.y].Type(pos.x)))if(c=='[')level++;else if(c==']')level--; if(level<0)break;}}else
  906. if(c==']'){for(dec(pos); posValid(pos); dec(pos)){Char c=lines[pos.y][pos.x]; if(type==MainType(lines[pos.y].Type(pos.x)))if(c=='[')level++;else if(c==']')level--; if(level>0)break;}}
  907. Rect_LU(offset+posVisual(test), CE.ts.colWidth(), CE.ts.lineHeight()).draw(Theme.colors[TOKEN_BRACE_HIGHLIGHT]);
  908. if(pos!=test && posValid(pos)){Rect_LU(offset+posVisual(pos ), CE.ts.colWidth(), CE.ts.lineHeight()).draw(Theme.colors[TOKEN_BRACE_HIGHLIGHT]); drawSelection(Theme.colors[TOKEN_BRACE_HIGHLIGHT], pos, test, true);}
  909. }
  910. }
  911. // draw current line highlight
  912. if(Theme.colors[TOKEN_LINE_HIGHLIGHT].a)
  913. {
  914. Int line=cur.y; if(CE.view_mode())line=realToView(line);
  915. Vec2 pos =offset+posVisual(VecI2(0, line));
  916. D.lineX(Theme.colors[TOKEN_LINE_HIGHLIGHT], pos.y , _crect.min.x, _crect.max.x);
  917. D.lineX(Theme.colors[TOKEN_LINE_HIGHLIGHT], pos.y-CE.ts.lineHeight(), _crect.min.x, _crect.max.x);
  918. }
  919. }
  920. if(InRange(highlight_line, lines))
  921. {
  922. Int line=highlight_line; if(CE.view_mode())line=realToView(line);
  923. Vec2 pos =offset+posVisual(VecI2(0, line));
  924. Rect(_crect.min.x, pos.y-CE.ts.lineHeight(), _crect.max.x, pos.y).draw(ColorAlpha(CYAN, highlight_time*0.5f));
  925. }
  926. // highlight token definition
  927. Source *lit_token_source=null;
  928. Int lit_token_line =-1;
  929. UID lit_elm_id=UIDZero;
  930. if(CE.view_mode() && hasMsFocus())
  931. {
  932. VecI2 view=Trunc(posCur(Ms.pos())), real;
  933. if(viewToReal(view, real))
  934. {
  935. SymbolPtr symbol;
  936. Macro *macro=null;
  937. VecI2 x_range;
  938. if(getSymbolMacroID(real, symbol, macro, lit_elm_id, &x_range))
  939. {
  940. // highlight line of the definition
  941. if(macro && macro ->source) {lit_token_source=macro ->source; lit_token_line= macro->line;}
  942. if(symbol && symbol->source){Int token=symbol->token_index; if(InRange(token, symbol->source->tokens)){lit_token_source=symbol->source; lit_token_line=symbol->source->tokens[token]->lineIndex();}}
  943. if(lit_token_source==this && lit_token_line>=0)
  944. {
  945. Vec2 pos=offset+posVisual(VecI2(0, realToView(lit_token_line)));
  946. Rect lit_rect(_crect.min.x, pos.y-CE.ts.lineHeight(), _crect.max.x, pos.y);
  947. lit_rect.draw(ColorAlpha(CYAN, 0.2f));
  948. Rect screen_crect=(_crect-_crect.lu() + screenPos() + _crect.lu()-rect().lu()).extend(CE.ts.lineHeight()*-1.5f);
  949. if(Cuts(lit_rect, screen_crect))lit_token_source=null; // if definition is on the screen then don't draw its preview
  950. }
  951. // highlight token
  952. x_range.x=view_lines[view.y].findCol(x_range.x);
  953. x_range.y=view_lines[view.y].findCol(x_range.y);
  954. if(x_range.x>=0 && x_range.y>=0)drawSelection(Theme.colors[TOKEN_SELECT], view.y, x_range.x, x_range.y+1);
  955. }
  956. }
  957. }
  958. // highlight all occurences of token under keyboard cursor
  959. lit_symbol=null;
  960. if(!CE.view_mode() && hasKbFocus() && !CE.find.visible()) // don't display when finding something
  961. {
  962. SymbolPtr symbol;
  963. Macro *macro=null;
  964. UID id=UIDZero;
  965. if(getSymbolMacroID(cur, symbol, macro, id))lit_symbol=symbol();
  966. }
  967. lit_symbol_cpp_name=(lit_symbol ? lit_symbol->shortName() : S);
  968. // draw text lines and children
  969. super::draw(gpc);
  970. D.clip(_crect);
  971. // draw cursor
  972. if(hasKbFocus() && App.active() && !Kb._hidden)
  973. {
  974. Vec2 pos=offset+posVisual(cur); pos+=D.pixelToScreenSize(VecI2(1, 0));
  975. if(Overwrite && sel.x<0)DrawKeyboardCursorOverwrite(pos, CE.ts.lineHeight(), CE.ts, '\0'); // don't draw overwrite if we have selection
  976. else DrawKeyboardCursor (pos, CE.ts.lineHeight());
  977. }
  978. // draw line numbers
  979. if(CE.options.line_numbers())
  980. {
  981. Rect r=_crect; /*r.min.x+=D.pixelToScreenSize().x*0.5f;*/ r.max.x=r.min.x+CE.lineNumberSize();
  982. Flt offset =slidebar[1].offset(), line_offset=offset/CE.ts.lineHeight();
  983. Int line_add=Trunc(line_offset);
  984. r.draw(Theme.colors[TOKEN_LINE_NUM_BACKGROUND]);
  985. TextStyleParams ts=CE.ts_small; ts.color=Theme.colors[TOKEN_LINE_NUM]; ts.align.set(1, -1); ts.space.y=CE.ts.lineHeight()/ts.size.y;
  986. Char8 text[1024], temp[256]; text[0]=0; Int lines=Ceil(r.h()/CE.ts.lineHeight())+1; Clamp(lines, 0, 1024); FREP(lines){Int li=line_add+i; Append(text, TextInt(CE.view_mode() ? viewToReal(li) : li, temp)); Append(text, "\n");}
  987. r+=Vec2(0, (line_offset-line_add)*CE.ts.lineHeight() + (CE.ts.lineHeight()-CE.ts_small.lineHeight())*-0.2f);
  988. D.text(ts, r, text);
  989. }
  990. // draw function information
  991. if(hasKbFocus() && !Kb.b(KB_RCTRL)) // Right Control can be used to hide it
  992. {
  993. Int c; if(findPrevToken(cur, c))for(Int i=c, level=0; i>=0; i--)
  994. {
  995. Int func_i=i;
  996. Token &func =*tokens[func_i];
  997. if(func=='(')level++;else
  998. if(func==')')level--;
  999. if(func=='{' || func=='}' || func==';')break;
  1000. if(func.type==TOKEN_CODE && level>0)
  1001. {
  1002. if(InRange(func_i+1, tokens))
  1003. {
  1004. if(CE.view_elm_names && func=="UID" && func.line) // don't display info for this function if we're inside project element UID(..), instead continue to previous function "func(|UID(..))"
  1005. {
  1006. UID id; VecI2 range; if(TextToIDAt(*func.line, func.col, id, range))if(CE.cei().idToText(id).is())continue; // only if it's a text ID
  1007. }
  1008. Token &op=*tokens[func_i+1];
  1009. if(op=='(' || op=='<' || op==TMPL_B) // if func name is followed by '<' or '(' : "func(..)", "func<template>()", ..
  1010. {
  1011. parseCurFunc(); // parse func to detect templates and vars
  1012. if(Symbol *symbol=finalSymbol(func_i, true))
  1013. if(symbol->type==Symbol::FUNC || symbol->type==Symbol::FUNC_LIST || symbol->isVar() || symbol->type==Symbol::CLASS) // function, operator, constructor (through class "Vec2(0, 0)" or default value "Vec2 x(0, 0)")
  1014. if(!(symbol->type==Symbol::FUNC && func.def_decl && (symbol->params.elms() || (symbol->modifiers&Symbol::MODIF_FUNC_BODY)))) // don't use when defining function (and typing params)
  1015. {
  1016. Bool force_ctor=(symbol->type==Symbol::FUNC && func.def_decl && !symbol->params.elms() && !(symbol->modifiers&Symbol::MODIF_FUNC_BODY));
  1017. if(symbol->type==Symbol::CLASS)
  1018. {
  1019. if(func==*symbol && func.parent==symbol)symbol=null; // defining constructor/destructor, don't list parameters
  1020. if(symbol && InRange(func_i-2, tokens)) // X.X X.~X // defining constructor/destructor, don't list parameters
  1021. {
  1022. Token &token=*tokens[func_i-1],
  1023. &prev =*tokens[func_i-2];
  1024. if(token=='.' && prev==func
  1025. || token=='~' && prev=='.' && (InRange(func_i-3, tokens) && *tokens[func_i]==func))symbol=null;
  1026. }
  1027. if(symbol)symbol=SymbolPtr().find(symbol->full_name+SEP+*symbol)(); // set symbol to constructor function list
  1028. }
  1029. if(symbol)for(; i<tokens.elms(); )
  1030. {
  1031. Token &op=*tokens[i++];
  1032. if(op=='(')
  1033. {
  1034. Memc<Message> msgs;
  1035. Compiler compiler(msgs, tokens, this, null); compiler.relax();
  1036. // detect parameters
  1037. Int cur_param=0;
  1038. Memc<Expr> params;
  1039. if(InRange(i, tokens))for(Int level=0, param_start=i; ; i++)
  1040. {
  1041. Token &token=*tokens[i];
  1042. if(token=='(' || token=='[' || token==TMPL_B)level++;else
  1043. if(token==')' || token==']' || token==TMPL_E)level--;
  1044. Bool end =(token==';' || level< 0 || token=='{' || token=='}' || i>=tokens.elms()-1),
  1045. finish_param=(token==',' && level==0);
  1046. if(end || finish_param)
  1047. {
  1048. Int param_end=i-1;
  1049. if( param_end>=param_start // valid range
  1050. || finish_param // after ','
  1051. || (end && params.elms())) // encountered end without valid range but with previous elements present
  1052. {
  1053. if(c+1>=param_start)cur_param=params.elms();
  1054. compiler.compileTokens(param_start, param_end, params.New());
  1055. }
  1056. param_start=i+1;
  1057. }
  1058. if(end)break;
  1059. }
  1060. Memc<FuncMatch > matches;
  1061. Memc<Symbol::Modif> templates;
  1062. if(!force_ctor && symbol->type==Symbol::FUNC && symbol->parent && symbol->parent->type==Symbol::FUNC_LIST)symbol=symbol->parent(); // display all functions
  1063. if(!force_ctor && symbol->type==Symbol::FUNC )AddMatch (matches, symbol , true, params.data(), params.elms(), templates, compiler);else
  1064. if(!force_ctor && symbol->type==Symbol::FUNC_LIST)AddMatches(matches, symbol->funcs, true, params.data(), params.elms(), templates, compiler);else
  1065. {
  1066. // variable
  1067. if(symbol=GetFinalSymbol(symbol))if(symbol->type==Symbol::CLASS)
  1068. {
  1069. if(func.def_decl) // being defined, so it's a constructor
  1070. {
  1071. if(symbol=SymbolPtr().find(symbol->full_name+SEP+*symbol)()) // set symbol to constructor function list
  1072. AddMatches(matches, symbol->funcs, true, params.data(), params.elms(), templates, compiler);
  1073. }else // was already defined, so it's an operator
  1074. {
  1075. if(symbol=FindChild("operator()", symbol)()) // get class () operators
  1076. AddMatches(matches, symbol->funcs, true, params.data(), params.elms(), templates, compiler);
  1077. }
  1078. }
  1079. }
  1080. // remove double functions (definition/declaration)
  1081. REPA(matches){Symbol *a=matches[i].func; REPD(j, i)if(a->sameFunc(*matches[j].func)){matches.remove((a->modifiers&Symbol::MODIF_FUNC_BODY) ? i : j, true); break;}} // remove the one with function body (because it doesn't have default parameters listed, and full class path)
  1082. matches.sort(CompareAverage);
  1083. if(matches.elms())
  1084. {
  1085. Memc<Str> t;
  1086. //Int match=matches[0].average_match;
  1087. Int match=matches.last().lowest_match; REPA(matches)MAX(match, matches[i].lowest_match);
  1088. FREPA(matches){FuncMatch &fm=matches[i]; t.New()=S+((fm.lowest_match==match) ? "[col=000F]" : "[col=0008]")+fm.func->funcDefinition(cur_param)+fm.func->commentsCode()+"[/col]";}
  1089. Str code; FREPA(t){if(i)code+='\n'; code+=t[i];}
  1090. Str text; Memt<TextCodeData> codes; SetTextCode(code, text, codes);
  1091. Flt w=0; t=Split(text, '\n'); REPA(t)MAX(w, CE.ts_small.textWidth(t[i]));
  1092. MIN(w, clientWidth()*0.9f);
  1093. Vec2 fp =offset+Vec2(0, CE.fontSpaceOffset())+posVisual(VecI2(func.col, cur.y)), // use cursor.y so information is displayed below cursor (needed for multi-line functions)
  1094. size=suggestions_region.size(),
  1095. pos =suggestions_region.screenPos();
  1096. Flt ext=0.01f;
  1097. Rect_LU r(fp.x, suggestions_region.visible() ? pos.y-size.y-ext : fp.y-CE.ts.lineHeight()-ext, w, 0); if(r.max.x>=_crect.max.x)r-=Vec2(r.max.x-_crect.max.x, 0);
  1098. Int lines=CE.ts_small.textLines(text, r.w(), AUTO_LINE_SPACE_SPLIT); r.min.y=r.max.y-lines*CE.ts_small.lineHeight();
  1099. Rect(r).extend(ext).draw(WHITE);
  1100. Rect(r).extend(ext).draw(Color(0, 0, 0, 112), false);
  1101. CE.ts_small.drawCode(r, text, AUTO_LINE_SPACE_SPLIT, codes.data(), codes.elms());
  1102. }
  1103. break;
  1104. }
  1105. }
  1106. }
  1107. }
  1108. }
  1109. break;
  1110. }
  1111. }
  1112. }
  1113. // draw suggestion information
  1114. if(Suggestion *sugg=suggestions_list())
  1115. {
  1116. Symbol *symbol=sugg->symbol();
  1117. if(symbol || sugg->is_macro)
  1118. {
  1119. Memc<Str> t;
  1120. if(symbol)
  1121. {
  1122. // get list of functions
  1123. Memc<Symbol*> funcs; FREPA(symbol->funcs)funcs.add(symbol->funcs[i]());
  1124. // remove double functions (definition/declaration)
  1125. REPA(funcs){Symbol *a=funcs[i]; REPD(j, i)if(a->sameFunc(*funcs[j])){funcs.remove((a->modifiers&Symbol::MODIF_FUNC_BODY) ? i : j, true); break;}} // remove the one with function body (because it doesn't have default parameters listed, and full class path)
  1126. // use function if only one is present
  1127. if(symbol->type==Symbol::FUNC_LIST && funcs.elms()==1)symbol=funcs[0];
  1128. t.New()="Type:";
  1129. t.New()=S+" "+symbol->typeName();
  1130. if(symbol->type!=Symbol::FUNC_LIST)
  1131. {
  1132. if(t.elms())t.New();
  1133. t.New()="Definition:";
  1134. t.New()=S+" "+symbol->definition();
  1135. }
  1136. if(symbol->type!=Symbol::KEYWORD && symbol->type!=Symbol::PREPROC)
  1137. {
  1138. if(t.elms())t.New();
  1139. t.New()="Full name:";
  1140. t.New()=S+" "+symbol->fullCppName();
  1141. }
  1142. Str comments=symbol->comments();
  1143. if( comments.is())
  1144. {
  1145. Memc<Str> c=Split(S+" "+comments, '\n');
  1146. if(t.elms())t.New();
  1147. t.New()="Comments:";
  1148. FREPA(c)Swap(t.New(), c[i]);
  1149. }
  1150. if(symbol->type==Symbol::FUNC_LIST)
  1151. {
  1152. if(t.elms())t.New();
  1153. t.New()="Functions:";
  1154. FREPA(funcs)t.New()=S+" "+funcs[i]->funcDefinition(-1)+funcs[i]->commentsCode();
  1155. }
  1156. }else
  1157. {
  1158. t.New()="Type:";
  1159. t.New()=" Macro";
  1160. t.New();
  1161. t.New()="Definition:";
  1162. t.New()=S+" "+sugg->macro_def;
  1163. }
  1164. Str code; FREPA(t){if(i)code+='\n'; code+=t[i];}
  1165. Str text; Memt<TextCodeData> codes; SetTextCode(code, text, codes);
  1166. Flt w=0; t=Split(text, '\n'); REPA(t)MAX(w, CE.ts_small.textWidth(t[i]));
  1167. MIN(w, D.pixelToScreenSize().x*520);
  1168. Vec2 size=suggestions_region.size(),
  1169. pos =suggestions_region.screenPos();
  1170. Flt space=D.pixelToScreenSize().x*24;
  1171. Rect_LU r(pos+Vec2(size.x+space, 0), w, 0); if(r.max.x>=D.w())r-=Vec2(r.max.x-pos.x+space, 0); if(r.min.x<rect().min.x)r+=Vec2(pos.x+size.x+space-r.min.x, 0);
  1172. Int lines=CE.ts_small.textLines(text, r.w(), AUTO_LINE_SPACE_SPLIT); r.min.y=r.max.y-lines*CE.ts_small.lineHeight();
  1173. if(Gui.skin)
  1174. {
  1175. Rect rect=r; rect.extend(0.01f);
  1176. if(Gui.skin->region.normal)Gui.skin->region.normal->draw(Gui.skin->region.normal_color, rect);else
  1177. if(Gui.skin->region.normal_color.a) rect.draw(Gui.skin->region.normal_color);
  1178. }
  1179. CE.ts_small.drawCode(r, text, AUTO_LINE_SPACE_SPLIT, codes.data(), codes.elms());
  1180. }
  1181. if(sugg->elm_id.valid()) // project element
  1182. {
  1183. Rect rect =suggestions_region.screenRect()|suggestions_textline.screenRect();
  1184. Flt space=D.pixelToScreenSize().x*24;
  1185. Rect_LU r(rect.lu()+Vec2(rect.w()+space, 0), rect.size()); if(r.max.x>=D.w())r-=Vec2(r.max.x-rect.min.x+space, 0); if(r.min.x<rect.min.x)r+=Vec2(rect.max.x+space-r.min.x, 0);
  1186. CE.cei().elmPreview(sugg->elm_id, r.lu(), false, r);
  1187. }
  1188. }
  1189. // draw too long lines
  1190. if(CE.view_mode() && hasMsFocus())
  1191. {
  1192. Vec2 cur=posCur(Ms.pos());
  1193. Int l =Trunc(cur.y); if(InRange(l, view_lines))
  1194. {
  1195. ViewLine &view_line=view_lines[l];
  1196. if(view_line.rect().w()>=clientWidth()*0.95f)
  1197. {
  1198. ViewLine cl=view_line;
  1199. for(; cl[0]==' '; )cl.remove(0); // skip start spaces
  1200. // remove spaces and insert lines
  1201. REPA(cl)if(cl[i]==' ')
  1202. {
  1203. if(cl[i-1]==' ' // " " -> " "
  1204. || cl[i-1]=='(' // "( " -> "("
  1205. || cl[i+1]==')' // " )" -> ")"
  1206. || cl[i+1]==',' // " ," -> ","
  1207. || cl[i+1]==';' // " ;" -> ";"
  1208. || cl[i-1]=='=' // "= " -> "="
  1209. || cl[i-2]=='(' && cl[i-1]=='*' // "(* " -> "(*"
  1210. || cl[i-2]==' ' && cl[i-1]=='&' // " & " -> " &"
  1211. || cl[i+1]=='(' && cl.CodeLine::type(i)==TOKEN_NONE // " (" -> "("
  1212. )cl.remove(i);else
  1213. if(cl[i-1]==';' // "; " -> ";\n"
  1214. || cl[i+1]=='/' && cl[i+2]=='/' // " //" -> "\n//"
  1215. )cl.cols[i].c='\n';
  1216. }
  1217. // align 1st and 2nd lines
  1218. // "void set(Int x);" -> "void set(Int x);"
  1219. // "Int get(); " "Int get(); "
  1220. FREPA(cl)
  1221. {
  1222. if(cl[i]=='\n')break;
  1223. if(cl[i]!=' ' && cl[i-1]==' ') // find first text after space
  1224. {
  1225. for(Int l=i+1; l<Elms(cl); l++)if(cl[l]=='\n') // find next line
  1226. {
  1227. l++;
  1228. if(cl[l]!='/') // 2nd line does not start from comment
  1229. for(Int j=l; j<Elms(cl); j++)
  1230. {
  1231. if(cl[j]=='\n')break;
  1232. if(cl[j]!=' ' && cl[j-1]==' ') // find first text after space
  1233. {
  1234. Int ai=i, aj=j-l; // distance from start of the line to texts
  1235. Str spaces; REPD(s, Abs(ai-aj))spaces+=' ';
  1236. cl.insert((ai<aj) ? i : j, spaces, TOKEN_NONE);
  1237. break;
  1238. }
  1239. }
  1240. break;
  1241. }
  1242. break;
  1243. }
  1244. }
  1245. Str code =cl.textCode(), text; Memt<TextCodeData> codes; SetTextCode(code, text, codes);
  1246. Vec2 pos =screenPos();
  1247. Flt min_x=pos.x+ 0.1f,
  1248. max_x=pos.x+clientWidth()-0.1f,
  1249. max_y=Ms.pos().y-0.15f;
  1250. Int lines=CE.ts.textLines(text, max_x-min_x, AUTO_LINE_SPACE_SPLIT);
  1251. Flt min_y=max_y-lines*CE.ts.lineHeight();
  1252. Rect rect (min_x, min_y, max_x, max_y);
  1253. if(rect.min.y-0.05f<=pos.y-clientHeight())rect+=Vec2(0, rect.h() + 0.15f*2);
  1254. Rect rect_e=rect; rect_e.extend(0.02f);
  1255. D.drawShadow(190, rect_e, 0.0875f);
  1256. rect_e.draw (Theme.colors[TOKEN_NONE]);
  1257. CE.ts.drawCode(rect, text, AUTO_LINE_SPACE_SPLIT, codes.data(), codes.elms());
  1258. }
  1259. }
  1260. }
  1261. // draw source preview
  1262. if(lit_token_source && lit_token_line>=0)
  1263. {
  1264. if(lit_token_source!=this)lit_token_source->prepareForDraw();
  1265. GuiPC gpc;
  1266. gpc.visible =true;
  1267. gpc.enabled =true;
  1268. gpc.clip =(_crect-_crect.lu() + screenPos() + _crect.lu()-rect().lu()).extend(-0.05f); gpc.clip.min.x=gpc.clip.lerpX(0.6f);
  1269. gpc.client_rect=gpc.clip;
  1270. gpc.offset =gpc.clip.left();
  1271. gpc.offset.y +=(lit_token_source->realToView(lit_token_line)+0.5f)*CE.ts.lineHeight();
  1272. Rect rect_e=gpc.clip; rect_e.extend(0.01f);
  1273. D.drawShadow(190, rect_e, 0.0875f);
  1274. rect_e.draw (Theme.colors[TOKEN_NONE]);
  1275. Rect_L(gpc.clip.left(), gpc.clip.w(), CE.ts.lineHeight()).draw(ColorAlpha(CYAN, 0.2f));
  1276. REPAO(lit_token_source-> lines).draw(gpc);
  1277. REPAO(lit_token_source->view_lines).draw(gpc);
  1278. D.clip(null);
  1279. }
  1280. // draw element preview
  1281. CE.cei().elmPreview(lit_elm_id, Ms.pos(), true, _crect);
  1282. }
  1283. }
  1284. /******************************************************************************/
  1285. }}
  1286. /******************************************************************************/