CE Source.cpp 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. namespace Edit{
  5. /******************************************************************************/
  6. void LineMode::setTokens(Line &line)
  7. {
  8. C Str &s=line;
  9. tokens.clear();
  10. FREPA(s)
  11. {
  12. TOKEN_TYPE t=Type(i);
  13. if(TokenType(t))
  14. {
  15. Token &token=tokens.New(); token.setBorrowed(s()+i, 1, i, line, t);
  16. if(t==TOKEN_OPERATOR)
  17. {
  18. Char c=s[i+1];
  19. switch(s[i])
  20. {
  21. case '<': if(c=='='){token.extend(); i++;}else if(c=='<' && s[i+2]=='='){token.extend(2); i+=2; } break; // <= <<= (<< is not processed here due to possible template problems, processed later)
  22. case '>': if(c=='='){token.extend(); i++;}else if(c=='>' && s[i+2]=='='){token.extend(2); i+=2; } break; // >= >>= (>> is not processed here due to possible template problems, processed later)
  23. case '!': if(c=='='){token.extend(); i++;}else if(c=='!' && s[i+2]=='='){token.extend(2); i+=2; } break; // == !!=
  24. case '=': if(c=='='){token.extend(); i++; if(s[i+1]=='='){token.extend(); i++; }} break; // == ===
  25. case '+': if(c=='='){token.extend(); i++;}else if(c=='+'){token.extend(); i++;} break; // += ++
  26. case '-': if(c=='='){token.extend(); i++;}else if(c=='-'){token.extend(); i++;}else if(c=='>'){token.extend(); i++; if(s[i+1]=='*'){token.extend(); i++;}} break; // -= -- -> ->*
  27. case '*': if(c=='='){token.extend(); i++;} break; // *=
  28. case '/': if(c=='='){token.extend(); i++;} break; // /=
  29. case '%': if(c=='='){token.extend(); i++;} break; // %=
  30. case '&': if(c=='='){token.extend(); i++;}else if(c=='&'){token.extend(); i++;} break; // &= &&
  31. case '|': if(c=='='){token.extend(); i++;}else if(c=='|'){token.extend(); i++;} break; // |= ||
  32. case '^': if(c=='='){token.extend(); i++;}else if(c=='^'){token.extend(); i++;} break; // ^= ^^
  33. case ':': if(c==':'){token.extend(); i++;} break; // ::
  34. case '.': if(c=='*'){token.extend(); i++;}else if(c=='.' && s[i+2]=='.'){token.extend(2); i+=2;} break; // .* ... (can occur in "catch(...)")
  35. case '#': if(c=='#'){token.extend(); i++;} break; // ##
  36. }
  37. }else
  38. {
  39. for(; Type(i+1)==t; i++)token.extend();
  40. if(t==TOKEN_CODE)
  41. {
  42. BStr &bs=token;
  43. // #force_preproc, normal keyword first then preproc keyword
  44. // 2nd token in preproc is always preproc
  45. Keyword *keyword;
  46. if(preproc && !starts_with_preproc && tokens.elms()==2)goto preproc_only; // if we're processing the "#TOKEN"
  47. if(keyword=BinaryFind(NativeKeywords, NativeKeywordsElms, bs, CompareCS))
  48. {
  49. if(keyword->cpp
  50. ? true // cpp keywords are supported everywhere
  51. : !line.source->cpp) // .es keywords are supported only in .es files
  52. t=TOKEN_KEYWORD;
  53. }else
  54. if(preproc)
  55. {
  56. preproc_only:;
  57. if(BinaryHas(PreprocKeywords, PreprocKeywordsElms, bs, CompareCS))t=TOKEN_PREPROC;
  58. }
  59. if(t!=token.type)
  60. {
  61. token.type=t; REP(token.length())type[token.col+i]=t;
  62. }
  63. }
  64. }
  65. }
  66. }
  67. }
  68. /******************************************************************************/
  69. static Bool _HasUnicode(CChar *t, Char stop)
  70. {
  71. for(;;)
  72. {
  73. Char c=*t++;
  74. if(c=='\\') // check for escape characters
  75. {
  76. c=*t++; if(!c)break;
  77. if(c=='x' || c=='u' || c=='U')return true; // Warning: for simplicity we assume that the next char is a wide char
  78. continue;
  79. }
  80. if(!c || c==stop)break;
  81. if(HasUnicode(c))return true;
  82. }
  83. return false;
  84. }
  85. void LineMode::resetType(Line &line) // this just resets the 'type' without changing the tokens
  86. {
  87. C Str &s=line;
  88. type.clear();
  89. TOKEN_TYPE t=(starts_with_comment ? TOKEN_COMMENT : TOKEN_NONE);
  90. Bool ignore_rest=false, ignore_char=false, real=false;
  91. FREPA(s)
  92. {
  93. TOKEN_TYPE p=t;
  94. if(ignore_char)
  95. {
  96. ignore_char=false;
  97. type.add(t);
  98. }else
  99. {
  100. Char c;
  101. if(!ignore_rest)
  102. {
  103. c=s[i];
  104. if(TextType(t) && c=='\\' )ignore_char=true;else
  105. if(t!=TOKEN_COMMENT && !TextType(t))
  106. {
  107. if(c=='/' && s[i+1]=='*' ){t=TOKEN_COMMENT; type.add(t); i++;}else // process 2 chars at a time
  108. if(c=='/' && s[i+1]=='/' ){t=TOKEN_COMMENT; ignore_rest=true;}else
  109. if(c==' ' ) t=TOKEN_NONE ;else
  110. if(c=='"' ){Char p=s[i-1]; if(p=='L' || p=='u' || p=='U')type.last()=t=TOKEN_TEXT16;else t=(line.source->cpp ? TOKEN_TEXT8 : _HasUnicode(s()+i+1, c) ? TOKEN_TEXT16 : TOKEN_TEXT8);}else
  111. if(c=='\'' ){Char p=s[i-1]; if(p=='L' || p=='u' || p=='U')type.last()=t=TOKEN_CHAR16;else t=(line.source->cpp ? TOKEN_CHAR8 : _HasUnicode(s()+i+1, c) ? TOKEN_CHAR16 : TOKEN_CHAR8);}else
  112. if(CodeCharType(c)==CHART_SIGN)
  113. {
  114. if(c=='.' && (t==TOKEN_NUMBER || CharFlag(s[i+1])&CHARF_DIG)){t=TOKEN_NUMBER; real=true;}else // was already a number or the next char is a digit
  115. if((c=='-' || c=='+') && t==TOKEN_NUMBER && real && s[i-1]=='e')t=TOKEN_NUMBER;else // 1.2e-3, 1.2e+3
  116. t=((c=='#' && s[i+1]!='#' && s[i-1]!='#') ? TOKEN_PREPROC : TOKEN_OPERATOR);
  117. }else
  118. if(t!=TOKEN_CODE && t!=TOKEN_KEYWORD && CharFlag(c)&CHARF_DIG)t=TOKEN_NUMBER;else
  119. if(t!=TOKEN_CODE && t!=TOKEN_KEYWORD && t!=TOKEN_NUMBER && CodeCharType(s[i-1])!=CHART_CHAR)t=TOKEN_CODE;
  120. }
  121. }
  122. type.add(t);
  123. if(t==TOKEN_PREPROC)preproc=true;
  124. if(t!=TOKEN_NUMBER )real =false;
  125. if(!ignore_rest && !ignore_char)
  126. {
  127. if( t==TOKEN_COMMENT && c=='*' && s[i+1]=='/'){type.add(t); i++; t=TOKEN_NONE;}else // process 2 chars at a time
  128. if((p==TOKEN_TEXT8 || p==TOKEN_TEXT16) && c=='"' )t=TOKEN_NONE;else
  129. if((p==TOKEN_CHAR8 || p==TOKEN_CHAR16) && c=='\'')t=TOKEN_NONE;else
  130. if( t==TOKEN_OPERATOR)t=TOKEN_NONE;
  131. }
  132. }
  133. }
  134. if(t!=TOKEN_COMMENT || ignore_rest)t=TOKEN_NONE;
  135. ends_with_comment=(t==TOKEN_COMMENT);
  136. ends_with_preproc=(preproc && s.last()=='\\');
  137. }
  138. void LineMode::setType(Line &line, Bool starts_with_comment, Bool starts_with_preproc)
  139. {
  140. T.starts_with_comment=starts_with_comment;
  141. T.preproc=T.starts_with_preproc=starts_with_preproc;
  142. resetType(line);
  143. setTokens(line);
  144. }
  145. /******************************************************************************/
  146. enum
  147. {
  148. SWC=1<<0,
  149. EWC=1<<1,
  150. PRP=1<<2,
  151. SWP=1<<3,
  152. EWP=1<<4,
  153. SWM=1<<5,
  154. };
  155. Bool LineMode::save(File &f, StrLibrary &sl, C Str &text)C
  156. {
  157. f.putByte((starts_with_comment ? SWC : 0) | (ends_with_comment ? EWC : 0) | (preproc ? PRP : 0) | (starts_with_preproc ? SWP : 0) | (ends_with_preproc ? EWP : 0) | (starts_with_macro_param ? SWM : 0));
  158. if(type.saveRaw(f))
  159. {
  160. f.cmpUIntV(tokens.elms()); FREPA(tokens)if(!tokens[i].save(f, sl, text))return false;
  161. return f.ok();
  162. }
  163. return false;
  164. }
  165. Bool LineMode::load(File &f, StrLibrary &sl, C Str &text, Line &line, Str &temp)
  166. {
  167. Byte flag; f>>flag; starts_with_comment=FlagTest(flag, SWC); ends_with_comment=FlagTest(flag, EWC); preproc=FlagTest(flag, PRP); starts_with_preproc=FlagTest(flag, SWP); ends_with_preproc=FlagTest(flag, EWP); starts_with_macro_param=FlagTest(flag, SWM);
  168. if(type.loadRaw(f))
  169. {
  170. tokens.setNum(f.decUIntV()); FREPA(tokens)if(!tokens[i].load(f, sl, text, line, temp))goto error;
  171. if(f.ok())return true;
  172. }
  173. error:
  174. /*del();*/ return false;
  175. }
  176. /******************************************************************************/
  177. Line::~Line()
  178. {
  179. if(preproc)preprocChanged();
  180. }
  181. void Line::preprocChanged()
  182. {
  183. if(source)
  184. if(source->preproc_line_changed<0 || line<source->preproc_line_changed)
  185. source->preproc_line_changed=line;
  186. }
  187. void Line::resetType()
  188. {
  189. LineMode::resetType(T);
  190. }
  191. void Line::setType(Bool starts_with_comment, Bool starts_with_preproc)
  192. {
  193. if(changed)
  194. {
  195. Bool was_preproc=preproc;
  196. LineMode::setType(T, starts_with_comment, starts_with_preproc);
  197. comment_mode. setType(T, !starts_with_comment, starts_with_preproc);
  198. changed=false;
  199. if(was_preproc || preproc)preprocChanged();
  200. }else
  201. {
  202. if(starts_with_comment!=T.starts_with_comment)
  203. {
  204. LineMode &cur=T;
  205. Swap(cur, comment_mode);
  206. }
  207. }
  208. }
  209. /******************************************************************************/
  210. static Bool LineFileName(C Str &text, Int x, VecI2 &range) // detect if position is inside 24 char ID text "........."
  211. {
  212. if(text[x]!='"')
  213. {
  214. Int a=x-1; for(; a>=0 ; a--)if(text[a]=='"')break; a++;
  215. Int b=x+1; for(; b<text.length(); b++)if(text[b]=='"')break;
  216. if(b-a==24)
  217. {
  218. Str s; for(Int i=a; i<b; i++)s+=text[i]; UID id; if(DecodeFileName(s, id)){range.set(a, b); return true;}
  219. }
  220. }
  221. return false;
  222. }
  223. static Bool Individual(Char c)
  224. {
  225. switch(c)
  226. {
  227. case '{': case '(': case '[': case ';':
  228. case '}': case ')': case ']': case ',': return true;
  229. default : return false;
  230. }
  231. }
  232. Int Line::wordStart(Int x)
  233. {
  234. Clamp(x, 0, length()-1); CHAR_TYPE type=CodeCharType(T[x]);
  235. for(; x>0 && CodeCharType(T[x-1])==type && !Individual(T[x-1]); x--);
  236. VecI2 range; if(CE.view_elm_names){UID id; if(TextToIDInside(T, x, id, range))return range.x;} if(LineFileName(T, x, range))return range.x;
  237. return x;
  238. }
  239. Int Line::wordEnd(Int x)
  240. {
  241. Clamp(x, 0, length()); CHAR_TYPE type=CodeCharType(T[x]); if(x<length())x++;
  242. for(; x<length() && CodeCharType(T[x])==type && !Individual(T[x]); x++);
  243. VecI2 range; if(CE.view_elm_names){UID id; if(TextToIDInside(T, x, id, range))return range.y+1;} if(LineFileName(T, x, range))return range.y;
  244. return x;
  245. }
  246. Int Line::wordBegin(Int x) // skips all spaces until first word encountered
  247. {
  248. Clamp(x, 0, length()-1); for(; x>0 && T[x]==' '; x--); return wordStart(x);
  249. }
  250. /******************************************************************************/
  251. Bool Line::saveData(File &f)C
  252. {
  253. f<<id;
  254. f.putStr(T);
  255. return f.ok();
  256. }
  257. Bool Line::loadData(File &f)
  258. {
  259. f>>id;
  260. f.getStr(T);
  261. if(f.ok())return true;
  262. /*del();*/ return false;
  263. }
  264. enum
  265. {
  266. TOKENS_PREPROC_USE =1<<0,
  267. TOKENS_PREPROC_CONDITION_UNAVAILABLE=1<<1,
  268. };
  269. Bool Line::save(File &f, StrLibrary &sl)C
  270. {
  271. f.putStr(T).putByte((tokens_preproc_use ? TOKENS_PREPROC_USE : 0)|(tokens_preproc_condition_unavailable ? TOKENS_PREPROC_CONDITION_UNAVAILABLE : 0));
  272. if(LineMode ::save(f, sl, T))
  273. //if(comment_mode.save(f, sl, T))
  274. {
  275. if(tokens_preproc_use){f.cmpUIntV(tokens_preproc.elms()); FREPA(tokens_preproc)if(!tokens_preproc[i].save(f, sl, T))return false;}
  276. return f.ok();
  277. }
  278. return false;
  279. }
  280. Bool Line::load(File &f, StrLibrary &sl, Int line, Source &source, Str &temp)
  281. {
  282. tokens_preproc.clear();
  283. T.line = line ;
  284. T.source=&source;
  285. Byte flag;
  286. f.getStr(T)>>flag;
  287. tokens_preproc_use =FlagTest(flag, TOKENS_PREPROC_USE);
  288. tokens_preproc_condition_unavailable=FlagTest(flag, TOKENS_PREPROC_CONDITION_UNAVAILABLE);
  289. if(LineMode ::load(f, sl, T, T, temp))
  290. //if(comment_mode.load(f, sl, T, T, temp))
  291. {
  292. if(tokens_preproc_use){tokens_preproc.setNum(f.decUIntV()); FREPA(tokens_preproc)if(!tokens_preproc[i].load(f, sl, T, T, temp))goto error;}
  293. if(f.ok())return true;
  294. }
  295. error:
  296. /*del();*/ return false;
  297. }
  298. /******************************************************************************/
  299. Str Line::textTokens()C
  300. {
  301. Str text; TOKEN_TYPE last_type=TOKEN_NONE; C Memc<Token> &tokens=T.Tokens();
  302. FREPA(tokens)
  303. {
  304. C Token &token=tokens[i];
  305. if(MustSeparate(last_type, token.type))text+=' ';
  306. text+=token;
  307. last_type=token.type;
  308. }
  309. return text;
  310. }
  311. Str Line::textCode() // must be in sync with "Source::ViewLine::textCode"
  312. {
  313. Str code;
  314. if(tokens_preproc_condition_unavailable)
  315. {
  316. code ="[color=";
  317. code+=Theme.colors[TOKEN_PREPROC_DISABLED].asHex();
  318. code+="][nocode]";
  319. code+=T;
  320. }else
  321. {
  322. // parse functions to detect symbols and their colorization
  323. {
  324. Memc<Token> &tokens=Tokens(); FREPA(tokens)if(Symbol *symbol=tokens[i].parent)if(Symbol *func=symbol->func())if(func->source)func->source->parseFunc(*func);
  325. }
  326. TOKEN_TYPE last_type =TOKEN_NONE;
  327. Color last_color=TRANSPARENT;
  328. Token *token_org_cur, *token_org_end, *token_prc_cur, *token_prc_end;
  329. if(tokens .elms()){token_org_cur=tokens .data(); token_org_end=token_org_cur+tokens .elms();}else token_org_cur=null; // set null when there are no elements
  330. if(Tokens().elms()){token_prc_cur=Tokens().data(); token_prc_end=token_prc_cur+Tokens().elms();}else token_prc_cur=null; // set null when there are no elements
  331. FREPA(T)
  332. {
  333. TOKEN_TYPE type=Type(i);
  334. if(last_type!=type)
  335. {
  336. last_type=type;
  337. // check if data type or namespace
  338. if(token_prc_cur)
  339. {
  340. for(; token_prc_cur->col<i; )
  341. {
  342. token_prc_cur++;
  343. if(token_prc_cur==token_prc_end){token_prc_cur=null; break;}
  344. }
  345. if(token_prc_cur && token_prc_cur->col==i)
  346. if(token_prc_cur->macro)type=TOKEN_KEYWORD;else
  347. if(Symbol *symbol=token_prc_cur->symbol())
  348. {
  349. if(symbol->type==Symbol::ENUM )type=TOKEN_ENUM_TYPE;else
  350. if(symbol->type==Symbol::ENUM_ELM )type=TOKEN_ENUM_ELM ;else
  351. if((symbol->type==Symbol::FUNC_LIST || symbol->type==Symbol::FUNC) && symbol->isGlobalOrStatic())type=TOKEN_FUNC ;else
  352. if((symbol->modifiers&Symbol::MODIF_DATA_TYPE) || symbol->type==Symbol::NAMESPACE )type=TOKEN_KEYWORD;
  353. }
  354. }
  355. // check if macro
  356. if(token_org_cur)
  357. {
  358. for(; token_org_cur->col<i; )
  359. {
  360. token_org_cur++;
  361. if(token_org_cur==token_org_end){token_org_cur=null; break;}
  362. }
  363. if(token_org_cur && token_org_cur->col==i && token_org_cur->macro)type=TOKEN_MACRO;
  364. }
  365. if(ValidType(type))
  366. {
  367. Color c=Theme.colors[type];
  368. if(last_color!=c)
  369. {
  370. last_color=c;
  371. if(code.is())code+="[/nocode][/color]";
  372. code+="[color=";
  373. code+=c.asHex();
  374. code+="][nocode]";
  375. }
  376. }
  377. }
  378. code+=T[i];
  379. }
  380. }
  381. return code;
  382. }
  383. Str Source::ViewLine::textCode() // must be in sync with "Line::textCode"
  384. {
  385. Str code;
  386. if(Line *line=((source && InRange(T.line(), source->lines)) ? &source->lines[T.line()] : null))
  387. if(line->tokens_preproc_condition_unavailable)
  388. {
  389. code ="[color=";
  390. code+=Theme.colors[TOKEN_PREPROC_DISABLED].asHex();
  391. code+="][nocode]";
  392. code+=asStr();
  393. }else
  394. {
  395. // parse functions to detect symbols and their colorization
  396. {
  397. Memc<Token> &tokens=line->Tokens(); FREPA(tokens)if(Symbol *symbol=tokens[i].parent)if(Symbol *func=symbol->func())if(func->source)func->source->parseFunc(*func);
  398. }
  399. TOKEN_TYPE last_type =TOKEN_NONE;
  400. Color last_color=TRANSPARENT;
  401. Token *token_org_cur, *token_org_end, *token_prc_cur, *token_prc_end;
  402. if(line->tokens .elms()){token_org_cur=line->tokens .data(); token_org_end=token_org_cur+line->tokens .elms();}else token_org_cur=null; // set null when there are no elements
  403. if(line->Tokens().elms()){token_prc_cur=line->Tokens().data(); token_prc_end=token_prc_cur+line->Tokens().elms();}else token_prc_cur=null; // set null when there are no elements
  404. FREPA(T)
  405. {
  406. TOKEN_TYPE type=CodeLine::type(i);
  407. Int col = cols[i].pos.x;
  408. if(last_type!=type)
  409. {
  410. last_type=type;
  411. // check if data type or namespace
  412. if(token_prc_cur)
  413. {
  414. for(; token_prc_cur->col<col; )
  415. {
  416. token_prc_cur++;
  417. if(token_prc_cur==token_prc_end){token_prc_cur=null; break;}
  418. }
  419. if(token_prc_cur && token_prc_cur->col==col)
  420. if(token_prc_cur->macro)type=TOKEN_KEYWORD;else
  421. if(Symbol *symbol=token_prc_cur->symbol())
  422. {
  423. if(symbol->type==Symbol::ENUM )type=TOKEN_ENUM_TYPE;else
  424. if(symbol->type==Symbol::ENUM_ELM )type=TOKEN_ENUM_ELM ;else
  425. if((symbol->type==Symbol::FUNC_LIST || symbol->type==Symbol::FUNC) && symbol->isGlobalOrStatic())type=TOKEN_FUNC ;else
  426. if((symbol->modifiers&Symbol::MODIF_DATA_TYPE) || symbol->type==Symbol::NAMESPACE )type=TOKEN_KEYWORD;
  427. }
  428. }
  429. // check if macro
  430. if(token_org_cur)
  431. {
  432. for(; token_org_cur->col<col; )
  433. {
  434. token_org_cur++;
  435. if(token_org_cur==token_org_end){token_org_cur=null; break;}
  436. }
  437. if(token_org_cur && token_org_cur->col==col && token_org_cur->macro)type=TOKEN_MACRO;
  438. }
  439. if(ValidType(type))
  440. {
  441. Color c=Theme.colors[type];
  442. if(last_color!=c)
  443. {
  444. last_color=c;
  445. if(code.is())code+="[/nocode][/color]";
  446. code+="[color=";
  447. code+=c.asHex();
  448. code+="][nocode]";
  449. }
  450. }
  451. }
  452. code+=T[i];
  453. }
  454. }
  455. return code;
  456. }
  457. void Source::ViewLine::setRect(Int i)
  458. {
  459. Flt lh=CE.ts.lineHeight();
  460. super::rect(Rect_LU(0, -lh*i + CE.fontSpaceOffset(), CE.ts.textWidth(asStr()), lh));
  461. }
  462. /******************************************************************************/
  463. // DETECT
  464. /******************************************************************************/
  465. Bool Source::getSymbolMacroID(C VecI2 &cur, SymbolPtr &symbol_ptr, Macro* &macro_ptr, UID &id, VecI2 *x_range, Bool prefer_definition)
  466. {
  467. macro_ptr=null;
  468. symbol_ptr=null;
  469. id.zero();
  470. if(InRange(cur.y, lines))
  471. {
  472. Line &line=lines[cur.y];
  473. // check for ID
  474. VecI2 range; if(CE.view_elm_names)if(TextToIDInside(line, cur.x, id, x_range ? *x_range : range))return true;
  475. // check for macro
  476. Int x=line.wordStart(cur.x);
  477. FREPA(line.tokens)
  478. {
  479. Token &token=line.tokens[i];
  480. if(token.macro && token.col==x)
  481. {
  482. REPA(ProjectMacros)
  483. {
  484. Macro &macro=ProjectMacros[i];
  485. if(token==macro.name)
  486. {
  487. macro_ptr=&macro;
  488. if(x_range)x_range->set(x, x+macro.name.length()-1);
  489. return true;
  490. }
  491. }
  492. break;
  493. }
  494. }
  495. }
  496. // check for symbol
  497. Int token; if(Token *t=findToken(cur, token))
  498. {
  499. parseFunc(cur); // parse func to detect templates and vars
  500. if(Symbol *symbol=finalSymbol(token))
  501. {
  502. if(symbol->isFunc())
  503. {
  504. Memt<SymbolPtr> funcs; // definition + declaration
  505. SymbolPtr func_list; if(func_list.find(GetPath(symbol->full_name)))REPA(func_list->funcs)if(symbol->sameFunc(*func_list->funcs[i]))funcs.add(func_list->funcs[i]); // gather same funcs into container
  506. Symbol *preference=null; REPA(funcs)if(FlagTest(funcs[i]->modifiers, Symbol::MODIF_FUNC_BODY)==prefer_definition){preference=funcs[i](); break;} // find preference
  507. if(preference && preference->source==this && InRange(preference->token_index, tokens) && tokens[preference->token_index]->lineIndex()==cur.y)preference=null; // if we're already at the preference and trying to jump to it again, then cancel jumping to preference
  508. if(preference)symbol=preference;else REPA(funcs)if(funcs[i]==symbol){symbol=funcs[(i+1)%funcs.elms()](); break;} // if preference is available then jump to it, if not then select next function in the list
  509. }
  510. symbol_ptr=symbol;
  511. if(x_range)x_range->set(t->col, t->col+t->length()-1);
  512. return true;
  513. }
  514. }
  515. return false;
  516. }
  517. /******************************************************************************/
  518. void Source::jumpToCur()
  519. {
  520. SymbolPtr symbol;
  521. Macro *macro=null;
  522. UID id;
  523. if(getSymbolMacroID(cur, symbol, macro, id))
  524. {
  525. forceCreateNextUndo(); clearSuggestions();
  526. if(symbol )CE.jumpTo(symbol());else
  527. if(macro )CE.jumpTo(macro );else
  528. if(id.valid())CE.cei().elmOpen(id);
  529. }
  530. }
  531. /******************************************************************************/
  532. void Source::findAllReferences()
  533. {
  534. SymbolPtr symbol;
  535. Macro *macro=null;
  536. UID id;
  537. if(getSymbolMacroID(cur, symbol, macro, id))
  538. {
  539. forceCreateNextUndo(); clearSuggestions();
  540. if(symbol )CE.findAllReferences(symbol() );else
  541. if(macro )CE.findAllReferences(macro->name);else
  542. if(id.valid())CE.findAllReferences(id );
  543. }
  544. }
  545. /******************************************************************************/
  546. // PARSE
  547. /******************************************************************************/
  548. void Source::delSymbols()
  549. {
  550. REPAO(lines).resetTokens();
  551. symbols.clear();
  552. decls .clear();
  553. }
  554. void Source::parseFunc(Symbol &func, Memc<Command> &cmds, Memc<Message> &msgs)
  555. {
  556. /**
  557. exceptions of possible var/func declarations are:
  558. ;TYPE ..
  559. {TYPE ..
  560. }TYPE ..
  561. if(TYPE ..)TYPE ..
  562. else TYPE ..
  563. for(TYPE ..; TYPE ..; )TYPE ..
  564. do TYPE ..
  565. while(TYPE ..)TYPE ..
  566. switch(TYPE ..)
  567. ;label:TYPE
  568. {label:TYPE
  569. }label:TYPE
  570. case X:
  571. /**/
  572. if(used() && (func.modifiers&Symbol::MODIF_FUNC_BODY) && !(func.helper&Symbol::HELPER_FUNC_PARSED)) // if function has body and was not yet parsed
  573. {
  574. func.helper|=Symbol::HELPER_FUNC_PARSED; // set as parsed
  575. Int body_start=getBodyStart(func.token_index); // get body start ('{' token)
  576. if(InRange(body_start, tokens))
  577. {
  578. body_start++; // proceed to first token after '{'
  579. Str temp;
  580. Memc<Symbol::Modif> templates;
  581. Memc<Int > new_templates;
  582. // set ctor initializers
  583. func.ctor_inits.clear();
  584. for(Int i=func.token_index; i<body_start && i<tokens.elms(); i++)
  585. {
  586. Token &token=*tokens[i];
  587. if(token.ctor_initializer && token.type==TOKEN_CODE)
  588. if(token.symbol=FindChild(temp=token, func.Parent(), null, false))
  589. func.ctor_inits.add(token.symbol);
  590. }
  591. // set sub spaces
  592. Int body_end=body_start; // use copy because variable passed to ReadCommands is passed as reference
  593. ReadCommands(T, body_end, func, cmds, msgs);
  594. // detect vars and sub-spaces
  595. for(Int i=body_start; i<body_end && i<tokens.elms(); )
  596. {
  597. Int start=i;
  598. Token &token=*tokens[i];
  599. if(token=='>')
  600. {
  601. ParseFuncTemplate(tokens, i);
  602. }else
  603. if(token=="goto")
  604. {
  605. if(InRange(i+1, tokens))
  606. {
  607. Token &label=*tokens[++i]; i++;
  608. label.symbol.find(func.full_name+SEP+'@'+label);
  609. }
  610. }else
  611. {
  612. Bool detect=true;
  613. if(token=='.' || token=="->" || token=="::")
  614. if(InRange(i-1, tokens))
  615. {
  616. Token &c=*tokens[i-1];
  617. if(c==')' || c==']' || c.type==TOKEN_KEYWORD || c.type==TOKEN_CODE || c==TMPL_E)detect=false; // don't detect if we're preceeded by some possible symbol
  618. }
  619. // detect var definition
  620. Symbol *symbol;
  621. if(detect && IsVarFuncDefinition(tokens, i, temp, symbol, templates, token.parent))
  622. if(symbol && (symbol->modifiers&Symbol::MODIF_DATA_TYPE) && isDeclaration(symbol, start))
  623. ReadVarFunc(symbol, symbols, tokens, i, start, SPACE_NORMAL, temp, templates, new_templates);
  624. }
  625. MAX(i, start+1);
  626. }
  627. }
  628. }
  629. }
  630. void Source::parseFunc(Symbol &func)
  631. {
  632. Memc<Command> cmds;
  633. Memc<Message> msgs;
  634. parseFunc(func, cmds, msgs);
  635. }
  636. void Source::parseFunc(Token &token)
  637. {
  638. if(token.parent)if(Symbol *func=token.parent->func())parseFunc(*func);
  639. }
  640. void Source::parseFunc(C VecI2 &cur)
  641. {
  642. if(!active && !header)return;
  643. Int i; if(Token *token=findPrevToken(cur, i))parseFunc(*token);
  644. }
  645. void Source::parseCurFunc() {parseFunc(cur);}
  646. /******************************************************************************/
  647. // GET
  648. /******************************************************************************/
  649. Bool Source::hasUnicode( )C {REPA(lines)if(HasUnicode(lines[i]))return true; return false;}
  650. Bool Source::used ( )C {return active || header;}
  651. Bool Source::modified ( )C {return undos.undos()!=undo_original_state;}
  652. Bool Source::hasKbFocus( )C {if(CE.view_mode() || Gui.kb()==&suggestions_textline)return false; return contains(Gui.kb()) || ((!Gui.kb() || Gui.kb()==Gui.desktop()) && visibleFull() && parent());}
  653. Bool Source::hasFocus (GuiObj *go)C {return App.active() && contains(go) && !slidebar[0].contains(go) && !slidebar[1].contains(go) && !view.contains(go) && !suggestions_region.contains(go) && go!=&suggestions_textline;}
  654. Bool Source::isCurVisible()C
  655. {
  656. return cur.y >=Ceil ( slidebar[1].offset() /CE.ts.lineHeight())
  657. && cur.y+1<=Trunc((slidebar[1].offset()+clientHeight() )/CE.ts.lineHeight())
  658. && cur.x >=Ceil ((slidebar[0].offset()+CE.lineNumberSize())/CE.ts. colWidth ())
  659. && cur.x+1<=Trunc((slidebar[0].offset()+clientWidth() )/CE.ts. colWidth ());
  660. }
  661. Bool Source::insideRSTBrackets(C VecI2 &pos) // if inside Round Square Template brackets
  662. {
  663. Int level=0, i; if(findPrevToken(pos, i))for(; i>=0; )
  664. {
  665. Token &token=*tokens[i--];
  666. if(token=='(' || token=='[' || token=='<' || token==TMPL_B)level++;else
  667. if(token==')' || token==']' || token=='>' || token==TMPL_E)level--;
  668. if(token=='{' || token=='}')break;
  669. }
  670. return level>0;
  671. }
  672. Vec2 Source::posVisual(C VecI2 &pos)C {return pos*Vec2(CE.ts.colWidth(), -CE.ts.lineHeight());}
  673. Vec2 Source::posCur (C Vec2 &pos)C {Vec2 c=pos-T.pos(); c-=offset(); c/=Vec2(CE.ts.colWidth(), -CE.ts.lineHeight()); return c;}
  674. Vec2 Source::offset ( )C {return Vec2(-slidebar[0].offset()+CE.lineNumberSize(), slidebar[1].offset());}
  675. Int Source::viewToReal(Int view)C
  676. {
  677. if(InRange(view, view_lines))return view_lines[view].line();
  678. if(view_lines.elms())return view_lines.last().line()+view-view_lines.elms()+1;
  679. return view;
  680. }
  681. Bool Source::viewToReal(C VecI2 &view, VecI2 &real)C
  682. {
  683. if(InRange(view.y, view_lines))
  684. {
  685. C ViewLine &view_line=view_lines[view.y];
  686. if(InRange(view_line.line(), lines))
  687. {
  688. C Line &line=lines[view_line.line()];
  689. if(InRange(view.x, view_line))
  690. {
  691. real.set(view_line.cols[view.x].pos.x, view_line.line()); return InRange(real.x, line);
  692. }
  693. real.set(line.length(), view_line.line()); return false; // set 'real' as close as possible
  694. }
  695. }
  696. real.set(0, lines.elms()); return false; // set 'real' as close as possible
  697. }
  698. Int Source::realToView(Int real)C
  699. {
  700. REPAD(y, view_lines)if(view_lines[y].line()<=real)return y;
  701. return 0;
  702. }
  703. VecI2 Source::realToView(C VecI2 &real)C
  704. {
  705. Int y=realToView(real.y);
  706. if(InRange(y, view_lines))
  707. {
  708. C ViewLine &view_line=view_lines[y];
  709. if(view_line.line()==real.y)return VecI2(Max(0, view_line.findCol(real.x)), y);
  710. return VecI2(0, y);
  711. }
  712. return VecI2(0, 0);
  713. }
  714. /******************************************************************************/
  715. Str Source::textTokens()C
  716. {
  717. Str out; FREPA(lines)out.line()+=lines[i].textTokens();
  718. return out;
  719. }
  720. /******************************************************************************/
  721. // OPERATIONS
  722. /******************************************************************************/
  723. void Source::replacePath(C Str &src, C Str &dest) {loc.replacePath(src, dest);}
  724. void Source::setScroll()
  725. {
  726. Bool immediate=CE.options.imm_scroll();
  727. Flt relative =(immediate ? 0 : 0.5f),
  728. base =(immediate ? CE.ts.lineHeight()*7 : 0 );
  729. slidebar[0].scrollOptions(relative, base, immediate);
  730. slidebar[1].scrollOptions(relative, base, immediate);
  731. }
  732. void Source::setHideSlideBar()
  733. {
  734. alwaysHideHorizontalSlideBar(CE.options.hide_horizontal_slidebar());
  735. }
  736. void Source::curSel(VecI2 &min, VecI2 &max)
  737. {
  738. min=sel;
  739. max=cur;
  740. if(max.y<min.y || (max.y==min.y && max.x<min.x))Swap(min, max);
  741. }
  742. VecI2& Source::dec(VecI2 &p)
  743. {
  744. if(p.x>=0)p.x--;
  745. for(; p.x<0 && p.y>=0; )
  746. {
  747. p.y--;
  748. if(InRange(p.y, lines))p.x=lines[p.y].length()-1;
  749. }
  750. return p;
  751. }
  752. VecI2& Source::inc(VecI2 &p)
  753. {
  754. if(InRange(p.y, lines))
  755. {
  756. if(p.x>=lines[p.y].length()-1)
  757. {
  758. p.x=0;
  759. do p.y++; while(InRange(p.y, lines) && !lines[p.y].length());
  760. }else p.x++;
  761. }
  762. return p;
  763. }
  764. void Source::setTokenPtrs()
  765. {
  766. // set continuous tokens for the whole file
  767. Int token_index=0;
  768. tokens.clear(); FREPA(lines)
  769. {
  770. Memc<Token> &tokens=lines[i].Tokens(); FREPA(tokens)
  771. {
  772. Token &token=tokens[i]; T.tokens.add(&token); token.source_index=token_index++;
  773. }
  774. }
  775. }
  776. void Source::changed(Int from, Int num)
  777. {
  778. view_lines.del();
  779. // validate types and line indexes of all lines (this is fast because types are recalculated only on change, and also line indexes/sources are required to setup everytime for all lines)
  780. Line *prev=null;
  781. FREPA(lines)
  782. {
  783. Line &l=lines[i]; l.line=i; l.source=this;
  784. l.setType(prev ? prev->ends_with_comment : false, prev ? prev->ends_with_preproc : false);
  785. prev=&l;
  786. }
  787. // process
  788. delSymbols ();
  789. detectDefines ();
  790. preprocess (from, num);
  791. detectDataTypes();
  792. linkDataTypes ();
  793. detectVarFuncs ();
  794. parseCurFunc ();
  795. if(CE.view_mode() && CE.cur()==this)validateView();
  796. // set gui
  797. setGui();
  798. // set modification time
  799. modify_time.getUTC();
  800. }
  801. void Source::exist(Int x, Int y)
  802. {
  803. if(y>=lines.elms())lines.setNum(y+1);
  804. REP(x-lines[y].length())lines[y].append(' ');
  805. }
  806. /******************************************************************************/
  807. static Int CompareToken(Token* C &token, C VecI2 &pos)
  808. {
  809. if(Int c=Compare(token->lineIndex(), pos.y))return c;
  810. return Compare(token->col , pos.x);
  811. }
  812. static Int CompareTokenOverlap(Token* C &token, C VecI2 &pos)
  813. {
  814. if(Int c=Compare(token->lineIndex(), pos.y))return c;
  815. if(token->col+token->length()-1<pos.x)return -1;
  816. if(token->col >pos.x)return +1;
  817. return 0;
  818. }
  819. Token* Source::findPrevToken(C VecI2 &pos, Int &i)
  820. {
  821. VecI2 p(pos.x-1, pos.y);
  822. if(tokens.binarySearch(p, i, CompareToken))return tokens[i];
  823. if(InRange(--i, tokens))
  824. {
  825. Token &token=*tokens[i]; if(token.macro_col<0)return &token; // this is not a token made by macro
  826. // go left to first token that is created by that macro
  827. for(; ; i--)
  828. {
  829. if(InRange(i-1, tokens))
  830. {
  831. Token &prev=*tokens[i-1];
  832. if(prev.sameMacro(token))continue;
  833. }
  834. break;
  835. }
  836. // look by going right
  837. Token *nearest=null;
  838. Int nearest_i;
  839. for(; i<tokens.elms(); i++)
  840. {
  841. Token *cur=tokens[i]; switch(CompareTokenOverlap(cur, p))
  842. {
  843. case 0: return cur;
  844. case 1: goto too_far;
  845. case -1: nearest=cur; nearest_i=i; break;
  846. }
  847. }
  848. too_far:
  849. if(nearest){i=nearest_i; return nearest;}
  850. }
  851. i=-1; return null;
  852. }
  853. Token* Source::findToken(C VecI2 &pos, Int &i)
  854. {
  855. VecI2 p=pos;
  856. if(tokens.binarySearch(p, i, CompareToken))return tokens[i];
  857. if(InRange(--i, tokens))
  858. {
  859. Token &token=*tokens[i]; if(token.lineIndex()==pos.y && token.col+token.length()>pos.x)return &token;
  860. if(token.macro_col>=0)
  861. {
  862. // go left to first token that is created by that macro
  863. for(; ; i--)
  864. {
  865. if(InRange(i-1, tokens))
  866. {
  867. Token &prev=*tokens[i-1];
  868. if(prev.sameMacro(token))continue;
  869. }
  870. break;
  871. }
  872. // look by going right
  873. for(; i<tokens.elms(); i++)
  874. {
  875. Token *cur=tokens[i]; if(!cur->sameMacro(token))break;
  876. if(cur->lineIndex()==pos.y && cur->col+cur->length()>pos.x)return cur;
  877. }
  878. }
  879. }
  880. i=-1; return null;
  881. }
  882. /******************************************************************************/
  883. // MAIN
  884. /******************************************************************************/
  885. Source::~Source()
  886. {
  887. CE.sourceRemoved(T);
  888. if(win_io_save)Delete(win_io_save);
  889. }
  890. /******************************************************************************/
  891. // IO
  892. /******************************************************************************/
  893. enum
  894. {
  895. HEADER =1<<0,
  896. EE_HEADER=1<<1,
  897. CPP =1<<2,
  898. SRC_CONST=1<<3,
  899. };
  900. Bool Source::save(File &f, StrLibrary &sl)C
  901. {
  902. f.putByte((header ? HEADER : 0)|(ee_header ? EE_HEADER : 0)|(cpp ? CPP : 0)|(Const ? SRC_CONST : 0));
  903. sl.putStr(f, loc.file_name);
  904. if(!modify_time.save(f))return false;
  905. f.cmpUIntV(symbols.elms()); FREPA(symbols){C SymbolDef &symbol=symbols[i]; sl.putStr(f, symbol.name()); if(!symbol->save(f, sl))return false;} // write before 'lines'
  906. f.cmpUIntV(decls .elms()); FREPA(decls ){C SymbolDecl &decl = decls[i]; sl.putStr(f, decl .name()); if(!decl ->save(f, sl))return false;} // write before 'lines'
  907. f.cmpUIntV(lines .elms()); FREPA(lines ){C Line &line = lines[i]; if(!line .save(f, sl))return false;}
  908. return f.ok();
  909. }
  910. #define SOURCE_DEBUG_SPEED_SIZE 0
  911. #if SOURCE_DEBUG_SPEED_SIZE
  912. static Dbl F0, F1, F2;
  913. static Int S0, S1, S2;
  914. struct XX{~XX(){Exit(S+F0+" "+F1+" "+F2+"\n"+S0+" "+S1+" "+S2);}}x;
  915. #endif
  916. Bool Source::load(File &f, StrLibrary &sl, Str &temp)
  917. {
  918. Byte flag; f>>flag;
  919. header =FlagTest(flag, HEADER );
  920. ee_header=FlagTest(flag, EE_HEADER);
  921. cpp =FlagTest(flag, CPP );
  922. Const =FlagTest(flag, SRC_CONST);
  923. sl.getStr(f, loc.file_name); loc.setFile(loc.file_name);
  924. if(!modify_time.load(f))goto error;
  925. #if SOURCE_DEBUG_SPEED_SIZE
  926. Dbl t=Time.curTime(); Int s=f.pos();
  927. #endif
  928. symbols.setNum(f.decUIntV()); FREPA(symbols){sl.getStr(f, temp); if(!symbols[i].require(temp)->load(f, sl, T))goto error;}
  929. #if SOURCE_DEBUG_SPEED_SIZE
  930. F0+=Time.curTime()-t; t=Time.curTime(); S0+=f.pos()-s; s=f.pos();
  931. #endif
  932. decls .setNum(f.decUIntV()); FREPA(decls){sl.getStr(f, temp); if(!decls[i].require(temp)->load(f, sl, T))goto error;}
  933. #if SOURCE_DEBUG_SPEED_SIZE
  934. F1+=Time.curTime()-t; t=Time.curTime(); S1+=f.pos()-s; s=f.pos();
  935. #endif
  936. lines .setNum(f.decUIntV()); FREPA(lines)if(!lines[i].load(f, sl, i, T, temp))goto error;
  937. #if SOURCE_DEBUG_SPEED_SIZE
  938. F2+=Time.curTime()-t; t=Time.curTime(); S2+=f.pos()-s; s=f.pos();
  939. #endif
  940. setTokenPtrs();
  941. setGui();
  942. if(f.ok())return true;
  943. error:
  944. del(); return false;
  945. }
  946. /******************************************************************************/
  947. ERROR_TYPE Source::load()
  948. {
  949. ERROR_TYPE error;
  950. cpp=false;
  951. Str data;
  952. if(!loc.is())error=EE_ERR_ELM_NOT_FOUND;else
  953. {
  954. if(loc.file)
  955. {
  956. cpp=true;
  957. FileText f; if(!f.read(loc.file_name))error=EE_ERR_FILE_NOT_FOUND;else{error=EE_ERR_NONE; f.getAll(data);}
  958. }else
  959. {
  960. error=CE.cei().sourceDataLoad(loc.id, data);
  961. }
  962. }
  963. sel=-1; cur=0;
  964. fromText(data);
  965. if(error==EE_ERR_NONE && loc.file){FileInfo fi; if(fi.getSystem(loc.file_name)){modify_time=fi.modify_time_utc; Const=FlagTest(fi.attrib, FATTRIB_READ_ONLY);}} // adjust modification time after 'changed'
  966. return error;
  967. }
  968. void Source::reload()
  969. {
  970. Bool modified=T.modified(); delUndo();
  971. if( modified)load();
  972. }
  973. ERROR_TYPE Source::load(C SourceLoc &loc)
  974. {
  975. if(lines.elms())setUndo();
  976. T.loc=loc;
  977. undo_original_state=undos.undos();
  978. return load();
  979. }
  980. Bool Source::saveTxt(C Str &name)
  981. {
  982. FileText f; if(f.write(name, hasUnicode() ? UTF_8 : ANSI))
  983. {
  984. FREPA(lines){if(i)f.endLine(); f.putText(lines[i]);}
  985. if(f.flushOK())return true;
  986. //f.del(); FDelFile(name); no need to delete partially written text files
  987. }
  988. return false;
  989. }
  990. Bool Source::save(C SourceLoc &loc)
  991. {
  992. Bool ok=false;
  993. if(!loc.is())
  994. {
  995. Gui.msgBox(S, S+"Invalid save location");
  996. }else
  997. if(loc.file)
  998. {
  999. ok=saveTxt(loc.file_name);
  1000. if(!ok)Gui.msgBox(S, S+"Error saving to \""+loc.file_name+'"');
  1001. }else
  1002. {
  1003. ok=CE.cei().sourceDataSave(loc.id, asText());
  1004. if(!ok)Gui.msgBox(S, S+"Error saving source code");
  1005. }
  1006. if(ok)
  1007. {
  1008. T.loc=loc;
  1009. undo_original_state=undos.undos();
  1010. modify_time.getUTC(); // when saving, adjust new modification time of the file, this is needed in case of: start build, made change during build, end build, build again (during first build .obj file could get generated with date newer than change applied during build, adjusting date during save would guarantee newer version of the file)
  1011. forceCreateNextUndo();
  1012. if(loc.file)FTimeUTC(loc.file_name, modify_time);
  1013. if(header)CE.rebuildSymbols(true);
  1014. if(CE.cur()==this)CE.cei().sourceChanged();
  1015. }
  1016. return ok;
  1017. }
  1018. static void SaveEs(C Str &name, Source &source) {CE.save(&source, name);}
  1019. void Source::save()
  1020. {
  1021. if(!win_io_save)New(win_io_save)->create(CODE_EXT, S, GetPath(GetPath(DataPath())), SaveEs, SaveEs, T);
  1022. win_io_save->ext(GetExt(loc.file_name)+'|'+CODE_EXT); // use already existing extension as the 1st and most important
  1023. win_io_save->save();
  1024. }
  1025. Bool Source::overwrite() // returns true if saved successfully (immediately)
  1026. {
  1027. if(loc.is())return save(loc);
  1028. save();
  1029. return false;
  1030. }
  1031. /******************************************************************************/
  1032. static void SuggestionChanged(Source &src) {src.refreshSuggestions();}
  1033. Source::Source() : undos(true, this, 15)
  1034. {
  1035. lit_symbol=null;
  1036. win_io_save=null;
  1037. active=was_active=header=ee_header=false; opened=false; Const=false; view_comments=view_funcs=true; view_func_bodies=view_private_members=false;
  1038. recursive=parse_count=0; preproc_line_changed=highlight_line=-1; highlight_time=0; cur=0; sel=sel_temp=-1; lc_offset.zero(); delUndo();
  1039. create().skin(&CE.source_skin, false); kb_lit=false; setScroll(); setHideSlideBar();
  1040. T+=suggestions_region .create().hide();
  1041. T+=suggestions_textline.create().func(SuggestionChanged, T).hide();
  1042. ListColumn lc[]=
  1043. {
  1044. ListColumn(MEMBER(Suggestion, icon ), 0.05f , "icon"),
  1045. ListColumn(MEMBER(Suggestion, display), LCW_DATA, "text"),
  1046. };
  1047. suggestions_region+=suggestions_list.create(lc, Elms(lc), true).skin(&CE.suggestions_skin);
  1048. FlagDisable(suggestions_list.flag, LIST_SEARCHABLE|LIST_SORTABLE);
  1049. suggestions_list.cur_mode=LCM_ALWAYS;
  1050. resize();
  1051. }
  1052. /******************************************************************************/
  1053. }}
  1054. /******************************************************************************/