From 3685714ea45c0ea1f1e76a7e914a0886b9cccdeb Mon Sep 17 00:00:00 2001 From: Martin Date: Sat, 24 Jul 2021 20:04:28 -0300 Subject: [PATCH 1/8] Make proper character deletion with DEL key --- kilo.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/kilo.c b/kilo.c index 406eb7be..1b85d952 100644 --- a/kilo.c +++ b/kilo.c @@ -791,7 +791,25 @@ void editorDelChar() { if (row) editorUpdateRow(row); E.dirty++; } +void editorInvDelChar() { + int filerow = E.rowoff+E.cy; + int filecol = E.coloff+E.cx; + erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; + erow *followingRow = (filerow+1 >= E.numrows) ? NULL : &E.row[filerow+1]; + if (!row || (filecol == 0 && filerow == 0)) return; + if (filecol == row->size) { + /* Handle the case of last column, we need to move the following line + * on the right of the current one. */ + editorRowAppendString(&E.row[filerow],followingRow->chars,followingRow->size); + editorDelRow(filerow+1); + row = NULL; + } else { + editorRowDelChar(row,filecol); + } + if (row) editorUpdateRow(row); + E.dirty++; +} /* Load the specified program in the editor memory and returns 0 on success * or 1 on error. */ int editorOpen(char *filename) { @@ -1215,9 +1233,11 @@ void editorProcessKeypress(int fd) { case CTRL_F: editorFind(fd); break; + case DEL_KEY: + editorInvDelChar(); + break; case BACKSPACE: /* Backspace */ case CTRL_H: /* Ctrl-h */ - case DEL_KEY: editorDelChar(); break; case PAGE_UP: From 6295ee80663eb854bd4be6b1c23aea2bb1016ac8 Mon Sep 17 00:00:00 2001 From: Martin Date: Sat, 24 Jul 2021 20:34:41 -0300 Subject: [PATCH 2/8] Added variable tab size --- kilo.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/kilo.c b/kilo.c index 1b85d952..364098bc 100644 --- a/kilo.c +++ b/kilo.c @@ -68,6 +68,8 @@ #define HL_HIGHLIGHT_STRINGS (1<<0) #define HL_HIGHLIGHT_NUMBERS (1<<1) +#define TAB_SIZE 4 + struct editorSyntax { char **filematch; char **keywords; @@ -564,18 +566,18 @@ void editorUpdateRow(erow *row) { if (row->chars[j] == TAB) tabs++; unsigned long long allocsize = - (unsigned long long) row->size + tabs*8 + nonprint*9 + 1; + (unsigned long long) row->size + tabs*TAB_SIZE + nonprint*9 + 1; if (allocsize > UINT32_MAX) { printf("Some line of the edited file is too long for kilo\n"); exit(1); } - row->render = malloc(row->size + tabs*8 + nonprint*9 + 1); + row->render = malloc(row->size + tabs*TAB_SIZE + nonprint*9 + 1); idx = 0; for (j = 0; j < row->size; j++) { if (row->chars[j] == TAB) { row->render[idx++] = ' '; - while((idx+1) % 8 != 0) row->render[idx++] = ' '; + while((idx) % TAB_SIZE != 0) row->render[idx++] = ' '; } else { row->render[idx++] = row->chars[j]; } @@ -1004,7 +1006,7 @@ void editorRefreshScreen(void) { erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; if (row) { for (j = E.coloff; j < (E.cx+E.coloff); j++) { - if (j < row->size && row->chars[j] == TAB) cx += 7-((cx)%8); + if (j < row->size && row->chars[j] == TAB) cx += TAB_SIZE-((cx)%TAB_SIZE); cx++; } } From 7f42e392758b2118d321aff6d8b3f42db4e9c465 Mon Sep 17 00:00:00 2001 From: Martin Date: Sat, 24 Jul 2021 20:40:22 -0300 Subject: [PATCH 3/8] Converted from spaces to tabs --- kilo.c | 1992 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 996 insertions(+), 996 deletions(-) diff --git a/kilo.c b/kilo.c index 364098bc..d25251a6 100644 --- a/kilo.c +++ b/kilo.c @@ -71,73 +71,73 @@ #define TAB_SIZE 4 struct editorSyntax { - char **filematch; - char **keywords; - char singleline_comment_start[2]; - char multiline_comment_start[3]; - char multiline_comment_end[3]; - int flags; + char **filematch; + char **keywords; + char singleline_comment_start[2]; + char multiline_comment_start[3]; + char multiline_comment_end[3]; + int flags; }; /* This structure represents a single line of the file we are editing. */ typedef struct erow { - int idx; /* Row index in the file, zero-based. */ - int size; /* Size of the row, excluding the null term. */ - int rsize; /* Size of the rendered row. */ - char *chars; /* Row content. */ - char *render; /* Row content "rendered" for screen (for TABs). */ - unsigned char *hl; /* Syntax highlight type for each character in render.*/ - int hl_oc; /* Row had open comment at end in last syntax highlight - check. */ + int idx; /* Row index in the file, zero-based. */ + int size; /* Size of the row, excluding the null term. */ + int rsize; /* Size of the rendered row. */ + char *chars; /* Row content. */ + char *render; /* Row content "rendered" for screen (for TABs). */ + unsigned char *hl; /* Syntax highlight type for each character in render.*/ + int hl_oc; /* Row had open comment at end in last syntax highlight + check. */ } erow; typedef struct hlcolor { - int r,g,b; + int r,g,b; } hlcolor; struct editorConfig { - int cx,cy; /* Cursor x and y position in characters */ - int rowoff; /* Offset of row displayed. */ - int coloff; /* Offset of column displayed. */ - int screenrows; /* Number of rows that we can show */ - int screencols; /* Number of cols that we can show */ - int numrows; /* Number of rows */ - int rawmode; /* Is terminal raw mode enabled? */ - erow *row; /* Rows */ - int dirty; /* File modified but not saved. */ - char *filename; /* Currently open filename */ - char statusmsg[80]; - time_t statusmsg_time; - struct editorSyntax *syntax; /* Current syntax highlight, or NULL. */ + int cx,cy; /* Cursor x and y position in characters */ + int rowoff; /* Offset of row displayed. */ + int coloff; /* Offset of column displayed. */ + int screenrows; /* Number of rows that we can show */ + int screencols; /* Number of cols that we can show */ + int numrows; /* Number of rows */ + int rawmode; /* Is terminal raw mode enabled? */ + erow *row; /* Rows */ + int dirty; /* File modified but not saved. */ + char *filename; /* Currently open filename */ + char statusmsg[80]; + time_t statusmsg_time; + struct editorSyntax *syntax; /* Current syntax highlight, or NULL. */ }; static struct editorConfig E; enum KEY_ACTION{ - KEY_NULL = 0, /* NULL */ - CTRL_C = 3, /* Ctrl-c */ - CTRL_D = 4, /* Ctrl-d */ - CTRL_F = 6, /* Ctrl-f */ - CTRL_H = 8, /* Ctrl-h */ - TAB = 9, /* Tab */ - CTRL_L = 12, /* Ctrl+l */ - ENTER = 13, /* Enter */ - CTRL_Q = 17, /* Ctrl-q */ - CTRL_S = 19, /* Ctrl-s */ - CTRL_U = 21, /* Ctrl-u */ - ESC = 27, /* Escape */ - BACKSPACE = 127, /* Backspace */ - /* The following are just soft codes, not really reported by the - * terminal directly. */ - ARROW_LEFT = 1000, - ARROW_RIGHT, - ARROW_UP, - ARROW_DOWN, - DEL_KEY, - HOME_KEY, - END_KEY, - PAGE_UP, - PAGE_DOWN + KEY_NULL = 0, /* NULL */ + CTRL_C = 3, /* Ctrl-c */ + CTRL_D = 4, /* Ctrl-d */ + CTRL_F = 6, /* Ctrl-f */ + CTRL_H = 8, /* Ctrl-h */ + TAB = 9, /* Tab */ + CTRL_L = 12, /* Ctrl+l */ + ENTER = 13, /* Enter */ + CTRL_Q = 17, /* Ctrl-q */ + CTRL_S = 19, /* Ctrl-s */ + CTRL_U = 21, /* Ctrl-u */ + ESC = 27, /* Escape */ + BACKSPACE = 127, /* Backspace */ + /* The following are just soft codes, not really reported by the + * terminal directly. */ + ARROW_LEFT = 1000, + ARROW_RIGHT, + ARROW_UP, + ARROW_DOWN, + DEL_KEY, + HOME_KEY, + END_KEY, + PAGE_UP, + PAGE_DOWN }; void editorSetStatusMessage(const char *fmt, ...); @@ -181,20 +181,20 @@ char *C_HL_keywords[] = { "typeid","typename","virtual","xor","xor_eq", /* C types */ - "int|","long|","double|","float|","char|","unsigned|","signed|", - "void|","short|","auto|","const|","bool|",NULL + "int|","long|","double|","float|","char|","unsigned|","signed|", + "void|","short|","auto|","const|","bool|",NULL }; /* Here we define an array of syntax highlights by extensions, keywords, * comments delimiters and flags. */ struct editorSyntax HLDB[] = { - { - /* C / C++ */ - C_HL_extensions, - C_HL_keywords, - "//","/*","*/", - HL_HIGHLIGHT_STRINGS | HL_HIGHLIGHT_NUMBERS - } + { + /* C / C++ */ + C_HL_extensions, + C_HL_keywords, + "//","/*","*/", + HL_HIGHLIGHT_STRINGS | HL_HIGHLIGHT_NUMBERS + } }; #define HLDB_ENTRIES (sizeof(HLDB)/sizeof(HLDB[0])) @@ -204,432 +204,432 @@ struct editorSyntax HLDB[] = { static struct termios orig_termios; /* In order to restore at exit.*/ void disableRawMode(int fd) { - /* Don't even check the return value as it's too late. */ - if (E.rawmode) { - tcsetattr(fd,TCSAFLUSH,&orig_termios); - E.rawmode = 0; - } + /* Don't even check the return value as it's too late. */ + if (E.rawmode) { + tcsetattr(fd,TCSAFLUSH,&orig_termios); + E.rawmode = 0; + } } /* Called at exit to avoid remaining in raw mode. */ void editorAtExit(void) { - disableRawMode(STDIN_FILENO); + disableRawMode(STDIN_FILENO); } /* Raw mode: 1960 magic shit. */ int enableRawMode(int fd) { - struct termios raw; - - if (E.rawmode) return 0; /* Already enabled. */ - if (!isatty(STDIN_FILENO)) goto fatal; - atexit(editorAtExit); - if (tcgetattr(fd,&orig_termios) == -1) goto fatal; - - raw = orig_termios; /* modify the original mode */ - /* input modes: no break, no CR to NL, no parity check, no strip char, - * no start/stop output control. */ - raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); - /* output modes - disable post processing */ - raw.c_oflag &= ~(OPOST); - /* control modes - set 8 bit chars */ - raw.c_cflag |= (CS8); - /* local modes - choing off, canonical off, no extended functions, - * no signal chars (^Z,^C) */ - raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); - /* control chars - set return condition: min number of bytes and timer. */ - raw.c_cc[VMIN] = 0; /* Return each byte, or zero for timeout. */ - raw.c_cc[VTIME] = 1; /* 100 ms timeout (unit is tens of second). */ - - /* put terminal in raw mode after flushing */ - if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal; - E.rawmode = 1; - return 0; + struct termios raw; + + if (E.rawmode) return 0; /* Already enabled. */ + if (!isatty(STDIN_FILENO)) goto fatal; + atexit(editorAtExit); + if (tcgetattr(fd,&orig_termios) == -1) goto fatal; + + raw = orig_termios; /* modify the original mode */ + /* input modes: no break, no CR to NL, no parity check, no strip char, + * no start/stop output control. */ + raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + /* output modes - disable post processing */ + raw.c_oflag &= ~(OPOST); + /* control modes - set 8 bit chars */ + raw.c_cflag |= (CS8); + /* local modes - choing off, canonical off, no extended functions, + * no signal chars (^Z,^C) */ + raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + /* control chars - set return condition: min number of bytes and timer. */ + raw.c_cc[VMIN] = 0; /* Return each byte, or zero for timeout. */ + raw.c_cc[VTIME] = 1; /* 100 ms timeout (unit is tens of second). */ + + /* put terminal in raw mode after flushing */ + if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal; + E.rawmode = 1; + return 0; fatal: - errno = ENOTTY; - return -1; + errno = ENOTTY; + return -1; } /* Read a key from the terminal put in raw mode, trying to handle * escape sequences. */ int editorReadKey(int fd) { - int nread; - char c, seq[3]; - while ((nread = read(fd,&c,1)) == 0); - if (nread == -1) exit(1); - - while(1) { - switch(c) { - case ESC: /* escape sequence */ - /* If this is just an ESC, we'll timeout here. */ - if (read(fd,seq,1) == 0) return ESC; - if (read(fd,seq+1,1) == 0) return ESC; - - /* ESC [ sequences. */ - if (seq[0] == '[') { - if (seq[1] >= '0' && seq[1] <= '9') { - /* Extended escape, read additional byte. */ - if (read(fd,seq+2,1) == 0) return ESC; - if (seq[2] == '~') { - switch(seq[1]) { - case '3': return DEL_KEY; - case '5': return PAGE_UP; - case '6': return PAGE_DOWN; - } - } - } else { - switch(seq[1]) { - case 'A': return ARROW_UP; - case 'B': return ARROW_DOWN; - case 'C': return ARROW_RIGHT; - case 'D': return ARROW_LEFT; - case 'H': return HOME_KEY; - case 'F': return END_KEY; - } - } - } - - /* ESC O sequences. */ - else if (seq[0] == 'O') { - switch(seq[1]) { - case 'H': return HOME_KEY; - case 'F': return END_KEY; - } - } - break; - default: - return c; - } - } + int nread; + char c, seq[3]; + while ((nread = read(fd,&c,1)) == 0); + if (nread == -1) exit(1); + + while(1) { + switch(c) { + case ESC: /* escape sequence */ + /* If this is just an ESC, we'll timeout here. */ + if (read(fd,seq,1) == 0) return ESC; + if (read(fd,seq+1,1) == 0) return ESC; + + /* ESC [ sequences. */ + if (seq[0] == '[') { + if (seq[1] >= '0' && seq[1] <= '9') { + /* Extended escape, read additional byte. */ + if (read(fd,seq+2,1) == 0) return ESC; + if (seq[2] == '~') { + switch(seq[1]) { + case '3': return DEL_KEY; + case '5': return PAGE_UP; + case '6': return PAGE_DOWN; + } + } + } else { + switch(seq[1]) { + case 'A': return ARROW_UP; + case 'B': return ARROW_DOWN; + case 'C': return ARROW_RIGHT; + case 'D': return ARROW_LEFT; + case 'H': return HOME_KEY; + case 'F': return END_KEY; + } + } + } + + /* ESC O sequences. */ + else if (seq[0] == 'O') { + switch(seq[1]) { + case 'H': return HOME_KEY; + case 'F': return END_KEY; + } + } + break; + default: + return c; + } + } } /* Use the ESC [6n escape sequence to query the horizontal cursor position * and return it. On error -1 is returned, on success the position of the * cursor is stored at *rows and *cols and 0 is returned. */ int getCursorPosition(int ifd, int ofd, int *rows, int *cols) { - char buf[32]; - unsigned int i = 0; - - /* Report cursor location */ - if (write(ofd, "\x1b[6n", 4) != 4) return -1; - - /* Read the response: ESC [ rows ; cols R */ - while (i < sizeof(buf)-1) { - if (read(ifd,buf+i,1) != 1) break; - if (buf[i] == 'R') break; - i++; - } - buf[i] = '\0'; - - /* Parse it. */ - if (buf[0] != ESC || buf[1] != '[') return -1; - if (sscanf(buf+2,"%d;%d",rows,cols) != 2) return -1; - return 0; + char buf[32]; + unsigned int i = 0; + + /* Report cursor location */ + if (write(ofd, "\x1b[6n", 4) != 4) return -1; + + /* Read the response: ESC [ rows ; cols R */ + while (i < sizeof(buf)-1) { + if (read(ifd,buf+i,1) != 1) break; + if (buf[i] == 'R') break; + i++; + } + buf[i] = '\0'; + + /* Parse it. */ + if (buf[0] != ESC || buf[1] != '[') return -1; + if (sscanf(buf+2,"%d;%d",rows,cols) != 2) return -1; + return 0; } /* Try to get the number of columns in the current terminal. If the ioctl() * call fails the function will try to query the terminal itself. * Returns 0 on success, -1 on error. */ int getWindowSize(int ifd, int ofd, int *rows, int *cols) { - struct winsize ws; - - if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { - /* ioctl() failed. Try to query the terminal itself. */ - int orig_row, orig_col, retval; - - /* Get the initial position so we can restore it later. */ - retval = getCursorPosition(ifd,ofd,&orig_row,&orig_col); - if (retval == -1) goto failed; - - /* Go to right/bottom margin and get position. */ - if (write(ofd,"\x1b[999C\x1b[999B",12) != 12) goto failed; - retval = getCursorPosition(ifd,ofd,rows,cols); - if (retval == -1) goto failed; - - /* Restore position. */ - char seq[32]; - snprintf(seq,32,"\x1b[%d;%dH",orig_row,orig_col); - if (write(ofd,seq,strlen(seq)) == -1) { - /* Can't recover... */ - } - return 0; - } else { - *cols = ws.ws_col; - *rows = ws.ws_row; - return 0; - } + struct winsize ws; + + if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { + /* ioctl() failed. Try to query the terminal itself. */ + int orig_row, orig_col, retval; + + /* Get the initial position so we can restore it later. */ + retval = getCursorPosition(ifd,ofd,&orig_row,&orig_col); + if (retval == -1) goto failed; + + /* Go to right/bottom margin and get position. */ + if (write(ofd,"\x1b[999C\x1b[999B",12) != 12) goto failed; + retval = getCursorPosition(ifd,ofd,rows,cols); + if (retval == -1) goto failed; + + /* Restore position. */ + char seq[32]; + snprintf(seq,32,"\x1b[%d;%dH",orig_row,orig_col); + if (write(ofd,seq,strlen(seq)) == -1) { + /* Can't recover... */ + } + return 0; + } else { + *cols = ws.ws_col; + *rows = ws.ws_row; + return 0; + } failed: - return -1; + return -1; } /* ====================== Syntax highlight color scheme ==================== */ int is_separator(int c) { - return c == '\0' || isspace(c) || strchr(",.()+-/*=~%[];",c) != NULL; + return c == '\0' || isspace(c) || strchr(",.()+-/*=~%[];",c) != NULL; } /* Return true if the specified row last char is part of a multi line comment * that starts at this row or at one before, and does not end at the end * of the row but spawns to the next row. */ int editorRowHasOpenComment(erow *row) { - if (row->hl && row->rsize && row->hl[row->rsize-1] == HL_MLCOMMENT && - (row->rsize < 2 || (row->render[row->rsize-2] != '*' || - row->render[row->rsize-1] != '/'))) return 1; - return 0; + if (row->hl && row->rsize && row->hl[row->rsize-1] == HL_MLCOMMENT && + (row->rsize < 2 || (row->render[row->rsize-2] != '*' || + row->render[row->rsize-1] != '/'))) return 1; + return 0; } /* Set every byte of row->hl (that corresponds to every character in the line) * to the right syntax highlight type (HL_* defines). */ void editorUpdateSyntax(erow *row) { - row->hl = realloc(row->hl,row->rsize); - memset(row->hl,HL_NORMAL,row->rsize); - - if (E.syntax == NULL) return; /* No syntax, everything is HL_NORMAL. */ - - int i, prev_sep, in_string, in_comment; - char *p; - char **keywords = E.syntax->keywords; - char *scs = E.syntax->singleline_comment_start; - char *mcs = E.syntax->multiline_comment_start; - char *mce = E.syntax->multiline_comment_end; - - /* Point to the first non-space char. */ - p = row->render; - i = 0; /* Current char offset */ - while(*p && isspace(*p)) { - p++; - i++; - } - prev_sep = 1; /* Tell the parser if 'i' points to start of word. */ - in_string = 0; /* Are we inside "" or '' ? */ - in_comment = 0; /* Are we inside multi-line comment? */ - - /* If the previous line has an open comment, this line starts - * with an open comment state. */ - if (row->idx > 0 && editorRowHasOpenComment(&E.row[row->idx-1])) - in_comment = 1; - - while(*p) { - /* Handle // comments. */ - if (prev_sep && *p == scs[0] && *(p+1) == scs[1]) { - /* From here to end is a comment */ - memset(row->hl+i,HL_COMMENT,row->size-i); - return; - } - - /* Handle multi line comments. */ - if (in_comment) { - row->hl[i] = HL_MLCOMMENT; - if (*p == mce[0] && *(p+1) == mce[1]) { - row->hl[i+1] = HL_MLCOMMENT; - p += 2; i += 2; - in_comment = 0; - prev_sep = 1; - continue; - } else { - prev_sep = 0; - p++; i++; - continue; - } - } else if (*p == mcs[0] && *(p+1) == mcs[1]) { - row->hl[i] = HL_MLCOMMENT; - row->hl[i+1] = HL_MLCOMMENT; - p += 2; i += 2; - in_comment = 1; - prev_sep = 0; - continue; - } - - /* Handle "" and '' */ - if (in_string) { - row->hl[i] = HL_STRING; - if (*p == '\\') { - row->hl[i+1] = HL_STRING; - p += 2; i += 2; - prev_sep = 0; - continue; - } - if (*p == in_string) in_string = 0; - p++; i++; - continue; - } else { - if (*p == '"' || *p == '\'') { - in_string = *p; - row->hl[i] = HL_STRING; - p++; i++; - prev_sep = 0; - continue; - } - } - - /* Handle non printable chars. */ - if (!isprint(*p)) { - row->hl[i] = HL_NONPRINT; - p++; i++; - prev_sep = 0; - continue; - } - - /* Handle numbers */ - if ((isdigit(*p) && (prev_sep || row->hl[i-1] == HL_NUMBER)) || - (*p == '.' && i >0 && row->hl[i-1] == HL_NUMBER)) { - row->hl[i] = HL_NUMBER; - p++; i++; - prev_sep = 0; - continue; - } - - /* Handle keywords and lib calls */ - if (prev_sep) { - int j; - for (j = 0; keywords[j]; j++) { - int klen = strlen(keywords[j]); - int kw2 = keywords[j][klen-1] == '|'; - if (kw2) klen--; - - if (!memcmp(p,keywords[j],klen) && - is_separator(*(p+klen))) - { - /* Keyword */ - memset(row->hl+i,kw2 ? HL_KEYWORD2 : HL_KEYWORD1,klen); - p += klen; - i += klen; - break; - } - } - if (keywords[j] != NULL) { - prev_sep = 0; - continue; /* We had a keyword match */ - } - } - - /* Not special chars */ - prev_sep = is_separator(*p); - p++; i++; - } - - /* Propagate syntax change to the next row if the open commen - * state changed. This may recursively affect all the following rows - * in the file. */ - int oc = editorRowHasOpenComment(row); - if (row->hl_oc != oc && row->idx+1 < E.numrows) - editorUpdateSyntax(&E.row[row->idx+1]); - row->hl_oc = oc; + row->hl = realloc(row->hl,row->rsize); + memset(row->hl,HL_NORMAL,row->rsize); + + if (E.syntax == NULL) return; /* No syntax, everything is HL_NORMAL. */ + + int i, prev_sep, in_string, in_comment; + char *p; + char **keywords = E.syntax->keywords; + char *scs = E.syntax->singleline_comment_start; + char *mcs = E.syntax->multiline_comment_start; + char *mce = E.syntax->multiline_comment_end; + + /* Point to the first non-space char. */ + p = row->render; + i = 0; /* Current char offset */ + while(*p && isspace(*p)) { + p++; + i++; + } + prev_sep = 1; /* Tell the parser if 'i' points to start of word. */ + in_string = 0; /* Are we inside "" or '' ? */ + in_comment = 0; /* Are we inside multi-line comment? */ + + /* If the previous line has an open comment, this line starts + * with an open comment state. */ + if (row->idx > 0 && editorRowHasOpenComment(&E.row[row->idx-1])) + in_comment = 1; + + while(*p) { + /* Handle // comments. */ + if (prev_sep && *p == scs[0] && *(p+1) == scs[1]) { + /* From here to end is a comment */ + memset(row->hl+i,HL_COMMENT,row->size-i); + return; + } + + /* Handle multi line comments. */ + if (in_comment) { + row->hl[i] = HL_MLCOMMENT; + if (*p == mce[0] && *(p+1) == mce[1]) { + row->hl[i+1] = HL_MLCOMMENT; + p += 2; i += 2; + in_comment = 0; + prev_sep = 1; + continue; + } else { + prev_sep = 0; + p++; i++; + continue; + } + } else if (*p == mcs[0] && *(p+1) == mcs[1]) { + row->hl[i] = HL_MLCOMMENT; + row->hl[i+1] = HL_MLCOMMENT; + p += 2; i += 2; + in_comment = 1; + prev_sep = 0; + continue; + } + + /* Handle "" and '' */ + if (in_string) { + row->hl[i] = HL_STRING; + if (*p == '\\') { + row->hl[i+1] = HL_STRING; + p += 2; i += 2; + prev_sep = 0; + continue; + } + if (*p == in_string) in_string = 0; + p++; i++; + continue; + } else { + if (*p == '"' || *p == '\'') { + in_string = *p; + row->hl[i] = HL_STRING; + p++; i++; + prev_sep = 0; + continue; + } + } + + /* Handle non printable chars. */ + if (!isprint(*p)) { + row->hl[i] = HL_NONPRINT; + p++; i++; + prev_sep = 0; + continue; + } + + /* Handle numbers */ + if ((isdigit(*p) && (prev_sep || row->hl[i-1] == HL_NUMBER)) || + (*p == '.' && i >0 && row->hl[i-1] == HL_NUMBER)) { + row->hl[i] = HL_NUMBER; + p++; i++; + prev_sep = 0; + continue; + } + + /* Handle keywords and lib calls */ + if (prev_sep) { + int j; + for (j = 0; keywords[j]; j++) { + int klen = strlen(keywords[j]); + int kw2 = keywords[j][klen-1] == '|'; + if (kw2) klen--; + + if (!memcmp(p,keywords[j],klen) && + is_separator(*(p+klen))) + { + /* Keyword */ + memset(row->hl+i,kw2 ? HL_KEYWORD2 : HL_KEYWORD1,klen); + p += klen; + i += klen; + break; + } + } + if (keywords[j] != NULL) { + prev_sep = 0; + continue; /* We had a keyword match */ + } + } + + /* Not special chars */ + prev_sep = is_separator(*p); + p++; i++; + } + + /* Propagate syntax change to the next row if the open commen + * state changed. This may recursively affect all the following rows + * in the file. */ + int oc = editorRowHasOpenComment(row); + if (row->hl_oc != oc && row->idx+1 < E.numrows) + editorUpdateSyntax(&E.row[row->idx+1]); + row->hl_oc = oc; } /* Maps syntax highlight token types to terminal colors. */ int editorSyntaxToColor(int hl) { - switch(hl) { - case HL_COMMENT: - case HL_MLCOMMENT: return 36; /* cyan */ - case HL_KEYWORD1: return 33; /* yellow */ - case HL_KEYWORD2: return 32; /* green */ - case HL_STRING: return 35; /* magenta */ - case HL_NUMBER: return 31; /* red */ - case HL_MATCH: return 34; /* blu */ - default: return 37; /* white */ - } + switch(hl) { + case HL_COMMENT: + case HL_MLCOMMENT: return 36; /* cyan */ + case HL_KEYWORD1: return 33; /* yellow */ + case HL_KEYWORD2: return 32; /* green */ + case HL_STRING: return 35; /* magenta */ + case HL_NUMBER: return 31; /* red */ + case HL_MATCH: return 34; /* blu */ + default: return 37; /* white */ + } } /* Select the syntax highlight scheme depending on the filename, * setting it in the global state E.syntax. */ void editorSelectSyntaxHighlight(char *filename) { - for (unsigned int j = 0; j < HLDB_ENTRIES; j++) { - struct editorSyntax *s = HLDB+j; - unsigned int i = 0; - while(s->filematch[i]) { - char *p; - int patlen = strlen(s->filematch[i]); - if ((p = strstr(filename,s->filematch[i])) != NULL) { - if (s->filematch[i][0] != '.' || p[patlen] == '\0') { - E.syntax = s; - return; - } - } - i++; - } - } + for (unsigned int j = 0; j < HLDB_ENTRIES; j++) { + struct editorSyntax *s = HLDB+j; + unsigned int i = 0; + while(s->filematch[i]) { + char *p; + int patlen = strlen(s->filematch[i]); + if ((p = strstr(filename,s->filematch[i])) != NULL) { + if (s->filematch[i][0] != '.' || p[patlen] == '\0') { + E.syntax = s; + return; + } + } + i++; + } + } } /* ======================= Editor rows implementation ======================= */ /* Update the rendered version and the syntax highlight of a row. */ void editorUpdateRow(erow *row) { - unsigned int tabs = 0, nonprint = 0; - int j, idx; + unsigned int tabs = 0, nonprint = 0; + int j, idx; /* Create a version of the row we can directly print on the screen, - * respecting tabs, substituting non printable characters with '?'. */ - free(row->render); - for (j = 0; j < row->size; j++) - if (row->chars[j] == TAB) tabs++; - - unsigned long long allocsize = - (unsigned long long) row->size + tabs*TAB_SIZE + nonprint*9 + 1; - if (allocsize > UINT32_MAX) { - printf("Some line of the edited file is too long for kilo\n"); - exit(1); - } - - row->render = malloc(row->size + tabs*TAB_SIZE + nonprint*9 + 1); - idx = 0; - for (j = 0; j < row->size; j++) { - if (row->chars[j] == TAB) { - row->render[idx++] = ' '; - while((idx) % TAB_SIZE != 0) row->render[idx++] = ' '; - } else { - row->render[idx++] = row->chars[j]; - } - } - row->rsize = idx; - row->render[idx] = '\0'; - - /* Update the syntax highlighting attributes of the row. */ - editorUpdateSyntax(row); + * respecting tabs, substituting non printable characters with '?'. */ + free(row->render); + for (j = 0; j < row->size; j++) + if (row->chars[j] == TAB) tabs++; + + unsigned long long allocsize = + (unsigned long long) row->size + tabs*TAB_SIZE + nonprint*9 + 1; + if (allocsize > UINT32_MAX) { + printf("Some line of the edited file is too long for kilo\n"); + exit(1); + } + + row->render = malloc(row->size + tabs*TAB_SIZE + nonprint*9 + 1); + idx = 0; + for (j = 0; j < row->size; j++) { + if (row->chars[j] == TAB) { + row->render[idx++] = ' '; + while((idx) % TAB_SIZE != 0) row->render[idx++] = ' '; + } else { + row->render[idx++] = row->chars[j]; + } + } + row->rsize = idx; + row->render[idx] = '\0'; + + /* Update the syntax highlighting attributes of the row. */ + editorUpdateSyntax(row); } /* Insert a row at the specified position, shifting the other rows on the bottom * if required. */ void editorInsertRow(int at, char *s, size_t len) { - if (at > E.numrows) return; - E.row = realloc(E.row,sizeof(erow)*(E.numrows+1)); - if (at != E.numrows) { - memmove(E.row+at+1,E.row+at,sizeof(E.row[0])*(E.numrows-at)); - for (int j = at+1; j <= E.numrows; j++) E.row[j].idx++; - } - E.row[at].size = len; - E.row[at].chars = malloc(len+1); - memcpy(E.row[at].chars,s,len+1); - E.row[at].hl = NULL; - E.row[at].hl_oc = 0; - E.row[at].render = NULL; - E.row[at].rsize = 0; - E.row[at].idx = at; - editorUpdateRow(E.row+at); - E.numrows++; - E.dirty++; + if (at > E.numrows) return; + E.row = realloc(E.row,sizeof(erow)*(E.numrows+1)); + if (at != E.numrows) { + memmove(E.row+at+1,E.row+at,sizeof(E.row[0])*(E.numrows-at)); + for (int j = at+1; j <= E.numrows; j++) E.row[j].idx++; + } + E.row[at].size = len; + E.row[at].chars = malloc(len+1); + memcpy(E.row[at].chars,s,len+1); + E.row[at].hl = NULL; + E.row[at].hl_oc = 0; + E.row[at].render = NULL; + E.row[at].rsize = 0; + E.row[at].idx = at; + editorUpdateRow(E.row+at); + E.numrows++; + E.dirty++; } /* Free row's heap allocated stuff. */ void editorFreeRow(erow *row) { - free(row->render); - free(row->chars); - free(row->hl); + free(row->render); + free(row->chars); + free(row->hl); } /* Remove the row at the specified position, shifting the remainign on the * top. */ void editorDelRow(int at) { - erow *row; - - if (at >= E.numrows) return; - row = E.row+at; - editorFreeRow(row); - memmove(E.row+at,E.row+at+1,sizeof(E.row[0])*(E.numrows-at-1)); - for (int j = at; j < E.numrows-1; j++) E.row[j].idx++; - E.numrows--; - E.dirty++; + erow *row; + + if (at >= E.numrows) return; + row = E.row+at; + editorFreeRow(row); + memmove(E.row+at,E.row+at+1,sizeof(E.row[0])*(E.numrows-at-1)); + for (int j = at; j < E.numrows-1; j++) E.row[j].idx++; + E.numrows--; + E.dirty++; } /* Turn the editor rows into a single heap-allocated string. @@ -637,238 +637,238 @@ void editorDelRow(int at) { * integer pointed by 'buflen' with the size of the string, escluding * the final nulterm. */ char *editorRowsToString(int *buflen) { - char *buf = NULL, *p; - int totlen = 0; - int j; - - /* Compute count of bytes */ - for (j = 0; j < E.numrows; j++) - totlen += E.row[j].size+1; /* +1 is for "\n" at end of every row */ - *buflen = totlen; - totlen++; /* Also make space for nulterm */ - - p = buf = malloc(totlen); - for (j = 0; j < E.numrows; j++) { - memcpy(p,E.row[j].chars,E.row[j].size); - p += E.row[j].size; - *p = '\n'; - p++; - } - *p = '\0'; - return buf; + char *buf = NULL, *p; + int totlen = 0; + int j; + + /* Compute count of bytes */ + for (j = 0; j < E.numrows; j++) + totlen += E.row[j].size+1; /* +1 is for "\n" at end of every row */ + *buflen = totlen; + totlen++; /* Also make space for nulterm */ + + p = buf = malloc(totlen); + for (j = 0; j < E.numrows; j++) { + memcpy(p,E.row[j].chars,E.row[j].size); + p += E.row[j].size; + *p = '\n'; + p++; + } + *p = '\0'; + return buf; } /* Insert a character at the specified position in a row, moving the remaining * chars on the right if needed. */ void editorRowInsertChar(erow *row, int at, int c) { - if (at > row->size) { - /* Pad the string with spaces if the insert location is outside the - * current length by more than a single character. */ - int padlen = at-row->size; - /* In the next line +2 means: new char and null term. */ - row->chars = realloc(row->chars,row->size+padlen+2); - memset(row->chars+row->size,' ',padlen); - row->chars[row->size+padlen+1] = '\0'; - row->size += padlen+1; - } else { - /* If we are in the middle of the string just make space for 1 new - * char plus the (already existing) null term. */ - row->chars = realloc(row->chars,row->size+2); - memmove(row->chars+at+1,row->chars+at,row->size-at+1); - row->size++; - } - row->chars[at] = c; - editorUpdateRow(row); - E.dirty++; + if (at > row->size) { + /* Pad the string with spaces if the insert location is outside the + * current length by more than a single character. */ + int padlen = at-row->size; + /* In the next line +2 means: new char and null term. */ + row->chars = realloc(row->chars,row->size+padlen+2); + memset(row->chars+row->size,' ',padlen); + row->chars[row->size+padlen+1] = '\0'; + row->size += padlen+1; + } else { + /* If we are in the middle of the string just make space for 1 new + * char plus the (already existing) null term. */ + row->chars = realloc(row->chars,row->size+2); + memmove(row->chars+at+1,row->chars+at,row->size-at+1); + row->size++; + } + row->chars[at] = c; + editorUpdateRow(row); + E.dirty++; } /* Append the string 's' at the end of a row */ void editorRowAppendString(erow *row, char *s, size_t len) { - row->chars = realloc(row->chars,row->size+len+1); - memcpy(row->chars+row->size,s,len); - row->size += len; - row->chars[row->size] = '\0'; - editorUpdateRow(row); - E.dirty++; + row->chars = realloc(row->chars,row->size+len+1); + memcpy(row->chars+row->size,s,len); + row->size += len; + row->chars[row->size] = '\0'; + editorUpdateRow(row); + E.dirty++; } /* Delete the character at offset 'at' from the specified row. */ void editorRowDelChar(erow *row, int at) { - if (row->size <= at) return; - memmove(row->chars+at,row->chars+at+1,row->size-at); - editorUpdateRow(row); - row->size--; - E.dirty++; + if (row->size <= at) return; + memmove(row->chars+at,row->chars+at+1,row->size-at); + editorUpdateRow(row); + row->size--; + E.dirty++; } /* Insert the specified char at the current prompt position. */ void editorInsertChar(int c) { - int filerow = E.rowoff+E.cy; - int filecol = E.coloff+E.cx; - erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; - - /* If the row where the cursor is currently located does not exist in our - * logical representaion of the file, add enough empty rows as needed. */ - if (!row) { - while(E.numrows <= filerow) - editorInsertRow(E.numrows,"",0); - } - row = &E.row[filerow]; - editorRowInsertChar(row,filecol,c); - if (E.cx == E.screencols-1) - E.coloff++; - else - E.cx++; - E.dirty++; + int filerow = E.rowoff+E.cy; + int filecol = E.coloff+E.cx; + erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; + + /* If the row where the cursor is currently located does not exist in our + * logical representaion of the file, add enough empty rows as needed. */ + if (!row) { + while(E.numrows <= filerow) + editorInsertRow(E.numrows,"",0); + } + row = &E.row[filerow]; + editorRowInsertChar(row,filecol,c); + if (E.cx == E.screencols-1) + E.coloff++; + else + E.cx++; + E.dirty++; } /* Inserting a newline is slightly complex as we have to handle inserting a * newline in the middle of a line, splitting the line as needed. */ void editorInsertNewline(void) { - int filerow = E.rowoff+E.cy; - int filecol = E.coloff+E.cx; - erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; - - if (!row) { - if (filerow == E.numrows) { - editorInsertRow(filerow,"",0); - goto fixcursor; - } - return; - } - /* If the cursor is over the current line size, we want to conceptually - * think it's just over the last character. */ - if (filecol >= row->size) filecol = row->size; - if (filecol == 0) { - editorInsertRow(filerow,"",0); - } else { - /* We are in the middle of a line. Split it between two rows. */ - editorInsertRow(filerow+1,row->chars+filecol,row->size-filecol); - row = &E.row[filerow]; - row->chars[filecol] = '\0'; - row->size = filecol; - editorUpdateRow(row); - } + int filerow = E.rowoff+E.cy; + int filecol = E.coloff+E.cx; + erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; + + if (!row) { + if (filerow == E.numrows) { + editorInsertRow(filerow,"",0); + goto fixcursor; + } + return; + } + /* If the cursor is over the current line size, we want to conceptually + * think it's just over the last character. */ + if (filecol >= row->size) filecol = row->size; + if (filecol == 0) { + editorInsertRow(filerow,"",0); + } else { + /* We are in the middle of a line. Split it between two rows. */ + editorInsertRow(filerow+1,row->chars+filecol,row->size-filecol); + row = &E.row[filerow]; + row->chars[filecol] = '\0'; + row->size = filecol; + editorUpdateRow(row); + } fixcursor: - if (E.cy == E.screenrows-1) { - E.rowoff++; - } else { - E.cy++; - } - E.cx = 0; - E.coloff = 0; + if (E.cy == E.screenrows-1) { + E.rowoff++; + } else { + E.cy++; + } + E.cx = 0; + E.coloff = 0; } /* Delete the char at the current prompt position. */ void editorDelChar() { - int filerow = E.rowoff+E.cy; - int filecol = E.coloff+E.cx; - erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; - - if (!row || (filecol == 0 && filerow == 0)) return; - if (filecol == 0) { - /* Handle the case of column 0, we need to move the current line - * on the right of the previous one. */ - filecol = E.row[filerow-1].size; - editorRowAppendString(&E.row[filerow-1],row->chars,row->size); - editorDelRow(filerow); - row = NULL; - if (E.cy == 0) - E.rowoff--; - else - E.cy--; - E.cx = filecol; - if (E.cx >= E.screencols) { - int shift = (E.screencols-E.cx)+1; - E.cx -= shift; - E.coloff += shift; - } - } else { - editorRowDelChar(row,filecol-1); - if (E.cx == 0 && E.coloff) - E.coloff--; - else - E.cx--; - } - if (row) editorUpdateRow(row); - E.dirty++; + int filerow = E.rowoff+E.cy; + int filecol = E.coloff+E.cx; + erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; + + if (!row || (filecol == 0 && filerow == 0)) return; + if (filecol == 0) { + /* Handle the case of column 0, we need to move the current line + * on the right of the previous one. */ + filecol = E.row[filerow-1].size; + editorRowAppendString(&E.row[filerow-1],row->chars,row->size); + editorDelRow(filerow); + row = NULL; + if (E.cy == 0) + E.rowoff--; + else + E.cy--; + E.cx = filecol; + if (E.cx >= E.screencols) { + int shift = (E.screencols-E.cx)+1; + E.cx -= shift; + E.coloff += shift; + } + } else { + editorRowDelChar(row,filecol-1); + if (E.cx == 0 && E.coloff) + E.coloff--; + else + E.cx--; + } + if (row) editorUpdateRow(row); + E.dirty++; } void editorInvDelChar() { - int filerow = E.rowoff+E.cy; - int filecol = E.coloff+E.cx; - erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; - erow *followingRow = (filerow+1 >= E.numrows) ? NULL : &E.row[filerow+1]; - - if (!row || (filecol == 0 && filerow == 0)) return; - if (filecol == row->size) { - /* Handle the case of last column, we need to move the following line - * on the right of the current one. */ - editorRowAppendString(&E.row[filerow],followingRow->chars,followingRow->size); - editorDelRow(filerow+1); - row = NULL; - } else { - editorRowDelChar(row,filecol); - } - if (row) editorUpdateRow(row); - E.dirty++; + int filerow = E.rowoff+E.cy; + int filecol = E.coloff+E.cx; + erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; + erow *followingRow = (filerow+1 >= E.numrows) ? NULL : &E.row[filerow+1]; + + if (!row || (filecol == 0 && filerow == 0)) return; + if (filecol == row->size) { + /* Handle the case of last column, we need to move the following line + * on the right of the current one. */ + editorRowAppendString(&E.row[filerow],followingRow->chars,followingRow->size); + editorDelRow(filerow+1); + row = NULL; + } else { + editorRowDelChar(row,filecol); + } + if (row) editorUpdateRow(row); + E.dirty++; } /* Load the specified program in the editor memory and returns 0 on success * or 1 on error. */ int editorOpen(char *filename) { - FILE *fp; - - E.dirty = 0; - free(E.filename); - size_t fnlen = strlen(filename)+1; - E.filename = malloc(fnlen); - memcpy(E.filename,filename,fnlen); - - fp = fopen(filename,"r"); - if (!fp) { - if (errno != ENOENT) { - perror("Opening file"); - exit(1); - } - return 1; - } - - char *line = NULL; - size_t linecap = 0; - ssize_t linelen; - while((linelen = getline(&line,&linecap,fp)) != -1) { - if (linelen && (line[linelen-1] == '\n' || line[linelen-1] == '\r')) - line[--linelen] = '\0'; - editorInsertRow(E.numrows,line,linelen); - } - free(line); - fclose(fp); - E.dirty = 0; - return 0; + FILE *fp; + + E.dirty = 0; + free(E.filename); + size_t fnlen = strlen(filename)+1; + E.filename = malloc(fnlen); + memcpy(E.filename,filename,fnlen); + + fp = fopen(filename,"r"); + if (!fp) { + if (errno != ENOENT) { + perror("Opening file"); + exit(1); + } + return 1; + } + + char *line = NULL; + size_t linecap = 0; + ssize_t linelen; + while((linelen = getline(&line,&linecap,fp)) != -1) { + if (linelen && (line[linelen-1] == '\n' || line[linelen-1] == '\r')) + line[--linelen] = '\0'; + editorInsertRow(E.numrows,line,linelen); + } + free(line); + fclose(fp); + E.dirty = 0; + return 0; } /* Save the current file on disk. Return 0 on success, 1 on error. */ int editorSave(void) { - int len; - char *buf = editorRowsToString(&len); - int fd = open(E.filename,O_RDWR|O_CREAT,0644); - if (fd == -1) goto writeerr; - - /* Use truncate + a single write(2) call in order to make saving - * a bit safer, under the limits of what we can do in a small editor. */ - if (ftruncate(fd,len) == -1) goto writeerr; - if (write(fd,buf,len) != len) goto writeerr; - - close(fd); - free(buf); - E.dirty = 0; - editorSetStatusMessage("%d bytes written on disk", len); - return 0; + int len; + char *buf = editorRowsToString(&len); + int fd = open(E.filename,O_RDWR|O_CREAT,0644); + if (fd == -1) goto writeerr; + + /* Use truncate + a single write(2) call in order to make saving + * a bit safer, under the limits of what we can do in a small editor. */ + if (ftruncate(fd,len) == -1) goto writeerr; + if (write(fd,buf,len) != len) goto writeerr; + + close(fd); + free(buf); + E.dirty = 0; + editorSetStatusMessage("%d bytes written on disk", len); + return 0; writeerr: - free(buf); - if (fd != -1) close(fd); - editorSetStatusMessage("Can't save! I/O error: %s",strerror(errno)); - return 1; + free(buf); + if (fd != -1) close(fd); + editorSetStatusMessage("Can't save! I/O error: %s",strerror(errno)); + return 1; } /* ============================= Terminal update ============================ */ @@ -878,153 +878,153 @@ int editorSave(void) { * write all the escape sequences in a buffer and flush them to the standard * output in a single call, to avoid flickering effects. */ struct abuf { - char *b; - int len; + char *b; + int len; }; #define ABUF_INIT {NULL,0} void abAppend(struct abuf *ab, const char *s, int len) { - char *new = realloc(ab->b,ab->len+len); + char *new = realloc(ab->b,ab->len+len); - if (new == NULL) return; - memcpy(new+ab->len,s,len); - ab->b = new; - ab->len += len; + if (new == NULL) return; + memcpy(new+ab->len,s,len); + ab->b = new; + ab->len += len; } void abFree(struct abuf *ab) { - free(ab->b); + free(ab->b); } /* This function writes the whole screen using VT100 escape characters * starting from the logical state of the editor in the global state 'E'. */ void editorRefreshScreen(void) { - int y; - erow *r; - char buf[32]; - struct abuf ab = ABUF_INIT; - - abAppend(&ab,"\x1b[?25l",6); /* Hide cursor. */ - abAppend(&ab,"\x1b[H",3); /* Go home. */ - for (y = 0; y < E.screenrows; y++) { - int filerow = E.rowoff+y; - - if (filerow >= E.numrows) { - if (E.numrows == 0 && y == E.screenrows/3) { - char welcome[80]; - int welcomelen = snprintf(welcome,sizeof(welcome), - "Kilo editor -- verison %s\x1b[0K\r\n", KILO_VERSION); - int padding = (E.screencols-welcomelen)/2; - if (padding) { - abAppend(&ab,"~",1); - padding--; - } - while(padding--) abAppend(&ab," ",1); - abAppend(&ab,welcome,welcomelen); - } else { - abAppend(&ab,"~\x1b[0K\r\n",7); - } - continue; - } - - r = &E.row[filerow]; - - int len = r->rsize - E.coloff; - int current_color = -1; - if (len > 0) { - if (len > E.screencols) len = E.screencols; - char *c = r->render+E.coloff; - unsigned char *hl = r->hl+E.coloff; - int j; - for (j = 0; j < len; j++) { - if (hl[j] == HL_NONPRINT) { - char sym; - abAppend(&ab,"\x1b[7m",4); - if (c[j] <= 26) - sym = '@'+c[j]; - else - sym = '?'; - abAppend(&ab,&sym,1); - abAppend(&ab,"\x1b[0m",4); - } else if (hl[j] == HL_NORMAL) { - if (current_color != -1) { - abAppend(&ab,"\x1b[39m",5); - current_color = -1; - } - abAppend(&ab,c+j,1); - } else { - int color = editorSyntaxToColor(hl[j]); - if (color != current_color) { - char buf[16]; - int clen = snprintf(buf,sizeof(buf),"\x1b[%dm",color); - current_color = color; - abAppend(&ab,buf,clen); - } - abAppend(&ab,c+j,1); - } - } - } - abAppend(&ab,"\x1b[39m",5); - abAppend(&ab,"\x1b[0K",4); - abAppend(&ab,"\r\n",2); - } - - /* Create a two rows status. First row: */ - abAppend(&ab,"\x1b[0K",4); - abAppend(&ab,"\x1b[7m",4); - char status[80], rstatus[80]; - int len = snprintf(status, sizeof(status), "%.20s - %d lines %s", - E.filename, E.numrows, E.dirty ? "(modified)" : ""); - int rlen = snprintf(rstatus, sizeof(rstatus), - "%d/%d",E.rowoff+E.cy+1,E.numrows); - if (len > E.screencols) len = E.screencols; - abAppend(&ab,status,len); - while(len < E.screencols) { - if (E.screencols - len == rlen) { - abAppend(&ab,rstatus,rlen); - break; - } else { - abAppend(&ab," ",1); - len++; - } - } - abAppend(&ab,"\x1b[0m\r\n",6); - - /* Second row depends on E.statusmsg and the status message update time. */ - abAppend(&ab,"\x1b[0K",4); - int msglen = strlen(E.statusmsg); - if (msglen && time(NULL)-E.statusmsg_time < 5) - abAppend(&ab,E.statusmsg,msglen <= E.screencols ? msglen : E.screencols); - - /* Put cursor at its current position. Note that the horizontal position - * at which the cursor is displayed may be different compared to 'E.cx' - * because of TABs. */ - int j; - int cx = 1; - int filerow = E.rowoff+E.cy; - erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; - if (row) { - for (j = E.coloff; j < (E.cx+E.coloff); j++) { - if (j < row->size && row->chars[j] == TAB) cx += TAB_SIZE-((cx)%TAB_SIZE); - cx++; - } - } - snprintf(buf,sizeof(buf),"\x1b[%d;%dH",E.cy+1,cx); - abAppend(&ab,buf,strlen(buf)); - abAppend(&ab,"\x1b[?25h",6); /* Show cursor. */ - write(STDOUT_FILENO,ab.b,ab.len); - abFree(&ab); + int y; + erow *r; + char buf[32]; + struct abuf ab = ABUF_INIT; + + abAppend(&ab,"\x1b[?25l",6); /* Hide cursor. */ + abAppend(&ab,"\x1b[H",3); /* Go home. */ + for (y = 0; y < E.screenrows; y++) { + int filerow = E.rowoff+y; + + if (filerow >= E.numrows) { + if (E.numrows == 0 && y == E.screenrows/3) { + char welcome[80]; + int welcomelen = snprintf(welcome,sizeof(welcome), + "Kilo editor -- verison %s\x1b[0K\r\n", KILO_VERSION); + int padding = (E.screencols-welcomelen)/2; + if (padding) { + abAppend(&ab,"~",1); + padding--; + } + while(padding--) abAppend(&ab," ",1); + abAppend(&ab,welcome,welcomelen); + } else { + abAppend(&ab,"~\x1b[0K\r\n",7); + } + continue; + } + + r = &E.row[filerow]; + + int len = r->rsize - E.coloff; + int current_color = -1; + if (len > 0) { + if (len > E.screencols) len = E.screencols; + char *c = r->render+E.coloff; + unsigned char *hl = r->hl+E.coloff; + int j; + for (j = 0; j < len; j++) { + if (hl[j] == HL_NONPRINT) { + char sym; + abAppend(&ab,"\x1b[7m",4); + if (c[j] <= 26) + sym = '@'+c[j]; + else + sym = '?'; + abAppend(&ab,&sym,1); + abAppend(&ab,"\x1b[0m",4); + } else if (hl[j] == HL_NORMAL) { + if (current_color != -1) { + abAppend(&ab,"\x1b[39m",5); + current_color = -1; + } + abAppend(&ab,c+j,1); + } else { + int color = editorSyntaxToColor(hl[j]); + if (color != current_color) { + char buf[16]; + int clen = snprintf(buf,sizeof(buf),"\x1b[%dm",color); + current_color = color; + abAppend(&ab,buf,clen); + } + abAppend(&ab,c+j,1); + } + } + } + abAppend(&ab,"\x1b[39m",5); + abAppend(&ab,"\x1b[0K",4); + abAppend(&ab,"\r\n",2); + } + + /* Create a two rows status. First row: */ + abAppend(&ab,"\x1b[0K",4); + abAppend(&ab,"\x1b[7m",4); + char status[80], rstatus[80]; + int len = snprintf(status, sizeof(status), "%.20s - %d lines %s", + E.filename, E.numrows, E.dirty ? "(modified)" : ""); + int rlen = snprintf(rstatus, sizeof(rstatus), + "%d/%d",E.rowoff+E.cy+1,E.numrows); + if (len > E.screencols) len = E.screencols; + abAppend(&ab,status,len); + while(len < E.screencols) { + if (E.screencols - len == rlen) { + abAppend(&ab,rstatus,rlen); + break; + } else { + abAppend(&ab," ",1); + len++; + } + } + abAppend(&ab,"\x1b[0m\r\n",6); + + /* Second row depends on E.statusmsg and the status message update time. */ + abAppend(&ab,"\x1b[0K",4); + int msglen = strlen(E.statusmsg); + if (msglen && time(NULL)-E.statusmsg_time < 5) + abAppend(&ab,E.statusmsg,msglen <= E.screencols ? msglen : E.screencols); + + /* Put cursor at its current position. Note that the horizontal position + * at which the cursor is displayed may be different compared to 'E.cx' + * because of TABs. */ + int j; + int cx = 1; + int filerow = E.rowoff+E.cy; + erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; + if (row) { + for (j = E.coloff; j < (E.cx+E.coloff); j++) { + if (j < row->size && row->chars[j] == TAB) cx += TAB_SIZE-((cx)%TAB_SIZE); + cx++; + } + } + snprintf(buf,sizeof(buf),"\x1b[%d;%dH",E.cy+1,cx); + abAppend(&ab,buf,strlen(buf)); + abAppend(&ab,"\x1b[?25h",6); /* Show cursor. */ + write(STDOUT_FILENO,ab.b,ab.len); + abFree(&ab); } /* Set an editor status message for the second line of the status, at the * end of the screen. */ void editorSetStatusMessage(const char *fmt, ...) { - va_list ap; - va_start(ap,fmt); - vsnprintf(E.statusmsg,sizeof(E.statusmsg),fmt,ap); - va_end(ap); - E.statusmsg_time = time(NULL); + va_list ap; + va_start(ap,fmt); + vsnprintf(E.statusmsg,sizeof(E.statusmsg),fmt,ap); + va_end(ap); + E.statusmsg_time = time(NULL); } /* =============================== Find mode ================================ */ @@ -1032,299 +1032,299 @@ void editorSetStatusMessage(const char *fmt, ...) { #define KILO_QUERY_LEN 256 void editorFind(int fd) { - char query[KILO_QUERY_LEN+1] = {0}; - int qlen = 0; - int last_match = -1; /* Last line where a match was found. -1 for none. */ - int find_next = 0; /* if 1 search next, if -1 search prev. */ - int saved_hl_line = -1; /* No saved HL */ - char *saved_hl = NULL; + char query[KILO_QUERY_LEN+1] = {0}; + int qlen = 0; + int last_match = -1; /* Last line where a match was found. -1 for none. */ + int find_next = 0; /* if 1 search next, if -1 search prev. */ + int saved_hl_line = -1; /* No saved HL */ + char *saved_hl = NULL; #define FIND_RESTORE_HL do { \ - if (saved_hl) { \ - memcpy(E.row[saved_hl_line].hl,saved_hl, E.row[saved_hl_line].rsize); \ - free(saved_hl); \ - saved_hl = NULL; \ - } \ + if (saved_hl) { \ + memcpy(E.row[saved_hl_line].hl,saved_hl, E.row[saved_hl_line].rsize); \ + free(saved_hl); \ + saved_hl = NULL; \ + } \ } while (0) - /* Save the cursor position in order to restore it later. */ - int saved_cx = E.cx, saved_cy = E.cy; - int saved_coloff = E.coloff, saved_rowoff = E.rowoff; - - while(1) { - editorSetStatusMessage( - "Search: %s (Use ESC/Arrows/Enter)", query); - editorRefreshScreen(); - - int c = editorReadKey(fd); - if (c == DEL_KEY || c == CTRL_H || c == BACKSPACE) { - if (qlen != 0) query[--qlen] = '\0'; - last_match = -1; - } else if (c == ESC || c == ENTER) { - if (c == ESC) { - E.cx = saved_cx; E.cy = saved_cy; - E.coloff = saved_coloff; E.rowoff = saved_rowoff; - } - FIND_RESTORE_HL; - editorSetStatusMessage(""); - return; - } else if (c == ARROW_RIGHT || c == ARROW_DOWN) { - find_next = 1; - } else if (c == ARROW_LEFT || c == ARROW_UP) { - find_next = -1; - } else if (isprint(c)) { - if (qlen < KILO_QUERY_LEN) { - query[qlen++] = c; - query[qlen] = '\0'; - last_match = -1; - } - } - - /* Search occurrence. */ - if (last_match == -1) find_next = 1; - if (find_next) { - char *match = NULL; - int match_offset = 0; - int i, current = last_match; - - for (i = 0; i < E.numrows; i++) { - current += find_next; - if (current == -1) current = E.numrows-1; - else if (current == E.numrows) current = 0; - match = strstr(E.row[current].render,query); - if (match) { - match_offset = match-E.row[current].render; - break; - } - } - find_next = 0; - - /* Highlight */ - FIND_RESTORE_HL; - - if (match) { - erow *row = &E.row[current]; - last_match = current; - if (row->hl) { - saved_hl_line = current; - saved_hl = malloc(row->rsize); - memcpy(saved_hl,row->hl,row->rsize); - memset(row->hl+match_offset,HL_MATCH,qlen); - } - E.cy = 0; - E.cx = match_offset; - E.rowoff = current; - E.coloff = 0; - /* Scroll horizontally as needed. */ - if (E.cx > E.screencols) { - int diff = E.cx - E.screencols; - E.cx -= diff; - E.coloff += diff; - } - } - } - } + /* Save the cursor position in order to restore it later. */ + int saved_cx = E.cx, saved_cy = E.cy; + int saved_coloff = E.coloff, saved_rowoff = E.rowoff; + + while(1) { + editorSetStatusMessage( + "Search: %s (Use ESC/Arrows/Enter)", query); + editorRefreshScreen(); + + int c = editorReadKey(fd); + if (c == DEL_KEY || c == CTRL_H || c == BACKSPACE) { + if (qlen != 0) query[--qlen] = '\0'; + last_match = -1; + } else if (c == ESC || c == ENTER) { + if (c == ESC) { + E.cx = saved_cx; E.cy = saved_cy; + E.coloff = saved_coloff; E.rowoff = saved_rowoff; + } + FIND_RESTORE_HL; + editorSetStatusMessage(""); + return; + } else if (c == ARROW_RIGHT || c == ARROW_DOWN) { + find_next = 1; + } else if (c == ARROW_LEFT || c == ARROW_UP) { + find_next = -1; + } else if (isprint(c)) { + if (qlen < KILO_QUERY_LEN) { + query[qlen++] = c; + query[qlen] = '\0'; + last_match = -1; + } + } + + /* Search occurrence. */ + if (last_match == -1) find_next = 1; + if (find_next) { + char *match = NULL; + int match_offset = 0; + int i, current = last_match; + + for (i = 0; i < E.numrows; i++) { + current += find_next; + if (current == -1) current = E.numrows-1; + else if (current == E.numrows) current = 0; + match = strstr(E.row[current].render,query); + if (match) { + match_offset = match-E.row[current].render; + break; + } + } + find_next = 0; + + /* Highlight */ + FIND_RESTORE_HL; + + if (match) { + erow *row = &E.row[current]; + last_match = current; + if (row->hl) { + saved_hl_line = current; + saved_hl = malloc(row->rsize); + memcpy(saved_hl,row->hl,row->rsize); + memset(row->hl+match_offset,HL_MATCH,qlen); + } + E.cy = 0; + E.cx = match_offset; + E.rowoff = current; + E.coloff = 0; + /* Scroll horizontally as needed. */ + if (E.cx > E.screencols) { + int diff = E.cx - E.screencols; + E.cx -= diff; + E.coloff += diff; + } + } + } + } } /* ========================= Editor events handling ======================== */ /* Handle cursor position change because arrow keys were pressed. */ void editorMoveCursor(int key) { - int filerow = E.rowoff+E.cy; - int filecol = E.coloff+E.cx; - int rowlen; - erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; - - switch(key) { - case ARROW_LEFT: - if (E.cx == 0) { - if (E.coloff) { - E.coloff--; - } else { - if (filerow > 0) { - E.cy--; - E.cx = E.row[filerow-1].size; - if (E.cx > E.screencols-1) { - E.coloff = E.cx-E.screencols+1; - E.cx = E.screencols-1; - } - } - } - } else { - E.cx -= 1; - } - break; - case ARROW_RIGHT: - if (row && filecol < row->size) { - if (E.cx == E.screencols-1) { - E.coloff++; - } else { - E.cx += 1; - } - } else if (row && filecol == row->size) { - E.cx = 0; - E.coloff = 0; - if (E.cy == E.screenrows-1) { - E.rowoff++; - } else { - E.cy += 1; - } - } - break; - case ARROW_UP: - if (E.cy == 0) { - if (E.rowoff) E.rowoff--; - } else { - E.cy -= 1; - } - break; - case ARROW_DOWN: - if (filerow < E.numrows) { - if (E.cy == E.screenrows-1) { - E.rowoff++; - } else { - E.cy += 1; - } - } - break; - } - /* Fix cx if the current line has not enough chars. */ - filerow = E.rowoff+E.cy; - filecol = E.coloff+E.cx; - row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; - rowlen = row ? row->size : 0; - if (filecol > rowlen) { - E.cx -= filecol-rowlen; - if (E.cx < 0) { - E.coloff += E.cx; - E.cx = 0; - } - } + int filerow = E.rowoff+E.cy; + int filecol = E.coloff+E.cx; + int rowlen; + erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; + + switch(key) { + case ARROW_LEFT: + if (E.cx == 0) { + if (E.coloff) { + E.coloff--; + } else { + if (filerow > 0) { + E.cy--; + E.cx = E.row[filerow-1].size; + if (E.cx > E.screencols-1) { + E.coloff = E.cx-E.screencols+1; + E.cx = E.screencols-1; + } + } + } + } else { + E.cx -= 1; + } + break; + case ARROW_RIGHT: + if (row && filecol < row->size) { + if (E.cx == E.screencols-1) { + E.coloff++; + } else { + E.cx += 1; + } + } else if (row && filecol == row->size) { + E.cx = 0; + E.coloff = 0; + if (E.cy == E.screenrows-1) { + E.rowoff++; + } else { + E.cy += 1; + } + } + break; + case ARROW_UP: + if (E.cy == 0) { + if (E.rowoff) E.rowoff--; + } else { + E.cy -= 1; + } + break; + case ARROW_DOWN: + if (filerow < E.numrows) { + if (E.cy == E.screenrows-1) { + E.rowoff++; + } else { + E.cy += 1; + } + } + break; + } + /* Fix cx if the current line has not enough chars. */ + filerow = E.rowoff+E.cy; + filecol = E.coloff+E.cx; + row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; + rowlen = row ? row->size : 0; + if (filecol > rowlen) { + E.cx -= filecol-rowlen; + if (E.cx < 0) { + E.coloff += E.cx; + E.cx = 0; + } + } } /* Process events arriving from the standard input, which is, the user * is typing stuff on the terminal. */ #define KILO_QUIT_TIMES 3 void editorProcessKeypress(int fd) { - /* When the file is modified, requires Ctrl-q to be pressed N times - * before actually quitting. */ - static int quit_times = KILO_QUIT_TIMES; - - int c = editorReadKey(fd); - switch(c) { - case ENTER: /* Enter */ - editorInsertNewline(); - break; - case CTRL_C: /* Ctrl-c */ - /* We ignore ctrl-c, it can't be so simple to lose the changes - * to the edited file. */ - break; - case CTRL_Q: /* Ctrl-q */ - /* Quit if the file was already saved. */ - if (E.dirty && quit_times) { - editorSetStatusMessage("WARNING!!! File has unsaved changes. " - "Press Ctrl-Q %d more times to quit.", quit_times); - quit_times--; - return; - } - exit(0); - break; - case CTRL_S: /* Ctrl-s */ - editorSave(); - break; - case CTRL_F: - editorFind(fd); - break; - case DEL_KEY: - editorInvDelChar(); - break; - case BACKSPACE: /* Backspace */ - case CTRL_H: /* Ctrl-h */ - editorDelChar(); - break; - case PAGE_UP: - case PAGE_DOWN: - if (c == PAGE_UP && E.cy != 0) - E.cy = 0; - else if (c == PAGE_DOWN && E.cy != E.screenrows-1) - E.cy = E.screenrows-1; - { - int times = E.screenrows; - while(times--) - editorMoveCursor(c == PAGE_UP ? ARROW_UP: - ARROW_DOWN); - } - break; - - case ARROW_UP: - case ARROW_DOWN: - case ARROW_LEFT: - case ARROW_RIGHT: - editorMoveCursor(c); - break; - case CTRL_L: /* ctrl+l, clear screen */ - /* Just refresht the line as side effect. */ - break; - case ESC: - /* Nothing to do for ESC in this mode. */ - break; - default: - editorInsertChar(c); - break; - } - - quit_times = KILO_QUIT_TIMES; /* Reset it to the original value. */ + /* When the file is modified, requires Ctrl-q to be pressed N times + * before actually quitting. */ + static int quit_times = KILO_QUIT_TIMES; + + int c = editorReadKey(fd); + switch(c) { + case ENTER: /* Enter */ + editorInsertNewline(); + break; + case CTRL_C: /* Ctrl-c */ + /* We ignore ctrl-c, it can't be so simple to lose the changes + * to the edited file. */ + break; + case CTRL_Q: /* Ctrl-q */ + /* Quit if the file was already saved. */ + if (E.dirty && quit_times) { + editorSetStatusMessage("WARNING!!! File has unsaved changes. " + "Press Ctrl-Q %d more times to quit.", quit_times); + quit_times--; + return; + } + exit(0); + break; + case CTRL_S: /* Ctrl-s */ + editorSave(); + break; + case CTRL_F: + editorFind(fd); + break; + case DEL_KEY: + editorInvDelChar(); + break; + case BACKSPACE: /* Backspace */ + case CTRL_H: /* Ctrl-h */ + editorDelChar(); + break; + case PAGE_UP: + case PAGE_DOWN: + if (c == PAGE_UP && E.cy != 0) + E.cy = 0; + else if (c == PAGE_DOWN && E.cy != E.screenrows-1) + E.cy = E.screenrows-1; + { + int times = E.screenrows; + while(times--) + editorMoveCursor(c == PAGE_UP ? ARROW_UP: + ARROW_DOWN); + } + break; + + case ARROW_UP: + case ARROW_DOWN: + case ARROW_LEFT: + case ARROW_RIGHT: + editorMoveCursor(c); + break; + case CTRL_L: /* ctrl+l, clear screen */ + /* Just refresht the line as side effect. */ + break; + case ESC: + /* Nothing to do for ESC in this mode. */ + break; + default: + editorInsertChar(c); + break; + } + + quit_times = KILO_QUIT_TIMES; /* Reset it to the original value. */ } int editorFileWasModified(void) { - return E.dirty; + return E.dirty; } void updateWindowSize(void) { - if (getWindowSize(STDIN_FILENO,STDOUT_FILENO, - &E.screenrows,&E.screencols) == -1) { - perror("Unable to query the screen for size (columns / rows)"); - exit(1); - } - E.screenrows -= 2; /* Get room for status bar. */ + if (getWindowSize(STDIN_FILENO,STDOUT_FILENO, + &E.screenrows,&E.screencols) == -1) { + perror("Unable to query the screen for size (columns / rows)"); + exit(1); + } + E.screenrows -= 2; /* Get room for status bar. */ } void handleSigWinCh(int unused __attribute__((unused))) { - updateWindowSize(); - if (E.cy > E.screenrows) E.cy = E.screenrows - 1; - if (E.cx > E.screencols) E.cx = E.screencols - 1; - editorRefreshScreen(); + updateWindowSize(); + if (E.cy > E.screenrows) E.cy = E.screenrows - 1; + if (E.cx > E.screencols) E.cx = E.screencols - 1; + editorRefreshScreen(); } void initEditor(void) { - E.cx = 0; - E.cy = 0; - E.rowoff = 0; - E.coloff = 0; - E.numrows = 0; - E.row = NULL; - E.dirty = 0; - E.filename = NULL; - E.syntax = NULL; - updateWindowSize(); - signal(SIGWINCH, handleSigWinCh); + E.cx = 0; + E.cy = 0; + E.rowoff = 0; + E.coloff = 0; + E.numrows = 0; + E.row = NULL; + E.dirty = 0; + E.filename = NULL; + E.syntax = NULL; + updateWindowSize(); + signal(SIGWINCH, handleSigWinCh); } int main(int argc, char **argv) { - if (argc != 2) { - fprintf(stderr,"Usage: kilo \n"); - exit(1); - } - - initEditor(); - editorSelectSyntaxHighlight(argv[1]); - editorOpen(argv[1]); - enableRawMode(STDIN_FILENO); - editorSetStatusMessage( - "HELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find"); - while(1) { - editorRefreshScreen(); - editorProcessKeypress(STDIN_FILENO); - } - return 0; + if (argc != 2) { + fprintf(stderr,"Usage: kilo \n"); + exit(1); + } + + initEditor(); + editorSelectSyntaxHighlight(argv[1]); + editorOpen(argv[1]); + enableRawMode(STDIN_FILENO); + editorSetStatusMessage( + "HELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find"); + while(1) { + editorRefreshScreen(); + editorProcessKeypress(STDIN_FILENO); + } + return 0; } From 87d6b216642dfe15ee0cfcefc49e550c9b37cf41 Mon Sep 17 00:00:00 2001 From: Martin Date: Sun, 25 Jul 2021 00:24:08 -0300 Subject: [PATCH 4/8] include and define highlight --- kilo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kilo.c b/kilo.c index d25251a6..ae32c10f 100644 --- a/kilo.c +++ b/kilo.c @@ -170,6 +170,7 @@ char *C_HL_keywords[] = { "auto","break","case","continue","default","do","else","enum", "extern","for","goto","if","register","return","sizeof","static", "struct","switch","typedef","union","volatile","while","NULL", + "#include", "#define", /* C++ Keywords */ "alignas","alignof","and","and_eq","asm","bitand","bitor","class", From 0a8ffb3adc664742f71832b8833ba7c88f6f46cd Mon Sep 17 00:00:00 2001 From: Martin Date: Sun, 25 Jul 2021 00:24:56 -0300 Subject: [PATCH 5/8] HOME KEY and END KEY implementation --- kilo.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kilo.c b/kilo.c index ae32c10f..ff7a0fd8 100644 --- a/kilo.c +++ b/kilo.c @@ -1243,6 +1243,12 @@ void editorProcessKeypress(int fd) { case CTRL_H: /* Ctrl-h */ editorDelChar(); break; + case END_KEY: + E.cx = E.row[E.rowoff+E.cy].size; + break; + case HOME_KEY: + E.cx = 0; + break; case PAGE_UP: case PAGE_DOWN: if (c == PAGE_UP && E.cy != 0) From 1bb82ea24995ad02118bf1aa88ef56c740e08c32 Mon Sep 17 00:00:00 2001 From: M4rtin23 Date: Wed, 28 Jul 2021 23:44:17 -0300 Subject: [PATCH 6/8] Now shows column number --- kilo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kilo.c b/kilo.c index ff7a0fd8..b49e993d 100644 --- a/kilo.c +++ b/kilo.c @@ -978,7 +978,7 @@ void editorRefreshScreen(void) { int len = snprintf(status, sizeof(status), "%.20s - %d lines %s", E.filename, E.numrows, E.dirty ? "(modified)" : ""); int rlen = snprintf(rstatus, sizeof(rstatus), - "%d/%d",E.rowoff+E.cy+1,E.numrows); + "c: %d/%d l: %d/%d",E.coloff+E.cx,E.row[E.rowoff+E.cy].size,E.rowoff+E.cy+1,E.numrows); if (len > E.screencols) len = E.screencols; abAppend(&ab,status,len); while(len < E.screencols) { From 1fdd34082367f354a72e906825605aad136cad53 Mon Sep 17 00:00:00 2001 From: martin Date: Mon, 12 Dec 2022 16:45:22 -0300 Subject: [PATCH 7/8] Back to spaces --- kilo.c | 2006 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 1003 insertions(+), 1003 deletions(-) diff --git a/kilo.c b/kilo.c index b49e993d..1c1de603 100644 --- a/kilo.c +++ b/kilo.c @@ -71,73 +71,73 @@ #define TAB_SIZE 4 struct editorSyntax { - char **filematch; - char **keywords; - char singleline_comment_start[2]; - char multiline_comment_start[3]; - char multiline_comment_end[3]; - int flags; + char **filematch; + char **keywords; + char singleline_comment_start[2]; + char multiline_comment_start[3]; + char multiline_comment_end[3]; + int flags; }; /* This structure represents a single line of the file we are editing. */ typedef struct erow { - int idx; /* Row index in the file, zero-based. */ - int size; /* Size of the row, excluding the null term. */ - int rsize; /* Size of the rendered row. */ - char *chars; /* Row content. */ - char *render; /* Row content "rendered" for screen (for TABs). */ - unsigned char *hl; /* Syntax highlight type for each character in render.*/ - int hl_oc; /* Row had open comment at end in last syntax highlight - check. */ + int idx; /* Row index in the file, zero-based. */ + int size; /* Size of the row, excluding the null term. */ + int rsize; /* Size of the rendered row. */ + char *chars; /* Row content. */ + char *render; /* Row content "rendered" for screen (for TABs). */ + unsigned char *hl; /* Syntax highlight type for each character in render.*/ + int hl_oc; /* Row had open comment at end in last syntax highlight + check. */ } erow; typedef struct hlcolor { - int r,g,b; + int r,g,b; } hlcolor; struct editorConfig { - int cx,cy; /* Cursor x and y position in characters */ - int rowoff; /* Offset of row displayed. */ - int coloff; /* Offset of column displayed. */ - int screenrows; /* Number of rows that we can show */ - int screencols; /* Number of cols that we can show */ - int numrows; /* Number of rows */ - int rawmode; /* Is terminal raw mode enabled? */ - erow *row; /* Rows */ - int dirty; /* File modified but not saved. */ - char *filename; /* Currently open filename */ - char statusmsg[80]; - time_t statusmsg_time; - struct editorSyntax *syntax; /* Current syntax highlight, or NULL. */ + int cx,cy; /* Cursor x and y position in characters */ + int rowoff; /* Offset of row displayed. */ + int coloff; /* Offset of column displayed. */ + int screenrows; /* Number of rows that we can show */ + int screencols; /* Number of cols that we can show */ + int numrows; /* Number of rows */ + int rawmode; /* Is terminal raw mode enabled? */ + erow *row; /* Rows */ + int dirty; /* File modified but not saved. */ + char *filename; /* Currently open filename */ + char statusmsg[80]; + time_t statusmsg_time; + struct editorSyntax *syntax; /* Current syntax highlight, or NULL. */ }; static struct editorConfig E; enum KEY_ACTION{ - KEY_NULL = 0, /* NULL */ - CTRL_C = 3, /* Ctrl-c */ - CTRL_D = 4, /* Ctrl-d */ - CTRL_F = 6, /* Ctrl-f */ - CTRL_H = 8, /* Ctrl-h */ - TAB = 9, /* Tab */ - CTRL_L = 12, /* Ctrl+l */ - ENTER = 13, /* Enter */ - CTRL_Q = 17, /* Ctrl-q */ - CTRL_S = 19, /* Ctrl-s */ - CTRL_U = 21, /* Ctrl-u */ - ESC = 27, /* Escape */ - BACKSPACE = 127, /* Backspace */ - /* The following are just soft codes, not really reported by the - * terminal directly. */ - ARROW_LEFT = 1000, - ARROW_RIGHT, - ARROW_UP, - ARROW_DOWN, - DEL_KEY, - HOME_KEY, - END_KEY, - PAGE_UP, - PAGE_DOWN + KEY_NULL = 0, /* NULL */ + CTRL_C = 3, /* Ctrl-c */ + CTRL_D = 4, /* Ctrl-d */ + CTRL_F = 6, /* Ctrl-f */ + CTRL_H = 8, /* Ctrl-h */ + TAB = 9, /* Tab */ + CTRL_L = 12, /* Ctrl+l */ + ENTER = 13, /* Enter */ + CTRL_Q = 17, /* Ctrl-q */ + CTRL_S = 19, /* Ctrl-s */ + CTRL_U = 21, /* Ctrl-u */ + ESC = 27, /* Escape */ + BACKSPACE = 127, /* Backspace */ + /* The following are just soft codes, not really reported by the + * terminal directly. */ + ARROW_LEFT = 1000, + ARROW_RIGHT, + ARROW_UP, + ARROW_DOWN, + DEL_KEY, + HOME_KEY, + END_KEY, + PAGE_UP, + PAGE_DOWN }; void editorSetStatusMessage(const char *fmt, ...); @@ -170,7 +170,7 @@ char *C_HL_keywords[] = { "auto","break","case","continue","default","do","else","enum", "extern","for","goto","if","register","return","sizeof","static", "struct","switch","typedef","union","volatile","while","NULL", - "#include", "#define", + "#include", "#define", /* C++ Keywords */ "alignas","alignof","and","and_eq","asm","bitand","bitor","class", @@ -182,20 +182,20 @@ char *C_HL_keywords[] = { "typeid","typename","virtual","xor","xor_eq", /* C types */ - "int|","long|","double|","float|","char|","unsigned|","signed|", - "void|","short|","auto|","const|","bool|",NULL + "int|","long|","double|","float|","char|","unsigned|","signed|", + "void|","short|","auto|","const|","bool|",NULL }; /* Here we define an array of syntax highlights by extensions, keywords, * comments delimiters and flags. */ struct editorSyntax HLDB[] = { - { - /* C / C++ */ - C_HL_extensions, - C_HL_keywords, - "//","/*","*/", - HL_HIGHLIGHT_STRINGS | HL_HIGHLIGHT_NUMBERS - } + { + /* C / C++ */ + C_HL_extensions, + C_HL_keywords, + "//","/*","*/", + HL_HIGHLIGHT_STRINGS | HL_HIGHLIGHT_NUMBERS + } }; #define HLDB_ENTRIES (sizeof(HLDB)/sizeof(HLDB[0])) @@ -205,432 +205,432 @@ struct editorSyntax HLDB[] = { static struct termios orig_termios; /* In order to restore at exit.*/ void disableRawMode(int fd) { - /* Don't even check the return value as it's too late. */ - if (E.rawmode) { - tcsetattr(fd,TCSAFLUSH,&orig_termios); - E.rawmode = 0; - } + /* Don't even check the return value as it's too late. */ + if (E.rawmode) { + tcsetattr(fd,TCSAFLUSH,&orig_termios); + E.rawmode = 0; + } } /* Called at exit to avoid remaining in raw mode. */ void editorAtExit(void) { - disableRawMode(STDIN_FILENO); + disableRawMode(STDIN_FILENO); } /* Raw mode: 1960 magic shit. */ int enableRawMode(int fd) { - struct termios raw; - - if (E.rawmode) return 0; /* Already enabled. */ - if (!isatty(STDIN_FILENO)) goto fatal; - atexit(editorAtExit); - if (tcgetattr(fd,&orig_termios) == -1) goto fatal; - - raw = orig_termios; /* modify the original mode */ - /* input modes: no break, no CR to NL, no parity check, no strip char, - * no start/stop output control. */ - raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); - /* output modes - disable post processing */ - raw.c_oflag &= ~(OPOST); - /* control modes - set 8 bit chars */ - raw.c_cflag |= (CS8); - /* local modes - choing off, canonical off, no extended functions, - * no signal chars (^Z,^C) */ - raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); - /* control chars - set return condition: min number of bytes and timer. */ - raw.c_cc[VMIN] = 0; /* Return each byte, or zero for timeout. */ - raw.c_cc[VTIME] = 1; /* 100 ms timeout (unit is tens of second). */ - - /* put terminal in raw mode after flushing */ - if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal; - E.rawmode = 1; - return 0; + struct termios raw; + + if (E.rawmode) return 0; /* Already enabled. */ + if (!isatty(STDIN_FILENO)) goto fatal; + atexit(editorAtExit); + if (tcgetattr(fd,&orig_termios) == -1) goto fatal; + + raw = orig_termios; /* modify the original mode */ + /* input modes: no break, no CR to NL, no parity check, no strip char, + * no start/stop output control. */ + raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + /* output modes - disable post processing */ + raw.c_oflag &= ~(OPOST); + /* control modes - set 8 bit chars */ + raw.c_cflag |= (CS8); + /* local modes - choing off, canonical off, no extended functions, + * no signal chars (^Z,^C) */ + raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + /* control chars - set return condition: min number of bytes and timer. */ + raw.c_cc[VMIN] = 0; /* Return each byte, or zero for timeout. */ + raw.c_cc[VTIME] = 1; /* 100 ms timeout (unit is tens of second). */ + + /* put terminal in raw mode after flushing */ + if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal; + E.rawmode = 1; + return 0; fatal: - errno = ENOTTY; - return -1; + errno = ENOTTY; + return -1; } /* Read a key from the terminal put in raw mode, trying to handle * escape sequences. */ int editorReadKey(int fd) { - int nread; - char c, seq[3]; - while ((nread = read(fd,&c,1)) == 0); - if (nread == -1) exit(1); - - while(1) { - switch(c) { - case ESC: /* escape sequence */ - /* If this is just an ESC, we'll timeout here. */ - if (read(fd,seq,1) == 0) return ESC; - if (read(fd,seq+1,1) == 0) return ESC; - - /* ESC [ sequences. */ - if (seq[0] == '[') { - if (seq[1] >= '0' && seq[1] <= '9') { - /* Extended escape, read additional byte. */ - if (read(fd,seq+2,1) == 0) return ESC; - if (seq[2] == '~') { - switch(seq[1]) { - case '3': return DEL_KEY; - case '5': return PAGE_UP; - case '6': return PAGE_DOWN; - } - } - } else { - switch(seq[1]) { - case 'A': return ARROW_UP; - case 'B': return ARROW_DOWN; - case 'C': return ARROW_RIGHT; - case 'D': return ARROW_LEFT; - case 'H': return HOME_KEY; - case 'F': return END_KEY; - } - } - } - - /* ESC O sequences. */ - else if (seq[0] == 'O') { - switch(seq[1]) { - case 'H': return HOME_KEY; - case 'F': return END_KEY; - } - } - break; - default: - return c; - } - } + int nread; + char c, seq[3]; + while ((nread = read(fd,&c,1)) == 0); + if (nread == -1) exit(1); + + while(1) { + switch(c) { + case ESC: /* escape sequence */ + /* If this is just an ESC, we'll timeout here. */ + if (read(fd,seq,1) == 0) return ESC; + if (read(fd,seq+1,1) == 0) return ESC; + + /* ESC [ sequences. */ + if (seq[0] == '[') { + if (seq[1] >= '0' && seq[1] <= '9') { + /* Extended escape, read additional byte. */ + if (read(fd,seq+2,1) == 0) return ESC; + if (seq[2] == '~') { + switch(seq[1]) { + case '3': return DEL_KEY; + case '5': return PAGE_UP; + case '6': return PAGE_DOWN; + } + } + } else { + switch(seq[1]) { + case 'A': return ARROW_UP; + case 'B': return ARROW_DOWN; + case 'C': return ARROW_RIGHT; + case 'D': return ARROW_LEFT; + case 'H': return HOME_KEY; + case 'F': return END_KEY; + } + } + } + + /* ESC O sequences. */ + else if (seq[0] == 'O') { + switch(seq[1]) { + case 'H': return HOME_KEY; + case 'F': return END_KEY; + } + } + break; + default: + return c; + } + } } /* Use the ESC [6n escape sequence to query the horizontal cursor position * and return it. On error -1 is returned, on success the position of the * cursor is stored at *rows and *cols and 0 is returned. */ int getCursorPosition(int ifd, int ofd, int *rows, int *cols) { - char buf[32]; - unsigned int i = 0; - - /* Report cursor location */ - if (write(ofd, "\x1b[6n", 4) != 4) return -1; - - /* Read the response: ESC [ rows ; cols R */ - while (i < sizeof(buf)-1) { - if (read(ifd,buf+i,1) != 1) break; - if (buf[i] == 'R') break; - i++; - } - buf[i] = '\0'; - - /* Parse it. */ - if (buf[0] != ESC || buf[1] != '[') return -1; - if (sscanf(buf+2,"%d;%d",rows,cols) != 2) return -1; - return 0; + char buf[32]; + unsigned int i = 0; + + /* Report cursor location */ + if (write(ofd, "\x1b[6n", 4) != 4) return -1; + + /* Read the response: ESC [ rows ; cols R */ + while (i < sizeof(buf)-1) { + if (read(ifd,buf+i,1) != 1) break; + if (buf[i] == 'R') break; + i++; + } + buf[i] = '\0'; + + /* Parse it. */ + if (buf[0] != ESC || buf[1] != '[') return -1; + if (sscanf(buf+2,"%d;%d",rows,cols) != 2) return -1; + return 0; } /* Try to get the number of columns in the current terminal. If the ioctl() * call fails the function will try to query the terminal itself. * Returns 0 on success, -1 on error. */ int getWindowSize(int ifd, int ofd, int *rows, int *cols) { - struct winsize ws; - - if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { - /* ioctl() failed. Try to query the terminal itself. */ - int orig_row, orig_col, retval; - - /* Get the initial position so we can restore it later. */ - retval = getCursorPosition(ifd,ofd,&orig_row,&orig_col); - if (retval == -1) goto failed; - - /* Go to right/bottom margin and get position. */ - if (write(ofd,"\x1b[999C\x1b[999B",12) != 12) goto failed; - retval = getCursorPosition(ifd,ofd,rows,cols); - if (retval == -1) goto failed; - - /* Restore position. */ - char seq[32]; - snprintf(seq,32,"\x1b[%d;%dH",orig_row,orig_col); - if (write(ofd,seq,strlen(seq)) == -1) { - /* Can't recover... */ - } - return 0; - } else { - *cols = ws.ws_col; - *rows = ws.ws_row; - return 0; - } + struct winsize ws; + + if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { + /* ioctl() failed. Try to query the terminal itself. */ + int orig_row, orig_col, retval; + + /* Get the initial position so we can restore it later. */ + retval = getCursorPosition(ifd,ofd,&orig_row,&orig_col); + if (retval == -1) goto failed; + + /* Go to right/bottom margin and get position. */ + if (write(ofd,"\x1b[999C\x1b[999B",12) != 12) goto failed; + retval = getCursorPosition(ifd,ofd,rows,cols); + if (retval == -1) goto failed; + + /* Restore position. */ + char seq[32]; + snprintf(seq,32,"\x1b[%d;%dH",orig_row,orig_col); + if (write(ofd,seq,strlen(seq)) == -1) { + /* Can't recover... */ + } + return 0; + } else { + *cols = ws.ws_col; + *rows = ws.ws_row; + return 0; + } failed: - return -1; + return -1; } /* ====================== Syntax highlight color scheme ==================== */ int is_separator(int c) { - return c == '\0' || isspace(c) || strchr(",.()+-/*=~%[];",c) != NULL; + return c == '\0' || isspace(c) || strchr(",.()+-/*=~%[];",c) != NULL; } /* Return true if the specified row last char is part of a multi line comment * that starts at this row or at one before, and does not end at the end * of the row but spawns to the next row. */ int editorRowHasOpenComment(erow *row) { - if (row->hl && row->rsize && row->hl[row->rsize-1] == HL_MLCOMMENT && - (row->rsize < 2 || (row->render[row->rsize-2] != '*' || - row->render[row->rsize-1] != '/'))) return 1; - return 0; + if (row->hl && row->rsize && row->hl[row->rsize-1] == HL_MLCOMMENT && + (row->rsize < 2 || (row->render[row->rsize-2] != '*' || + row->render[row->rsize-1] != '/'))) return 1; + return 0; } /* Set every byte of row->hl (that corresponds to every character in the line) * to the right syntax highlight type (HL_* defines). */ void editorUpdateSyntax(erow *row) { - row->hl = realloc(row->hl,row->rsize); - memset(row->hl,HL_NORMAL,row->rsize); - - if (E.syntax == NULL) return; /* No syntax, everything is HL_NORMAL. */ - - int i, prev_sep, in_string, in_comment; - char *p; - char **keywords = E.syntax->keywords; - char *scs = E.syntax->singleline_comment_start; - char *mcs = E.syntax->multiline_comment_start; - char *mce = E.syntax->multiline_comment_end; - - /* Point to the first non-space char. */ - p = row->render; - i = 0; /* Current char offset */ - while(*p && isspace(*p)) { - p++; - i++; - } - prev_sep = 1; /* Tell the parser if 'i' points to start of word. */ - in_string = 0; /* Are we inside "" or '' ? */ - in_comment = 0; /* Are we inside multi-line comment? */ - - /* If the previous line has an open comment, this line starts - * with an open comment state. */ - if (row->idx > 0 && editorRowHasOpenComment(&E.row[row->idx-1])) - in_comment = 1; - - while(*p) { - /* Handle // comments. */ - if (prev_sep && *p == scs[0] && *(p+1) == scs[1]) { - /* From here to end is a comment */ - memset(row->hl+i,HL_COMMENT,row->size-i); - return; - } - - /* Handle multi line comments. */ - if (in_comment) { - row->hl[i] = HL_MLCOMMENT; - if (*p == mce[0] && *(p+1) == mce[1]) { - row->hl[i+1] = HL_MLCOMMENT; - p += 2; i += 2; - in_comment = 0; - prev_sep = 1; - continue; - } else { - prev_sep = 0; - p++; i++; - continue; - } - } else if (*p == mcs[0] && *(p+1) == mcs[1]) { - row->hl[i] = HL_MLCOMMENT; - row->hl[i+1] = HL_MLCOMMENT; - p += 2; i += 2; - in_comment = 1; - prev_sep = 0; - continue; - } - - /* Handle "" and '' */ - if (in_string) { - row->hl[i] = HL_STRING; - if (*p == '\\') { - row->hl[i+1] = HL_STRING; - p += 2; i += 2; - prev_sep = 0; - continue; - } - if (*p == in_string) in_string = 0; - p++; i++; - continue; - } else { - if (*p == '"' || *p == '\'') { - in_string = *p; - row->hl[i] = HL_STRING; - p++; i++; - prev_sep = 0; - continue; - } - } - - /* Handle non printable chars. */ - if (!isprint(*p)) { - row->hl[i] = HL_NONPRINT; - p++; i++; - prev_sep = 0; - continue; - } - - /* Handle numbers */ - if ((isdigit(*p) && (prev_sep || row->hl[i-1] == HL_NUMBER)) || - (*p == '.' && i >0 && row->hl[i-1] == HL_NUMBER)) { - row->hl[i] = HL_NUMBER; - p++; i++; - prev_sep = 0; - continue; - } - - /* Handle keywords and lib calls */ - if (prev_sep) { - int j; - for (j = 0; keywords[j]; j++) { - int klen = strlen(keywords[j]); - int kw2 = keywords[j][klen-1] == '|'; - if (kw2) klen--; - - if (!memcmp(p,keywords[j],klen) && - is_separator(*(p+klen))) - { - /* Keyword */ - memset(row->hl+i,kw2 ? HL_KEYWORD2 : HL_KEYWORD1,klen); - p += klen; - i += klen; - break; - } - } - if (keywords[j] != NULL) { - prev_sep = 0; - continue; /* We had a keyword match */ - } - } - - /* Not special chars */ - prev_sep = is_separator(*p); - p++; i++; - } - - /* Propagate syntax change to the next row if the open commen - * state changed. This may recursively affect all the following rows - * in the file. */ - int oc = editorRowHasOpenComment(row); - if (row->hl_oc != oc && row->idx+1 < E.numrows) - editorUpdateSyntax(&E.row[row->idx+1]); - row->hl_oc = oc; + row->hl = realloc(row->hl,row->rsize); + memset(row->hl,HL_NORMAL,row->rsize); + + if (E.syntax == NULL) return; /* No syntax, everything is HL_NORMAL. */ + + int i, prev_sep, in_string, in_comment; + char *p; + char **keywords = E.syntax->keywords; + char *scs = E.syntax->singleline_comment_start; + char *mcs = E.syntax->multiline_comment_start; + char *mce = E.syntax->multiline_comment_end; + + /* Point to the first non-space char. */ + p = row->render; + i = 0; /* Current char offset */ + while(*p && isspace(*p)) { + p++; + i++; + } + prev_sep = 1; /* Tell the parser if 'i' points to start of word. */ + in_string = 0; /* Are we inside "" or '' ? */ + in_comment = 0; /* Are we inside multi-line comment? */ + + /* If the previous line has an open comment, this line starts + * with an open comment state. */ + if (row->idx > 0 && editorRowHasOpenComment(&E.row[row->idx-1])) + in_comment = 1; + + while(*p) { + /* Handle // comments. */ + if (prev_sep && *p == scs[0] && *(p+1) == scs[1]) { + /* From here to end is a comment */ + memset(row->hl+i,HL_COMMENT,row->size-i); + return; + } + + /* Handle multi line comments. */ + if (in_comment) { + row->hl[i] = HL_MLCOMMENT; + if (*p == mce[0] && *(p+1) == mce[1]) { + row->hl[i+1] = HL_MLCOMMENT; + p += 2; i += 2; + in_comment = 0; + prev_sep = 1; + continue; + } else { + prev_sep = 0; + p++; i++; + continue; + } + } else if (*p == mcs[0] && *(p+1) == mcs[1]) { + row->hl[i] = HL_MLCOMMENT; + row->hl[i+1] = HL_MLCOMMENT; + p += 2; i += 2; + in_comment = 1; + prev_sep = 0; + continue; + } + + /* Handle "" and '' */ + if (in_string) { + row->hl[i] = HL_STRING; + if (*p == '\\') { + row->hl[i+1] = HL_STRING; + p += 2; i += 2; + prev_sep = 0; + continue; + } + if (*p == in_string) in_string = 0; + p++; i++; + continue; + } else { + if (*p == '"' || *p == '\'') { + in_string = *p; + row->hl[i] = HL_STRING; + p++; i++; + prev_sep = 0; + continue; + } + } + + /* Handle non printable chars. */ + if (!isprint(*p)) { + row->hl[i] = HL_NONPRINT; + p++; i++; + prev_sep = 0; + continue; + } + + /* Handle numbers */ + if ((isdigit(*p) && (prev_sep || row->hl[i-1] == HL_NUMBER)) || + (*p == '.' && i >0 && row->hl[i-1] == HL_NUMBER)) { + row->hl[i] = HL_NUMBER; + p++; i++; + prev_sep = 0; + continue; + } + + /* Handle keywords and lib calls */ + if (prev_sep) { + int j; + for (j = 0; keywords[j]; j++) { + int klen = strlen(keywords[j]); + int kw2 = keywords[j][klen-1] == '|'; + if (kw2) klen--; + + if (!memcmp(p,keywords[j],klen) && + is_separator(*(p+klen))) + { + /* Keyword */ + memset(row->hl+i,kw2 ? HL_KEYWORD2 : HL_KEYWORD1,klen); + p += klen; + i += klen; + break; + } + } + if (keywords[j] != NULL) { + prev_sep = 0; + continue; /* We had a keyword match */ + } + } + + /* Not special chars */ + prev_sep = is_separator(*p); + p++; i++; + } + + /* Propagate syntax change to the next row if the open commen + * state changed. This may recursively affect all the following rows + * in the file. */ + int oc = editorRowHasOpenComment(row); + if (row->hl_oc != oc && row->idx+1 < E.numrows) + editorUpdateSyntax(&E.row[row->idx+1]); + row->hl_oc = oc; } /* Maps syntax highlight token types to terminal colors. */ int editorSyntaxToColor(int hl) { - switch(hl) { - case HL_COMMENT: - case HL_MLCOMMENT: return 36; /* cyan */ - case HL_KEYWORD1: return 33; /* yellow */ - case HL_KEYWORD2: return 32; /* green */ - case HL_STRING: return 35; /* magenta */ - case HL_NUMBER: return 31; /* red */ - case HL_MATCH: return 34; /* blu */ - default: return 37; /* white */ - } + switch(hl) { + case HL_COMMENT: + case HL_MLCOMMENT: return 36; /* cyan */ + case HL_KEYWORD1: return 33; /* yellow */ + case HL_KEYWORD2: return 32; /* green */ + case HL_STRING: return 35; /* magenta */ + case HL_NUMBER: return 31; /* red */ + case HL_MATCH: return 34; /* blu */ + default: return 37; /* white */ + } } /* Select the syntax highlight scheme depending on the filename, * setting it in the global state E.syntax. */ void editorSelectSyntaxHighlight(char *filename) { - for (unsigned int j = 0; j < HLDB_ENTRIES; j++) { - struct editorSyntax *s = HLDB+j; - unsigned int i = 0; - while(s->filematch[i]) { - char *p; - int patlen = strlen(s->filematch[i]); - if ((p = strstr(filename,s->filematch[i])) != NULL) { - if (s->filematch[i][0] != '.' || p[patlen] == '\0') { - E.syntax = s; - return; - } - } - i++; - } - } + for (unsigned int j = 0; j < HLDB_ENTRIES; j++) { + struct editorSyntax *s = HLDB+j; + unsigned int i = 0; + while(s->filematch[i]) { + char *p; + int patlen = strlen(s->filematch[i]); + if ((p = strstr(filename,s->filematch[i])) != NULL) { + if (s->filematch[i][0] != '.' || p[patlen] == '\0') { + E.syntax = s; + return; + } + } + i++; + } + } } /* ======================= Editor rows implementation ======================= */ /* Update the rendered version and the syntax highlight of a row. */ void editorUpdateRow(erow *row) { - unsigned int tabs = 0, nonprint = 0; - int j, idx; + unsigned int tabs = 0, nonprint = 0; + int j, idx; /* Create a version of the row we can directly print on the screen, - * respecting tabs, substituting non printable characters with '?'. */ - free(row->render); - for (j = 0; j < row->size; j++) - if (row->chars[j] == TAB) tabs++; - - unsigned long long allocsize = - (unsigned long long) row->size + tabs*TAB_SIZE + nonprint*9 + 1; - if (allocsize > UINT32_MAX) { - printf("Some line of the edited file is too long for kilo\n"); - exit(1); - } - - row->render = malloc(row->size + tabs*TAB_SIZE + nonprint*9 + 1); - idx = 0; - for (j = 0; j < row->size; j++) { - if (row->chars[j] == TAB) { - row->render[idx++] = ' '; - while((idx) % TAB_SIZE != 0) row->render[idx++] = ' '; - } else { - row->render[idx++] = row->chars[j]; - } - } - row->rsize = idx; - row->render[idx] = '\0'; - - /* Update the syntax highlighting attributes of the row. */ - editorUpdateSyntax(row); + * respecting tabs, substituting non printable characters with '?'. */ + free(row->render); + for (j = 0; j < row->size; j++) + if (row->chars[j] == TAB) tabs++; + + unsigned long long allocsize = + (unsigned long long) row->size + tabs*TAB_SIZE + nonprint*9 + 1; + if (allocsize > UINT32_MAX) { + printf("Some line of the edited file is too long for kilo\n"); + exit(1); + } + + row->render = malloc(row->size + tabs*TAB_SIZE + nonprint*9 + 1); + idx = 0; + for (j = 0; j < row->size; j++) { + if (row->chars[j] == TAB) { + row->render[idx++] = ' '; + while((idx) % TAB_SIZE != 0) row->render[idx++] = ' '; + } else { + row->render[idx++] = row->chars[j]; + } + } + row->rsize = idx; + row->render[idx] = '\0'; + + /* Update the syntax highlighting attributes of the row. */ + editorUpdateSyntax(row); } /* Insert a row at the specified position, shifting the other rows on the bottom * if required. */ void editorInsertRow(int at, char *s, size_t len) { - if (at > E.numrows) return; - E.row = realloc(E.row,sizeof(erow)*(E.numrows+1)); - if (at != E.numrows) { - memmove(E.row+at+1,E.row+at,sizeof(E.row[0])*(E.numrows-at)); - for (int j = at+1; j <= E.numrows; j++) E.row[j].idx++; - } - E.row[at].size = len; - E.row[at].chars = malloc(len+1); - memcpy(E.row[at].chars,s,len+1); - E.row[at].hl = NULL; - E.row[at].hl_oc = 0; - E.row[at].render = NULL; - E.row[at].rsize = 0; - E.row[at].idx = at; - editorUpdateRow(E.row+at); - E.numrows++; - E.dirty++; + if (at > E.numrows) return; + E.row = realloc(E.row,sizeof(erow)*(E.numrows+1)); + if (at != E.numrows) { + memmove(E.row+at+1,E.row+at,sizeof(E.row[0])*(E.numrows-at)); + for (int j = at+1; j <= E.numrows; j++) E.row[j].idx++; + } + E.row[at].size = len; + E.row[at].chars = malloc(len+1); + memcpy(E.row[at].chars,s,len+1); + E.row[at].hl = NULL; + E.row[at].hl_oc = 0; + E.row[at].render = NULL; + E.row[at].rsize = 0; + E.row[at].idx = at; + editorUpdateRow(E.row+at); + E.numrows++; + E.dirty++; } /* Free row's heap allocated stuff. */ void editorFreeRow(erow *row) { - free(row->render); - free(row->chars); - free(row->hl); + free(row->render); + free(row->chars); + free(row->hl); } /* Remove the row at the specified position, shifting the remainign on the * top. */ void editorDelRow(int at) { - erow *row; - - if (at >= E.numrows) return; - row = E.row+at; - editorFreeRow(row); - memmove(E.row+at,E.row+at+1,sizeof(E.row[0])*(E.numrows-at-1)); - for (int j = at; j < E.numrows-1; j++) E.row[j].idx++; - E.numrows--; - E.dirty++; + erow *row; + + if (at >= E.numrows) return; + row = E.row+at; + editorFreeRow(row); + memmove(E.row+at,E.row+at+1,sizeof(E.row[0])*(E.numrows-at-1)); + for (int j = at; j < E.numrows-1; j++) E.row[j].idx++; + E.numrows--; + E.dirty++; } /* Turn the editor rows into a single heap-allocated string. @@ -638,238 +638,238 @@ void editorDelRow(int at) { * integer pointed by 'buflen' with the size of the string, escluding * the final nulterm. */ char *editorRowsToString(int *buflen) { - char *buf = NULL, *p; - int totlen = 0; - int j; - - /* Compute count of bytes */ - for (j = 0; j < E.numrows; j++) - totlen += E.row[j].size+1; /* +1 is for "\n" at end of every row */ - *buflen = totlen; - totlen++; /* Also make space for nulterm */ - - p = buf = malloc(totlen); - for (j = 0; j < E.numrows; j++) { - memcpy(p,E.row[j].chars,E.row[j].size); - p += E.row[j].size; - *p = '\n'; - p++; - } - *p = '\0'; - return buf; + char *buf = NULL, *p; + int totlen = 0; + int j; + + /* Compute count of bytes */ + for (j = 0; j < E.numrows; j++) + totlen += E.row[j].size+1; /* +1 is for "\n" at end of every row */ + *buflen = totlen; + totlen++; /* Also make space for nulterm */ + + p = buf = malloc(totlen); + for (j = 0; j < E.numrows; j++) { + memcpy(p,E.row[j].chars,E.row[j].size); + p += E.row[j].size; + *p = '\n'; + p++; + } + *p = '\0'; + return buf; } /* Insert a character at the specified position in a row, moving the remaining * chars on the right if needed. */ void editorRowInsertChar(erow *row, int at, int c) { - if (at > row->size) { - /* Pad the string with spaces if the insert location is outside the - * current length by more than a single character. */ - int padlen = at-row->size; - /* In the next line +2 means: new char and null term. */ - row->chars = realloc(row->chars,row->size+padlen+2); - memset(row->chars+row->size,' ',padlen); - row->chars[row->size+padlen+1] = '\0'; - row->size += padlen+1; - } else { - /* If we are in the middle of the string just make space for 1 new - * char plus the (already existing) null term. */ - row->chars = realloc(row->chars,row->size+2); - memmove(row->chars+at+1,row->chars+at,row->size-at+1); - row->size++; - } - row->chars[at] = c; - editorUpdateRow(row); - E.dirty++; + if (at > row->size) { + /* Pad the string with spaces if the insert location is outside the + * current length by more than a single character. */ + int padlen = at-row->size; + /* In the next line +2 means: new char and null term. */ + row->chars = realloc(row->chars,row->size+padlen+2); + memset(row->chars+row->size,' ',padlen); + row->chars[row->size+padlen+1] = '\0'; + row->size += padlen+1; + } else { + /* If we are in the middle of the string just make space for 1 new + * char plus the (already existing) null term. */ + row->chars = realloc(row->chars,row->size+2); + memmove(row->chars+at+1,row->chars+at,row->size-at+1); + row->size++; + } + row->chars[at] = c; + editorUpdateRow(row); + E.dirty++; } /* Append the string 's' at the end of a row */ void editorRowAppendString(erow *row, char *s, size_t len) { - row->chars = realloc(row->chars,row->size+len+1); - memcpy(row->chars+row->size,s,len); - row->size += len; - row->chars[row->size] = '\0'; - editorUpdateRow(row); - E.dirty++; + row->chars = realloc(row->chars,row->size+len+1); + memcpy(row->chars+row->size,s,len); + row->size += len; + row->chars[row->size] = '\0'; + editorUpdateRow(row); + E.dirty++; } /* Delete the character at offset 'at' from the specified row. */ void editorRowDelChar(erow *row, int at) { - if (row->size <= at) return; - memmove(row->chars+at,row->chars+at+1,row->size-at); - editorUpdateRow(row); - row->size--; - E.dirty++; + if (row->size <= at) return; + memmove(row->chars+at,row->chars+at+1,row->size-at); + editorUpdateRow(row); + row->size--; + E.dirty++; } /* Insert the specified char at the current prompt position. */ void editorInsertChar(int c) { - int filerow = E.rowoff+E.cy; - int filecol = E.coloff+E.cx; - erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; - - /* If the row where the cursor is currently located does not exist in our - * logical representaion of the file, add enough empty rows as needed. */ - if (!row) { - while(E.numrows <= filerow) - editorInsertRow(E.numrows,"",0); - } - row = &E.row[filerow]; - editorRowInsertChar(row,filecol,c); - if (E.cx == E.screencols-1) - E.coloff++; - else - E.cx++; - E.dirty++; + int filerow = E.rowoff+E.cy; + int filecol = E.coloff+E.cx; + erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; + + /* If the row where the cursor is currently located does not exist in our + * logical representaion of the file, add enough empty rows as needed. */ + if (!row) { + while(E.numrows <= filerow) + editorInsertRow(E.numrows,"",0); + } + row = &E.row[filerow]; + editorRowInsertChar(row,filecol,c); + if (E.cx == E.screencols-1) + E.coloff++; + else + E.cx++; + E.dirty++; } /* Inserting a newline is slightly complex as we have to handle inserting a * newline in the middle of a line, splitting the line as needed. */ void editorInsertNewline(void) { - int filerow = E.rowoff+E.cy; - int filecol = E.coloff+E.cx; - erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; - - if (!row) { - if (filerow == E.numrows) { - editorInsertRow(filerow,"",0); - goto fixcursor; - } - return; - } - /* If the cursor is over the current line size, we want to conceptually - * think it's just over the last character. */ - if (filecol >= row->size) filecol = row->size; - if (filecol == 0) { - editorInsertRow(filerow,"",0); - } else { - /* We are in the middle of a line. Split it between two rows. */ - editorInsertRow(filerow+1,row->chars+filecol,row->size-filecol); - row = &E.row[filerow]; - row->chars[filecol] = '\0'; - row->size = filecol; - editorUpdateRow(row); - } + int filerow = E.rowoff+E.cy; + int filecol = E.coloff+E.cx; + erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; + + if (!row) { + if (filerow == E.numrows) { + editorInsertRow(filerow,"",0); + goto fixcursor; + } + return; + } + /* If the cursor is over the current line size, we want to conceptually + * think it's just over the last character. */ + if (filecol >= row->size) filecol = row->size; + if (filecol == 0) { + editorInsertRow(filerow,"",0); + } else { + /* We are in the middle of a line. Split it between two rows. */ + editorInsertRow(filerow+1,row->chars+filecol,row->size-filecol); + row = &E.row[filerow]; + row->chars[filecol] = '\0'; + row->size = filecol; + editorUpdateRow(row); + } fixcursor: - if (E.cy == E.screenrows-1) { - E.rowoff++; - } else { - E.cy++; - } - E.cx = 0; - E.coloff = 0; + if (E.cy == E.screenrows-1) { + E.rowoff++; + } else { + E.cy++; + } + E.cx = 0; + E.coloff = 0; } /* Delete the char at the current prompt position. */ void editorDelChar() { - int filerow = E.rowoff+E.cy; - int filecol = E.coloff+E.cx; - erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; - - if (!row || (filecol == 0 && filerow == 0)) return; - if (filecol == 0) { - /* Handle the case of column 0, we need to move the current line - * on the right of the previous one. */ - filecol = E.row[filerow-1].size; - editorRowAppendString(&E.row[filerow-1],row->chars,row->size); - editorDelRow(filerow); - row = NULL; - if (E.cy == 0) - E.rowoff--; - else - E.cy--; - E.cx = filecol; - if (E.cx >= E.screencols) { - int shift = (E.screencols-E.cx)+1; - E.cx -= shift; - E.coloff += shift; - } - } else { - editorRowDelChar(row,filecol-1); - if (E.cx == 0 && E.coloff) - E.coloff--; - else - E.cx--; - } - if (row) editorUpdateRow(row); - E.dirty++; + int filerow = E.rowoff+E.cy; + int filecol = E.coloff+E.cx; + erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; + + if (!row || (filecol == 0 && filerow == 0)) return; + if (filecol == 0) { + /* Handle the case of column 0, we need to move the current line + * on the right of the previous one. */ + filecol = E.row[filerow-1].size; + editorRowAppendString(&E.row[filerow-1],row->chars,row->size); + editorDelRow(filerow); + row = NULL; + if (E.cy == 0) + E.rowoff--; + else + E.cy--; + E.cx = filecol; + if (E.cx >= E.screencols) { + int shift = (E.screencols-E.cx)+1; + E.cx -= shift; + E.coloff += shift; + } + } else { + editorRowDelChar(row,filecol-1); + if (E.cx == 0 && E.coloff) + E.coloff--; + else + E.cx--; + } + if (row) editorUpdateRow(row); + E.dirty++; } void editorInvDelChar() { - int filerow = E.rowoff+E.cy; - int filecol = E.coloff+E.cx; - erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; - erow *followingRow = (filerow+1 >= E.numrows) ? NULL : &E.row[filerow+1]; - - if (!row || (filecol == 0 && filerow == 0)) return; - if (filecol == row->size) { - /* Handle the case of last column, we need to move the following line - * on the right of the current one. */ - editorRowAppendString(&E.row[filerow],followingRow->chars,followingRow->size); - editorDelRow(filerow+1); - row = NULL; - } else { - editorRowDelChar(row,filecol); - } - if (row) editorUpdateRow(row); - E.dirty++; + int filerow = E.rowoff+E.cy; + int filecol = E.coloff+E.cx; + erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; + erow *followingRow = (filerow+1 >= E.numrows) ? NULL : &E.row[filerow+1]; + + if (!row || (filecol == 0 && filerow == 0)) return; + if (filecol == row->size) { + /* Handle the case of last column, we need to move the following line + * on the right of the current one. */ + editorRowAppendString(&E.row[filerow],followingRow->chars,followingRow->size); + editorDelRow(filerow+1); + row = NULL; + } else { + editorRowDelChar(row,filecol); + } + if (row) editorUpdateRow(row); + E.dirty++; } /* Load the specified program in the editor memory and returns 0 on success * or 1 on error. */ int editorOpen(char *filename) { - FILE *fp; - - E.dirty = 0; - free(E.filename); - size_t fnlen = strlen(filename)+1; - E.filename = malloc(fnlen); - memcpy(E.filename,filename,fnlen); - - fp = fopen(filename,"r"); - if (!fp) { - if (errno != ENOENT) { - perror("Opening file"); - exit(1); - } - return 1; - } - - char *line = NULL; - size_t linecap = 0; - ssize_t linelen; - while((linelen = getline(&line,&linecap,fp)) != -1) { - if (linelen && (line[linelen-1] == '\n' || line[linelen-1] == '\r')) - line[--linelen] = '\0'; - editorInsertRow(E.numrows,line,linelen); - } - free(line); - fclose(fp); - E.dirty = 0; - return 0; + FILE *fp; + + E.dirty = 0; + free(E.filename); + size_t fnlen = strlen(filename)+1; + E.filename = malloc(fnlen); + memcpy(E.filename,filename,fnlen); + + fp = fopen(filename,"r"); + if (!fp) { + if (errno != ENOENT) { + perror("Opening file"); + exit(1); + } + return 1; + } + + char *line = NULL; + size_t linecap = 0; + ssize_t linelen; + while((linelen = getline(&line,&linecap,fp)) != -1) { + if (linelen && (line[linelen-1] == '\n' || line[linelen-1] == '\r')) + line[--linelen] = '\0'; + editorInsertRow(E.numrows,line,linelen); + } + free(line); + fclose(fp); + E.dirty = 0; + return 0; } /* Save the current file on disk. Return 0 on success, 1 on error. */ int editorSave(void) { - int len; - char *buf = editorRowsToString(&len); - int fd = open(E.filename,O_RDWR|O_CREAT,0644); - if (fd == -1) goto writeerr; - - /* Use truncate + a single write(2) call in order to make saving - * a bit safer, under the limits of what we can do in a small editor. */ - if (ftruncate(fd,len) == -1) goto writeerr; - if (write(fd,buf,len) != len) goto writeerr; - - close(fd); - free(buf); - E.dirty = 0; - editorSetStatusMessage("%d bytes written on disk", len); - return 0; + int len; + char *buf = editorRowsToString(&len); + int fd = open(E.filename,O_RDWR|O_CREAT,0644); + if (fd == -1) goto writeerr; + + /* Use truncate + a single write(2) call in order to make saving + * a bit safer, under the limits of what we can do in a small editor. */ + if (ftruncate(fd,len) == -1) goto writeerr; + if (write(fd,buf,len) != len) goto writeerr; + + close(fd); + free(buf); + E.dirty = 0; + editorSetStatusMessage("%d bytes written on disk", len); + return 0; writeerr: - free(buf); - if (fd != -1) close(fd); - editorSetStatusMessage("Can't save! I/O error: %s",strerror(errno)); - return 1; + free(buf); + if (fd != -1) close(fd); + editorSetStatusMessage("Can't save! I/O error: %s",strerror(errno)); + return 1; } /* ============================= Terminal update ============================ */ @@ -879,153 +879,153 @@ int editorSave(void) { * write all the escape sequences in a buffer and flush them to the standard * output in a single call, to avoid flickering effects. */ struct abuf { - char *b; - int len; + char *b; + int len; }; #define ABUF_INIT {NULL,0} void abAppend(struct abuf *ab, const char *s, int len) { - char *new = realloc(ab->b,ab->len+len); + char *new = realloc(ab->b,ab->len+len); - if (new == NULL) return; - memcpy(new+ab->len,s,len); - ab->b = new; - ab->len += len; + if (new == NULL) return; + memcpy(new+ab->len,s,len); + ab->b = new; + ab->len += len; } void abFree(struct abuf *ab) { - free(ab->b); + free(ab->b); } /* This function writes the whole screen using VT100 escape characters * starting from the logical state of the editor in the global state 'E'. */ void editorRefreshScreen(void) { - int y; - erow *r; - char buf[32]; - struct abuf ab = ABUF_INIT; - - abAppend(&ab,"\x1b[?25l",6); /* Hide cursor. */ - abAppend(&ab,"\x1b[H",3); /* Go home. */ - for (y = 0; y < E.screenrows; y++) { - int filerow = E.rowoff+y; - - if (filerow >= E.numrows) { - if (E.numrows == 0 && y == E.screenrows/3) { - char welcome[80]; - int welcomelen = snprintf(welcome,sizeof(welcome), - "Kilo editor -- verison %s\x1b[0K\r\n", KILO_VERSION); - int padding = (E.screencols-welcomelen)/2; - if (padding) { - abAppend(&ab,"~",1); - padding--; - } - while(padding--) abAppend(&ab," ",1); - abAppend(&ab,welcome,welcomelen); - } else { - abAppend(&ab,"~\x1b[0K\r\n",7); - } - continue; - } - - r = &E.row[filerow]; - - int len = r->rsize - E.coloff; - int current_color = -1; - if (len > 0) { - if (len > E.screencols) len = E.screencols; - char *c = r->render+E.coloff; - unsigned char *hl = r->hl+E.coloff; - int j; - for (j = 0; j < len; j++) { - if (hl[j] == HL_NONPRINT) { - char sym; - abAppend(&ab,"\x1b[7m",4); - if (c[j] <= 26) - sym = '@'+c[j]; - else - sym = '?'; - abAppend(&ab,&sym,1); - abAppend(&ab,"\x1b[0m",4); - } else if (hl[j] == HL_NORMAL) { - if (current_color != -1) { - abAppend(&ab,"\x1b[39m",5); - current_color = -1; - } - abAppend(&ab,c+j,1); - } else { - int color = editorSyntaxToColor(hl[j]); - if (color != current_color) { - char buf[16]; - int clen = snprintf(buf,sizeof(buf),"\x1b[%dm",color); - current_color = color; - abAppend(&ab,buf,clen); - } - abAppend(&ab,c+j,1); - } - } - } - abAppend(&ab,"\x1b[39m",5); - abAppend(&ab,"\x1b[0K",4); - abAppend(&ab,"\r\n",2); - } - - /* Create a two rows status. First row: */ - abAppend(&ab,"\x1b[0K",4); - abAppend(&ab,"\x1b[7m",4); - char status[80], rstatus[80]; - int len = snprintf(status, sizeof(status), "%.20s - %d lines %s", - E.filename, E.numrows, E.dirty ? "(modified)" : ""); - int rlen = snprintf(rstatus, sizeof(rstatus), - "c: %d/%d l: %d/%d",E.coloff+E.cx,E.row[E.rowoff+E.cy].size,E.rowoff+E.cy+1,E.numrows); - if (len > E.screencols) len = E.screencols; - abAppend(&ab,status,len); - while(len < E.screencols) { - if (E.screencols - len == rlen) { - abAppend(&ab,rstatus,rlen); - break; - } else { - abAppend(&ab," ",1); - len++; - } - } - abAppend(&ab,"\x1b[0m\r\n",6); - - /* Second row depends on E.statusmsg and the status message update time. */ - abAppend(&ab,"\x1b[0K",4); - int msglen = strlen(E.statusmsg); - if (msglen && time(NULL)-E.statusmsg_time < 5) - abAppend(&ab,E.statusmsg,msglen <= E.screencols ? msglen : E.screencols); - - /* Put cursor at its current position. Note that the horizontal position - * at which the cursor is displayed may be different compared to 'E.cx' - * because of TABs. */ - int j; - int cx = 1; - int filerow = E.rowoff+E.cy; - erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; - if (row) { - for (j = E.coloff; j < (E.cx+E.coloff); j++) { - if (j < row->size && row->chars[j] == TAB) cx += TAB_SIZE-((cx)%TAB_SIZE); - cx++; - } - } - snprintf(buf,sizeof(buf),"\x1b[%d;%dH",E.cy+1,cx); - abAppend(&ab,buf,strlen(buf)); - abAppend(&ab,"\x1b[?25h",6); /* Show cursor. */ - write(STDOUT_FILENO,ab.b,ab.len); - abFree(&ab); + int y; + erow *r; + char buf[32]; + struct abuf ab = ABUF_INIT; + + abAppend(&ab,"\x1b[?25l",6); /* Hide cursor. */ + abAppend(&ab,"\x1b[H",3); /* Go home. */ + for (y = 0; y < E.screenrows; y++) { + int filerow = E.rowoff+y; + + if (filerow >= E.numrows) { + if (E.numrows == 0 && y == E.screenrows/3) { + char welcome[80]; + int welcomelen = snprintf(welcome,sizeof(welcome), + "Kilo editor -- verison %s\x1b[0K\r\n", KILO_VERSION); + int padding = (E.screencols-welcomelen)/2; + if (padding) { + abAppend(&ab,"~",1); + padding--; + } + while(padding--) abAppend(&ab," ",1); + abAppend(&ab,welcome,welcomelen); + } else { + abAppend(&ab,"~\x1b[0K\r\n",7); + } + continue; + } + + r = &E.row[filerow]; + + int len = r->rsize - E.coloff; + int current_color = -1; + if (len > 0) { + if (len > E.screencols) len = E.screencols; + char *c = r->render+E.coloff; + unsigned char *hl = r->hl+E.coloff; + int j; + for (j = 0; j < len; j++) { + if (hl[j] == HL_NONPRINT) { + char sym; + abAppend(&ab,"\x1b[7m",4); + if (c[j] <= 26) + sym = '@'+c[j]; + else + sym = '?'; + abAppend(&ab,&sym,1); + abAppend(&ab,"\x1b[0m",4); + } else if (hl[j] == HL_NORMAL) { + if (current_color != -1) { + abAppend(&ab,"\x1b[39m",5); + current_color = -1; + } + abAppend(&ab,c+j,1); + } else { + int color = editorSyntaxToColor(hl[j]); + if (color != current_color) { + char buf[16]; + int clen = snprintf(buf,sizeof(buf),"\x1b[%dm",color); + current_color = color; + abAppend(&ab,buf,clen); + } + abAppend(&ab,c+j,1); + } + } + } + abAppend(&ab,"\x1b[39m",5); + abAppend(&ab,"\x1b[0K",4); + abAppend(&ab,"\r\n",2); + } + + /* Create a two rows status. First row: */ + abAppend(&ab,"\x1b[0K",4); + abAppend(&ab,"\x1b[7m",4); + char status[80], rstatus[80]; + int len = snprintf(status, sizeof(status), "%.20s - %d lines %s", + E.filename, E.numrows, E.dirty ? "(modified)" : ""); + int rlen = snprintf(rstatus, sizeof(rstatus), + "c: %d/%d l: %d/%d",E.coloff+E.cx,E.row[E.rowoff+E.cy].size,E.rowoff+E.cy+1,E.numrows); + if (len > E.screencols) len = E.screencols; + abAppend(&ab,status,len); + while(len < E.screencols) { + if (E.screencols - len == rlen) { + abAppend(&ab,rstatus,rlen); + break; + } else { + abAppend(&ab," ",1); + len++; + } + } + abAppend(&ab,"\x1b[0m\r\n",6); + + /* Second row depends on E.statusmsg and the status message update time. */ + abAppend(&ab,"\x1b[0K",4); + int msglen = strlen(E.statusmsg); + if (msglen && time(NULL)-E.statusmsg_time < 5) + abAppend(&ab,E.statusmsg,msglen <= E.screencols ? msglen : E.screencols); + + /* Put cursor at its current position. Note that the horizontal position + * at which the cursor is displayed may be different compared to 'E.cx' + * because of TABs. */ + int j; + int cx = 1; + int filerow = E.rowoff+E.cy; + erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; + if (row) { + for (j = E.coloff; j < (E.cx+E.coloff); j++) { + if (j < row->size && row->chars[j] == TAB) cx += TAB_SIZE-((cx)%TAB_SIZE); + cx++; + } + } + snprintf(buf,sizeof(buf),"\x1b[%d;%dH",E.cy+1,cx); + abAppend(&ab,buf,strlen(buf)); + abAppend(&ab,"\x1b[?25h",6); /* Show cursor. */ + write(STDOUT_FILENO,ab.b,ab.len); + abFree(&ab); } /* Set an editor status message for the second line of the status, at the * end of the screen. */ void editorSetStatusMessage(const char *fmt, ...) { - va_list ap; - va_start(ap,fmt); - vsnprintf(E.statusmsg,sizeof(E.statusmsg),fmt,ap); - va_end(ap); - E.statusmsg_time = time(NULL); + va_list ap; + va_start(ap,fmt); + vsnprintf(E.statusmsg,sizeof(E.statusmsg),fmt,ap); + va_end(ap); + E.statusmsg_time = time(NULL); } /* =============================== Find mode ================================ */ @@ -1033,305 +1033,305 @@ void editorSetStatusMessage(const char *fmt, ...) { #define KILO_QUERY_LEN 256 void editorFind(int fd) { - char query[KILO_QUERY_LEN+1] = {0}; - int qlen = 0; - int last_match = -1; /* Last line where a match was found. -1 for none. */ - int find_next = 0; /* if 1 search next, if -1 search prev. */ - int saved_hl_line = -1; /* No saved HL */ - char *saved_hl = NULL; + char query[KILO_QUERY_LEN+1] = {0}; + int qlen = 0; + int last_match = -1; /* Last line where a match was found. -1 for none. */ + int find_next = 0; /* if 1 search next, if -1 search prev. */ + int saved_hl_line = -1; /* No saved HL */ + char *saved_hl = NULL; #define FIND_RESTORE_HL do { \ - if (saved_hl) { \ - memcpy(E.row[saved_hl_line].hl,saved_hl, E.row[saved_hl_line].rsize); \ - free(saved_hl); \ - saved_hl = NULL; \ - } \ + if (saved_hl) { \ + memcpy(E.row[saved_hl_line].hl,saved_hl, E.row[saved_hl_line].rsize); \ + free(saved_hl); \ + saved_hl = NULL; \ + } \ } while (0) - /* Save the cursor position in order to restore it later. */ - int saved_cx = E.cx, saved_cy = E.cy; - int saved_coloff = E.coloff, saved_rowoff = E.rowoff; - - while(1) { - editorSetStatusMessage( - "Search: %s (Use ESC/Arrows/Enter)", query); - editorRefreshScreen(); - - int c = editorReadKey(fd); - if (c == DEL_KEY || c == CTRL_H || c == BACKSPACE) { - if (qlen != 0) query[--qlen] = '\0'; - last_match = -1; - } else if (c == ESC || c == ENTER) { - if (c == ESC) { - E.cx = saved_cx; E.cy = saved_cy; - E.coloff = saved_coloff; E.rowoff = saved_rowoff; - } - FIND_RESTORE_HL; - editorSetStatusMessage(""); - return; - } else if (c == ARROW_RIGHT || c == ARROW_DOWN) { - find_next = 1; - } else if (c == ARROW_LEFT || c == ARROW_UP) { - find_next = -1; - } else if (isprint(c)) { - if (qlen < KILO_QUERY_LEN) { - query[qlen++] = c; - query[qlen] = '\0'; - last_match = -1; - } - } - - /* Search occurrence. */ - if (last_match == -1) find_next = 1; - if (find_next) { - char *match = NULL; - int match_offset = 0; - int i, current = last_match; - - for (i = 0; i < E.numrows; i++) { - current += find_next; - if (current == -1) current = E.numrows-1; - else if (current == E.numrows) current = 0; - match = strstr(E.row[current].render,query); - if (match) { - match_offset = match-E.row[current].render; - break; - } - } - find_next = 0; - - /* Highlight */ - FIND_RESTORE_HL; - - if (match) { - erow *row = &E.row[current]; - last_match = current; - if (row->hl) { - saved_hl_line = current; - saved_hl = malloc(row->rsize); - memcpy(saved_hl,row->hl,row->rsize); - memset(row->hl+match_offset,HL_MATCH,qlen); - } - E.cy = 0; - E.cx = match_offset; - E.rowoff = current; - E.coloff = 0; - /* Scroll horizontally as needed. */ - if (E.cx > E.screencols) { - int diff = E.cx - E.screencols; - E.cx -= diff; - E.coloff += diff; - } - } - } - } + /* Save the cursor position in order to restore it later. */ + int saved_cx = E.cx, saved_cy = E.cy; + int saved_coloff = E.coloff, saved_rowoff = E.rowoff; + + while(1) { + editorSetStatusMessage( + "Search: %s (Use ESC/Arrows/Enter)", query); + editorRefreshScreen(); + + int c = editorReadKey(fd); + if (c == DEL_KEY || c == CTRL_H || c == BACKSPACE) { + if (qlen != 0) query[--qlen] = '\0'; + last_match = -1; + } else if (c == ESC || c == ENTER) { + if (c == ESC) { + E.cx = saved_cx; E.cy = saved_cy; + E.coloff = saved_coloff; E.rowoff = saved_rowoff; + } + FIND_RESTORE_HL; + editorSetStatusMessage(""); + return; + } else if (c == ARROW_RIGHT || c == ARROW_DOWN) { + find_next = 1; + } else if (c == ARROW_LEFT || c == ARROW_UP) { + find_next = -1; + } else if (isprint(c)) { + if (qlen < KILO_QUERY_LEN) { + query[qlen++] = c; + query[qlen] = '\0'; + last_match = -1; + } + } + + /* Search occurrence. */ + if (last_match == -1) find_next = 1; + if (find_next) { + char *match = NULL; + int match_offset = 0; + int i, current = last_match; + + for (i = 0; i < E.numrows; i++) { + current += find_next; + if (current == -1) current = E.numrows-1; + else if (current == E.numrows) current = 0; + match = strstr(E.row[current].render,query); + if (match) { + match_offset = match-E.row[current].render; + break; + } + } + find_next = 0; + + /* Highlight */ + FIND_RESTORE_HL; + + if (match) { + erow *row = &E.row[current]; + last_match = current; + if (row->hl) { + saved_hl_line = current; + saved_hl = malloc(row->rsize); + memcpy(saved_hl,row->hl,row->rsize); + memset(row->hl+match_offset,HL_MATCH,qlen); + } + E.cy = 0; + E.cx = match_offset; + E.rowoff = current; + E.coloff = 0; + /* Scroll horizontally as needed. */ + if (E.cx > E.screencols) { + int diff = E.cx - E.screencols; + E.cx -= diff; + E.coloff += diff; + } + } + } + } } /* ========================= Editor events handling ======================== */ /* Handle cursor position change because arrow keys were pressed. */ void editorMoveCursor(int key) { - int filerow = E.rowoff+E.cy; - int filecol = E.coloff+E.cx; - int rowlen; - erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; - - switch(key) { - case ARROW_LEFT: - if (E.cx == 0) { - if (E.coloff) { - E.coloff--; - } else { - if (filerow > 0) { - E.cy--; - E.cx = E.row[filerow-1].size; - if (E.cx > E.screencols-1) { - E.coloff = E.cx-E.screencols+1; - E.cx = E.screencols-1; - } - } - } - } else { - E.cx -= 1; - } - break; - case ARROW_RIGHT: - if (row && filecol < row->size) { - if (E.cx == E.screencols-1) { - E.coloff++; - } else { - E.cx += 1; - } - } else if (row && filecol == row->size) { - E.cx = 0; - E.coloff = 0; - if (E.cy == E.screenrows-1) { - E.rowoff++; - } else { - E.cy += 1; - } - } - break; - case ARROW_UP: - if (E.cy == 0) { - if (E.rowoff) E.rowoff--; - } else { - E.cy -= 1; - } - break; - case ARROW_DOWN: - if (filerow < E.numrows) { - if (E.cy == E.screenrows-1) { - E.rowoff++; - } else { - E.cy += 1; - } - } - break; - } - /* Fix cx if the current line has not enough chars. */ - filerow = E.rowoff+E.cy; - filecol = E.coloff+E.cx; - row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; - rowlen = row ? row->size : 0; - if (filecol > rowlen) { - E.cx -= filecol-rowlen; - if (E.cx < 0) { - E.coloff += E.cx; - E.cx = 0; - } - } + int filerow = E.rowoff+E.cy; + int filecol = E.coloff+E.cx; + int rowlen; + erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; + + switch(key) { + case ARROW_LEFT: + if (E.cx == 0) { + if (E.coloff) { + E.coloff--; + } else { + if (filerow > 0) { + E.cy--; + E.cx = E.row[filerow-1].size; + if (E.cx > E.screencols-1) { + E.coloff = E.cx-E.screencols+1; + E.cx = E.screencols-1; + } + } + } + } else { + E.cx -= 1; + } + break; + case ARROW_RIGHT: + if (row && filecol < row->size) { + if (E.cx == E.screencols-1) { + E.coloff++; + } else { + E.cx += 1; + } + } else if (row && filecol == row->size) { + E.cx = 0; + E.coloff = 0; + if (E.cy == E.screenrows-1) { + E.rowoff++; + } else { + E.cy += 1; + } + } + break; + case ARROW_UP: + if (E.cy == 0) { + if (E.rowoff) E.rowoff--; + } else { + E.cy -= 1; + } + break; + case ARROW_DOWN: + if (filerow < E.numrows) { + if (E.cy == E.screenrows-1) { + E.rowoff++; + } else { + E.cy += 1; + } + } + break; + } + /* Fix cx if the current line has not enough chars. */ + filerow = E.rowoff+E.cy; + filecol = E.coloff+E.cx; + row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; + rowlen = row ? row->size : 0; + if (filecol > rowlen) { + E.cx -= filecol-rowlen; + if (E.cx < 0) { + E.coloff += E.cx; + E.cx = 0; + } + } } /* Process events arriving from the standard input, which is, the user * is typing stuff on the terminal. */ #define KILO_QUIT_TIMES 3 void editorProcessKeypress(int fd) { - /* When the file is modified, requires Ctrl-q to be pressed N times - * before actually quitting. */ - static int quit_times = KILO_QUIT_TIMES; - - int c = editorReadKey(fd); - switch(c) { - case ENTER: /* Enter */ - editorInsertNewline(); - break; - case CTRL_C: /* Ctrl-c */ - /* We ignore ctrl-c, it can't be so simple to lose the changes - * to the edited file. */ - break; - case CTRL_Q: /* Ctrl-q */ - /* Quit if the file was already saved. */ - if (E.dirty && quit_times) { - editorSetStatusMessage("WARNING!!! File has unsaved changes. " - "Press Ctrl-Q %d more times to quit.", quit_times); - quit_times--; - return; - } - exit(0); - break; - case CTRL_S: /* Ctrl-s */ - editorSave(); - break; - case CTRL_F: - editorFind(fd); - break; - case DEL_KEY: - editorInvDelChar(); - break; - case BACKSPACE: /* Backspace */ - case CTRL_H: /* Ctrl-h */ - editorDelChar(); - break; - case END_KEY: - E.cx = E.row[E.rowoff+E.cy].size; - break; - case HOME_KEY: - E.cx = 0; - break; - case PAGE_UP: - case PAGE_DOWN: - if (c == PAGE_UP && E.cy != 0) - E.cy = 0; - else if (c == PAGE_DOWN && E.cy != E.screenrows-1) - E.cy = E.screenrows-1; - { - int times = E.screenrows; - while(times--) - editorMoveCursor(c == PAGE_UP ? ARROW_UP: - ARROW_DOWN); - } - break; - - case ARROW_UP: - case ARROW_DOWN: - case ARROW_LEFT: - case ARROW_RIGHT: - editorMoveCursor(c); - break; - case CTRL_L: /* ctrl+l, clear screen */ - /* Just refresht the line as side effect. */ - break; - case ESC: - /* Nothing to do for ESC in this mode. */ - break; - default: - editorInsertChar(c); - break; - } - - quit_times = KILO_QUIT_TIMES; /* Reset it to the original value. */ + /* When the file is modified, requires Ctrl-q to be pressed N times + * before actually quitting. */ + static int quit_times = KILO_QUIT_TIMES; + + int c = editorReadKey(fd); + switch(c) { + case ENTER: /* Enter */ + editorInsertNewline(); + break; + case CTRL_C: /* Ctrl-c */ + /* We ignore ctrl-c, it can't be so simple to lose the changes + * to the edited file. */ + break; + case CTRL_Q: /* Ctrl-q */ + /* Quit if the file was already saved. */ + if (E.dirty && quit_times) { + editorSetStatusMessage("WARNING!!! File has unsaved changes. " + "Press Ctrl-Q %d more times to quit.", quit_times); + quit_times--; + return; + } + exit(0); + break; + case CTRL_S: /* Ctrl-s */ + editorSave(); + break; + case CTRL_F: + editorFind(fd); + break; + case DEL_KEY: + editorInvDelChar(); + break; + case BACKSPACE: /* Backspace */ + case CTRL_H: /* Ctrl-h */ + editorDelChar(); + break; + case END_KEY: + E.cx = E.row[E.rowoff+E.cy].size; + break; + case HOME_KEY: + E.cx = 0; + break; + case PAGE_UP: + case PAGE_DOWN: + if (c == PAGE_UP && E.cy != 0) + E.cy = 0; + else if (c == PAGE_DOWN && E.cy != E.screenrows-1) + E.cy = E.screenrows-1; + { + int times = E.screenrows; + while(times--) + editorMoveCursor(c == PAGE_UP ? ARROW_UP: + ARROW_DOWN); + } + break; + + case ARROW_UP: + case ARROW_DOWN: + case ARROW_LEFT: + case ARROW_RIGHT: + editorMoveCursor(c); + break; + case CTRL_L: /* ctrl+l, clear screen */ + /* Just refresht the line as side effect. */ + break; + case ESC: + /* Nothing to do for ESC in this mode. */ + break; + default: + editorInsertChar(c); + break; + } + + quit_times = KILO_QUIT_TIMES; /* Reset it to the original value. */ } int editorFileWasModified(void) { - return E.dirty; + return E.dirty; } void updateWindowSize(void) { - if (getWindowSize(STDIN_FILENO,STDOUT_FILENO, - &E.screenrows,&E.screencols) == -1) { - perror("Unable to query the screen for size (columns / rows)"); - exit(1); - } - E.screenrows -= 2; /* Get room for status bar. */ + if (getWindowSize(STDIN_FILENO,STDOUT_FILENO, + &E.screenrows,&E.screencols) == -1) { + perror("Unable to query the screen for size (columns / rows)"); + exit(1); + } + E.screenrows -= 2; /* Get room for status bar. */ } void handleSigWinCh(int unused __attribute__((unused))) { - updateWindowSize(); - if (E.cy > E.screenrows) E.cy = E.screenrows - 1; - if (E.cx > E.screencols) E.cx = E.screencols - 1; - editorRefreshScreen(); + updateWindowSize(); + if (E.cy > E.screenrows) E.cy = E.screenrows - 1; + if (E.cx > E.screencols) E.cx = E.screencols - 1; + editorRefreshScreen(); } void initEditor(void) { - E.cx = 0; - E.cy = 0; - E.rowoff = 0; - E.coloff = 0; - E.numrows = 0; - E.row = NULL; - E.dirty = 0; - E.filename = NULL; - E.syntax = NULL; - updateWindowSize(); - signal(SIGWINCH, handleSigWinCh); + E.cx = 0; + E.cy = 0; + E.rowoff = 0; + E.coloff = 0; + E.numrows = 0; + E.row = NULL; + E.dirty = 0; + E.filename = NULL; + E.syntax = NULL; + updateWindowSize(); + signal(SIGWINCH, handleSigWinCh); } int main(int argc, char **argv) { - if (argc != 2) { - fprintf(stderr,"Usage: kilo \n"); - exit(1); - } - - initEditor(); - editorSelectSyntaxHighlight(argv[1]); - editorOpen(argv[1]); - enableRawMode(STDIN_FILENO); - editorSetStatusMessage( - "HELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find"); - while(1) { - editorRefreshScreen(); - editorProcessKeypress(STDIN_FILENO); - } - return 0; + if (argc != 2) { + fprintf(stderr,"Usage: kilo \n"); + exit(1); + } + + initEditor(); + editorSelectSyntaxHighlight(argv[1]); + editorOpen(argv[1]); + enableRawMode(STDIN_FILENO); + editorSetStatusMessage( + "HELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find"); + while(1) { + editorRefreshScreen(); + editorProcessKeypress(STDIN_FILENO); + } + return 0; } From a4e73e233af28feef2315f195288ad8de834b2fe Mon Sep 17 00:00:00 2001 From: martin Date: Wed, 12 Mar 2025 16:38:12 -0300 Subject: [PATCH 8/8] Added code I had lying around that I don't remember what it does. This fork is a mess --- kilo.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/kilo.c b/kilo.c index 1c1de603..cdee7436 100644 --- a/kilo.c +++ b/kilo.c @@ -1134,11 +1134,13 @@ void editorMoveCursor(int key) { int filerow = E.rowoff+E.cy; int filecol = E.coloff+E.cx; int rowlen; + erow *rowP = &E.row[filerow-1]; erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; + erow *rowF = (filerow+1 >= E.numrows) ? NULL : &E.row[filerow+1]; switch(key) { case ARROW_LEFT: - if (E.cx == 0) { + if (E.cx <= 0) { if (E.coloff) { E.coloff--; } else { @@ -1156,6 +1158,9 @@ void editorMoveCursor(int key) { } break; case ARROW_RIGHT: + if(E.cx < 0){ + E.cx = 0; + } if (row && filecol < row->size) { if (E.cx == E.screencols-1) { E.coloff++; @@ -1177,6 +1182,18 @@ void editorMoveCursor(int key) { if (E.rowoff) E.rowoff--; } else { E.cy -= 1; + int tabs = 0, offset = 0, n = 0; + for (int j = 0; j < filecol; j++){ + if (row->chars[j] == TAB) { + tabs++; + } + } + for (int j = 0; j < filecol; j++){ + if (rowP->chars[j] == TAB/* && tabs >= 0*/) { + tabs--; + } + } + E.cx += tabs*(TAB_SIZE-1); } break; case ARROW_DOWN: @@ -1186,6 +1203,18 @@ void editorMoveCursor(int key) { } else { E.cy += 1; } + int tabs = 0; + for (int j = 0; j < filecol; j++){ + if (row->chars[j] == TAB) { + tabs++; + } + } + for (int j = 0; j < filecol; j++){ + if (rowF->chars[j] == TAB/* && tabs >= 0*/) { + tabs--; + } + } + E.cx += tabs*(TAB_SIZE-1); } break; }