Keine Beschreibung

kilo.c 40KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284
  1. /* Kilo -- A very simple editor in less than 1-kilo lines of code (as counted
  2. * by "cloc"). Does not depend on libcurses, directly emits VT100
  3. * escapes on the terminal.
  4. *
  5. * -----------------------------------------------------------------------
  6. *
  7. * Copyright (C) 2016 Salvatore Sanfilippo <antirez at gmail dot com>
  8. *
  9. * All rights reserved.
  10. *
  11. * Redistribution and use in source and binary forms, with or without
  12. * modification, are permitted provided that the following conditions are
  13. * met:
  14. *
  15. * * Redistributions of source code must retain the above copyright
  16. * notice, this list of conditions and the following disclaimer.
  17. *
  18. * * Redistributions in binary form must reproduce the above copyright
  19. * notice, this list of conditions and the following disclaimer in the
  20. * documentation and/or other materials provided with the distribution.
  21. *
  22. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  25. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  26. * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  27. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  28. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  29. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  30. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  31. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  32. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  33. */
  34. #define KILO_VERSION "0.0.1"
  35. #define _BSD_SOURCE
  36. #define _GNU_SOURCE
  37. #include <termios.h>
  38. #include <stdlib.h>
  39. #include <stdio.h>
  40. #include <errno.h>
  41. #include <string.h>
  42. #include <stdlib.h>
  43. #include <ctype.h>
  44. #include <sys/types.h>
  45. #include <sys/ioctl.h>
  46. #include <sys/time.h>
  47. #include <unistd.h>
  48. #include <stdarg.h>
  49. #include <fcntl.h>
  50. /* Syntax highlight types */
  51. #define HL_NORMAL 0
  52. #define HL_NONPRINT 1
  53. #define HL_COMMENT 2 /* Single line comment. */
  54. #define HL_MLCOMMENT 3 /* Multi-line comment. */
  55. #define HL_KEYWORD1 4
  56. #define HL_KEYWORD2 5
  57. #define HL_STRING 6
  58. #define HL_NUMBER 7
  59. #define HL_MATCH 8 /* Search match. */
  60. #define HL_HIGHLIGHT_STRINGS (1<<0)
  61. #define HL_HIGHLIGHT_NUMBERS (1<<1)
  62. struct editorSyntax {
  63. char **filematch;
  64. char **keywords;
  65. char singleline_comment_start[2];
  66. char multiline_comment_start[3];
  67. char multiline_comment_end[3];
  68. int flags;
  69. };
  70. /* This structure represents a single line of the file we are editing. */
  71. typedef struct erow {
  72. int idx; /* Row index in the file, zero-based. */
  73. int size; /* Size of the row, excluding the null term. */
  74. int rsize; /* Size of the rendered row. */
  75. char *chars; /* Row content. */
  76. char *render; /* Row content "rendered" for screen (for TABs). */
  77. unsigned char *hl; /* Syntax highlight type for each character in render.*/
  78. int hl_oc; /* Row had open comment at end in last syntax highlight
  79. check. */
  80. } erow;
  81. typedef struct hlcolor {
  82. int r,g,b;
  83. } hlcolor;
  84. struct editorConfig {
  85. int cx,cy; /* Cursor x and y position in characters */
  86. int rowoff; /* Offset of row displayed. */
  87. int coloff; /* Offset of column displayed. */
  88. int screenrows; /* Number of rows that we can show */
  89. int screencols; /* Number of cols that we can show */
  90. int numrows; /* Number of rows */
  91. int rawmode; /* Is terminal raw mode enabled? */
  92. erow *row; /* Rows */
  93. int dirty; /* File modified but not saved. */
  94. char *filename; /* Currently open filename */
  95. char statusmsg[80];
  96. time_t statusmsg_time;
  97. struct editorSyntax *syntax; /* Current syntax highlight, or NULL. */
  98. };
  99. static struct editorConfig E;
  100. enum KEY_ACTION{
  101. KEY_NULL = 0, /* NULL */
  102. CTRL_C = 3, /* Ctrl-c */
  103. CTRL_D = 4, /* Ctrl-d */
  104. CTRL_F = 6, /* Ctrl-f */
  105. CTRL_H = 8, /* Ctrl-h */
  106. TAB = 9, /* Tab */
  107. CTRL_L = 12, /* Ctrl+l */
  108. ENTER = 13, /* Enter */
  109. CTRL_Q = 17, /* Ctrl-q */
  110. CTRL_S = 19, /* Ctrl-s */
  111. CTRL_U = 21, /* Ctrl-u */
  112. ESC = 27, /* Escape */
  113. BACKSPACE = 127, /* Backspace */
  114. /* The following are just soft codes, not really reported by the
  115. * terminal directly. */
  116. ARROW_LEFT = 1000,
  117. ARROW_RIGHT,
  118. ARROW_UP,
  119. ARROW_DOWN,
  120. DEL_KEY,
  121. HOME_KEY,
  122. END_KEY,
  123. PAGE_UP,
  124. PAGE_DOWN
  125. };
  126. void editorSetStatusMessage(const char *fmt, ...);
  127. /* =========================== Syntax highlights DB =========================
  128. *
  129. * In order to add a new syntax, define two arrays with a list of file name
  130. * matches and keywords. The file name matches are used in order to match
  131. * a given syntax with a given file name: if a match pattern starts with a
  132. * dot, it is matched as the last past of the filename, for example ".c".
  133. * Otherwise the pattern is just searched inside the filenme, like "Makefile").
  134. *
  135. * The list of keywords to highlight is just a list of words, however if they
  136. * a trailing '|' character is added at the end, they are highlighted in
  137. * a different color, so that you can have two different sets of keywords.
  138. *
  139. * Finally add a stanza in the HLDB global variable with two two arrays
  140. * of strings, and a set of flags in order to enable highlighting of
  141. * comments and numbers.
  142. *
  143. * The characters for single and multi line comments must be exactly two
  144. * and must be provided as well (see the C language example).
  145. *
  146. * There is no support to highlight patterns currently. */
  147. /* C / C++ */
  148. char *C_HL_extensions[] = {".c",".cpp",NULL};
  149. char *C_HL_keywords[] = {
  150. /* A few C / C++ keywords */
  151. "switch","if","while","for","break","continue","return","else",
  152. "struct","union","typedef","static","enum","class",
  153. /* C types */
  154. "int|","long|","double|","float|","char|","unsigned|","signed|",
  155. "void|",NULL
  156. };
  157. /* Here we define an array of syntax highlights by extensions, keywords,
  158. * comments delimiters and flags. */
  159. struct editorSyntax HLDB[] = {
  160. {
  161. /* C / C++ */
  162. C_HL_extensions,
  163. C_HL_keywords,
  164. "//","/*","*/",
  165. HL_HIGHLIGHT_STRINGS | HL_HIGHLIGHT_NUMBERS
  166. }
  167. };
  168. #define HLDB_ENTRIES (sizeof(HLDB)/sizeof(HLDB[0]))
  169. /* ======================= Low level terminal handling ====================== */
  170. static struct termios orig_termios; /* In order to restore at exit.*/
  171. void disableRawMode(int fd) {
  172. /* Don't even check the return value as it's too late. */
  173. if (E.rawmode) {
  174. tcsetattr(fd,TCSAFLUSH,&orig_termios);
  175. E.rawmode = 0;
  176. }
  177. }
  178. /* Called at exit to avoid remaining in raw mode. */
  179. void editorAtExit(void) {
  180. disableRawMode(STDIN_FILENO);
  181. }
  182. /* Raw mode: 1960 magic shit. */
  183. int enableRawMode(int fd) {
  184. struct termios raw;
  185. if (E.rawmode) return 0; /* Already enabled. */
  186. if (!isatty(STDIN_FILENO)) goto fatal;
  187. atexit(editorAtExit);
  188. if (tcgetattr(fd,&orig_termios) == -1) goto fatal;
  189. raw = orig_termios; /* modify the original mode */
  190. /* input modes: no break, no CR to NL, no parity check, no strip char,
  191. * no start/stop output control. */
  192. raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
  193. /* output modes - disable post processing */
  194. raw.c_oflag &= ~(OPOST);
  195. /* control modes - set 8 bit chars */
  196. raw.c_cflag |= (CS8);
  197. /* local modes - choing off, canonical off, no extended functions,
  198. * no signal chars (^Z,^C) */
  199. raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
  200. /* control chars - set return condition: min number of bytes and timer. */
  201. raw.c_cc[VMIN] = 0; /* Return each byte, or zero for timeout. */
  202. raw.c_cc[VTIME] = 1; /* 100 ms timeout (unit is tens of second). */
  203. /* put terminal in raw mode after flushing */
  204. if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal;
  205. E.rawmode = 1;
  206. return 0;
  207. fatal:
  208. errno = ENOTTY;
  209. return -1;
  210. }
  211. /* Read a key from the terminal put in raw mode, trying to handle
  212. * escape sequences. */
  213. int editorReadKey(int fd) {
  214. int nread;
  215. char c, seq[3];
  216. while ((nread = read(fd,&c,1)) == 0);
  217. if (nread == -1) exit(1);
  218. while(1) {
  219. switch(c) {
  220. case ESC: /* escape sequence */
  221. /* If this is just an ESC, we'll timeout here. */
  222. if (read(fd,seq,1) == 0) return ESC;
  223. if (read(fd,seq+1,1) == 0) return ESC;
  224. /* ESC [ sequences. */
  225. if (seq[0] == '[') {
  226. if (seq[1] >= '0' && seq[1] <= '9') {
  227. /* Extended escape, read additional byte. */
  228. if (read(fd,seq+2,1) == 0) return ESC;
  229. if (seq[2] == '~') {
  230. switch(seq[1]) {
  231. case '3': return DEL_KEY;
  232. case '5': return PAGE_UP;
  233. case '6': return PAGE_DOWN;
  234. }
  235. }
  236. } else {
  237. switch(seq[1]) {
  238. case 'A': return ARROW_UP;
  239. case 'B': return ARROW_DOWN;
  240. case 'C': return ARROW_RIGHT;
  241. case 'D': return ARROW_LEFT;
  242. case 'H': return HOME_KEY;
  243. case 'F': return END_KEY;
  244. }
  245. }
  246. }
  247. /* ESC O sequences. */
  248. else if (seq[0] == 'O') {
  249. switch(seq[1]) {
  250. case 'H': return HOME_KEY;
  251. case 'F': return END_KEY;
  252. }
  253. }
  254. break;
  255. default:
  256. return c;
  257. }
  258. }
  259. }
  260. /* Use the ESC [6n escape sequence to query the horizontal cursor position
  261. * and return it. On error -1 is returned, on success the position of the
  262. * cursor is stored at *rows and *cols and 0 is returned. */
  263. int getCursorPosition(int ifd, int ofd, int *rows, int *cols) {
  264. char buf[32];
  265. unsigned int i = 0;
  266. /* Report cursor location */
  267. if (write(ofd, "\x1b[6n", 4) != 4) return -1;
  268. /* Read the response: ESC [ rows ; cols R */
  269. while (i < sizeof(buf)-1) {
  270. if (read(ifd,buf+i,1) != 1) break;
  271. if (buf[i] == 'R') break;
  272. i++;
  273. }
  274. buf[i] = '\0';
  275. /* Parse it. */
  276. if (buf[0] != ESC || buf[1] != '[') return -1;
  277. if (sscanf(buf+2,"%d;%d",rows,cols) != 2) return -1;
  278. return 0;
  279. }
  280. /* Try to get the number of columns in the current terminal. If the ioctl()
  281. * call fails the function will try to query the terminal itself.
  282. * Returns 0 on success, -1 on error. */
  283. int getWindowSize(int ifd, int ofd, int *rows, int *cols) {
  284. struct winsize ws;
  285. if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
  286. /* ioctl() failed. Try to query the terminal itself. */
  287. int orig_row, orig_col, retval;
  288. /* Get the initial position so we can restore it later. */
  289. retval = getCursorPosition(ifd,ofd,&orig_row,&orig_col);
  290. if (retval == -1) goto failed;
  291. /* Go to right/bottom margin and get position. */
  292. if (write(ofd,"\x1b[999C\x1b[999B",12) != 12) goto failed;
  293. retval = getCursorPosition(ifd,ofd,rows,cols);
  294. if (retval == -1) goto failed;
  295. /* Restore position. */
  296. char seq[32];
  297. snprintf(seq,32,"\x1b[%d;%dH",orig_row,orig_col);
  298. if (write(ofd,seq,strlen(seq)) == -1) {
  299. /* Can't recover... */
  300. }
  301. return 0;
  302. } else {
  303. *cols = ws.ws_col;
  304. *rows = ws.ws_row;
  305. return 0;
  306. }
  307. failed:
  308. return -1;
  309. }
  310. /* ====================== Syntax highlight color scheme ==================== */
  311. int is_separator(int c) {
  312. return c == '\0' || isspace(c) || strchr(",.()+-/*=~%[];",c) != NULL;
  313. }
  314. /* Return true if the specified row last char is part of a multi line comment
  315. * that starts at this row or at one before, and does not end at the end
  316. * of the row but spawns to the next row. */
  317. int editorRowHasOpenComment(erow *row) {
  318. if (row->hl && row->rsize && row->hl[row->rsize-1] == HL_MLCOMMENT &&
  319. (row->rsize < 2 || (row->render[row->rsize-2] != '*' ||
  320. row->render[row->rsize-1] != '/'))) return 1;
  321. return 0;
  322. }
  323. /* Set every byte of row->hl (that corresponds to every character in the line)
  324. * to the right syntax highlight type (HL_* defines). */
  325. void editorUpdateSyntax(erow *row) {
  326. row->hl = realloc(row->hl,row->rsize);
  327. memset(row->hl,HL_NORMAL,row->rsize);
  328. if (E.syntax == NULL) return; /* No syntax, everything is HL_NORMAL. */
  329. int i, prev_sep, in_string, in_comment;
  330. char *p;
  331. char **keywords = E.syntax->keywords;
  332. char *scs = E.syntax->singleline_comment_start;
  333. char *mcs = E.syntax->multiline_comment_start;
  334. char *mce = E.syntax->multiline_comment_end;
  335. /* Point to the first non-space char. */
  336. p = row->render;
  337. i = 0; /* Current char offset */
  338. while(*p && isspace(*p)) {
  339. p++;
  340. i++;
  341. }
  342. prev_sep = 1; /* Tell the parser if 'i' points to start of word. */
  343. in_string = 0; /* Are we inside "" or '' ? */
  344. in_comment = 0; /* Are we inside multi-line comment? */
  345. /* If the previous line has an open comment, this line starts
  346. * with an open comment state. */
  347. if (row->idx > 0 && editorRowHasOpenComment(&E.row[row->idx-1]))
  348. in_comment = 1;
  349. while(*p) {
  350. /* Handle // comments. */
  351. if (prev_sep && *p == scs[0] && *(p+1) == scs[1]) {
  352. /* From here to end is a comment */
  353. memset(row->hl+i,HL_COMMENT,row->size-i);
  354. return;
  355. }
  356. /* Handle multi line comments. */
  357. if (in_comment) {
  358. row->hl[i] = HL_MLCOMMENT;
  359. if (*p == mce[0] && *(p+1) == mce[1]) {
  360. row->hl[i+1] = HL_MLCOMMENT;
  361. p += 2; i += 2;
  362. in_comment = 0;
  363. prev_sep = 1;
  364. continue;
  365. } else {
  366. prev_sep = 0;
  367. p++; i++;
  368. continue;
  369. }
  370. } else if (*p == mcs[0] && *(p+1) == mcs[1]) {
  371. row->hl[i] = HL_MLCOMMENT;
  372. row->hl[i+1] = HL_MLCOMMENT;
  373. p += 2; i += 2;
  374. in_comment = 1;
  375. prev_sep = 0;
  376. continue;
  377. }
  378. /* Handle "" and '' */
  379. if (in_string) {
  380. row->hl[i] = HL_STRING;
  381. if (*p == '\\') {
  382. row->hl[i+1] = HL_STRING;
  383. p += 2; i += 2;
  384. prev_sep = 0;
  385. continue;
  386. }
  387. if (*p == in_string) in_string = 0;
  388. p++; i++;
  389. continue;
  390. } else {
  391. if (*p == '"' || *p == '\'') {
  392. in_string = *p;
  393. row->hl[i] = HL_STRING;
  394. p++; i++;
  395. prev_sep = 0;
  396. continue;
  397. }
  398. }
  399. /* Handle non printable chars. */
  400. if (!isprint(*p)) {
  401. row->hl[i] = HL_NONPRINT;
  402. p++; i++;
  403. prev_sep = 0;
  404. continue;
  405. }
  406. /* Handle numbers */
  407. if ((isdigit(*p) && (prev_sep || row->hl[i-1] == HL_NUMBER)) ||
  408. (*p == '.' && i >0 && row->hl[i-1] == HL_NUMBER)) {
  409. row->hl[i] = HL_NUMBER;
  410. p++; i++;
  411. prev_sep = 0;
  412. continue;
  413. }
  414. /* Handle keywords and lib calls */
  415. if (prev_sep) {
  416. int j;
  417. for (j = 0; keywords[j]; j++) {
  418. int klen = strlen(keywords[j]);
  419. int kw2 = keywords[j][klen-1] == '|';
  420. if (kw2) klen--;
  421. if (!memcmp(p,keywords[j],klen) &&
  422. is_separator(*(p+klen)))
  423. {
  424. /* Keyword */
  425. memset(row->hl+i,kw2 ? HL_KEYWORD2 : HL_KEYWORD1,klen);
  426. p += klen;
  427. i += klen;
  428. break;
  429. }
  430. }
  431. if (keywords[j] != NULL) {
  432. prev_sep = 0;
  433. continue; /* We had a keyword match */
  434. }
  435. }
  436. /* Not special chars */
  437. prev_sep = is_separator(*p);
  438. p++; i++;
  439. }
  440. /* Propagate syntax change to the next row if the open commen
  441. * state changed. This may recursively affect all the following rows
  442. * in the file. */
  443. int oc = editorRowHasOpenComment(row);
  444. if (row->hl_oc != oc && row->idx+1 < E.numrows)
  445. editorUpdateSyntax(&E.row[row->idx+1]);
  446. row->hl_oc = oc;
  447. }
  448. /* Maps syntax highlight token types to terminal colors. */
  449. int editorSyntaxToColor(int hl) {
  450. switch(hl) {
  451. case HL_COMMENT:
  452. case HL_MLCOMMENT: return 36; /* cyan */
  453. case HL_KEYWORD1: return 33; /* yellow */
  454. case HL_KEYWORD2: return 32; /* green */
  455. case HL_STRING: return 35; /* magenta */
  456. case HL_NUMBER: return 31; /* red */
  457. case HL_MATCH: return 34; /* blu */
  458. default: return 37; /* white */
  459. }
  460. }
  461. /* Select the syntax highlight scheme depending on the filename,
  462. * setting it in the global state E.syntax. */
  463. void editorSelectSyntaxHighlight(char *filename) {
  464. for (unsigned int j = 0; j < HLDB_ENTRIES; j++) {
  465. struct editorSyntax *s = HLDB+j;
  466. unsigned int i = 0;
  467. while(s->filematch[i]) {
  468. char *p;
  469. int patlen = strlen(s->filematch[i]);
  470. if ((p = strstr(filename,s->filematch[i])) != NULL) {
  471. if (s->filematch[i][0] != '.' || p[patlen] == '\0') {
  472. E.syntax = s;
  473. return;
  474. }
  475. }
  476. i++;
  477. }
  478. }
  479. }
  480. /* ======================= Editor rows implementation ======================= */
  481. /* Update the rendered version and the syntax highlight of a row. */
  482. void editorUpdateRow(erow *row) {
  483. int tabs = 0, nonprint = 0, j, idx;
  484. /* Create a version of the row we can directly print on the screen,
  485. * respecting tabs, substituting non printable characters with '?'. */
  486. free(row->render);
  487. for (j = 0; j < row->size; j++)
  488. if (row->chars[j] == TAB) tabs++;
  489. row->render = malloc(row->size + tabs*8 + nonprint*9 + 1);
  490. idx = 0;
  491. for (j = 0; j < row->size; j++) {
  492. if (row->chars[j] == TAB) {
  493. row->render[idx++] = ' ';
  494. while((idx+1) % 8 != 0) row->render[idx++] = ' ';
  495. } else {
  496. row->render[idx++] = row->chars[j];
  497. }
  498. }
  499. row->rsize = idx;
  500. row->render[idx] = '\0';
  501. /* Update the syntax highlighting attributes of the row. */
  502. editorUpdateSyntax(row);
  503. }
  504. /* Insert a row at the specified position, shifting the other rows on the bottom
  505. * if required. */
  506. void editorInsertRow(int at, char *s, size_t len) {
  507. if (at > E.numrows) return;
  508. E.row = realloc(E.row,sizeof(erow)*(E.numrows+1));
  509. if (at != E.numrows) {
  510. memmove(E.row+at+1,E.row+at,sizeof(E.row[0])*(E.numrows-at));
  511. for (int j = at+1; j <= E.numrows; j++) E.row[j].idx++;
  512. }
  513. E.row[at].size = len;
  514. E.row[at].chars = malloc(len+1);
  515. memcpy(E.row[at].chars,s,len+1);
  516. E.row[at].hl = NULL;
  517. E.row[at].hl_oc = 0;
  518. E.row[at].render = NULL;
  519. E.row[at].rsize = 0;
  520. E.row[at].idx = at;
  521. editorUpdateRow(E.row+at);
  522. E.numrows++;
  523. E.dirty++;
  524. }
  525. /* Free row's heap allocated stuff. */
  526. void editorFreeRow(erow *row) {
  527. free(row->render);
  528. free(row->chars);
  529. free(row->hl);
  530. }
  531. /* Remove the row at the specified position, shifting the remainign on the
  532. * top. */
  533. void editorDelRow(int at) {
  534. erow *row;
  535. if (at >= E.numrows) return;
  536. row = E.row+at;
  537. editorFreeRow(row);
  538. memmove(E.row+at,E.row+at+1,sizeof(E.row[0])*(E.numrows-at-1));
  539. for (int j = at; j < E.numrows-1; j++) E.row[j].idx++;
  540. E.numrows--;
  541. E.dirty++;
  542. }
  543. /* Turn the editor rows into a single heap-allocated string.
  544. * Returns the pointer to the heap-allocated string and populate the
  545. * integer pointed by 'buflen' with the size of the string, escluding
  546. * the final nulterm. */
  547. char *editorRowsToString(int *buflen) {
  548. char *buf = NULL, *p;
  549. int totlen = 0;
  550. int j;
  551. /* Compute count of bytes */
  552. for (j = 0; j < E.numrows; j++)
  553. totlen += E.row[j].size+1; /* +1 is for "\n" at end of every row */
  554. *buflen = totlen;
  555. totlen++; /* Also make space for nulterm */
  556. p = buf = malloc(totlen);
  557. for (j = 0; j < E.numrows; j++) {
  558. memcpy(p,E.row[j].chars,E.row[j].size);
  559. p += E.row[j].size;
  560. *p = '\n';
  561. p++;
  562. }
  563. *p = '\0';
  564. return buf;
  565. }
  566. /* Insert a character at the specified position in a row, moving the remaining
  567. * chars on the right if needed. */
  568. void editorRowInsertChar(erow *row, int at, int c) {
  569. if (at > row->size) {
  570. /* Pad the string with spaces if the insert location is outside the
  571. * current length by more than a single character. */
  572. int padlen = at-row->size;
  573. /* In the next line +2 means: new char and null term. */
  574. row->chars = realloc(row->chars,row->size+padlen+2);
  575. memset(row->chars+row->size,' ',padlen);
  576. row->chars[row->size+padlen+1] = '\0';
  577. row->size += padlen+1;
  578. } else {
  579. /* If we are in the middle of the string just make space for 1 new
  580. * char plus the (already existing) null term. */
  581. row->chars = realloc(row->chars,row->size+2);
  582. memmove(row->chars+at+1,row->chars+at,row->size-at+1);
  583. row->size++;
  584. }
  585. row->chars[at] = c;
  586. editorUpdateRow(row);
  587. E.dirty++;
  588. }
  589. /* Append the string 's' at the end of a row */
  590. void editorRowAppendString(erow *row, char *s, size_t len) {
  591. row->chars = realloc(row->chars,row->size+len+1);
  592. memcpy(row->chars+row->size,s,len);
  593. row->size += len;
  594. row->chars[row->size] = '\0';
  595. editorUpdateRow(row);
  596. E.dirty++;
  597. }
  598. /* Delete the character at offset 'at' from the specified row. */
  599. void editorRowDelChar(erow *row, int at) {
  600. if (row->size <= at) return;
  601. memmove(row->chars+at,row->chars+at+1,row->size-at);
  602. editorUpdateRow(row);
  603. row->size--;
  604. E.dirty++;
  605. }
  606. /* Insert the specified char at the current prompt position. */
  607. void editorInsertChar(int c) {
  608. int filerow = E.rowoff+E.cy;
  609. int filecol = E.coloff+E.cx;
  610. erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow];
  611. /* If the row where the cursor is currently located does not exist in our
  612. * logical representaion of the file, add enough empty rows as needed. */
  613. if (!row) {
  614. while(E.numrows <= filerow)
  615. editorInsertRow(E.numrows,"",0);
  616. }
  617. row = &E.row[filerow];
  618. editorRowInsertChar(row,filecol,c);
  619. if (E.cx == E.screencols-1)
  620. E.coloff++;
  621. else
  622. E.cx++;
  623. E.dirty++;
  624. }
  625. /* Inserting a newline is slightly complex as we have to handle inserting a
  626. * newline in the middle of a line, splitting the line as needed. */
  627. void editorInsertNewline(void) {
  628. int filerow = E.rowoff+E.cy;
  629. int filecol = E.coloff+E.cx;
  630. erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow];
  631. if (!row) {
  632. if (filerow == E.numrows) {
  633. editorInsertRow(filerow,"",0);
  634. goto fixcursor;
  635. }
  636. return;
  637. }
  638. /* If the cursor is over the current line size, we want to conceptually
  639. * think it's just over the last character. */
  640. if (filecol >= row->size) filecol = row->size;
  641. if (filecol == 0) {
  642. editorInsertRow(filerow,"",0);
  643. } else {
  644. /* We are in the middle of a line. Split it between two rows. */
  645. editorInsertRow(filerow+1,row->chars+filecol,row->size-filecol);
  646. row = &E.row[filerow];
  647. row->chars[filecol] = '\0';
  648. row->size = filecol;
  649. editorUpdateRow(row);
  650. }
  651. fixcursor:
  652. if (E.cy == E.screenrows-1) {
  653. E.rowoff++;
  654. } else {
  655. E.cy++;
  656. }
  657. E.cx = 0;
  658. E.coloff = 0;
  659. }
  660. /* Delete the char at the current prompt position. */
  661. void editorDelChar() {
  662. int filerow = E.rowoff+E.cy;
  663. int filecol = E.coloff+E.cx;
  664. erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow];
  665. if (!row || (filecol == 0 && filerow == 0)) return;
  666. if (filecol == 0) {
  667. /* Handle the case of column 0, we need to move the current line
  668. * on the right of the previous one. */
  669. filecol = E.row[filerow-1].size;
  670. editorRowAppendString(&E.row[filerow-1],row->chars,row->size);
  671. editorDelRow(filerow);
  672. row = NULL;
  673. if (E.cy == 0)
  674. E.rowoff--;
  675. else
  676. E.cy--;
  677. E.cx = filecol;
  678. if (E.cx >= E.screencols) {
  679. int shift = (E.screencols-E.cx)+1;
  680. E.cx -= shift;
  681. E.coloff += shift;
  682. }
  683. } else {
  684. editorRowDelChar(row,filecol-1);
  685. if (E.cx == 0 && E.coloff)
  686. E.coloff--;
  687. else
  688. E.cx--;
  689. }
  690. if (row) editorUpdateRow(row);
  691. E.dirty++;
  692. }
  693. /* Load the specified program in the editor memory and returns 0 on success
  694. * or 1 on error. */
  695. int editorOpen(char *filename) {
  696. FILE *fp;
  697. E.dirty = 0;
  698. free(E.filename);
  699. E.filename = strdup(filename);
  700. fp = fopen(filename,"r");
  701. if (!fp) {
  702. if (errno != ENOENT) {
  703. perror("Opening file");
  704. exit(1);
  705. }
  706. return 1;
  707. }
  708. char *line = NULL;
  709. size_t linecap = 0;
  710. ssize_t linelen;
  711. while((linelen = getline(&line,&linecap,fp)) != -1) {
  712. if (linelen && (line[linelen-1] == '\n' || line[linelen-1] == '\r'))
  713. line[--linelen] = '\0';
  714. editorInsertRow(E.numrows,line,linelen);
  715. }
  716. free(line);
  717. fclose(fp);
  718. E.dirty = 0;
  719. return 0;
  720. }
  721. /* Save the current file on disk. Return 0 on success, 1 on error. */
  722. int editorSave(void) {
  723. int len;
  724. char *buf = editorRowsToString(&len);
  725. int fd = open(E.filename,O_RDWR|O_CREAT,0644);
  726. if (fd == -1) goto writeerr;
  727. /* Use truncate + a single write(2) call in order to make saving
  728. * a bit safer, under the limits of what we can do in a small editor. */
  729. if (ftruncate(fd,len) == -1) goto writeerr;
  730. if (write(fd,buf,len) != len) goto writeerr;
  731. close(fd);
  732. free(buf);
  733. E.dirty = 0;
  734. editorSetStatusMessage("%d bytes written on disk", len);
  735. return 0;
  736. writeerr:
  737. free(buf);
  738. if (fd != -1) close(fd);
  739. editorSetStatusMessage("Can't save! I/O error: %s",strerror(errno));
  740. return 1;
  741. }
  742. /* ============================= Terminal update ============================ */
  743. /* We define a very simple "append buffer" structure, that is an heap
  744. * allocated string where we can append to. This is useful in order to
  745. * write all the escape sequences in a buffer and flush them to the standard
  746. * output in a single call, to avoid flickering effects. */
  747. struct abuf {
  748. char *b;
  749. int len;
  750. };
  751. #define ABUF_INIT {NULL,0}
  752. void abAppend(struct abuf *ab, const char *s, int len) {
  753. char *new = realloc(ab->b,ab->len+len);
  754. if (new == NULL) return;
  755. memcpy(new+ab->len,s,len);
  756. ab->b = new;
  757. ab->len += len;
  758. }
  759. void abFree(struct abuf *ab) {
  760. free(ab->b);
  761. }
  762. /* This function writes the whole screen using VT100 escape characters
  763. * starting from the logical state of the editor in the global state 'E'. */
  764. void editorRefreshScreen(void) {
  765. int y;
  766. erow *r;
  767. char buf[32];
  768. struct abuf ab = ABUF_INIT;
  769. abAppend(&ab,"\x1b[?25l",6); /* Hide cursor. */
  770. abAppend(&ab,"\x1b[H",3); /* Go home. */
  771. for (y = 0; y < E.screenrows; y++) {
  772. int filerow = E.rowoff+y;
  773. if (filerow >= E.numrows) {
  774. if (E.numrows == 0 && y == E.screenrows/3) {
  775. char welcome[80];
  776. int welcomelen = snprintf(welcome,sizeof(welcome),
  777. "Kilo editor -- verison %s\x1b[0K\r\n", KILO_VERSION);
  778. int padding = (E.screencols-welcomelen)/2;
  779. if (padding) {
  780. abAppend(&ab,"~",1);
  781. padding--;
  782. }
  783. while(padding--) abAppend(&ab," ",1);
  784. abAppend(&ab,welcome,welcomelen);
  785. } else {
  786. abAppend(&ab,"~\x1b[0K\r\n",7);
  787. }
  788. continue;
  789. }
  790. r = &E.row[filerow];
  791. int len = r->rsize - E.coloff;
  792. int current_color = -1;
  793. if (len > 0) {
  794. if (len > E.screencols) len = E.screencols;
  795. char *c = r->render+E.coloff;
  796. unsigned char *hl = r->hl+E.coloff;
  797. int j;
  798. for (j = 0; j < len; j++) {
  799. if (hl[j] == HL_NONPRINT) {
  800. char sym;
  801. abAppend(&ab,"\x1b[7m",4);
  802. if (c[j] <= 26)
  803. sym = '@'+c[j];
  804. else
  805. sym = '?';
  806. abAppend(&ab,&sym,1);
  807. abAppend(&ab,"\x1b[0m",4);
  808. } else if (hl[j] == HL_NORMAL) {
  809. if (current_color != -1) {
  810. // command to return font to normal... rafa
  811. abAppend(&ab,"\x1b[39m",5);
  812. current_color = -1;
  813. }
  814. // characters not colored
  815. abAppend(&ab,c+j,1);
  816. } else {
  817. int color = editorSyntaxToColor(hl[j]);
  818. if (color != current_color) {
  819. char buf[16];
  820. int clen = snprintf(buf,sizeof(buf),"\x1b[%dm",color);
  821. current_color = color;
  822. abAppend(&ab,buf,clen);
  823. }
  824. // characters that are colored
  825. abAppend(&ab,c+j,1);
  826. //rafa
  827. // abAppend(&ab,"N",1);
  828. }
  829. }
  830. }
  831. abAppend(&ab,"\x1b[39m",5);
  832. abAppend(&ab,"\x1b[0K",4);
  833. abAppend(&ab,"\r\n",2);
  834. }
  835. /* Create a two rows status. First row: */
  836. abAppend(&ab,"\x1b[0K",4);
  837. abAppend(&ab,"\x1b[7m",4);
  838. char status[80], rstatus[80];
  839. int len = snprintf(status, sizeof(status), "%.20s - %d lines %s",
  840. E.filename, E.numrows, E.dirty ? "(modified)" : "");
  841. int rlen = snprintf(rstatus, sizeof(rstatus),
  842. "%d/%d",E.rowoff+E.cy+1,E.numrows);
  843. if (len > E.screencols) len = E.screencols;
  844. abAppend(&ab,status,len);
  845. while(len < E.screencols) {
  846. if (E.screencols - len == rlen) {
  847. abAppend(&ab,rstatus,rlen);
  848. break;
  849. } else {
  850. abAppend(&ab," ",1);
  851. len++;
  852. }
  853. }
  854. abAppend(&ab,"\x1b[0m\r\n",6);
  855. /* Second row depends on E.statusmsg and the status message update time. */
  856. abAppend(&ab,"\x1b[0K",4);
  857. int msglen = strlen(E.statusmsg);
  858. if (msglen && time(NULL)-E.statusmsg_time < 5)
  859. abAppend(&ab,E.statusmsg,msglen <= E.screencols ? msglen : E.screencols);
  860. /* Put cursor at its current position. Note that the horizontal position
  861. * at which the cursor is displayed may be different compared to 'E.cx'
  862. * because of TABs. */
  863. int j;
  864. int cx = 1;
  865. int filerow = E.rowoff+E.cy;
  866. erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow];
  867. if (row) {
  868. for (j = E.coloff; j < (E.cx+E.coloff); j++) {
  869. if (j < row->size && row->chars[j] == TAB) cx += 7-((cx)%8);
  870. cx++;
  871. }
  872. }
  873. snprintf(buf,sizeof(buf),"\x1b[%d;%dH",E.cy+1,cx);
  874. abAppend(&ab,buf,strlen(buf));
  875. abAppend(&ab,"\x1b[?25h",6); /* Show cursor. */
  876. // Writing to the stdout? rafa
  877. write(STDOUT_FILENO,ab.b,ab.len);
  878. // rafa
  879. unsigned int ui = 0;
  880. for (ui = 0; ui < E.numrows; ui++) {
  881. write(2,E.row[ui].chars,E.row[ui].size);
  882. write(2,"\n",1);
  883. }
  884. abFree(&ab);
  885. }
  886. /* Set an editor status message for the second line of the status, at the
  887. * end of the screen. */
  888. void editorSetStatusMessage(const char *fmt, ...) {
  889. va_list ap;
  890. va_start(ap,fmt);
  891. vsnprintf(E.statusmsg,sizeof(E.statusmsg),fmt,ap);
  892. va_end(ap);
  893. E.statusmsg_time = time(NULL);
  894. }
  895. /* =============================== Find mode ================================ */
  896. #define KILO_QUERY_LEN 256
  897. void editorFind(int fd) {
  898. char query[KILO_QUERY_LEN+1] = {0};
  899. int qlen = 0;
  900. int last_match = -1; /* Last line where a match was found. -1 for none. */
  901. int find_next = 0; /* if 1 search next, if -1 search prev. */
  902. int saved_hl_line = -1; /* No saved HL */
  903. char *saved_hl = NULL;
  904. #define FIND_RESTORE_HL do { \
  905. if (saved_hl) { \
  906. memcpy(E.row[saved_hl_line].hl,saved_hl, E.row[saved_hl_line].rsize); \
  907. saved_hl = NULL; \
  908. } \
  909. } while (0)
  910. /* Save the cursor position in order to restore it later. */
  911. int saved_cx = E.cx, saved_cy = E.cy;
  912. int saved_coloff = E.coloff, saved_rowoff = E.rowoff;
  913. while(1) {
  914. editorSetStatusMessage(
  915. "Search: %s (Use ESC/Arrows/Enter)", query);
  916. editorRefreshScreen();
  917. int c = editorReadKey(fd);
  918. if (c == DEL_KEY || c == CTRL_H || c == BACKSPACE) {
  919. if (qlen != 0) query[--qlen] = '\0';
  920. last_match = -1;
  921. } else if (c == ESC || c == ENTER) {
  922. if (c == ESC) {
  923. E.cx = saved_cx; E.cy = saved_cy;
  924. E.coloff = saved_coloff; E.rowoff = saved_rowoff;
  925. }
  926. FIND_RESTORE_HL;
  927. editorSetStatusMessage("");
  928. return;
  929. } else if (c == ARROW_RIGHT || c == ARROW_DOWN) {
  930. find_next = 1;
  931. } else if (c == ARROW_LEFT || c == ARROW_UP) {
  932. find_next = -1;
  933. } else if (isprint(c)) {
  934. if (qlen < KILO_QUERY_LEN) {
  935. query[qlen++] = c;
  936. query[qlen] = '\0';
  937. last_match = -1;
  938. }
  939. }
  940. /* Search occurrence. */
  941. if (last_match == -1) find_next = 1;
  942. if (find_next) {
  943. char *match = NULL;
  944. int match_offset = 0;
  945. int i, current = last_match;
  946. for (i = 0; i < E.numrows; i++) {
  947. current += find_next;
  948. if (current == -1) current = E.numrows-1;
  949. else if (current == E.numrows) current = 0;
  950. match = strstr(E.row[current].render,query);
  951. if (match) {
  952. match_offset = match-E.row[current].render;
  953. break;
  954. }
  955. }
  956. find_next = 0;
  957. /* Highlight */
  958. FIND_RESTORE_HL;
  959. if (match) {
  960. erow *row = &E.row[current];
  961. last_match = current;
  962. if (row->hl) {
  963. saved_hl_line = current;
  964. saved_hl = malloc(row->rsize);
  965. memcpy(saved_hl,row->hl,row->rsize);
  966. memset(row->hl+match_offset,HL_MATCH,qlen);
  967. }
  968. E.cy = 0;
  969. E.cx = match_offset;
  970. E.rowoff = current;
  971. E.coloff = 0;
  972. /* Scroll horizontally as needed. */
  973. if (E.cx > E.screencols) {
  974. int diff = E.cx - E.screencols;
  975. E.cx -= diff;
  976. E.coloff += diff;
  977. }
  978. }
  979. }
  980. }
  981. }
  982. /* ========================= Editor events handling ======================== */
  983. /* Handle cursor position change because arrow keys were pressed. */
  984. void editorMoveCursor(int key) {
  985. int filerow = E.rowoff+E.cy;
  986. int filecol = E.coloff+E.cx;
  987. int rowlen;
  988. erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow];
  989. switch(key) {
  990. case ARROW_LEFT:
  991. if (E.cx == 0) {
  992. if (E.coloff) {
  993. E.coloff--;
  994. } else {
  995. if (filerow > 0) {
  996. E.cy--;
  997. E.cx = E.row[filerow-1].size;
  998. if (E.cx > E.screencols-1) {
  999. E.coloff = E.cx-E.screencols+1;
  1000. E.cx = E.screencols-1;
  1001. }
  1002. }
  1003. }
  1004. } else {
  1005. E.cx -= 1;
  1006. }
  1007. break;
  1008. case ARROW_RIGHT:
  1009. if (row && filecol < row->size) {
  1010. if (E.cx == E.screencols-1) {
  1011. E.coloff++;
  1012. } else {
  1013. E.cx += 1;
  1014. }
  1015. } else if (row && filecol == row->size) {
  1016. E.cx = 0;
  1017. E.coloff = 0;
  1018. if (E.cy == E.screenrows-1) {
  1019. E.rowoff++;
  1020. } else {
  1021. E.cy += 1;
  1022. }
  1023. }
  1024. break;
  1025. case ARROW_UP:
  1026. if (E.cy == 0) {
  1027. if (E.rowoff) E.rowoff--;
  1028. } else {
  1029. E.cy -= 1;
  1030. }
  1031. break;
  1032. case ARROW_DOWN:
  1033. if (filerow < E.numrows) {
  1034. if (E.cy == E.screenrows-1) {
  1035. E.rowoff++;
  1036. } else {
  1037. E.cy += 1;
  1038. }
  1039. }
  1040. break;
  1041. }
  1042. /* Fix cx if the current line has not enough chars. */
  1043. filerow = E.rowoff+E.cy;
  1044. filecol = E.coloff+E.cx;
  1045. row = (filerow >= E.numrows) ? NULL : &E.row[filerow];
  1046. rowlen = row ? row->size : 0;
  1047. if (filecol > rowlen) {
  1048. E.cx -= filecol-rowlen;
  1049. if (E.cx < 0) {
  1050. E.coloff += E.cx;
  1051. E.cx = 0;
  1052. }
  1053. }
  1054. }
  1055. /* Process events arriving from the standard input, which is, the user
  1056. * is typing stuff on the terminal. */
  1057. #define KILO_QUIT_TIMES 3
  1058. void editorProcessKeypress(int fd) {
  1059. /* When the file is modified, requires Ctrl-q to be pressed N times
  1060. * before actually quitting. */
  1061. static int quit_times = KILO_QUIT_TIMES;
  1062. int c = editorReadKey(fd);
  1063. switch(c) {
  1064. case ENTER: /* Enter */
  1065. editorInsertNewline();
  1066. break;
  1067. case CTRL_C: /* Ctrl-c */
  1068. /* We ignore ctrl-c, it can't be so simple to lose the changes
  1069. * to the edited file. */
  1070. break;
  1071. case CTRL_Q: /* Ctrl-q */
  1072. /* Quit if the file was already saved. */
  1073. if (E.dirty && quit_times) {
  1074. editorSetStatusMessage("WARNING!!! File has unsaved changes. "
  1075. "Press Ctrl-Q %d more times to quit.", quit_times);
  1076. quit_times--;
  1077. return;
  1078. }
  1079. exit(0);
  1080. break;
  1081. case CTRL_S: /* Ctrl-s */
  1082. editorSave();
  1083. break;
  1084. case CTRL_F:
  1085. editorFind(fd);
  1086. break;
  1087. case BACKSPACE: /* Backspace */
  1088. case CTRL_H: /* Ctrl-h */
  1089. case DEL_KEY:
  1090. editorDelChar();
  1091. break;
  1092. case PAGE_UP:
  1093. case PAGE_DOWN:
  1094. if (c == PAGE_UP && E.cy != 0)
  1095. E.cy = 0;
  1096. else if (c == PAGE_DOWN && E.cy != E.screenrows-1)
  1097. E.cy = E.screenrows-1;
  1098. {
  1099. int times = E.screenrows;
  1100. while(times--)
  1101. editorMoveCursor(c == PAGE_UP ? ARROW_UP:
  1102. ARROW_DOWN);
  1103. }
  1104. break;
  1105. case ARROW_UP:
  1106. case ARROW_DOWN:
  1107. case ARROW_LEFT:
  1108. case ARROW_RIGHT:
  1109. editorMoveCursor(c);
  1110. break;
  1111. case CTRL_L: /* ctrl+l, clear screen */
  1112. /* Just refresht the line as side effect. */
  1113. break;
  1114. case ESC:
  1115. /* Nothing to do for ESC in this mode. */
  1116. break;
  1117. default:
  1118. editorInsertChar(c);
  1119. break;
  1120. }
  1121. quit_times = KILO_QUIT_TIMES; /* Reset it to the original value. */
  1122. }
  1123. int editorFileWasModified(void) {
  1124. return E.dirty;
  1125. }
  1126. void initEditor(void) {
  1127. E.cx = 0;
  1128. E.cy = 0;
  1129. E.rowoff = 0;
  1130. E.coloff = 0;
  1131. E.numrows = 0;
  1132. E.row = NULL;
  1133. E.dirty = 0;
  1134. E.filename = NULL;
  1135. E.syntax = NULL;
  1136. if (getWindowSize(STDIN_FILENO,STDOUT_FILENO,
  1137. &E.screenrows,&E.screencols) == -1)
  1138. {
  1139. perror("Unable to query the screen for size (columns / rows)");
  1140. exit(1);
  1141. }
  1142. E.screenrows -= 2; /* Get room for status bar. */
  1143. }
  1144. int main(int argc, char **argv) {
  1145. if (argc != 2) {
  1146. fprintf(stderr,"Usage: kilo <filename>\n");
  1147. exit(1);
  1148. }
  1149. initEditor();
  1150. editorSelectSyntaxHighlight(argv[1]);
  1151. editorOpen(argv[1]);
  1152. enableRawMode(STDIN_FILENO);
  1153. editorSetStatusMessage(
  1154. "HELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find");
  1155. while(1) {
  1156. editorRefreshScreen();
  1157. editorProcessKeypress(STDIN_FILENO);
  1158. }
  1159. return 0;
  1160. }