basic editor navigation

This commit is contained in:
2024-10-30 04:12:38 +01:00
parent 277759d11b
commit b35706bab6
4 changed files with 482 additions and 75 deletions

2
Makefile Normal file
View File

@@ -0,0 +1,2 @@
texteditor: editor.c
$(CC) editor.c -o texteditor -Wall -Wextra -pedantic -std=c99

View File

@@ -1,2 +1,4 @@
# nice-nano # texteditor
https://viewsourcecode.org/snaptoken/kilo/index.html

530
editor.c
View File

@@ -1,98 +1,498 @@
// INCLUDE
#define _DEFAULT_SOURCE
#define _BSD_SOURCE
#define _GNU_SOURCE
#include <ctype.h>
#include <stdio.h> #include <stdio.h>
#include <errno.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <termios.h> #include <termios.h>
#include <unistd.h> #include <unistd.h>
#include <string.h>
// DEFINE
#define EDITOR_VERSION "0.0.1-alpha1"
#define CTRL_KEY(k) ((k) & 0x1f) #define CTRL_KEY(k) ((k) & 0x1f)
enum editorKey
{
ARROW_LEFT = 1000,
ARROW_RIGHT,
ARROW_UP,
ARROW_DOWN,
DEL_KEY,
HOME_KEY,
END_KEY,
PAGE_UP,
PAGE_DOWN
};
// DATA
typedef struct erow
{
int size;
char *chars;
} erow;
struct editorConfig
{
int cx, cy;
int rowoff;
int coloff;
int screenrows;
int screencols;
int numrows;
erow *row;
struct termios orig_termios; struct termios orig_termios;
};
struct editorConfig E;
void disable_raw_mode() { // TERMINAL
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
void die(const char *s)
{
write(STDOUT_FILENO, "\x1b[2J", 4);
write(STDOUT_FILENO, "\x1b[H", 3);
perror(s);
exit(1);
} }
void enable_raw_mode() { void disableRawMode()
tcgetattr(STDIN_FILENO, &orig_termios); {
atexit(disable_raw_mode); if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &E.orig_termios) == -1)
die("tcsetattr");
struct termios raw = orig_termios;
raw.c_lflag &= ~(ECHO | ICANON | ISIG);
raw.c_iflag &= ~(IXON);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
} }
void load_file(const char *filename) { void enableRawMode()
FILE *file = fopen(filename, "r"); {
if (file == NULL) { if (tcgetattr(STDIN_FILENO, &E.orig_termios) == -1)
printf("Fehler beim Öffnen der Datei %s.\n", filename); die("tcgetattr");
return;
atexit(disableRawMode);
struct termios raw = E.orig_termios;
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
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 line[256]; int editorReadKey()
while (fgets(line, sizeof(line), file)) { {
printf("%s", line); int nread;
char c;
while ((nread = read(STDIN_FILENO, &c, 1)) != 1)
{
if (nread == -1 && errno != EAGAIN)
die("read");
} }
if (c == '\x1b')
fclose(file); {
}
void editor_loop() {
int ch;
while (1) {
ch = getchar();
if (ch == '\x1b') {
char seq[3]; char seq[3];
if (read(STDIN_FILENO, &seq[0], 1) != 1) continue; if (read(STDIN_FILENO, &seq[0], 1) != 1)
if (read(STDIN_FILENO, &seq[1], 1) != 1) continue; return '\x1b';
if (read(STDIN_FILENO, &seq[1], 1) != 1)
if (seq[0] == '[') { return '\x1b';
switch (seq[1]) { if (seq[0] == '[')
{
if (seq[1] >= '0' && seq[1] <= '9')
{
if (read(STDIN_FILENO, &seq[2], 1) != 1)
return '\x1b';
if (seq[2] == '~')
{
switch (seq[1])
{
case '1':
return HOME_KEY;
case '4':
return END_KEY;
case '3':
return DEL_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': case 'A':
// Cursor nach oben bewegen (nicht implementiert) return ARROW_UP;
printf("\033[A");
break;
case 'B': case 'B':
// Cursor nach unten bewegen (nicht implementiert) return ARROW_DOWN;
printf("\033[B");
break;
case 'C': case 'C':
// Cursor nach rechts bewegen return ARROW_RIGHT;
printf("\033[C");
break;
case 'D': case 'D':
// Cursor nach links bewegen return ARROW_LEFT;
printf("\033[D"); case 'H':
break; return HOME_KEY;
case 'F':
return END_KEY;
} }
} }
} else if (ch == 27) { }
// ESC-Taste zum Beenden else if (seq[0] == 'O')
break; {
} else { switch (seq[1])
switch (ch) { {
case 127: case 'H':
// Zeichen löschen (Rückschritt) return HOME_KEY;
printf("\b \b"); case 'F':
break; return END_KEY;
default:
// Zeichen anzeigen
putchar(ch);
break;
} }
} }
fflush(stdout); return '\x1b';
}
else
{
return c;
} }
} }
int main() { int getCursorPosition(int *rows, int *cols)
enable_raw_mode(); {
char buf[32];
unsigned int i = 0;
printf("Willkommen zu deinem Texteditor! Drücke ESC zum Beenden.\n"); if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4)
load_file("test.txt"); return -1;
editor_loop(); 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; 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, "\x1b[999C\x1b[999B", 12) != 12)
return -1;
return getCursorPosition(rows, cols);
return -1;
}
else
{
*cols = ws.ws_col;
*rows = ws.ws_row;
return 0;
}
}
// ROW OPERATIONS
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.numrows++;
}
// FILE I/O
void editorOpen(char *filename)
{
FILE *fp = fopen(filename, "r");
if (!fp)
die("fopen");
char *line = NULL;
size_t linecap = 0;
ssize_t linelen;
linelen = getline(&line, &linecap, fp);
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
{
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);
if (new == NULL)
return;
memcpy(&new[ab->len], s, len);
ab->b = new;
ab->len += len;
}
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 (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--;
}
break;
case ARROW_DOWN:
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()
{
int c = editorReadKey();
switch (c)
{
case CTRL_KEY('q'):
write(STDOUT_FILENO, "\x1b[2J", 4);
write(STDOUT_FILENO, "\x1b[H", 3);
exit(0);
break;
case HOME_KEY:
E.cx = 0;
break;
case END_KEY:
E.cx = E.screencols - 1;
break;
case PAGE_UP:
case PAGE_DOWN:
{
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;
}
}
// OUTPUT
void editorScroll()
{
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.cx < E.coloff)
{
E.coloff = E.cx;
}
if (E.cx >= E.coloff + E.screencols)
{
E.coloff = E.cx - 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.numrows == 0 && y == E.screenrows / 3)
{
char welcome[80];
int welcomelen = snprintf(welcome, sizeof(welcome),
"Thank you for using text editor -- version %s", EDITOR_VERSION);
if (welcomelen > E.screencols)
welcomelen = E.screencols;
int padding = (E.screencols - welcomelen) / 2;
if (padding)
{
char buffer[32];
int len = snprintf(buffer, sizeof(buffer), "%d", y + 1);
abAppend(ab, buffer, len);
padding--;
}
while (padding--)
abAppend(ab, " ", 1);
abAppend(ab, welcome, welcomelen);
}
else
{
char buffer[32];
int len = snprintf(buffer, sizeof(buffer), "%d", y + 1);
abAppend(ab, buffer, len);
}
}
else
{
int len = E.row[filerow].size - E.coloff;
if (len < 0)
len = 0;
if (len > E.screencols)
len = E.screencols;
abAppend(ab, &E.row[filerow].chars[E.coloff], len);
}
abAppend(ab, "\x1b[K", 3);
if (y < E.screenrows - 1)
{
abAppend(ab, "\r\n", 2);
}
}
}
void editorRefreshScreen()
{
editorScroll();
struct abuf ab = ABUF_INIT;
abAppend(&ab, "\x1b[?25l", 6);
abAppend(&ab, "\x1b[H", 3);
editorDrawRows(&ab);
char buf[32];
snprintf(buf, sizeof(buf), "\x1b[%d;%dH", (E.cy - E.rowoff) + 1, (E.cx - E.coloff) + 1);
abAppend(&ab, buf, strlen(buf));
abAppend(&ab, "\x1b[?25h", 6);
write(STDOUT_FILENO, ab.b, ab.len);
abFree(&ab);
}
// INIT
void initEditor()
{
E.cx = 0;
E.cy = 0;
E.rowoff = 0;
E.coloff = 0;
E.numrows = 0;
E.row = NULL;
if (getWindowSize(&E.screenrows, &E.screencols) == -1)
die("getWindowSize");
}
int main(int argc, char *argv[])
{
enableRawMode();
initEditor();
if (argc >= 2)
{
editorOpen(argv[1]);
}
while (1)
{
editorRefreshScreen();
editorProcessKeypress();
}
return 0;
}

View File

@@ -11,3 +11,6 @@ if(i == 0){
} }
Passt es? Passt es?
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
Hallo