commit f3c336684ccfab9b657cf999ba0dde2494f0dfa0
Author: Jan P. Pasierb <me@janpasierb.com>
Date: Sat, 5 Mar 2022 14:28:58 +0000
Initial commit
Diffstat:
A | .gitignore | | | 3 | +++ |
A | LICENSE | | | 24 | ++++++++++++++++++++++++ |
A | Makefile | | | 48 | ++++++++++++++++++++++++++++++++++++++++++++++++ |
A | README | | | 19 | +++++++++++++++++++ |
A | TODO | | | 10 | ++++++++++ |
A | appbuf.c | | | 17 | +++++++++++++++++ |
A | appbuf.h | | | 12 | ++++++++++++ |
A | editor.c | | | 72 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | editor.h | | | 10 | ++++++++++ |
A | file.c | | | 91 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | file.h | | | 7 | +++++++ |
A | find.c | | | 88 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | find.h | | | 6 | ++++++ |
A | input.c | | | 272 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | input.h | | | 7 | +++++++ |
A | jim.1 | | | 16 | ++++++++++++++++ |
A | jim.h | | | 98 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | main.c | | | 46 | ++++++++++++++++++++++++++++++++++++++++++++++ |
A | output.c | | | 183 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | output.h | | | 8 | ++++++++ |
A | row.c | | | 129 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | row.h | | | 16 | ++++++++++++++++ |
A | syntax.c | | | 262 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | syntax.h | | | 22 | ++++++++++++++++++++++ |
A | term.c | | | 108 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | term.h | | | 10 | ++++++++++ |
26 files changed, 1584 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,3 @@
+jim
+*.o
+tags
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2016, Salvatore Sanfilippo <antirez at gmail dot com>
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile b/Makefile
@@ -0,0 +1,48 @@
+.POSIX:
+CC = cc
+CFLAGS = -std=c99 -pedantic -Wall -Wextra
+PREFIX = /usr/local
+MANPREFIX = $(PREFIX)/share/man
+OBJ = main.o appbuf.o editor.o file.o find.o input.o output.o row.o syntax.o term.o
+SRC = main.c appbuf.c editor.c file.c find.c input.c output.c row.c syntax.c term.c
+HDR = jim.h appbuf.h editor.h file.h find.h input.h output.h row.h syntax.h term.h
+VERSION = 0.0.1
+
+all: jim
+
+install: jim
+ mkdir -p $(DESTDIR)$(PREFIX)/bin
+ cp -f jim $(DESTDIR)$(PREFIX)/bin
+ chmod 755 $(DESTDIR)$(PREFIX)/bin/jim
+ mkdir -p $(DESTDIR)$(MANPREFIX)/man1
+ sed "s/VERSION/$(VERSION)/g" < jim.1 > $(DESTDIR)$(MANPREFIX)/man1/jim.1
+ gzip -f $(DESTDIR)$(MANPREFIX)/man1/jim.1
+ rm -f $(DESTDIR)$(MANPREFIX)/man1/jim.1
+ chmod 644 $(DESTDIR)$(MANPREFIX)/man1/jim.1.gz
+
+jim: main.o appbuf.o editor.o file.o find.o input.o output.o row.o syntax.o term.o
+ $(CC) $(LDFLAGS) -o jim $(OBJ)
+
+main.o: main.c jim.h output.h term.h file.h input.h output.h
+appbuf.o: appbuf.c appbuf.h
+editor.o: editor.c editor.h jim.h row.h
+file.o: file.c file.h row.h output.h input.h
+find.o: find.c find.h output.h input.h
+input.o: input.c input.h editor.h row.h file.h find.h output.h
+output.o: output.c output.h jim.h appbuf.h syntax.h
+row.o: row.c row.h
+syntax.o: syntax.c syntax.h jim.h
+term.o: term.c term.h jim.h output.h
+
+clean:
+ rm -f jim $(OBJ) jim-$(VERSION).tar.gz
+
+dist: clean
+ mkdir -p jim-${VERSION}
+ cp -R LICENSE Makefile README jim.1 ${HDR} ${SRC} jim-${VERSION}
+ tar -cf jim-${VERSION}.tar jim-${VERSION}
+ gzip jim-${VERSION}.tar
+ rm -rf jim-${VERSION}
+
+uninstall:
+ rm -rf $(DESTDIR)$(PREFIX)/bin/jim $(DESTDIR)$(MANPREFIX)/man1/jim.1 $(DESTDIR)$(MANPREFIX)/man1/jim.1.gz
diff --git a/README b/README
@@ -0,0 +1,19 @@
+jim
+==========
+Jan's vim - a simple, lightweight, modal, vim-inspired text editor. This is a fork of a KILO editor by <antirez>
+
+Requirements
+------------
+This binary is self-contained
+
+Installation
+------------
+Enter the following command to build and install jim (as root, if necessary):
+
+ make clean install
+
+You may additionally provide new or modify existing macros by supplying them in form of name=value to make's argument list
+
+Running jim
+------------------
+You can run jim on its own or by passing a file you wish to edit as an argument. Manpage is also provided
diff --git a/TODO b/TODO
@@ -0,0 +1,10 @@
+Missing features/bugs:
+ - Highlighting:
+ - remaining keywords (e.g. NULL)
+ - proper highlighting for octals & hex (0 & 0x)
+ - escape sequences
+ - printf format specifiers (including '.', numbers, etc)
+ - Other:
+ - add line numbers
+ - implement linewrap
+ - undo/redo (infinite + elegant: piece tables?)
diff --git a/appbuf.c b/appbuf.c
@@ -0,0 +1,17 @@
+#include"appbuf.h"
+#include<stdlib.h>
+#include<string.h>
+
+void abAppend(struct abuf* ab, const char* s, int 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;
+}
+
+void abFree(struct abuf* ab) {
+ free(ab->b);
+}
diff --git a/appbuf.h b/appbuf.h
@@ -0,0 +1,12 @@
+#ifndef APPBUF_HEADER
+#define APPBUF_HEADER
+
+struct abuf {
+ char *b;
+ int len;
+};
+
+void abAppend(struct abuf*, const char*, int);
+void abFree(struct abuf*);
+
+#endif
diff --git a/editor.c b/editor.c
@@ -0,0 +1,72 @@
+#include"editor.h"
+#include"jim.h"
+#include"row.h"
+
+void editorInsertChar(int c) {
+ if(E.cy == E.numrows) {
+ editorInsertRow(E.numrows, "", 0);
+ }
+
+ editorRowInsertChar(&E.row[E.cy], E.cx, c);
+ E.cx++;
+ E.dirty++;
+}
+
+void editorJumpToNewline(char p) {
+ switch(p) {
+ case NEWLINE_BELOW:
+ E.cy++;
+ //fallthrough
+ case NEWLINE_ABOVE:
+ editorInsertRow(E.cy, "", 0);
+ break;
+ }
+
+ E.cx = 0;
+}
+
+void editorInsertNewline() {
+ if(E.cx == 0) {
+ editorInsertRow(E.cy, "", 0);
+ } else {
+ erow* row = &E.row[E.cy];
+ editorInsertRow(E.cy + 1, &row->chars[E.cx], row->size - E.cx);
+ row = &E.row[E.cy];
+ row->size = E.cx;
+ row->chars[row->size] = '\0';
+ editorUpdateRow(row);
+ }
+
+ E.cy++;
+ E.cx = 0;
+}
+
+void editorDelChar() {
+ if(E.cy == E.numrows) return;
+
+ erow* row = &E.row[E.cy];
+ if(E.cx == row->size) {
+ if(E.cy == E.numrows - 1)
+ return;
+ erow* nrow = &E.row[E.cy + 1];
+ editorRowAppendString(row, nrow->chars, nrow->size);
+ editorDelRow(E.cy + 1);
+ } else {
+ editorRowDelChar(row, E.cx);
+ }
+}
+
+void editorBackspaceChar() {
+ if(E.cy == E.numrows) return;
+ if(E.cy == 0 && E.cx == 0) return;
+
+ erow* row = &E.row[E.cy];
+ if(E.cx == 0) {
+ E.cx = E.row[E.cy - 1].size;
+ editorRowAppendString(&E.row[E.cy - 1], row->chars, row->size);
+ editorDelRow(E.cy);
+ } else {
+ editorRowDelChar(row, E.cx - 1);
+ E.cx--;
+ }
+}
diff --git a/editor.h b/editor.h
@@ -0,0 +1,10 @@
+#ifndef EDITOR_HEADER
+#define EDITOR_HEADER
+
+void editorInsertNewline();
+void editorInsertChar(int);
+void editorJumpToNewline(char);
+void editorDelChar();
+void editorBackspaceChar();
+
+#endif
diff --git a/file.c b/file.c
@@ -0,0 +1,91 @@
+#include"file.h"
+#include"jim.h"
+#include"row.h"
+#include"output.h"
+#include"input.h"
+#include"term.h"
+#include"syntax.h"
+#include<stdlib.h>
+#include<string.h>
+#include<stdio.h>
+#include<errno.h>
+#include<fcntl.h>
+#include<unistd.h>
+
+char* editorRowsToString(int* buflen) {
+ int totlen = 0;
+ int j;
+
+ for(j = 0; j < E.numrows; j++)
+ totlen += E.row[j].size + 1;
+ *buflen = totlen;
+
+ char* buf = malloc(totlen);
+ char* p = buf;
+
+ 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++;
+ }
+
+ return buf;
+}
+
+void editorOpen(char* filename) {
+ free(E.filename);
+ E.filename = strdup(filename);
+
+ editorSelectSyntaxHighlight();
+
+ FILE* fp = fopen(filename, "r");
+ if (!fp) die("fopen");
+
+ char* line = NULL;
+ size_t linecap = 0;
+ ssize_t linelen;
+ while((linelen = getline(&line, &linecap, fp)) != -1) {
+ while(linelen > 0 && (line[linelen - 1] == '\n' || line[linelen - 1] == RETURN))
+ linelen--;
+ editorInsertRow(E.numrows, line, linelen);
+ }
+
+ free(line);
+ fclose(fp);
+ E.dirty = 0;
+}
+
+int editorSave() {
+ if(!E.dirty) return 0;
+ if(E.filename == NULL) {
+ E.filename = editorPrompt("Save as: %s", NULL);
+ if(E.filename == NULL) {
+ return 1;
+ }
+ editorSelectSyntaxHighlight();
+ }
+
+ int len, bw;
+ char* buf = editorRowsToString(&len);
+
+ int fd = open(E.filename, O_RDWR | O_CREAT, 0644);
+ if(fd != -1) {
+ if(ftruncate(fd, len) != -1) {
+ if((bw = write(fd, buf, len)) != -1) {
+ close(fd);
+ free(buf);
+ E.dirty = 0;
+ editorSetStatusMessage("\"%.20s\" %dL, %dB written", E.filename, E.numrows, bw);
+ return 0;
+ }
+ }
+ close(fd);
+ }
+
+ free(buf);
+ E.isbold = 1;
+ E.isr = 0;
+ editorSetStatusMessage("Failed to write file to disk! I/O error: %s", strerror(errno));
+ return 1;
+}
diff --git a/file.h b/file.h
@@ -0,0 +1,7 @@
+#ifndef FILE_HEADER
+#define FILE_HEADER
+
+void editorOpen(char*);
+int editorSave();
+
+#endif
diff --git a/find.c b/find.c
@@ -0,0 +1,88 @@
+#include"find.h"
+#include"jim.h"
+#include"output.h"
+#include"input.h"
+#include"row.h"
+#include"syntax.h"
+#include<string.h>
+#include<stdlib.h>
+
+void editorFindCallback(char* query, int key) {
+ static int l_match = -1;
+ static int direction = 1;
+
+ static int saved_hl_line;
+ static char* 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;
+ }
+
+ switch(key) {
+ case ESCAPE:
+ case RETURN:
+ l_match = -1;
+ direction = 1;
+ return;
+ case CTRL_KEY(NEXT):
+ direction = 1;
+ break;
+ case CTRL_KEY(PREVIOUS):
+ direction = -1;
+ break;
+ default:
+ l_match = -1;
+ direction = 1;
+ break;
+ }
+
+ if(l_match == -1)
+ direction = 1;
+
+ int current = l_match;
+ int i;
+ for(i = 0; i < E.numrows; i++) {
+ current += direction;
+ if(current == -1)
+ current = E.numrows - 1;
+ else if(current == E.numrows)
+ current = 0;
+
+ erow* row = &E.row[current];
+ char* match = strstr(row->render, query);
+ if(match) {
+ l_match = current;
+ E.cy = current;
+ E.cx = editorRowRxToCx(row, match - row->render);
+ E.rowoff = E.numrows;
+
+ saved_hl_line = current;
+ saved_hl = malloc(row->rsize);
+ memcpy(saved_hl, row->hl, row->rsize);
+ memset(&row->hl[match - row->render], HL_MATCH, strlen(query));
+ break;
+ }
+ }
+}
+
+void editorFind() {
+ int s_cx = E.cx;
+ int s_cy = E.cy;
+ int s_coloff = E.coloff;
+ int s_rowoff = E.rowoff;
+
+ char* query = editorPrompt("/%s", editorFindCallback);
+
+ if(query) {
+ free(query);
+ } else {
+ E.cx = s_cx;
+ E.cy = s_cy;
+ E.coloff = s_coloff;
+ E.rowoff = s_rowoff;
+ }
+
+ editorSetDefaultStatusMessage();
+}
diff --git a/find.h b/find.h
@@ -0,0 +1,6 @@
+#ifndef FIND_HEADER
+#define FIND_HEADER
+
+void editorFind();
+
+#endif
diff --git a/input.c b/input.c
@@ -0,0 +1,272 @@
+#include"input.h"
+#include"jim.h"
+#include"editor.h"
+#include"row.h"
+#include"file.h"
+#include"find.h"
+#include"output.h"
+#include"term.h"
+#include<stdlib.h>
+#include<ctype.h>
+
+void processClusterKey(char c) {
+ switch(E.clusterkey) {
+ case SCREEN_POS:
+ switch(c) {
+ case SCREEN_POS:
+ {
+ int mid = E.cy - E.screenrows / 2;
+ E.rowoff = mid < 0 ? 0 : mid;
+ }
+ break;
+ case T:
+ E.rowoff = E.cy;
+ break;
+ case B:
+ {
+ int bottom = E.cy - E.screenrows;
+ E.rowoff = bottom < 0 ? 0 : bottom;
+ }
+ break;
+ }
+ break;
+ case FORCE:
+ switch(c) {
+ case QUIT:
+ editorClearAndResetScreen();
+ exit(0);
+ }
+ break;
+ }
+
+ E.clusterkey = '\0';
+}
+
+char* editorPrompt(char* prompt, void (*callback)(char*, int)) {
+ size_t bufsize = 128;
+ char* buf = malloc(bufsize);
+ size_t buflen = 0;
+ buf[0] = '\0';
+
+ while(1) {
+ editorSetStatusMessage(prompt, buf);
+ editorRefreshScreen();
+
+ char c = editorReadKey();
+ switch(c) {
+ case BACKSPACE:
+ if(buflen != 0)
+ buf[--buflen] = '\0';
+ break;
+ case ESCAPE:
+ if(callback)
+ callback(buf, c);
+ free(buf);
+ return NULL;
+ break;
+ case RETURN:
+ if(buflen != 0) {
+ if(callback)
+ callback(buf, c);
+ return buf;
+ }
+ break;
+ default:
+ if(!iscntrl(c)) {
+ if(buflen == bufsize - 1) {
+ bufsize *= 2;
+ buf = realloc(buf, bufsize);
+ }
+
+ buf[buflen++] = c;
+ buf[buflen] = '\0';
+ }
+ break;
+ }
+
+ if(callback)
+ callback(buf, c);
+ }
+}
+
+int editorMoveCursor(char key) {
+ erow* row = (E.cy >= E.numrows) ? NULL : &E.row[E.cy];
+ int ret = 0;
+
+ switch(key) {
+ case LEFT:
+ if(E.cx != 0) {
+ E.cx--;
+ }
+ break;
+ case RIGHT:
+ if(row && E.cx < row->size - 1) {
+ E.cx++;
+ }
+ break;
+ case UP:
+ if(E.cy != 0) {
+ E.cy--;
+ }
+ break;
+ case DOWN:
+ if(E.cy + 1 < E.numrows) {
+ E.cy++;
+ }
+ break;
+ }
+
+ row = (E.cy >= E.numrows) ? NULL : &E.row[E.cy];
+ int rowlen = row ? row->size : 0;
+ if(E.cx >= rowlen) {
+ E.cx = rowlen ? rowlen - 1 : 0;
+ }
+
+ return ret;
+}
+
+void editorProcessKeypress() {
+ char c = editorReadKey();
+
+ if(E.mode == INSERT) {
+ switch(c) {
+ case RETURN:
+ editorInsertNewline();
+ break;
+ case DEL:
+ editorDelChar();
+ break;
+ case BACKSPACE:
+ editorBackspaceChar();
+ break;
+ case ESCAPE:
+ if(E.cx > 0) {
+ E.cx--;
+ }
+ E.mode = NORMAL;
+ editorSetDefaultStatusMessage();
+ break;
+ default:
+ editorInsertChar(c);
+ break;
+ }
+ }
+ else if(E.mode == COMMAND) {
+ switch(c) {
+ case ESCAPE:
+ E.mode = NORMAL;
+ editorSetDefaultStatusMessage();
+ break;
+ }
+ }
+ else if(E.mode == NORMAL) {
+ editorSetDefaultStatusMessage();
+
+ if(E.clusterkey != '\0') {
+ processClusterKey(c);
+ } else {
+ switch(c) {
+ case QUIT:
+ if(E.dirty) {
+ E.isbold = 1;
+ E.isr = 0;
+ editorSetStatusMessage("There are unwritten changes (prepend '!' to override)");
+ } else {
+ editorClearAndResetScreen();
+ exit(0);
+ }
+ break;
+ case S:
+ if(!editorSave()) {
+ editorClearAndResetScreen();
+ exit(0);
+ } else {
+ editorSetDefaultStatusMessage();
+ }
+ break;
+ case COLON:
+ E.mode = COMMAND;
+ E.isr = 0;
+ editorSetStatusMessage(":");
+ break;
+ case DELETE_LINE:
+ editorDelRow(E.cy);
+ E.cx = 0;
+ break;
+ case INSERT_LINE:
+ E.cx = 0;
+ E.mode = INSERT;
+ E.isbold = 1;
+ editorSetStatusMessage("-- INSERT --");
+ break;
+ case APPEND_LINE:
+ if(E.row[E.cy].size > 0)
+ E.cx = E.row[E.cy].size - 1;
+ //fallthrough
+ case APPEND:
+ if(E.row[E.cy].size > 0)
+ E.cx++;
+ //fallthrough
+ case INS:
+ E.mode = INSERT;
+ E.isbold = 1;
+ editorSetStatusMessage("-- INSERT --");
+ break;
+ case NEWLINE_ABOVE:
+ case NEWLINE_BELOW:
+ editorJumpToNewline(c);
+ E.mode = INSERT;
+ E.isbold = 1;
+ editorSetStatusMessage("-- INSERT --");
+ break;
+ case DEL_NOR:
+ editorDelChar();
+ if(E.cx == E.row[E.cy].size)
+ E.cx--;
+ break;
+ case SAVE:
+ if(editorSave())
+ editorSetDefaultStatusMessage();
+ break;
+ case LEFT:
+ case DOWN:
+ case UP:
+ case RIGHT:
+ editorMoveCursor(c);
+ break;
+ case GOTO:
+ E.cy = 0;
+ E.cx = 0;
+ break;
+ case FORCE:
+ case SCREEN_POS:
+ E.clusterkey = c;
+ break;
+ case FILE_BOTTOM:
+ E.cx = 0;
+ E.cy = E.numrows - 1;
+ break;
+ case LINE_START:
+ E.cx = 0;
+ break;
+ case LINE_END:
+ if(E.cy < E.numrows)
+ E.cx = E.row[E.cy].size - 1;
+ break;
+ case CTRL_KEY(B):
+ E.cy = E.rowoff;
+ int back = E.cy - E.screenrows;
+ E.rowoff = back < 0 ? 0 : back;
+ break;
+ case CTRL_KEY(F):
+ E.cy = E.rowoff + E.screenrows - 1;
+ if(E.cy > E.numrows) E.cy = E.numrows;
+ E.rowoff = E.cy + E.screenrows - 1;
+ break;
+ case SEARCH:
+ editorFind();
+ break;
+ }
+ }
+ }
+}
diff --git a/input.h b/input.h
@@ -0,0 +1,7 @@
+#ifndef INPUT_HEADER
+#define INPUT_HEADER
+
+char* editorPrompt(char*, void (*callback)(char*, int));
+void editorProcessKeypress();
+
+#endif
diff --git a/jim.1 b/jim.1
@@ -0,0 +1,16 @@
+.\" Manpage for jim
+.TH man 1 "02 March 2022" VERSION "jim man page"
+.SH NAME
+jim \- Jan's vim-inspired modal text editor
+.SH SYNOPSIS
+.B jim
+[filename]
+.SH DESCRIPTION
+A simple, lightweight modal text editor with syntax highlighting for C's .c and .h files, basic search functionality and a collection of basic vim-like key-bindings
+.SH OPTIONS
+.B filename
+\- Name of the file you wish to edit
+.SH BUGS
+Please see the TODO file
+.SH AUTHOR
+Jan P. Pasierb <me@janpasierb.com>
diff --git a/jim.h b/jim.h
@@ -0,0 +1,98 @@
+#ifndef HEADER_JIM
+#define HEADER_JIM
+
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
+#define _GNU_SOURCE
+
+#include<termios.h>
+
+#define JIM_VER "0.0.1"
+#define EOF_PLACEHOLDER "+"
+
+#define CTRL_KEY(k) ((k) & 0x1F)
+
+#define CURSOR_TOP "\x1b[H"
+
+enum editorKey {
+ DEL = '\x08',
+ DEL_NOR = 'x',
+ ESCAPE = '\x1b',
+ BACKSPACE = '\x7f',
+ RETURN = '\r',
+ QUIT = 'q',
+ NEWLINE_BELOW = 'o',
+ NEWLINE_ABOVE = 'O',
+ LEFT = 'h',
+ DOWN = 'j',
+ UP = 'k',
+ RIGHT = 'l',
+ GOTO = 'g',
+ FILE_BOTTOM = 'G',
+ SCREEN_POS = 'z',
+ SAVE = 's',
+ S = 'S',
+ T = 't',
+ B = 'b',
+ F = 'f',
+ NEXT = 'n',
+ PREVIOUS = 'p',
+ INS = 'i',
+ INSERT_LINE = 'I',
+ APPEND = 'a',
+ APPEND_LINE = 'A',
+ DELETE_LINE = 'd',
+ LINE_START = '0',
+ LINE_END = '$',
+ COLON = ':',
+ PERIOD = '.',
+ FORCE = '!',
+ SEARCH = '/',
+ SINGLE_QUOTE = '\'',
+ DOUBLE_QUOTE = '"',
+ BACKSLASH = '\\',
+ LESS_THAN = '<',
+ MORE_THAN = '>',
+ CONTROL = '@',
+ NONPRINTABLE = '?'
+};
+
+enum editorMode {
+ NORMAL,
+ INSERT,
+ COMMAND
+};
+
+typedef struct erow {
+ int idx;
+ int size;
+ int rsize;
+ char* chars;
+ char* render;
+ unsigned char* hl;
+ int hl_open_comment;
+} erow;
+
+struct editorConfig {
+ int cx, cy;
+ int rx;
+ int rowoff;
+ int coloff;
+ int screenrows;
+ int screencols;
+ int numrows;
+ int dirty;
+ int isr;
+ int isbold;
+ erow* row;
+ char* filename;
+ char statusmsg[80];
+ char clusterkey;
+ enum editorMode mode;
+ struct termios orig_termios;
+ struct editorSyntax* syntax;
+};
+
+extern struct editorConfig E;
+
+#endif
diff --git a/main.c b/main.c
@@ -0,0 +1,46 @@
+#include"jim.h"
+#include"output.h"
+#include"term.h"
+#include"file.h"
+#include"input.h"
+#include"output.h"
+#include<stddef.h>
+
+struct editorConfig E;
+
+void initEditor() {
+ E.rowoff = 0;
+ E.coloff = 0;
+ E.numrows = 0;
+ E.row = NULL;
+ E.filename = NULL;
+ E.statusmsg[0] = '\0';
+ E.clusterkey = '\0';
+ E.mode = NORMAL;
+ E.dirty = 0;
+ E.isr = 1;
+ E.isbold = 0;
+ E.cx = 0;
+ E.cy = 0;
+ E.rx = 0;
+ E.syntax = NULL;
+
+ if(getWindowSize(&E.screenrows, &E.screencols) == -1) die("getWindowSize");
+ E.screenrows--;
+}
+
+int main(int argc, char* argv[]) {
+ enableRawMode();
+ initEditor();
+ if(argc >= 2)
+ editorOpen(argv[1]);
+
+ editorSetStatusMessage("\"%.20s\" %dL", E.filename ? E.filename : "[No Name]", E.numrows);
+
+ while(1) {
+ editorRefreshScreen();
+ editorProcessKeypress();
+ }
+
+ return 0;
+}
diff --git a/output.c b/output.c
@@ -0,0 +1,183 @@
+#include"output.h"
+#include"jim.h"
+#include"appbuf.h"
+#include"syntax.h"
+#include"row.h"
+#include<stdio.h>
+#include<ctype.h>
+#include<string.h>
+#include<stdarg.h>
+#include<unistd.h>
+
+#define ABUF_INIT {NULL, 0}
+
+#define CLEAR_LINE "\x1b[K"
+#define CURSOR_HIDE "\x1b[?25l"
+#define CURSOR_SHOW "\x1b[?25h"
+#define COLOUR "\x1b[%dm"
+#define MAKE_BOLD "\x1b[1m"
+#define INVERT_COLORS "\x1b[7m"
+#define RESTORE_ATTRIBUTES "\x1b[m"
+#define RESTORE_COLOURS "\x1b[39;49m"
+
+void editorScroll() {
+ E.rx = 0;
+
+ if(E.cy < E.numrows) {
+ E.rx = editorRowCxToRx(&E.row[E.cy], E.cx);
+ }
+
+ if(E.cy < E.rowoff) {
+ E.rowoff = E.cy;
+ }
+
+ if(E.cy >= E.rowoff + E.screenrows) {
+ E.rowoff = E.cy - E.screenrows + 1;
+ }
+
+ if(E.rx < E.coloff) {
+ E.coloff = E.rx;
+ }
+
+ if(E.rx >= E.coloff + E.screencols) {
+ E.coloff = E.rx - E.screencols + 1;
+ }
+}
+
+void editorDrawRows(struct abuf *ab) {
+ int y;
+
+ for (y = 0; y < E.screenrows; y++) {
+ int filerow = y + E.rowoff;
+
+ if(filerow >= E.numrows) {
+ if(E.cx == 0 && E.numrows == 0 && y == E.screenrows / 2) {
+ char welcome[80];
+ int welcomelen = snprintf(welcome, sizeof(welcome), "JIM editor -- version %s", JIM_VER);
+ if(welcomelen > E.screencols) welcomelen = E.screencols;
+ int padding = (E.screencols - welcomelen) / 2;
+ if(padding) {
+ abAppend(ab, EOF_PLACEHOLDER, 1);
+ padding--;
+ }
+ while(padding--) abAppend(ab, " ", 1);
+ abAppend(ab, welcome, welcomelen);
+ } else if(y != 0) {
+ abAppend(ab, EOF_PLACEHOLDER, 1);
+ }
+ } else {
+ int len = E.row[filerow].rsize - E.coloff;
+ if(len < 0 ) len = 0;
+ if(len > E.screencols) len = E.screencols;
+ char* c = &E.row[filerow].render[E.coloff];
+ unsigned char* hl = &E.row[filerow].hl[E.coloff];
+ int cur_col = -1;
+ for(int j = 0; j < len; j++) {
+ if(iscntrl(c[j])) {
+ char symbol = (c[j] <= 26) ? CONTROL + c[j] : NONPRINTABLE;
+ abAppend(ab, INVERT_COLORS, 4);
+ abAppend(ab, &symbol, 1);
+ abAppend(ab, RESTORE_ATTRIBUTES, 3);
+ if(cur_col != -1) {
+ char buf[16];
+ int col_len = snprintf(buf, sizeof(buf), COLOUR, cur_col);
+ abAppend(ab, RESTORE_COLOURS, 8);
+ abAppend(ab, buf, col_len);
+ }
+ } else if(hl[j] == HL_NORMAL) {
+ if(cur_col != -1) {
+ abAppend(ab, RESTORE_COLOURS, 8);
+ cur_col = -1;
+ }
+ abAppend(ab, &c[j], 1);
+ } else {
+ int colour = editorSyntaxToColour(hl[j]);
+ if(colour != cur_col) {
+ cur_col = colour;
+ char buf[16];
+ int col_len = snprintf(buf, sizeof(buf), COLOUR, colour);
+ abAppend(ab, RESTORE_COLOURS, 8);
+ abAppend(ab, buf, col_len);
+ }
+
+ abAppend(ab, &c[j], 1);
+ }
+ }
+
+ abAppend(ab, RESTORE_COLOURS, 8);
+ }
+
+ abAppend(ab, CLEAR_LINE, 3);
+ abAppend(ab, "\r\n", 2);
+ }
+}
+
+void editorDrawStatusBar(struct abuf *ab) {
+ abAppend(ab, CLEAR_LINE, 3);
+ abAppend(ab, INVERT_COLORS, 4);
+
+ char rstatus[80];
+ int rlen = 0;
+ int msglen = strlen(E.statusmsg);
+
+ if(msglen > E.screencols) msglen = E.screencols;
+
+ if(E.isbold)
+ abAppend(ab, MAKE_BOLD, 4);
+ abAppend(ab, E.statusmsg, msglen);
+ abAppend(ab, RESTORE_ATTRIBUTES, 3);
+ abAppend(ab, INVERT_COLORS, 4);
+
+ if(E.isr) {
+ float posr = ((E.cy + 1) / (float)E.numrows) * 100;
+ rlen = snprintf(rstatus, sizeof(rstatus), "%d,%d %.0f%%",
+ E.cy + 1, E.rx + 1, posr < 1.0 ? 1.0 : posr);
+ }
+
+ while(msglen < E.screencols) {
+ if(E.screencols - msglen == rlen) {
+ abAppend(ab, rstatus, rlen);
+ break;
+ } else {
+ abAppend(ab, " ", 1);
+ msglen++;
+ }
+ }
+
+ abAppend(ab, RESTORE_ATTRIBUTES, 3);
+}
+
+void editorRefreshScreen() {
+ editorScroll();
+
+ struct abuf ab = ABUF_INIT;
+
+ abAppend(&ab, CURSOR_HIDE, 6);
+ abAppend(&ab, CURSOR_TOP, 3);
+
+ editorDrawRows(&ab);
+ editorDrawStatusBar(&ab);
+
+ char buf[32];
+ snprintf(buf, sizeof(buf), "\x1B[%d;%dH", (E.cy - E.rowoff) + 1, (E.rx - E.coloff) + 1);
+
+ abAppend(&ab, buf, strlen(buf));
+
+ abAppend(&ab, CURSOR_SHOW, 6);
+
+ write(STDOUT_FILENO, ab.b, ab.len);
+ abFree(&ab);
+}
+
+void editorSetStatusMessage(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(E.statusmsg, sizeof(E.statusmsg), fmt, ap);
+ va_end(ap);
+}
+
+void editorSetDefaultStatusMessage() {
+ E.isbold = 0;
+ E.isr = 1;
+ editorSetStatusMessage("\"%.20s\" %dL", E.filename ? E.filename : "[No Name]", E.numrows);
+}
diff --git a/output.h b/output.h
@@ -0,0 +1,8 @@
+#ifndef OUTPUT_HEADER
+#define OUTPUT_HEADER
+
+void editorRefreshScreen();
+void editorSetStatusMessage(const char*, ...);
+void editorSetDefaultStatusMessage();
+
+#endif
diff --git a/row.c b/row.c
@@ -0,0 +1,129 @@
+#include"row.h"
+#include"jim.h"
+#include"syntax.h"
+#include<stdlib.h>
+#include<string.h>
+
+#define TAB_STOP 4
+
+int editorRowCxToRx(erow* row, int cx) {
+ int rx = 0;
+
+ for(int j = 0; j < cx; j++) {
+ if(row->chars[j] == '\t')
+ rx += (TAB_STOP - 1) - (rx % TAB_STOP);
+ rx++;
+ }
+
+ return rx;
+}
+
+int editorRowRxToCx(erow* row, int rx) {
+ int cur_rx = 0;
+ int cx;
+
+ for(cx = 0; cx < row->size; cx++) {
+ if(row->chars[cx] == '\t')
+ cur_rx += (TAB_STOP - 1) - (cur_rx % TAB_STOP);
+ cur_rx++;
+
+ if(cur_rx > rx) return cx;
+ }
+
+ return cx;
+}
+
+void editorUpdateRow(erow* row) {
+ int tabs = 0;
+ int j;
+
+ for(j = 0; j < row->size; j++)
+ if(row->chars[j] == '\t') tabs++;
+
+ free(row->render);
+ row->render = malloc(row->size + tabs * (TAB_STOP - 1) + 1);
+
+ int idx = 0;
+ for(j = 0; j < row->size; j++) {
+ if(row->chars[j] == '\t') {
+ row->render[idx++] = ' ';
+ while(idx % TAB_STOP != 0) row->render[idx++] = ' ';
+ } else {
+ row->render[idx++] = row->chars[j];
+ }
+ }
+
+ row->render[idx] = '\0';
+ row->rsize = idx;
+
+ editorUpdateSyntax(row);
+}
+
+void editorInsertRow(int at, char* s, size_t len) {
+ if(at < 0 || at > E.numrows) return;
+
+ E.row = realloc(E.row, sizeof(erow) * (E.numrows + 1));
+ memmove(&E.row[at + 1], &E.row[at], sizeof(erow) * (E.numrows - at));
+ for(int j = at + 1; j <= E.numrows; j++)
+ E.row[j].idx++;
+
+ E.row[at].idx = at;
+
+ E.row[at].size = len;
+ E.row[at].chars = malloc(len + 1);
+ memcpy(E.row[at].chars, s, len);
+ E.row[at].chars[len] = '\0';
+
+ E.row[at].rsize = 0;
+ E.row[at].render = NULL;
+ E.row[at].hl = NULL;
+ E.row[at].hl_open_comment = 0;
+ editorUpdateRow(&E.row[at]);
+
+ E.numrows++;
+ E.dirty++;
+}
+
+void editorFreeRow(erow *row) {
+ free(row->render);
+ free(row->chars);
+ free(row->hl);
+}
+
+void editorDelRow(int at) {
+ if(at < 0 || at >= E.numrows) return;
+ editorFreeRow(&E.row[at]);
+ memmove(&E.row[at], &E.row[at + 1], sizeof(erow) * (E.numrows - at - 1));
+ for(int j = at; j < E.numrows - 1; j++)
+ E.row[j].idx--;
+ E.numrows--;
+ if(E.cy == E.numrows)
+ E.cy--;
+ E.dirty++;
+}
+
+void editorRowInsertChar(erow* row, int at, char c) {
+ if(at < 0 || at > row->size) at = row->size;
+ 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);
+}
+
+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++;
+}
+
+void editorRowDelChar(erow *row, int at) {
+ if(at < 0 || at >= row->size) return;
+ memmove(&row->chars[at], &row->chars[at + 1], row->size - at);
+ row->size--;
+ editorUpdateRow(row);
+ E.dirty++;
+}
diff --git a/row.h b/row.h
@@ -0,0 +1,16 @@
+#ifndef ROW_HEADER
+#define ROW_HEADER
+
+#include"jim.h"
+#include<stddef.h>
+
+int editorRowCxToRx(erow*, int);
+int editorRowRxToCx(erow*, int);
+void editorUpdateRow(erow*);
+void editorInsertRow(int, char*, size_t);
+void editorDelRow(int);
+void editorRowInsertChar(erow*, int, char);
+void editorRowAppendString(erow*, char*, size_t);
+void editorRowDelChar(erow*, int);
+
+#endif
diff --git a/syntax.c b/syntax.c
@@ -0,0 +1,262 @@
+#include"syntax.h"
+#include<stdlib.h>
+#include<ctype.h>
+#include<string.h>
+
+#define FG_RED 31
+#define FG_GREEN 32
+#define FG_YELLOW 33
+#define FG_BLUE 34
+#define FG_MAGENTA 35
+#define FG_CYAN 36
+#define FG_WHITE 37
+#define BG_GREY 100
+
+#define HL_HIGHLIGHT_NUMBERS (1<<0)
+#define HL_HIGHLIGHT_STRINGS (1<<1)
+
+struct editorSyntax {
+ char* filetype;
+ char** filematch;
+ char** directives;
+ char** keywords;
+ char** datatypes;
+ char* sl_comment_start;
+ char* ml_comment_start;
+ char* ml_comment_end;
+ int flags;
+};
+
+char* C_HL_extensions[] = {".c", ".h", NULL};
+char* C_HL_keywords[] = {"switch", "if", "while", "for", "break", "continue",
+ "return", "else", "union", "static", "case", "sizeof", "const", "default", NULL};
+char* C_HL_datatypes[] = {"int", "long", "double", "float", "char", "unsigned", "signed",
+ "void", "struct", "typedef", "enum", "size_t", "va_list", "ssize_t", "FILE", NULL};
+char* C_HL_directives[] = {"#define", "#include", "#ifdef", "#undef", "#ifndef", "#if", "#else",
+ "#elif", "#endif", "#error", "#pragma", NULL};
+
+struct editorSyntax HLDB[] = {
+ {
+ "c",
+ C_HL_extensions,
+ C_HL_directives,
+ C_HL_keywords,
+ C_HL_datatypes,
+ "//",
+ "/*",
+ "*/",
+ HL_HIGHLIGHT_NUMBERS | HL_HIGHLIGHT_STRINGS
+ },
+};
+
+#define HLDB_ENTRIES (sizeof(HLDB) / sizeof(HLDB[0]))
+
+int isSeparator(int c) {
+ return isspace(c) || c == '\0' || strchr(",.()+-/'*=~%<>[];:{}\"", c) != NULL;
+}
+
+void editorUpdateSyntax(erow* row) {
+ row->hl = realloc(row->hl, row->rsize);
+ memset(row->hl, HL_NORMAL, row->rsize);
+
+ if(E.syntax == NULL)
+ return;
+
+ char** directives = E.syntax->directives;
+ char** keywords = E.syntax->keywords;
+ char** datatypes = E.syntax->datatypes;
+
+ char* scs = E.syntax->sl_comment_start;
+ char* mcs = E.syntax->ml_comment_start;
+ char* mce = E.syntax->ml_comment_end;
+
+ int scs_len = scs ? strlen(scs) : 0;
+ int mcs_len = mcs ? strlen(mcs) : 0;
+ int mce_len = mce ? strlen(mce) : 0;
+
+ int prev_sep = 1;
+ int in_string = 0;
+ int in_comment = (row->idx > 0 && E.row[row->idx - 1].hl_open_comment);
+ int cont_dir = 0, dir_lt = 0;
+
+ int i = 0;
+ while(i < row->rsize) {
+ char c = row->render[i];
+ unsigned char prev_hl = (i > 0) ? row->hl[i - 1] : HL_NORMAL;
+
+ if(scs_len && !in_string && !in_comment) {
+ if(!strncmp(&row->render[i], scs, scs_len)) {
+ memset(&row->hl[i], HL_COMMENT, row->rsize - i);
+ break;
+ }
+ }
+
+ if(mcs_len && mce_len && !in_string) {
+ if(in_comment) {
+ row->hl[i] = HL_MLCOMMENT;
+ if(!strncmp(&row->render[i], mce, mce_len)) {
+ memset(&row->hl[i], HL_MLCOMMENT, mce_len);
+ i += mce_len;
+ in_comment = 0;
+ prev_sep = 1;
+ continue;
+ } else {
+ i++;
+ continue;
+ }
+ } else if(!strncmp(&row->render[i], mcs, mcs_len)) {
+ memset(&row->hl[i], HL_MLCOMMENT, mcs_len);
+ i += mcs_len;
+ in_comment = 1;
+ continue;
+ }
+ }
+
+ for(int j = 0; directives[j]; j++) {
+ int dir_len = strlen(directives[j]);
+
+ if(!strncmp(&row->render[i], directives[j], dir_len) &&
+ isSeparator(row->render[i + dir_len]) && !in_string) {
+ memset(&row->hl[i], HL_DIRECTIVE, dir_len);
+ cont_dir = 1;
+ i += dir_len;
+ c = row->render[i];
+ break;
+ }
+ }
+
+ if(E.syntax->flags & HL_HIGHLIGHT_STRINGS) {
+ if(in_string) {
+ row->hl[i] = HL_STRING;
+ if(c == BACKSLASH && i + 1 < row->rsize) {
+ row->hl[i + 1] = HL_STRING;
+ i += 2;
+ continue;
+ }
+ if(c == in_string)
+ in_string = 0;
+ i++;
+ prev_sep = 1;
+ continue;
+ } else {
+ if(c == DOUBLE_QUOTE || c == SINGLE_QUOTE) {
+ in_string = c;
+ row->hl[i] = HL_STRING;
+ i++;
+ continue;
+ }
+ }
+ }
+
+ if(cont_dir && !in_string) {
+ if(c == LESS_THAN) {
+ dir_lt = i;
+ } else if(c == MORE_THAN) {
+ memset(&row->hl[dir_lt], HL_STRING, i - dir_lt + 1);
+ } else if(dir_lt) {
+ row->hl[i] = HL_NORMAL;
+ } else {
+ row->hl[i] = HL_DIRECTIVE;
+ }
+ }
+
+ if(E.syntax->flags & HL_HIGHLIGHT_NUMBERS) {
+ if((isdigit(c) && (prev_sep || prev_hl == HL_NUMBER)) ||
+ (c == PERIOD && prev_hl == HL_NUMBER)) {
+ row->hl[i] = HL_NUMBER;
+ i++;
+ prev_sep = 0;
+ continue;
+ }
+ }
+
+ if(prev_sep) {
+ int j, found = 0;
+ for(j = 0; keywords[j]; j++) {
+ int kwd_len = strlen(keywords[j]);
+
+ if(!strncmp(&row->render[i], keywords[j], kwd_len) &&
+ isSeparator(row->render[i + kwd_len])) {
+ memset(&row->hl[i], HL_KEYWORD, kwd_len);
+ i += kwd_len;
+ found++;
+ break;
+ }
+ }
+ for(j = 0; datatypes[j]; j++) {
+ int dt_len = strlen(datatypes[j]);
+
+ if(!strncmp(&row->render[i], datatypes[j], dt_len) &&
+ isSeparator(row->render[i + dt_len])) {
+ memset(&row->hl[i], HL_DATATYPE, dt_len);
+ i += dt_len;
+ found++;
+ break;
+ }
+ }
+
+ if(found) {
+ prev_sep = 0;
+ continue;
+ }
+}
+
+ prev_sep = isSeparator(c);
+ i++;
+ }
+
+ int changed = (row->hl_open_comment != in_comment);
+ row->hl_open_comment = in_comment;
+ if(changed && row->idx + 1 < E.numrows)
+ editorUpdateSyntax(&E.row[row->idx + 1]);
+}
+
+int editorSyntaxToColour(int hl) {
+ switch(hl) {
+ case HL_COMMENT:
+ case HL_MLCOMMENT:
+ return FG_CYAN;
+ case HL_DIRECTIVE:
+ return FG_MAGENTA;
+ case HL_KEYWORD:
+ return FG_YELLOW;
+ case HL_DATATYPE:
+ return FG_GREEN;
+ case HL_STRING:
+ return FG_RED;
+ case HL_NUMBER:
+ return FG_RED;
+ case HL_MATCH:
+ return BG_GREY;
+ default:
+ return FG_WHITE;
+ }
+}
+
+void editorSelectSyntaxHighlight() {
+ E.syntax = NULL;
+ if(E.filename == NULL)
+ return;
+
+ char* ext = strrchr(E.filename, PERIOD);
+
+ for(unsigned int j = 0; j < HLDB_ENTRIES; j++) {
+ struct editorSyntax* s = &HLDB[j];
+ unsigned int i = 0;
+ while(s->filematch[i]) {
+ int is_ext = (s->filematch[i][0] == PERIOD);
+ if((is_ext && ext && !strcmp(ext, s->filematch[i])) ||
+ (!is_ext && strstr(E.filename, s->filematch[i]))) {
+ E.syntax = s;
+
+ for(int filerow = 0; filerow < E.numrows; filerow++) {
+ editorUpdateSyntax(&E.row[filerow]);
+ }
+
+ return;
+ }
+
+ i++;
+ }
+ }
+}
diff --git a/syntax.h b/syntax.h
@@ -0,0 +1,22 @@
+#ifndef SYNTAX_HEADER
+#define SYNTAX_HEADER
+
+#include"jim.h"
+
+enum editorHighlight {
+ HL_NORMAL = 0,
+ HL_COMMENT,
+ HL_MLCOMMENT,
+ HL_DIRECTIVE,
+ HL_KEYWORD,
+ HL_DATATYPE,
+ HL_STRING,
+ HL_NUMBER,
+ HL_MATCH
+};
+
+void editorUpdateSyntax(erow*);
+int editorSyntaxToColour(int);
+void editorSelectSyntaxHighlight();
+
+#endif
diff --git a/term.c b/term.c
@@ -0,0 +1,108 @@
+#include"term.h"
+#include"jim.h"
+#include"output.h"
+#include<unistd.h>
+#include<stdlib.h>
+#include<stdio.h>
+#include<errno.h>
+#include<sys/ioctl.h>
+
+#define CLEAR_SCRN "\x1b[2J"
+#define CURSOR_FWD_DOWN "\x1B[999C\x1B[999B"
+#define TERM_STAT_INFO "\x1B[6n"
+
+void editorClearAndResetScreen() {
+ write(STDOUT_FILENO, CLEAR_SCRN, 4);
+ write(STDOUT_FILENO, CURSOR_TOP, 3);
+}
+
+void die(const char* s) {
+ editorClearAndResetScreen();
+
+ perror(s);
+ exit(1);
+}
+
+void disableRawMode() {
+ if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &E.orig_termios) == -1)
+ die("tcsetattr");
+}
+
+void enableRawMode() {
+ if(tcgetattr(STDIN_FILENO, &E.orig_termios) == -1) die("tcgetattr");
+ atexit(disableRawMode);
+
+ struct termios raw = E.orig_termios;
+ raw.c_iflag &= ~(ICRNL | IXON | BRKINT | INPCK | ISTRIP);
+ raw.c_oflag &= ~(OPOST);
+ raw.c_cflag |= (CS8);
+ raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+ raw.c_cc[VMIN] = 0;
+ raw.c_cc[VTIME] = 1;
+
+ if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) die("tcsetattr");
+}
+
+char editorReadKey() {
+ int nread;
+ char c;
+
+ while((nread = read(STDIN_FILENO, &c, 1)) != 1) {
+ if(nread == -1 && errno != EAGAIN) die("read");
+
+ if(getWindowSize(&E.screenrows, &E.screencols) == -1) die("getWindowSize");
+ E.screenrows--;
+ editorRefreshScreen();
+ }
+
+ if(c == ESCAPE) {
+ char seq[2];
+
+ if(read(STDIN_FILENO, &seq[0], 1) != 1) return ESCAPE;
+ if(read(STDIN_FILENO, &seq[1], 1) != 1) return ESCAPE;
+
+ if(seq[0] == '[') {
+ switch(seq[1]) {
+ case 'P':
+ return DEL;
+ }
+ }
+
+ return ESCAPE;
+ } else {
+ return c;
+ }
+}
+
+int getCursorPosition(int* rows, int* cols) {
+ char buf[32];
+ unsigned int i = 0;
+
+ if(write(STDOUT_FILENO, TERM_STAT_INFO, 4) != 4) return -1;
+
+ while(i < sizeof(buf) - 1) {
+ if(read(STDIN_FILENO, &buf[i], 1) != 1) break;
+ if(buf[i] == 'R') break;
+ i++;
+ }
+
+ buf[i] = '\0';
+
+ if(buf[0] != ESCAPE || buf[1] != '[') return -1;
+ if(sscanf(&buf[2], "%d;%d", rows, cols) != 2) return -1;
+
+ return 0;
+}
+
+int getWindowSize(int* rows, int* cols) {
+ struct winsize ws;
+
+ if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
+ if(write(STDOUT_FILENO, CURSOR_FWD_DOWN, 12) != 12) return -1;
+ return getCursorPosition(rows, cols);
+ } else {
+ *cols = ws.ws_col;
+ *rows = ws.ws_row;
+ return 0;
+ }
+}
diff --git a/term.h b/term.h
@@ -0,0 +1,10 @@
+#ifndef TERM_HEADER
+#define TERM_HEADER
+
+void editorClearAndResetScreen();
+void die(const char*);
+void enableRawMode();
+char editorReadKey();
+int getWindowSize(int*, int*);
+
+#endif