11 Commits

Author SHA1 Message Date
e69d63dd57 start word nav 2025-11-22 23:26:47 -08:00
536a81bc02 move nix to nixos config 2025-11-22 15:34:33 -08:00
1722dbee0b Fix check for render realloc. 2025-11-22 12:50:22 -08:00
14199afeb5 bump version
Some checks are pending
Release / Bump Homebrew formula (push) Waiting to run
2025-11-22 12:36:41 -08:00
9f3558c430 put it in build, not build/bin
Some checks are pending
Release / Bump Homebrew formula (push) Waiting to run
2025-11-22 12:27:02 -08:00
ce64e4637d clean up 2025-11-22 12:15:53 -08:00
36013e42e4 version bump
Some checks are pending
Release / Bump Homebrew formula (push) Waiting to run
2025-11-22 12:13:56 -08:00
dd667c1ef5 revert back to older version with some previous fixes
the ai was getting wild
2025-11-22 12:13:03 -08:00
c9977b0fc0 checkpoint
Some checks are pending
Release / Bump Homebrew formula (push) Waiting to run
2025-11-22 12:00:50 -08:00
dd2c888766 update man page
Some checks failed
Release / Bump Homebrew formula (push) Has been cancelled
2025-11-22 01:48:31 -08:00
2967998893 Update README
Some checks failed
Release / Bump Homebrew formula (push) Has been cancelled
2025-11-22 01:45:59 -08:00
4 changed files with 88 additions and 119 deletions

View File

@@ -2,20 +2,20 @@ cmake_minimum_required(VERSION 3.15)
project(ke C) # Specify C language explicitly project(ke C) # Specify C language explicitly
set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD 99)
set(KE_VERSION "1.0.4") set(KE_VERSION "1.0.11")
set(CMAKE_C_FLAGS "-Wall -Wextra -pedantic -Wshadow -Werror -std=c99 -g") set(CMAKE_C_FLAGS "-Wall -Wextra -pedantic -Wshadow -Werror -std=c99 -g")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_DEFAULT_SOURCE -D_XOPEN_SOURCE") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_DEFAULT_SOURCE -D_XOPEN_SOURCE")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
include(GNUInstallDirs)
# Add executable # Add executable
add_executable(ke main.c) add_executable(ke main.c)
# Define KE_VERSION for use in C code (e.g., #define KE_VERSION)
target_compile_definitions(ke PRIVATE KE_VERSION="ke version ${KE_VERSION}") target_compile_definitions(ke PRIVATE KE_VERSION="ke version ${KE_VERSION}")
install(TARGETS ke RUNTIME DESTINATION bin)
install(FILES ke.1 TYPE MAN)
install(TARGETS ke RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES ke.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
# Set output properties
set_target_properties(ke PROPERTIES
FOLDER bin
RUNTIME_OUTPUT_DIRECTORY bin
)

View File

@@ -6,6 +6,13 @@ used fairly often.
See the man page for more info. See the man page for more info.
It should be available via homebrew, even. It should be available via homebrew, even:
brew tap kisom/homebrew-tap
brew install ke
To get verbose ASAN messages:
export LSAN_OPTIONS=verbosity=1:log_threads=1
Released under an ISC license. Released under an ISC license.

4
ke.1
View File

@@ -24,10 +24,10 @@ saving a file can be done with either C-k s or C-k C-s.
.Bl -tag -width xxxxxxxxxxxx -offset indent .Bl -tag -width xxxxxxxxxxxx -offset indent
.It C-k BACKSPACE .It C-k BACKSPACE
Delete from the cursor to the beginning of the line. Delete from the cursor to the beginning of the line.
.It C-k C-d
Delete the current row.
.It C-k d .It C-k d
Delete from the cursor to the end of the line. Delete from the cursor to the end of the line.
.It C-k C-d
Delete the entire link.
.It C-k e .It C-k e
Edit a new file. Also C-k C-e. Edit a new file. Also C-k C-e.
.It C-k f .It C-k f

166
main.c
View File

@@ -14,7 +14,6 @@
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <limits.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
@@ -25,6 +24,10 @@
#include <unistd.h> #include <unistd.h>
#ifndef KE_VERSION
#define KE_VERSION "ke dev build"
#endif
#define ESCSEQ "\x1b[" #define ESCSEQ "\x1b["
#define CTRL_KEY(key) ((key)&0x1f) #define CTRL_KEY(key) ((key)&0x1f)
#define TAB_STOP 8 #define TAB_STOP 8
@@ -44,33 +47,35 @@
#define TAB_STOP 8 #define TAB_STOP 8
#define INITIAL_BUFSIZE 64 #define INITIAL_CAPACITY 64
int int
next_power_of_2(int n) next_power_of_2(int n)
{ {
if (n < 2) {
n = 2;
}
n--; n--;
n |= n >> 1; n |= n >> 1;
n |= n >> 2; n |= n >> 2;
n |= n >> 4; n |= n >> 4;
n |= n >> 8; n |= n >> 8;
n |= n >> 16; n |= n >> 16;
return n + 1; return n + 1;
} }
/*
* cap_growth is a generalized strategy to growing buffers.
*/
int int
cap_growth(int cap, int sz) cap_growth(int cap, int sz)
{ {
if (cap == 0) { if (cap == 0) {
return INITIAL_BUFSIZE; cap = INITIAL_CAPACITY;
} }
while (sz < cap) { while (cap <= sz) {
cap = next_power_of_2(cap); cap = next_power_of_2(cap + 1);
} }
return cap; return cap;
@@ -107,8 +112,8 @@ struct erow {
char nibble_to_hex(char c); char nibble_to_hex(char c);
int erow_render_to_cursor(struct erow *row, int cx); int erow_render_to_cursor(struct erow *row, int cx);
int erow_cursor_to_render(struct erow *row, int rx); int erow_cursor_to_render(struct erow *row, int rx);
int erow_init(struct erow *row, int len);
void erow_update(struct erow *row); void erow_update(struct erow *row);
int erow_init(struct erow *row, int len);
void erow_insert(int at, char *s, int len); void erow_insert(int at, char *s, int len);
void erow_free(struct erow *row); void erow_free(struct erow *row);
void editor_set_status(const char *fmt, ...); void editor_set_status(const char *fmt, ...);
@@ -117,6 +122,7 @@ void editor_set_status(const char *fmt, ...);
void die(const char *s); void die(const char *s);
int get_winsz(int *rows, int *cols); int get_winsz(int *rows, int *cols);
void goto_line(void); void goto_line(void);
int cursor_at_eol(void);
void delete_row(int at); void delete_row(int at);
void row_append_row(struct erow *row, char *s, int len); void row_append_row(struct erow *row, char *s, int len);
void row_insert_ch(struct erow *row, int at, int16_t c); void row_insert_ch(struct erow *row, int at, int16_t c);
@@ -137,7 +143,6 @@ void move_cursor(int16_t c);
void newline(void); void newline(void);
void process_kcommand(int16_t c); void process_kcommand(int16_t c);
void process_normal(int16_t c); void process_normal(int16_t c);
void navonly_escape(int16_t c);
void process_escape(int16_t c); void process_escape(int16_t c);
int process_keypress(void); int process_keypress(void);
void enable_termraw(void); void enable_termraw(void);
@@ -216,7 +221,7 @@ init_editor(void)
editor.rows = 0; editor.rows = 0;
if (get_winsz(&editor.rows, &editor.cols) == -1) { if (get_winsz(&editor.rows, &editor.cols) == -1) {
// die("can't get window size - is this an interactive terminal?"); die("can't get window size");
} }
editor.rows--; /* status bar */ editor.rows--; /* status bar */
editor.rows--; /* message line */ editor.rows--; /* message line */
@@ -237,8 +242,7 @@ init_editor(void)
/* /*
* reset_editor presumes that editor has been initialized. That is, * reset_editor presumes that editor has been initialized.
* before reset_editor is called, init_editor should be called.
*/ */
void void
reset_editor(void) reset_editor(void)
@@ -260,37 +264,24 @@ reset_editor(void)
void void
ab_append(struct abuf *buf, const char *s, int len) ab_append(struct abuf *buf, const char *s, int len)
{ {
const int delta = (len < 0) ? 0 : len;
char *nc = buf->b; char *nc = buf->b;
int sz; int sz = buf->len + len;
assert((delta >= 0 && buf->len < INT_MAX - delta)); if (sz >= buf->cap) {
sz = buf->len + delta; while (sz > buf->cap) {
if (sz > buf->cap) {
if (buf->cap == 0) { if (buf->cap == 0) {
buf->cap = 64; buf->cap = 1;
} } else {
while (sz < buf->cap) {
if (buf->cap > INT_MAX/2) {
buf->cap = INT_MAX;
break;
}
buf->cap *= 2; buf->cap *= 2;
} }
}
assert(sz <= buf->cap);
nc = realloc(nc, buf->cap); nc = realloc(nc, buf->cap);
assert(nc != NULL); assert(nc != NULL);
} }
if (delta > 0) { memcpy(&nc[buf->len], s, len);
memcpy(&nc[buf->len], s, (size_t)delta);
buf->b = nc; buf->b = nc;
buf->len += delta; buf->len += len; /* DANGER: overflow */
}
} }
@@ -374,7 +365,7 @@ erow_update(struct erow *row)
} }
} }
if (row->rsize) { if (row->rsize || row->render != NULL) {
free(row->render); free(row->render);
row->rsize = 0; row->rsize = 0;
} }
@@ -461,14 +452,14 @@ erow_free(struct erow *row)
void void
die(const char *s) die(const char *s)
{ {
/*
* NOTE(kyle): this is a duplication of the code in display.c
* but I would like to be able to import these files from there.
*/
write(STDOUT_FILENO, "\x1b[2J", 4); write(STDOUT_FILENO, "\x1b[2J", 4);
write(STDOUT_FILENO, "\x1b[H", 3); write(STDOUT_FILENO, "\x1b[H", 3);
if (errno != 0) {
perror(s); perror(s);
} else {
fprintf(stderr, "%s\n", s);
}
exit(1); exit(1);
} }
@@ -518,6 +509,30 @@ goto_line(void)
} }
int
cursor_at_eol(int curx, int cury)
{
assert(curx >= 0);
assert(cury >= 0);
assert(cury < editor.nrows);
assert(curx < editor.row[cury].size);
return curx == editor.row[cury].size;
}
void
find_next_word()
{
int x = editor.curx;
int y = editor.cury;
while (1) {
}
}
void void
delete_row(int at) delete_row(int at)
{ {
@@ -549,9 +564,6 @@ row_append_row(struct erow *row, char *s, int len)
void void
row_insert_ch(struct erow *row, int at, int16_t c) row_insert_ch(struct erow *row, int at, int16_t c)
{ {
int ncap = 0;
char *nline = NULL;
/* /*
* row_insert_ch just concerns itself with how to update a row. * row_insert_ch just concerns itself with how to update a row.
*/ */
@@ -560,19 +572,6 @@ row_insert_ch(struct erow *row, int at, int16_t c)
} }
assert(c > 0); assert(c > 0);
if (row->size == row->cap) {
ncap = cap_growth(row->cap, row->size+1);
nline = realloc(row->line, ncap);
assert(nline != NULL);
if (nline == NULL) {
return;
}
row->cap = ncap;
row->line = nline;
}
row->line = realloc(row->line, row->size+2); row->line = realloc(row->line, row->size+2);
assert(row->line != NULL); assert(row->line != NULL);
memmove(&row->line[at+1], &row->line[at], row->size - at + 1); memmove(&row->line[at+1], &row->line[at], row->size - at + 1);
@@ -697,14 +696,9 @@ rows_to_buffer(int *buflen)
} }
if (len == 0) { if (len == 0) {
if (buflen != NULL) {
*buflen = 0;
}
return NULL; return NULL;
} }
assert(buflen != NULL);
*buflen = len; *buflen = len;
buf = malloc(len); buf = malloc(len);
assert(buf != NULL); assert(buf != NULL);
@@ -762,7 +756,7 @@ save_file(void)
status = 0; status = 0;
save_exit: save_exit:
if (fd != -1) close(fd); if (fd) close(fd);
if (buf) { if (buf) {
free(buf); free(buf);
buf = NULL; buf = NULL;
@@ -1008,11 +1002,7 @@ editor_openfile(void)
return; return;
} }
/* open_file() will handle freeing the previous file/buffer state
* via reset_editor() and will strdup() the filename internally. */
open_file(filename); open_file(filename);
free(filename);
} }
@@ -1230,25 +1220,7 @@ process_normal(int16_t c)
void void
process_escape(int16_t c) process_escape(int16_t c)
{ {
struct erow *row = NULL; struct erow *row = &editor.row[editor.cury];
/* if there are no rows, there's nothing to do */
if (editor.nrows <= 0) {
return;
}
if (editor.cury <0) {
editor.cury = 0;
} else if (editor.cury >= editor.nrows) {
editor.cury = editor.nrows - 1;
}
row = &editor.row[editor.cury];
if (editor.curx < 0) {
editor.curx = 0;
} else if (editor.curx > row->size) {
editor.curx = row->size;
}
editor_set_status("hi"); editor_set_status("hi");
@@ -1262,12 +1234,7 @@ process_escape(int16_t c)
editor.curx = 0; editor.curx = 0;
break; break;
case BACKSPACE: case BACKSPACE:
row = &editor.row[editor.cury]; /* cury may have changed */ if (isalnum(row->line[editor.curx])) {
if (editor.curx == 0 || editor.curx < row->size) {
break;
}
if ((unsigned char)isalnum(row->line[editor.curx])) {
editor_set_status("is alnum"); editor_set_status("is alnum");
while (editor.curx > 0 && isalnum(row->line[editor.curx])) { while (editor.curx > 0 && isalnum(row->line[editor.curx])) {
process_normal(BACKSPACE); process_normal(BACKSPACE);
@@ -1396,8 +1363,7 @@ draw_rows(struct abuf *ab)
{ {
assert(editor.cols >= 0); assert(editor.cols >= 0);
int cols = editor.cols > 0 ? editor.cols : 1; char buf[editor.cols];
char buf[cols];
int buflen, filerow, padding; int buflen, filerow, padding;
int y; int y;
@@ -1420,6 +1386,7 @@ draw_rows(struct abuf *ab)
ab_append(ab, "|", 1); ab_append(ab, "|", 1);
} }
} else { } else {
erow_update(&editor.row[filerow]);
buflen = editor.row[filerow].rsize - editor.coloffs; buflen = editor.row[filerow].rsize - editor.coloffs;
if (buflen < 0) { if (buflen < 0) {
buflen = 0; buflen = 0;
@@ -1428,7 +1395,6 @@ draw_rows(struct abuf *ab)
if (buflen > editor.cols) { if (buflen > editor.cols) {
buflen = editor.cols; buflen = editor.cols;
} }
ab_append(ab, editor.row[filerow].render+editor.coloffs, ab_append(ab, editor.row[filerow].render+editor.coloffs,
buflen); buflen);
} }
@@ -1571,10 +1537,10 @@ editor_set_status(const char *fmt, ...)
void void
loop(void) loop(void)
{ {
display_refresh(); int up = 1; /* update on the first runthrough */
while (1) { while (1) {
int update = 0; if (up) display_refresh();
/* /*
* ke should only refresh the display if it has received keyboard * ke should only refresh the display if it has received keyboard
@@ -1582,12 +1548,8 @@ loop(void)
* handling pastes without massive screen flicker. * handling pastes without massive screen flicker.
* *
*/ */
while (process_keypress()) { if ((up = process_keypress()) != 0) {
update = 1; while (process_keypress()) ;
}
if (update) {
display_refresh();
} }
} }
} }