hun

terminal text editor
git clone git://moonbender.net/hun
Log | Files | Refs | README

commit 852e8a136763c309ef72779ed71c1fa80d5f6e4d
Author: calliope <me@calliope.sh>
Date:   Wed, 17 Sep 2025 01:23:43 -0500

hello hello

Diffstat:
A.gitignore | 4++++
AREADME | 19+++++++++++++++++++
Ahun.c | 245+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 268 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,4 @@ +hun +*sw* +*core* +*.o diff --git a/README b/README @@ -0,0 +1,19 @@ +Honey Text Editor for your terminal! It's not finished. + +this is just my attempt to follow along with https://viewsourcecode.org/snaptoken/kilo/ but I might make some added customizations. + +relevant docs: https://vt100.net/docs/vt100-ug/ + +Features: +- Edits text? +- vi-similar controls (modified for workman layout) +- NOT unicode (maybe someday) +- 💋 + +Dependencies: +- Terminal emulator that supports vt-100 escape sequences +- C compiler + +To build: + +cc -std=c89 -o hun hun.c diff --git a/hun.c b/hun.c @@ -0,0 +1,245 @@ +#include <stdlib.h> +#include <unistd.h> +#include <termios.h> +#include <ctype.h> +#include <stdio.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <string.h> + +#define CTRL_KEY(k) ((k) & 0x1f) + +#define NUN_VERSION "1" + +typedef struct +{ + int cx, cy; + struct termios termios_original; + int screenrows; + int screencols; +} EditorConfig; + +EditorConfig ed_state; + +void die(const char *s) +{ + write(STDOUT_FILENO, "\x1b[2J", 4); + write(STDOUT_FILENO, "\x1b[H", 3); + + perror(s); + exit(1); +} + +void raw_mode_disable(void) +{ + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &ed_state.termios_original) == -1) + die("tcsetattr in raw_mode_disable"); +} + +void raw_mode_enable(void) +{ + struct termios raw; + if (tcgetattr(STDIN_FILENO, &ed_state.termios_original) == -1) die("tcgetattr"); + atexit(raw_mode_disable); + + raw = ed_state.termios_original; + raw.c_iflag &= ~(ICRNL | IXON | BRKINT | INPCK | ISTRIP); + raw.c_oflag &= ~(OPOST); + raw.c_cflag |= ~(CS8); + raw.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN); + raw.c_cc[VMIN] = 0; + raw.c_cc[VTIME] = 1; + + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) die("tcsetattr"); +} + +char editor_read_key(void) +{ + int nread; + char ch; + + while ((nread = read(STDIN_FILENO, &ch, 1)) != 1) + { + if (nread == -1 && errno != EAGAIN) die("read"); + } + return ch; +} + +int get_cursor_position(int *rows, int *cols) +{ + char buf[32]; + unsigned int i = 0; + + 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; + i++; + } + buf[i] = '\0'; + + if (buf[0] != '\x1b' || buf[1] != '[') return -1; + if (sscanf(&buf[2], "%d;%d", rows, cols) != 2) return -1; + + return 0; +} + +int get_window_size(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; + return get_cursor_position(rows, cols); + } + else + { + *cols = ws.ws_col; + *rows = ws.ws_row; + return 0; + } +} + +/*** append buffer ***/ + +typedef struct { + char *b; + size_t len; +} AppendBuf; + +#define ABUF_INIT {NULL, 0} + +void abuf_append(AppendBuf *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 abuf_free(AppendBuf *ab) +{ + free(ab->b); +} + +/*** output ***/ + +void editor_draw_rows(AppendBuf *ab) +{ + int y; + for (y = 0; y < ed_state.screenrows; y++) + { + if (y == ed_state.screenrows / 3) + { + char welcome[80]; + int welcomelen; + int padding; + welcomelen = snprintf(welcome, sizeof(welcome), "Honey Text Editor -- Version %s", NUN_VERSION); + if (welcomelen > ed_state.screencols) + welcomelen = ed_state.screencols; + padding = (ed_state.screencols - welcomelen) / 2; + if (padding) + { + abuf_append(ab, "~", 1); + padding--; + } + while (padding--) abuf_append(ab, " ", 1); + abuf_append(ab, welcome, welcomelen); + } + else + { + abuf_append(ab, "~", 1); + } + + abuf_append(ab, "\x1b[K", 3); + if (y < ed_state.screenrows - 1) + abuf_append(ab, "\r\n", 2); + } +} + +void editor_refresh_screen(void) +{ + char buf[32]; + AppendBuf ab = ABUF_INIT; + + abuf_append(&ab, "\x1b[?25l", 6); + abuf_append(&ab, "\x1b[H", 3); + + editor_draw_rows(&ab); + + snprintf(buf, sizeof(buf), "\x1b[%d;%dH", ed_state.cy + 1, ed_state.cx + 1); + abuf_append(&ab, buf, strlen(buf)); + + abuf_append(&ab, "\x1b[?25h", 6); + + write(STDOUT_FILENO, ab.b, ab.len); + abuf_free(&ab); +} + +/*** input ***/ + +void editor_move_cursor(char key) +{ + switch (key) + { + case 'n': + ed_state.cy++; + break; + case 'e': + ed_state.cy--; + break; + case 'y': + ed_state.cx--; + break; + case 'o': + ed_state.cx++; + break; + } +} + +void editor_process_keypress(void) +{ + char ch = editor_read_key(); + + switch(ch) + { + case CTRL_KEY('q'): + write(STDOUT_FILENO, "\x1b[2J", 4); + write(STDOUT_FILENO, "\x1b[H", 3); + exit(0); + break; + + case 'n': + case 'e': + case 'y': + case 'o': + editor_move_cursor(ch); + break; + } +} + +/*** init ***/ + +void editor_init(void) +{ + ed_state.cx = 0; + ed_state.cy = 0; + if (get_window_size(&ed_state.screenrows, &ed_state.screencols) == -1) die("get_window_size"); +} + +int main(void) +{ + raw_mode_enable(); + editor_init(); + while (1) + { + editor_refresh_screen(); + editor_process_keypress(); + } + return 0; +}