CodeEditor.cxx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. //
  2. // "$Id: CodeEditor.cxx 9980 2013-09-21 16:41:23Z greg.ercolano $"
  3. //
  4. // Code editor widget for the Fast Light Tool Kit (FLTK).
  5. //
  6. // Copyright 1998-2010 by Bill Spitzak and others.
  7. //
  8. // This library is free software. Distribution and use rights are outlined in
  9. // the file "COPYING" which should have been included with this file. If this
  10. // file is missing or damaged, see the license at:
  11. //
  12. // http://www.fltk.org/COPYING.php
  13. //
  14. // Please report all bugs and problems on the following page:
  15. //
  16. // http://www.fltk.org/str.php
  17. //
  18. //
  19. // Include necessary headers...
  20. //
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <ctype.h>
  25. #include "CodeEditor.h"
  26. Fl_Text_Display::Style_Table_Entry CodeEditor::
  27. styletable[] = { // Style table
  28. { FL_FOREGROUND_COLOR, FL_COURIER, 11 }, // A - Plain
  29. { FL_DARK_GREEN, FL_COURIER_ITALIC, 11 }, // B - Line comments
  30. { FL_DARK_GREEN, FL_COURIER_ITALIC, 11 }, // C - Block comments
  31. { FL_BLUE, FL_COURIER, 11 }, // D - Strings
  32. { FL_DARK_RED, FL_COURIER, 11 }, // E - Directives
  33. { FL_DARK_RED, FL_COURIER_BOLD, 11 }, // F - Types
  34. { FL_BLUE, FL_COURIER_BOLD, 11 } // G - Keywords
  35. };
  36. const char * const CodeEditor::
  37. code_keywords[] = { // Sorted list of C/C++ keywords...
  38. "and",
  39. "and_eq",
  40. "asm",
  41. "bitand",
  42. "bitor",
  43. "break",
  44. "case",
  45. "catch",
  46. "compl",
  47. "continue",
  48. "default",
  49. "delete",
  50. "do",
  51. "else",
  52. "false",
  53. "for",
  54. "goto",
  55. "if",
  56. "new",
  57. "not",
  58. "not_eq",
  59. "operator",
  60. "or",
  61. "or_eq",
  62. "return",
  63. "switch",
  64. "template",
  65. "this",
  66. "throw",
  67. "true",
  68. "try",
  69. "while",
  70. "xor",
  71. "xor_eq"
  72. };
  73. const char * const CodeEditor::
  74. code_types[] = { // Sorted list of C/C++ types...
  75. "auto",
  76. "bool",
  77. "char",
  78. "class",
  79. "const",
  80. "const_cast",
  81. "double",
  82. "dynamic_cast",
  83. "enum",
  84. "explicit",
  85. "extern",
  86. "float",
  87. "friend",
  88. "inline",
  89. "int",
  90. "long",
  91. "mutable",
  92. "namespace",
  93. "private",
  94. "protected",
  95. "public",
  96. "register",
  97. "short",
  98. "signed",
  99. "sizeof",
  100. "static",
  101. "static_cast",
  102. "struct",
  103. "template",
  104. "typedef",
  105. "typename",
  106. "union",
  107. "unsigned",
  108. "virtual",
  109. "void",
  110. "volatile"
  111. };
  112. // attempt to make the fluid code editor widget honour textsize setting
  113. void CodeEditor::textsize(Fl_Fontsize s) {
  114. Fl_Text_Editor::textsize(s); // call base class method
  115. // now attempt to update our styletable to honour the new size...
  116. int entries = sizeof(styletable) / sizeof(styletable[0]);
  117. for(int iter = 0; iter < entries; iter++) {
  118. styletable[iter].size = s;
  119. }
  120. } // textsize
  121. // 'compare_keywords()' - Compare two keywords...
  122. int CodeEditor::compare_keywords(const void *a, const void *b) {
  123. return (strcmp(*((const char **)a), *((const char **)b)));
  124. }
  125. // 'style_parse()' - Parse text and produce style data.
  126. void CodeEditor::style_parse(const char *text, char *style, int length) {
  127. char current;
  128. int col;
  129. int last;
  130. char buf[255],
  131. *bufptr;
  132. const char *temp;
  133. // Style letters:
  134. //
  135. // A - Plain
  136. // B - Line comments
  137. // C - Block comments
  138. // D - Strings
  139. // E - Directives
  140. // F - Types
  141. // G - Keywords
  142. for (current = *style, col = 0, last = 0; length > 0; length --, text ++) {
  143. if (current == 'B' || current == 'F' || current == 'G') current = 'A';
  144. if (current == 'A') {
  145. // Check for directives, comments, strings, and keywords...
  146. if (col == 0 && *text == '#') {
  147. // Set style to directive
  148. current = 'E';
  149. } else if (strncmp(text, "//", 2) == 0) {
  150. current = 'B';
  151. for (; length > 0 && *text != '\n'; length --, text ++) *style++ = 'B';
  152. if (length == 0) break;
  153. } else if (strncmp(text, "/*", 2) == 0) {
  154. current = 'C';
  155. } else if (strncmp(text, "\\\"", 2) == 0) {
  156. // Quoted quote...
  157. *style++ = current;
  158. *style++ = current;
  159. text ++;
  160. length --;
  161. col += 2;
  162. continue;
  163. } else if (*text == '\"') {
  164. current = 'D';
  165. } else if (!last && (islower(*text) || *text == '_')) {
  166. // Might be a keyword...
  167. for (temp = text, bufptr = buf;
  168. (islower(*temp) || *temp == '_') && bufptr < (buf + sizeof(buf) - 1);
  169. *bufptr++ = *temp++);
  170. if (!islower(*temp) && *temp != '_') {
  171. *bufptr = '\0';
  172. bufptr = buf;
  173. if (bsearch(&bufptr, code_types,
  174. sizeof(code_types) / sizeof(code_types[0]),
  175. sizeof(code_types[0]), compare_keywords)) {
  176. while (text < temp) {
  177. *style++ = 'F';
  178. text ++;
  179. length --;
  180. col ++;
  181. }
  182. text --;
  183. length ++;
  184. last = 1;
  185. continue;
  186. } else if (bsearch(&bufptr, code_keywords,
  187. sizeof(code_keywords) / sizeof(code_keywords[0]),
  188. sizeof(code_keywords[0]), compare_keywords)) {
  189. while (text < temp) {
  190. *style++ = 'G';
  191. text ++;
  192. length --;
  193. col ++;
  194. }
  195. text --;
  196. length ++;
  197. last = 1;
  198. continue;
  199. }
  200. }
  201. }
  202. } else if (current == 'C' && strncmp(text, "*/", 2) == 0) {
  203. // Close a C comment...
  204. *style++ = current;
  205. *style++ = current;
  206. text ++;
  207. length --;
  208. current = 'A';
  209. col += 2;
  210. continue;
  211. } else if (current == 'D') {
  212. // Continuing in string...
  213. if (strncmp(text, "\\\"", 2) == 0) {
  214. // Quoted end quote...
  215. *style++ = current;
  216. *style++ = current;
  217. text ++;
  218. length --;
  219. col += 2;
  220. continue;
  221. } else if (*text == '\"') {
  222. // End quote...
  223. *style++ = current;
  224. col ++;
  225. current = 'A';
  226. continue;
  227. }
  228. }
  229. // Copy style info...
  230. if (current == 'A' && (*text == '{' || *text == '}')) *style++ = 'G';
  231. else *style++ = current;
  232. col ++;
  233. last = isalnum(*text) || *text == '_' || *text == '.';
  234. if (*text == '\n') {
  235. // Reset column and possibly reset the style
  236. col = 0;
  237. if (current == 'B' || current == 'E') current = 'A';
  238. }
  239. }
  240. }
  241. // 'style_unfinished_cb()' - Update unfinished styles.
  242. void CodeEditor::style_unfinished_cb(int, void*) { }
  243. // 'style_update()' - Update the style buffer...
  244. void CodeEditor::style_update(int pos, int nInserted, int nDeleted,
  245. int /*nRestyled*/, const char * /*deletedText*/,
  246. void *cbArg) {
  247. CodeEditor *editor = (CodeEditor *)cbArg;
  248. int start, // Start of text
  249. end; // End of text
  250. char last, // Last style on line
  251. *style, // Style data
  252. *text; // Text data
  253. // If this is just a selection change, just unselect the style buffer...
  254. if (nInserted == 0 && nDeleted == 0) {
  255. editor->mStyleBuffer->unselect();
  256. return;
  257. }
  258. // Track changes in the text buffer...
  259. if (nInserted > 0) {
  260. // Insert characters into the style buffer...
  261. style = new char[nInserted + 1];
  262. memset(style, 'A', nInserted);
  263. style[nInserted] = '\0';
  264. editor->mStyleBuffer->replace(pos, pos + nDeleted, style);
  265. delete[] style;
  266. } else {
  267. // Just delete characters in the style buffer...
  268. editor->mStyleBuffer->remove(pos, pos + nDeleted);
  269. }
  270. // Select the area that was just updated to avoid unnecessary
  271. // callbacks...
  272. editor->mStyleBuffer->select(pos, pos + nInserted - nDeleted);
  273. // Re-parse the changed region; we do this by parsing from the
  274. // beginning of the line of the changed region to the end of
  275. // the line of the changed region... Then we check the last
  276. // style character and keep updating if we have a multi-line
  277. // comment character...
  278. start = editor->mBuffer->line_start(pos);
  279. end = editor->mBuffer->line_end(pos + nInserted);
  280. text = editor->mBuffer->text_range(start, end);
  281. style = editor->mStyleBuffer->text_range(start, end);
  282. if (start==end)
  283. last = 0;
  284. else
  285. last = style[end - start - 1];
  286. style_parse(text, style, end - start);
  287. editor->mStyleBuffer->replace(start, end, style);
  288. editor->redisplay_range(start, end);
  289. if (start==end || last != style[end - start - 1]) {
  290. // The last character on the line changed styles, so reparse the
  291. // remainder of the buffer...
  292. free(text);
  293. free(style);
  294. end = editor->mBuffer->length();
  295. text = editor->mBuffer->text_range(start, end);
  296. style = editor->mStyleBuffer->text_range(start, end);
  297. style_parse(text, style, end - start);
  298. editor->mStyleBuffer->replace(start, end, style);
  299. editor->redisplay_range(start, end);
  300. }
  301. free(text);
  302. free(style);
  303. }
  304. int CodeEditor::auto_indent(int, CodeEditor* e) {
  305. if (e->buffer()->selected()) {
  306. e->insert_position(e->buffer()->primary_selection()->start());
  307. e->buffer()->remove_selection();
  308. }
  309. int pos = e->insert_position();
  310. int start = e->line_start(pos);
  311. char *text = e->buffer()->text_range(start, pos);
  312. char *ptr;
  313. for (ptr = text; isspace(*ptr); ptr ++);
  314. *ptr = '\0';
  315. if (*text) {
  316. // use only a single 'insert' call to avoid redraw issues
  317. int n = strlen(text);
  318. char *b = (char*)malloc(n+2);
  319. *b = '\n';
  320. strcpy(b+1, text);
  321. e->insert(b);
  322. free(b);
  323. } else {
  324. e->insert("\n");
  325. }
  326. e->show_insert_position();
  327. e->set_changed();
  328. if (e->when()&FL_WHEN_CHANGED) e->do_callback();
  329. free(text);
  330. return 1;
  331. }
  332. // Create a CodeEditor widget...
  333. CodeEditor::CodeEditor(int X, int Y, int W, int H, const char *L) :
  334. Fl_Text_Editor(X, Y, W, H, L) {
  335. buffer(new Fl_Text_Buffer);
  336. char *style = new char[mBuffer->length() + 1];
  337. char *text = mBuffer->text();
  338. memset(style, 'A', mBuffer->length());
  339. style[mBuffer->length()] = '\0';
  340. highlight_data(new Fl_Text_Buffer(mBuffer->length()), styletable,
  341. sizeof(styletable) / sizeof(styletable[0]),
  342. 'A', style_unfinished_cb, this);
  343. style_parse(text, style, mBuffer->length());
  344. mStyleBuffer->text(style);
  345. delete[] style;
  346. free(text);
  347. mBuffer->add_modify_callback(style_update, this);
  348. add_key_binding(FL_Enter, FL_TEXT_EDITOR_ANY_STATE,
  349. (Fl_Text_Editor::Key_Func)auto_indent);
  350. }
  351. // Destroy a CodeEditor widget...
  352. CodeEditor::~CodeEditor() {
  353. Fl_Text_Buffer *buf = mStyleBuffer;
  354. mStyleBuffer = 0;
  355. delete buf;
  356. buf = mBuffer;
  357. buffer(0);
  358. delete buf;
  359. }
  360. CodeViewer::CodeViewer(int X, int Y, int W, int H, const char *L)
  361. : CodeEditor(X, Y, W, H, L)
  362. {
  363. default_key_function(kf_ignore);
  364. remove_all_key_bindings(&key_bindings);
  365. cursor_style(CARET_CURSOR);
  366. }
  367. int CodeViewer::handle(int ev) {
  368. if(ev == FL_RELEASE) do_callback();
  369. return Fl_Text_Display::handle(ev);
  370. }
  371. void CodeEditor::change_text_size(int fsize){
  372. textsize(fsize);
  373. int nStyles = sizeof(styletable) / sizeof(styletable[0]);
  374. for(int i=0; i< nStyles; i++) styletable[i].size = fsize;
  375. redraw();
  376. }
  377. //
  378. // End of "$Id: CodeEditor.cxx 9980 2013-09-21 16:41:23Z greg.ercolano $".
  379. //