CE Code Col Line.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. namespace Edit{
  5. /******************************************************************************/
  6. // CODE LINE
  7. /******************************************************************************/
  8. Bool CodeLine::operator==(C Str &text)C
  9. {
  10. if(cols.elms()!=text.length())return false;
  11. REPA(cols)if(!EqualCS(cols[i].c, text[i]))return false;
  12. return true;
  13. }
  14. /******************************************************************************/
  15. Bool CodeLine::isKeyword(Int i, CChar8 *keyword)C
  16. {
  17. if(type(i-1)!=TOKEN_KEYWORD && keyword)for(; ; i++)
  18. {
  19. Char8 k=*keyword++; if(!k)return type(i)!=TOKEN_KEYWORD;
  20. if(type(i)!=TOKEN_KEYWORD || T[i]!=k)break;
  21. }
  22. return false;
  23. }
  24. Bool CodeLine::starts(Int i, CChar8 *text)C
  25. {
  26. if(InRange(i, cols) && text)for(;;)
  27. {
  28. Char c=*text++; if(!c)return true;
  29. if(cols[i++].c!=c)break;
  30. }
  31. return false;
  32. }
  33. /******************************************************************************/
  34. // SOURCE LOC
  35. /******************************************************************************/
  36. Int SourceLoc::Compare(C SourceLoc &a, C SourceLoc &b)
  37. {
  38. if(Int c=::Compare(a.file, b.file))return c;
  39. return a.file ? ::Compare(a.file_name, b.file_name) : ::Compare(a.id, b.id);
  40. }
  41. Str SourceLoc::asText()C
  42. {
  43. return file ? file_name : CE.cei().elmFullName(id);
  44. }
  45. /******************************************************************************/
  46. // LINE MAP
  47. /******************************************************************************/
  48. void LineMap::get(Int line, SourceLoc &src, Int &original_index, UID &original_id)C
  49. {
  50. REPA(sections)
  51. {
  52. C Section &section =sections[i];
  53. Int section_line=line-section.offset;
  54. if(InRange(section_line, section.target_to_original))
  55. {
  56. src=section.src;
  57. original_index=section.target_to_original[section_line].line_index;
  58. original_id =section.target_to_original[section_line].line_id;
  59. return;
  60. }
  61. }
  62. src.clear(); original_index=-1; original_id.zero();
  63. }
  64. void LineMap::operator++(int)
  65. {
  66. Section *section=null;
  67. Int offset =0;
  68. if(sections.elms())
  69. {
  70. Section &last=sections.last(); offset=last.offset+last.target_to_original.elms();
  71. if(!last.src.is())section=&last;
  72. }
  73. if(!section)
  74. {
  75. section=&sections.New();
  76. section->offset=offset;
  77. }
  78. Section::Map &map=section->target_to_original.New();
  79. map.line_index=-1;
  80. map.line_id.zero();
  81. }
  82. void LineMap::add(C SourceLoc &src, C Memc<CodeLine> &lines)
  83. {
  84. Section *section=null;
  85. Int offset =0;
  86. if(sections.elms())
  87. {
  88. Section &last=sections.last(); offset=last.offset+last.target_to_original.elms();
  89. if(last.src==src)section=&last;
  90. }
  91. if(!section)
  92. {
  93. section=&sections.New();
  94. section->src =src;
  95. section->offset=offset;
  96. }
  97. Source *source=CE.findSource(src);
  98. FREPA(lines) // order important
  99. {
  100. Section::Map &map=section->target_to_original.New();
  101. map.line_index=lines[i].lines.y; // use Y because that one can be -1
  102. map.line_id.zero();
  103. if(source && InRange(map.line_index, source->lines))map.line_id=source->lines[map.line_index].id;
  104. }
  105. }
  106. /******************************************************************************/
  107. // FUNCTIONS
  108. /******************************************************************************/
  109. Int FindLineI(Memc<CodeLine> &code_lines, Int line) {REPA(code_lines)if(code_lines[i].hasLine(line))return i; return -1;}
  110. CodeLine* FindLine (Memc<CodeLine> &code_lines, Int line) {Int i=FindLineI(code_lines, line); return (i>=0) ? &code_lines[i] : null;}
  111. CodeLine* FindLineCol(Memc<CodeLine> &code_lines, C VecI2 &pos, Int &cl_col) {VecI2 cl_pos; if(FindLineCol(code_lines, pos, cl_pos)){cl_col=cl_pos.x; return &code_lines[cl_pos.y];} cl_col=-1; return null;}
  112. Bool FindLineCol(Memc<CodeLine> &code_lines, C VecI2 &pos, VecI2 &cl_pos)
  113. {
  114. if(pos.x>=0 && pos.y>=0)
  115. REPA(code_lines) // all need to be checked because they are not sorted, and there may be multiple code lines pointing to the same line
  116. {
  117. CodeLine &cl=code_lines[i]; if(cl.hasLine(pos.y))
  118. {
  119. Int cl_col=cl.findPos(pos); if(cl_col>=0){cl_pos.set(cl_col, i); return true;} // operate on temporary 'cl_col' in case 'cl_pos' is actually 'pos'
  120. }
  121. }
  122. return false;
  123. }
  124. Bool FindLineCol(Memc<CodeLine> &code_lines, Int token_index, Int token_line, Bool token_start, VecI2 &cl_pos)
  125. {
  126. if(token_index>=0 && token_line>=0)
  127. REPA(code_lines) // all need to be checked because they are not sorted, and there may be multiple code lines pointing to the same line
  128. {
  129. CodeLine &cl=code_lines[i]; if(cl.hasLine(token_line))
  130. {
  131. cl_pos.x=cl.findToken(token_index, token_start); if(cl_pos.x>=0){cl_pos.y=i; return true;}
  132. }
  133. }
  134. return false;
  135. }
  136. /******************************************************************************/
  137. void Remove(Memc<CodeLine> &lines, C VecI2 &start, C VecI2 &end, Bool definite)
  138. {
  139. for(Int y=end.y; y>=start.y; y--)
  140. {
  141. if(y>start.y && y<end.y)lines.remove(y, true);else
  142. {
  143. CodeLine &cl=lines[y];
  144. Int start_x=((y==start.y) ? start.x : 0),
  145. end_x=((y== end.y) ? end.x : cl.cols.elms()-1);
  146. for(Int x=start_x; x<=end_x; x++)if(definite)cl.remove(start_x);else cl.cols[x].remove();
  147. }
  148. }
  149. }
  150. /******************************************************************************/
  151. void Clean(Memc<CodeLine> &lines)
  152. {
  153. // remove empty lines
  154. REPA(lines)
  155. {
  156. CodeLine &line=lines[i];
  157. if(line.toRemove())
  158. {
  159. lines.remove(i, true);
  160. // check for empty lines between this removed line and previous line to be removed
  161. /* does not work well with "}\nprivate" because "\n" gets removed
  162. REPD(j, i) // find previous line to be removed
  163. {
  164. CodeLine &line=lines[j];
  165. if(!line.empty ())break; // if encountered not empty line then break the process
  166. if( line.toRemove()) // if found another line to be removed
  167. {
  168. REPD(k, i-j-1)lines.remove(--i, true); // remove all between current (i) and previous (j) "j+1 .. i-1"
  169. break;
  170. }
  171. }*/
  172. // after removing removed line, check if previous and next lines are empty, in this case remove one of them
  173. if(InRange(i-1, lines)
  174. && InRange(i , lines))
  175. {
  176. CodeLine &prev=lines[i-1]; Bool prev_can_be_removed=true;
  177. CodeLine &next=lines[i ]; Bool next_can_be_removed=true;
  178. REPAD(c, prev)if(prev.cols[c].type!=TOKEN_NONE)if(prev.cols[c].c!='{')goto has_valid;else {prev_can_be_removed=false; break;} // allow '{'
  179. FREPAD(c, next)if(next.cols[c].type!=TOKEN_NONE)if(next.cols[c].c!='}')goto has_valid;else {next_can_be_removed=false; break;} // allow '}'
  180. if(next_can_be_removed)lines.remove(i , true);else
  181. if(prev_can_be_removed)lines.remove(i-1, true);
  182. has_valid:;
  183. }
  184. }
  185. }
  186. FREPAD(y, lines)
  187. {
  188. CodeLine &l=lines[y];
  189. FREPA(l)if(l.type(i)==TOKEN_REMOVE)
  190. {
  191. Int y0, y1;
  192. Bool obstacle=false;
  193. if( !obstacle)for(y0=y; InRange(y0-1, lines); ){if(!InRange(i, lines[y0-1]) || lines[y0-1].type(i)==TOKEN_COMMENT || lines[y0-1][i]==')')break; y0--; if(ValidType(lines[y0].type(i)) || lines[y0][i-1]==',' || lines[y0][i+1]=='{' || MustSeparate(lines[y0].type(i-1), lines[y0].type(i+1))){obstacle=true; break;}}
  194. if( !obstacle)for(y1=y; InRange(y1+1, lines); ){if(!InRange(i, lines[y1+1]) || lines[y1+1].type(i)==TOKEN_COMMENT )break; y1++; if(ValidType(lines[y1].type(i)) || lines[y1][i-1]==',' || lines[y1][i+1]=='{' || MustSeparate(lines[y1].type(i-1), lines[y1].type(i+1))){obstacle=true; break;}}
  195. if( !obstacle)
  196. {
  197. Bool removed=false;
  198. for(; y0<=y1; y0++)
  199. {
  200. CodeLine &l=lines[y0];
  201. if(!(ValidType(l.type(i-1)) && l.type(i+1)==TOKEN_COMMENT)){l.remove(i); removed=true;}
  202. }
  203. if(removed)i--; // try again only if we've actually removed something
  204. }
  205. }
  206. // convert " ;" to "; "
  207. FREPA(l)if(l.type(i)==TOKEN_REMOVE)if(l[i+1]==';')l.remove(i).insert(i+1, ' ', TOKEN_REMOVE);
  208. }
  209. }
  210. /******************************************************************************/
  211. void Write(FileText &f, C Memc<CodeLine> &lines)
  212. {
  213. FREPA(lines){C CodeLine &cl=lines[i]; FREPA(cl.cols)f.putChar(cl.cols[i].c); f.endLine();}
  214. }
  215. /******************************************************************************/
  216. void AdjustNameSymbol(Memc<CodeLine> &lines, Symbol* &src, Symbol *dest)
  217. {
  218. if(src!=dest)
  219. {
  220. // test if 'dest' is a child of 'src' : go from 'dest' to the root and check if 'src' is along the way
  221. if(dest)for(Symbol *cur=dest->Parent(), *child=dest; ; )
  222. {
  223. if(cur==src)
  224. {
  225. src=child;
  226. {CodeLine &cl=lines.New(); REP(TabLength*src->level)cl.append(' ', TOKEN_NONE); cl.append("namespace", TOKEN_KEYWORD).append(' ', TOKEN_NONE).append(*src, TOKEN_CODE);}
  227. {CodeLine &cl=lines.New(); REP(TabLength*src->level)cl.append(' ', TOKEN_NONE); cl.append('{', TOKEN_OPERATOR);}
  228. AdjustNameSymbol(lines, src, dest);
  229. return;
  230. }
  231. if(!cur)break;
  232. child=cur;
  233. cur =cur->Parent();
  234. }
  235. // we must go back, because 'dest' is not a child of 'src'
  236. {CodeLine &cl=lines.New(); REP(TabLength*src->level)cl.append(' ', TOKEN_NONE); cl.append('}', TOKEN_OPERATOR).append(' ', TOKEN_NONE).append(S+"// namespace "+*src, TOKEN_COMMENT);}
  237. src=src->Parent();
  238. AdjustNameSymbol(lines, src, dest);
  239. return;
  240. }
  241. }
  242. /******************************************************************************/
  243. void Parse(Memc<CodeLine> &lines)
  244. {
  245. FREPA(lines)
  246. {
  247. // convert 8'' -> '', '' -> u''
  248. // convert 8"" -> "", "" -> u""
  249. // convert 0.0f -> 0.0f, 0.0 -> 0.0f, 0.0d -> 0.0
  250. // convert "<::" -> "< ::" (Apple Xcode 5 LLVM compiler fails)
  251. CodeLine &cl=lines[i];
  252. FREPA(cl)
  253. {
  254. TOKEN_TYPE type=cl.cols[i].type;
  255. if(type==TOKEN_CHAR16 && cl.type(i-1)!=TOKEN_CHAR16 && cl[i]=='\'')cl.insert(i, 'u', TOKEN_CHAR16);else // '' -> u''
  256. if(type==TOKEN_TEXT16 && cl.type(i-1)!=TOKEN_TEXT16 && cl[i]=='"' ) // "" -> u""
  257. {
  258. // don't parse for "include" and "extern"
  259. REPD(j, i)if(ValidType(cl.type(j)))
  260. {
  261. if(cl.type(j)==TOKEN_KEYWORD && cl[j]=='n' && cl[j-1]=='r' && cl[j-2]=='e' && cl[j-3]=='t' && cl[j-4]=='x' && cl[j-5]=='e' )goto skip_parse; // extern
  262. if(cl.type(j)==TOKEN_PREPROC && cl[j]=='e' && cl[j-1]=='d' && cl[j-2]=='u' && cl[j-3]=='l' && cl[j-4]=='c' && cl[j-5]=='n' && cl[j-6]=='i')goto skip_parse; // include
  263. break;
  264. }
  265. cl.insert(i, 'u', TOKEN_TEXT16);
  266. skip_parse:;
  267. }else
  268. if(type==TOKEN_NUMBER) // 0.0f -> 0.0f, 0.0 -> 0.0f, 0.0d -> 0.0
  269. {
  270. for(; cl.type(i)==TOKEN_NUMBER; i++)if(cl[i]=='.') // if floating point
  271. {
  272. for(; cl.type(i+1)==TOKEN_NUMBER; i++); // move 'i' to the last character of the number token
  273. if(cl[i]=='f' || cl[i]=='F'){}else // do nothing (0.0f -> 0.0f)
  274. {
  275. if(cl[i]=='d' || cl[i]=='D')cl.remove( i ); // remove double suffix (0.0d -> 0.0 )
  276. else cl.insert(++i, 'f', TOKEN_NUMBER); // add float suffix (0.0 -> 0.0f)
  277. }
  278. break;
  279. }
  280. }else
  281. if(type==TOKEN_OPERATOR)
  282. {
  283. if(cl[i]=='<' && cl[i+1]==':' && cl[i+2]==':')cl.insert(i+1, ' ', TOKEN_NONE);
  284. }
  285. }
  286. }
  287. }
  288. /******************************************************************************/
  289. Bool OverwriteOnChange(File &src, C Str &dest, Bool *changed)
  290. {
  291. File f; if(f.readStdTry(dest)){src.pos(0); if(src.equal(f)){if(changed)*changed=false; return true;}} if(changed)*changed=true;
  292. if(!f.writeTry(dest)){FCreateDirs(_GetPath(dest)); if(!f.writeTry(dest))return false;} src.pos(0); return src.copy(f) && f.flush();
  293. }
  294. Bool OverwriteOnChange(FileText &src, C Str &dest, Bool *changed)
  295. {
  296. #if 1 // binary mode
  297. return OverwriteOnChange(src._f, dest, changed);
  298. #else // text mode
  299. FileText f; if(f.read(dest))
  300. {
  301. Str s, d;
  302. for(src.rewind(); !src.end() || !f.end(); )
  303. {
  304. src.fullLine(s);
  305. f .fullLine(d);
  306. if(!Equal(s, d, true))goto different;
  307. }
  308. if(changed)*changed=false; return true;
  309. }
  310. different:
  311. if(changed)*changed=true;
  312. if(!f.write(dest, src.encoding())){FCreateDirs(_GetPath(dest)); if(!f.write(dest, src.encoding()))return false;}
  313. for(src.rewind(); !src.end(); )f.putLine(src.fullLine()); return f.flushOK();
  314. #endif
  315. }
  316. Bool OverwriteOnChange(XmlData &src, C Str &dest, Bool params_in_separate_lines, ENCODING encoding)
  317. {
  318. FileText ft; ft.writeMem(encoding);
  319. src.save(ft, params_in_separate_lines);
  320. return OverwriteOnChange(ft, dest);
  321. }
  322. Bool OverwriteOnChange(Memc<CodeLine> &src, C Str &dest)
  323. {
  324. FileText f; Write(f.writeMem(), src); return OverwriteOnChange(f, dest);
  325. }
  326. Bool OverwriteOnChangeLoud(File &src, C Str &dest ) {if(!OverwriteOnChange(src, dest ))return ErrorWrite(dest); return true;}
  327. Bool OverwriteOnChangeLoud(FileText &src, C Str &dest ) {if(!OverwriteOnChange(src, dest ))return ErrorWrite(dest); return true;}
  328. Bool OverwriteOnChangeLoud(XmlData &src, C Str &dest, Bool params_in_separate_lines, ENCODING encoding) {if(!OverwriteOnChange(src, dest, params_in_separate_lines, encoding))return ErrorWrite(dest); return true;}
  329. Bool OverwriteOnChangeLoud(Memc<CodeLine> &src, C Str &dest ) {if(!OverwriteOnChange(src, dest ))return ErrorWrite(dest); return true;}
  330. /******************************************************************************/
  331. }}
  332. /******************************************************************************/