commit 06dacdfa12189128fb57b9f611c2eff6e3d4169b
parent 1b9884eb1d08734ad600fd451764208332434652
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date: Sun, 9 Apr 2023 09:12:52 +0200
A Text viewer, clang-format
Diffstat:
A | .clang-format | | | 178 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | kilo.c | | | 329 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------- |
2 files changed, 414 insertions(+), 93 deletions(-)
diff --git a/.clang-format b/.clang-format
@@ -0,0 +1,178 @@
+---
+Language: Cpp
+# BasedOnStyle: LLVM
+AccessModifierOffset: -2
+AlignAfterOpenBracket: true
+AlignArrayOfStructures: Right
+AlignConsecutiveMacros: true
+AlignConsecutiveAssignments: None
+AlignConsecutiveBitFields: None
+AlignConsecutiveDeclarations: None
+AlignEscapedNewlines: Right
+AlignOperands: Align
+AlignTrailingComments: true
+AllowAllArgumentsOnNextLine: true
+AllowAllConstructorInitializersOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortEnumsOnASingleLine: true
+AllowShortBlocksOnASingleLine: true
+AllowShortCaseLabelsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: All
+AllowShortLambdasOnASingleLine: All
+AllowShortIfStatementsOnASingleLine: true
+AllowShortLoopsOnASingleLine: true
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: MultiLine
+AttributeMacros:
+ - __capability
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+ AfterCaseLabel: false
+ AfterClass: false
+ AfterControlStatement: Never
+ AfterEnum: false
+ AfterFunction: false
+ AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: false
+ AfterUnion: false
+ AfterExternBlock: false
+ BeforeCatch: false
+ BeforeElse: false
+ BeforeLambdaBody: false
+ BeforeWhile: false
+ IndentBraces: false
+ SplitEmptyFunction: true
+ SplitEmptyRecord: true
+ SplitEmptyNamespace: true
+BreakBeforeBinaryOperators: None
+BreakBeforeConceptDeclarations: true
+BreakBeforeBraces: Attach
+BreakBeforeInheritanceComma: false
+BreakInheritanceList: BeforeColon
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: false
+BreakConstructorInitializers: BeforeColon
+BreakAfterJavaFieldAnnotations: false
+BreakStringLiterals: true
+ColumnLimit: 80
+CommentPragmas: '^ IWYU pragma:'
+CompactNamespaces: false
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DeriveLineEnding: true
+DerivePointerAlignment: false
+DisableFormat: false
+EmptyLineAfterAccessModifier: Never
+EmptyLineBeforeAccessModifier: LogicalBlock
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros:
+ - foreach
+ - Q_FOREACH
+ - BOOST_FOREACH
+IfMacros:
+ - KJ_IF_MAYBE
+IncludeBlocks: Preserve
+IncludeCategories:
+ - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
+ Priority: 2
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: '^(<|"(gtest|gmock|isl|json)/)'
+ Priority: 3
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: '.*'
+ Priority: 1
+ SortPriority: 0
+ CaseSensitive: false
+IncludeIsMainRegex: '(Test)?$'
+IncludeIsMainSourceRegex: ''
+IndentAccessModifiers: false
+IndentCaseLabels: false
+IndentCaseBlocks: false
+IndentGotoLabels: true
+IndentPPDirectives: None
+IndentExternBlock: AfterExternBlock
+IndentRequires: false
+IndentWidth: 2
+IndentWrappedFunctionNames: false
+InsertTrailingCommas: None
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+LambdaBodyIndentation: Signature
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBinPackProtocolList: Auto
+ObjCBlockIndentWidth: 2
+ObjCBreakBeforeNestedBlockParam: true
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakAssignment: 2
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyBreakTemplateDeclaration: 10
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+PenaltyIndentedWhitespace: 0
+PointerAlignment: Right
+PPIndentWidth: -1
+ReferenceAlignment: Pointer
+ReflowComments: true
+ShortNamespaceLines: 1
+SortIncludes: CaseSensitive
+SortJavaStaticImport: Before
+SortUsingDeclarations: true
+SpaceAfterCStyleCast: false
+SpaceAfterLogicalNot: false
+SpaceAfterTemplateKeyword: true
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCaseColon: false
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceAroundPointerQualifiers: Default
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceInEmptyBlock: false
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: Never
+SpacesInConditionalStatement: false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInLineCommentPrefix:
+ Minimum: 1
+ Maximum: -1
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+SpaceBeforeSquareBrackets: false
+BitFieldColonSpacing: Both
+Standard: Latest
+StatementAttributeLikeMacros:
+ - Q_EMIT
+StatementMacros:
+ - Q_UNUSED
+ - QT_REQUIRE_VERSION
+TabWidth: 8
+UseCRLF: false
+UseTab: Never
+WhitespaceSensitiveMacros:
+ - STRINGIZE
+ - PP_STRINGIZE
+ - BOOST_PP_STRINGIZE
+ - NS_SWIFT_NAME
+ - CF_SWIFT_NAME
+...
+
diff --git a/kilo.c b/kilo.c
@@ -1,17 +1,25 @@
/*** includes ***/
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
+#define _GNU_SOURCE
+
#include <ctype.h>
#include <errno.h>
+#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
+#include <sys/types.h>
#include <termios.h>
+#include <time.h>
#include <unistd.h>
/*** defines ***/
-#define KILO_VERSION "0.0.1"
+#define KILO_VERSION "0.0.1"
+#define KILO_TAB_STOP 8
#define CTRL_KEY(k) ((k)&0x1f)
@@ -29,10 +37,25 @@ enum editorKey {
/*** data ***/
+typedef struct erow {
+ int size;
+ int rsize;
+ char *chars;
+ char *render;
+} erow;
+
struct editorConfig {
int cx, cy;
+ int rx;
+ int rowoff;
+ int coloff;
int screenrows;
int screencols;
+ int numrows;
+ erow *row;
+ char *filename;
+ char statusmsg[80];
+ time_t statusmsg_time;
struct termios orig_termios;
};
@@ -54,8 +77,7 @@ void disableRawMode() {
}
void enableRawMode() {
- if (tcgetattr(STDIN_FILENO, &E.orig_termios) == -1)
- die("tcgetattr");
+ if (tcgetattr(STDIN_FILENO, &E.orig_termios) == -1) die("tcgetattr");
atexit(disableRawMode);
struct termios raw = E.orig_termios;
@@ -67,70 +89,50 @@ void enableRawMode() {
raw.c_cc[VMIN] = 0;
raw.c_cc[VTIME] = 1;
- if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1)
- die("tcsetattr");
+ if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) die("tcsetattr");
}
int editorReadKey() {
int nread;
char c;
while ((nread = read(STDIN_FILENO, &c, 1)) != 1) {
- if (nread == -1 && errno != EAGAIN)
- die("read");
+ if (nread == -1 && errno != EAGAIN) die("read");
}
if (c == '\x1b') {
char seq[3];
- if (read(STDIN_FILENO, &seq[0], 1) != 1)
- return '\x1b';
- if (read(STDIN_FILENO, &seq[1], 1) != 1)
- return '\x1b';
+ if (read(STDIN_FILENO, &seq[0], 1) != 1) return '\x1b';
+ if (read(STDIN_FILENO, &seq[1], 1) != 1) return '\x1b';
if (seq[0] == '[') {
if (seq[1] >= '0' && seq[1] <= '9') {
- if (read(STDIN_FILENO, &seq[2], 1) != 1)
- return '\x1b';
+ if (read(STDIN_FILENO, &seq[2], 1) != 1) return '\x1b';
if (seq[2] == '~') {
switch (seq[1]) {
- case '1':
- return HOME_KEY;
- case '3':
- return DEL_KEY;
- case '4':
- return END_KEY;
- case '5':
- return PAGE_UP;
- case '6':
- return PAGE_DOWN;
- case '7':
- return HOME_KEY;
- case '8':
- return END_KEY;
+ case '1': return HOME_KEY;
+ case '3': return DEL_KEY;
+ case '4': return END_KEY;
+ case '5': return PAGE_UP;
+ case '6': return PAGE_DOWN;
+ case '7': return HOME_KEY;
+ case '8': return END_KEY;
}
}
} 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;
+ 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;
}
}
} else if (seq[0] == 'O') {
switch (seq[1]) {
- case 'H':
- return HOME_KEY;
- case 'F':
- return END_KEY;
+ case 'H': return HOME_KEY;
+ case 'F': return END_KEY;
}
}
return '\x1b';
@@ -143,22 +145,17 @@ int getCursorPosition(int *rows, int *cols) {
char buf[32];
unsigned int i = 0;
- if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4)
- return -1;
+ if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4) return -1;
while (i < sizeof(buf) - 1) {
- if (read(STDIN_FILENO, &buf[i], 1) != 1)
- break;
- if (buf[i] == 'R')
- break;
+ if (read(STDIN_FILENO, &buf[i], 1) != 1) break;
+ if (buf[i] == 'R') break;
i++;
}
buf[i] = '\0';
- if (buf[0] != '\x1b' || buf[1] != '[')
- return -1;
- if (sscanf(&buf[2], "%d;%d", rows, cols) != 2)
- return -1;
+ if (buf[0] != '\x1b' || buf[1] != '[') return -1;
+ if (sscanf(&buf[2], "%d;%d", rows, cols) != 2) return -1;
return 0;
}
@@ -167,8 +164,7 @@ int getWindowSize(int *rows, int *cols) {
struct winsize ws;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
- if (write(STDOUT_FILENO, "\x1b[999C\x1b[999B", 12) != 12)
- return -1;
+ if (write(STDOUT_FILENO, "\x1b[999C\x1b[999B", 12) != 12) return -1;
return getCursorPosition(rows, cols);
} else {
*cols = ws.ws_col;
@@ -177,6 +173,77 @@ int getWindowSize(int *rows, int *cols) {
return 0;
}
+/*** row operations ***/
+
+int editorRowCxToRx(erow *row, int cx) {
+ int rx = 0;
+ for (int j = 0; j < cx; j++) {
+ if (row->chars[j] == '\t') rx += (KILO_TAB_STOP - 1) - (rx % KILO_TAB_STOP);
+ rx++;
+ }
+ return rx;
+}
+
+void editorUpdateRow(erow *row) {
+ int tabs = 0;
+ for (int j = 0; j < row->size; j++)
+ if (row->chars[j] == '\t') tabs++;
+
+ free(row->render);
+ row->render = malloc(row->size + tabs * (KILO_TAB_STOP - 1) + 1);
+
+ int idx = 0;
+ for (int j = 0; j < row->size; j++) {
+ if (row->chars[j] == '\t') {
+ row->render[idx++] = ' ';
+ while (idx % KILO_TAB_STOP != 0) row->render[idx++] = ' ';
+ } else {
+ row->render[idx++] = row->chars[j];
+ }
+ }
+ row->render[idx] = '\0';
+ row->rsize = idx;
+}
+
+void editorAppendRow(char *s, size_t len) {
+ E.row = realloc(E.row, sizeof(erow) * (E.numrows + 1));
+
+ int at = E.numrows;
+ 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;
+ editorUpdateRow(&E.row[at]);
+
+ E.numrows++;
+}
+
+/*** file i/o ***/
+
+void editorOpen(char *filename) {
+ free(E.filename);
+ E.filename = strdup(filename);
+
+ 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] == '\r'))
+ linelen--;
+
+ editorAppendRow(line, linelen);
+ }
+ free(line);
+ fclose(fp);
+}
+
/*** append buffer ***/
struct abuf {
@@ -190,8 +257,7 @@ struct abuf {
void abAppend(struct abuf *ab, const char *s, int len) {
char *new = realloc(ab->b, ab->len + len);
- if (new == NULL)
- return;
+ if (new == NULL) return;
memcpy(&new[ab->len], s, len);
ab->b = new;
ab->len += len;
@@ -202,28 +268,36 @@ void abFree(struct abuf *ab) { free(ab->b); }
/*** input ***/
void editorMoveCursor(int key) {
+ erow *row = (E.cy >= E.numrows) ? NULL : &E.row[E.cy];
+
switch (key) {
case ARROW_LEFT:
if (E.cx != 0) {
E.cx--;
+ } else if (E.cy > 0) {
+ E.cy--;
+ E.cx = E.row[E.cy].size;
}
break;
case ARROW_RIGHT:
- if (E.cx != E.screencols - 1) {
+ if (row && E.cx < row->size) {
E.cx++;
+ } else if (row && E.cx == row->size) {
+ E.cy++;
+ E.cx = 0;
}
break;
case ARROW_UP:
- if (E.cy != 0) {
- E.cy--;
- }
+ if (E.cy != 0) { E.cy--; }
break;
case ARROW_DOWN:
- if (E.cx != E.screenrows - 1) {
- E.cy++;
- }
+ if (E.cy < 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; }
}
void editorProcessKeypress() {
@@ -236,68 +310,118 @@ void editorProcessKeypress() {
exit(0);
break;
- case HOME_KEY:
- E.cx = 0;
- break;
+ case HOME_KEY: E.cx = 0; break;
case END_KEY:
- E.cx = E.screencols - 1;
+ if (E.cy < E.numrows) E.cx = E.row[E.cy].size;
break;
case PAGE_UP:
case PAGE_DOWN: {
+ if (c == PAGE_UP) {
+ E.cy = E.rowoff;
+ } else if (c == PAGE_DOWN) {
+ E.cy = E.rowoff + E.screenrows - 1;
+ if (E.cy > E.numrows) E.cy = E.numrows;
+ }
+
int times = E.screenrows;
- while (times--)
- editorMoveCursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN);
+ 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 ARROW_RIGHT: editorMoveCursor(c); break;
}
}
/*** output ***/
+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) {
for (int y = 0; y < E.screenrows; y++) {
- if (y == E.screenrows / 3) {
- char welcome[80];
- int welcomelen = snprintf(welcome, sizeof(welcome),
- "Kilo editor -- version %s", KILO_VERSION);
- if (welcomelen > E.screencols)
- welcomelen = E.screencols;
- int padding = (E.screencols - welcomelen) / 2;
- if (padding) {
+ int filerow = y + E.rowoff;
+ if (filerow >= E.numrows) {
+ if (E.numrows == 0 && y == E.screenrows / 3) {
+ char welcome[80];
+ int welcomelen = snprintf(welcome, sizeof(welcome),
+ "Kilo editor -- version %s", KILO_VERSION);
+ if (welcomelen > E.screencols) welcomelen = E.screencols;
+ int padding = (E.screencols - welcomelen) / 2;
+ if (padding) {
+ abAppend(ab, "~", 1);
+ padding--;
+ }
+ while (padding--) abAppend(ab, " ", 1);
+ abAppend(ab, welcome, welcomelen);
+ } else {
abAppend(ab, "~", 1);
- padding--;
}
- while (padding--)
- abAppend(ab, " ", 1);
- abAppend(ab, welcome, welcomelen);
} else {
- abAppend(ab, "~", 1);
+ int len = E.row[filerow].rsize - E.coloff;
+ if (len < 0) len = 0;
+ if (len > E.screencols) len = E.screencols;
+ abAppend(ab, E.row[filerow].render + E.coloff, len);
}
abAppend(ab, "\x1b[K", 3); // clear current row
- if (y < E.screenrows - 1) {
- abAppend(ab, "\r\n", 2);
+ abAppend(ab, "\r\n", 2);
+ }
+}
+
+void editorDrawStatusBar(struct abuf *ab) {
+ abAppend(ab, "\x1b[7m", 4);
+ char status[80], rstatus[80];
+ int len = snprintf(status, sizeof(status), "%.20s - %d lines",
+ E.filename ? E.filename : "[No Name]", E.numrows);
+ int rlen = snprintf(rstatus, sizeof(rstatus), "%d/%d", 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[m", 3);
+ abAppend(ab, "\r\n", 2);
+}
+
+void editorDrawMessageBar(struct abuf *ab) {
+ abAppend(ab, "\x1b[K", 3);
+ int msglen = strlen(E.statusmsg);
+ if (msglen > E.screencols) msglen = E.screencols;
+ if (msglen && time(NULL) - E.statusmsg_time < 5)
+ abAppend(ab, E.statusmsg, msglen);
}
void editorRefreshScreen() {
+ editorScroll();
+
struct abuf ab = ABUF_INIT;
abAppend(&ab, "\x1b[?25l", 6); // hide cursor
abAppend(&ab, "\x1b[H", 3);
editorDrawRows(&ab);
+ editorDrawStatusBar(&ab);
+ editorDrawMessageBar(&ab);
char buf[32];
- snprintf(buf, sizeof(buf), "\x1b[%d;%dH", E.cy + 1, E.cx + 1);
+ snprintf(buf, sizeof(buf), "\x1b[%d;%dH", (E.cy - E.rowoff) + 1,
+ (E.rx - E.coloff) + 1);
abAppend(&ab, buf, strlen(buf));
abAppend(&ab, "\x1b[?25h", 6); // show cursor
@@ -306,19 +430,38 @@ void editorRefreshScreen() {
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);
+ E.statusmsg_time = time(NULL);
+}
+
/*** init ***/
void initEditor() {
E.cx = 0;
E.cy = 0;
-
- if (getWindowSize(&E.screenrows, &E.screencols) == -1)
- die("getWindowSize");
+ E.rx = 0;
+ E.rowoff = 0;
+ E.coloff = 0;
+ E.numrows = 0;
+ E.row = NULL;
+ E.filename = NULL;
+ E.statusmsg[0] = '\0';
+ E.statusmsg_time = 0;
+
+ if (getWindowSize(&E.screenrows, &E.screencols) == -1) die("getWindowSize");
+ E.screenrows -= 2;
}
-int main() {
+int main(int argc, char *argv[]) {
enableRawMode();
initEditor();
+ if (argc >= 2) { editorOpen(argv[1]); }
+
+ editorSetStatusMessage("HELP: Ctrl-Q = quit");
while (1) {
editorRefreshScreen();