Status bar / editing / saving file
This commit is contained in:
@@ -16,7 +16,7 @@ build: main.c src/*
|
||||
$(CC) main.c -o $(BELUGA_OUTPUT)/beluga src/*
|
||||
doxygen
|
||||
|
||||
DEBUG_FLAGS=-Wall -Wextra -pedantic -Werror -fsanitize=address,undefined -g
|
||||
DEBUG_FLAGS=-Wall -Wextra -pedantic -Werror -fsanitize=address -g
|
||||
|
||||
debug: main.c src/*
|
||||
if [ ! -d $(BELUGA_OUTPUT) ]; then mkdir $(BELUGA_OUTPUT); fi
|
||||
|
||||
+30
-11
@@ -2,10 +2,19 @@
|
||||
#define DATA_H_
|
||||
|
||||
#include <termios.h>
|
||||
#include <time.h>
|
||||
|
||||
/**
|
||||
* \struct erow
|
||||
* \brief Store one editor row
|
||||
* \param
|
||||
* */
|
||||
|
||||
typedef struct erow {
|
||||
int size;
|
||||
char *chars;
|
||||
int size; /**< Size of the line */
|
||||
int rsize; /**< Size of the render line */
|
||||
char *chars; /**< Characters of the line */
|
||||
char *render; /**< The actual line we will print */
|
||||
} erow;
|
||||
|
||||
/**
|
||||
@@ -13,18 +22,28 @@ typedef struct erow {
|
||||
* \brief Containing our editor state.
|
||||
*/
|
||||
struct editorConfig {
|
||||
int cursor_x, cursor_y;
|
||||
int row_offset;
|
||||
int screenrows;
|
||||
int screencols;
|
||||
int numrows;
|
||||
erow *row;
|
||||
struct termios orig_termios;
|
||||
int cursor_x, cursor_y; /**< Cursor position */
|
||||
int rx; /**< Position in the render*/
|
||||
int row_offset; /**< Position scroll of lines */
|
||||
int col_offset; /**< Position scroll of colomns*/
|
||||
int screenrows; /**< Terminal height*/
|
||||
int screencols; /**< Terminal width*/
|
||||
int numrows; /**< Number of rows contained */
|
||||
erow *row; /**< Store all the rows printed */
|
||||
char *filename;
|
||||
char status_msg[80];
|
||||
time_t status_msg_time;
|
||||
struct termios orig_termios; /**< Terminal communication interface */
|
||||
};
|
||||
|
||||
/**
|
||||
* \struct abuf
|
||||
* \brief Contains text to add before writing to screen.
|
||||
* */
|
||||
|
||||
struct abuf {
|
||||
char *b;
|
||||
int len;
|
||||
char *b; /**< Text that will be printed */
|
||||
int len; /**< Length of the text */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#define ERASE_END_LINE "\x1b[K"
|
||||
|
||||
enum editorKey {
|
||||
BACKSPACE = 127,
|
||||
CURSOR_LEFT = 1000,
|
||||
CURSOR_RIGHT,
|
||||
CURSOR_UP,
|
||||
@@ -23,5 +24,6 @@ enum editorKey {
|
||||
#define ABUF_INIT {NULL, 0}
|
||||
|
||||
#define BELUGA_VERSION "0.1"
|
||||
#define TAB_LENGTH 8
|
||||
|
||||
#endif // DEFINE_H_
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef EDITOR_OP_H_
|
||||
#define EDITOR_OP_H_
|
||||
|
||||
#include "data.h"
|
||||
void editorInsertChar(struct editorConfig *E, int c);
|
||||
|
||||
#endif // EDITOR_OP_H_
|
||||
@@ -8,6 +8,10 @@
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
char *editorRowsToString(struct editorConfig *E, int *buffer_len);
|
||||
|
||||
void editorOpen(struct editorConfig *E, char *filename);
|
||||
|
||||
void editorSave(struct editorConfig *E);
|
||||
|
||||
#endif // FILE_IO_H_
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "append_buffer.h"
|
||||
#include "data.h"
|
||||
#include "define.h"
|
||||
#include "row_op.h"
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -16,4 +17,12 @@ void editorDrawRows(struct editorConfig *E, struct abuf *ab);
|
||||
|
||||
void editorRefreshScreen(struct editorConfig *E);
|
||||
|
||||
void editorScroll(struct editorConfig *E);
|
||||
|
||||
void editorDrawStatusBar(struct editorConfig *E, struct abuf *ab);
|
||||
|
||||
void editorDrawMessageBar(struct editorConfig *E, struct abuf *ab);
|
||||
|
||||
void editorSetStatusMessage(struct editorConfig *E, const char *fmt, ...);
|
||||
|
||||
#endif // OUTPUT_H_
|
||||
|
||||
@@ -2,10 +2,17 @@
|
||||
#define ROW_OP_H_
|
||||
|
||||
#include "data.h"
|
||||
#include "define.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int editorRowCxToRx(erow *row, int cursor_x);
|
||||
|
||||
void editorUpdateRow(erow *row);
|
||||
|
||||
void editorAppendRow(struct editorConfig *E, char *s, size_t len);
|
||||
|
||||
void editorRowInsertChar(erow *row, int at, int c);
|
||||
|
||||
#endif // ROW_OP_H_
|
||||
|
||||
@@ -26,6 +26,8 @@ int main(int argc, char *argv[]) {
|
||||
editorOpen(&E, argv[1]);
|
||||
}
|
||||
|
||||
editorSetStatusMessage(&E, "HELP: Ctrl-Q = quit");
|
||||
|
||||
while (1) {
|
||||
editorRefreshScreen(&E);
|
||||
editorProcessKeypress(&E);
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#include "../include/editor_op.h"
|
||||
#include "../include/row_op.h"
|
||||
|
||||
void editorInsertChar(struct editorConfig *E, int c) {
|
||||
if (E->cursor_y == E->numrows) {
|
||||
editorAppendRow(E, "", 0);
|
||||
}
|
||||
editorRowInsertChar(&E->row[E->cursor_y], E->cursor_x, c);
|
||||
E->cursor_x++;
|
||||
}
|
||||
+49
-1
@@ -1,7 +1,40 @@
|
||||
#include "../include/file_io.h"
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
char *editorRowsToString(struct editorConfig *E, int *buffer_len) {
|
||||
int tot_len = 0;
|
||||
int j;
|
||||
char *buf;
|
||||
char *p;
|
||||
|
||||
for (j = 0; j < E->numrows; ++j) {
|
||||
tot_len += E->row[j].size + 1;
|
||||
}
|
||||
*buffer_len = tot_len;
|
||||
buf = malloc(tot_len);
|
||||
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(struct editorConfig *E, char *filename) {
|
||||
FILE *fp = fopen(filename, "r");
|
||||
FILE *fp;
|
||||
|
||||
free(E->filename);
|
||||
E->filename = strdup(filename);
|
||||
|
||||
fp = fopen(filename, "r");
|
||||
if (!fp)
|
||||
die("fopen");
|
||||
|
||||
@@ -19,3 +52,18 @@ void editorOpen(struct editorConfig *E, char *filename) {
|
||||
free(line);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void editorSave(struct editorConfig *E) {
|
||||
int len;
|
||||
char *buf;
|
||||
int fd;
|
||||
if (E->filename == NULL) {
|
||||
return;
|
||||
}
|
||||
buf = editorRowsToString(E, &len);
|
||||
fd = open(E->filename, O_RDWR | O_CREAT, 0644);
|
||||
ftruncate(fd, len);
|
||||
write(fd, buf, len);
|
||||
close(fd);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
@@ -3,10 +3,16 @@
|
||||
void initEditor(struct editorConfig *E) {
|
||||
E->cursor_x = 0;
|
||||
E->cursor_y = 0;
|
||||
E->rx = 0;
|
||||
E->row_offset = 0;
|
||||
E->col_offset = 0;
|
||||
E->numrows = 0;
|
||||
E->row = NULL;
|
||||
E->filename = NULL;
|
||||
E->status_msg[0] = '\0';
|
||||
E->status_msg_time = 0;
|
||||
if (getWindowSize(&E->screenrows, &E->screencols) == -1) {
|
||||
die("getWindowSize");
|
||||
}
|
||||
E->screenrows -= 2;
|
||||
}
|
||||
|
||||
+53
-6
@@ -1,14 +1,22 @@
|
||||
#include "../include/input.h"
|
||||
#include "../include/editor_op.h"
|
||||
#include "../include/file_io.h"
|
||||
#include <stdint.h>
|
||||
|
||||
void editorMoveCursor(struct editorConfig *E, int key) {
|
||||
erow *row = (E->cursor_y >= E->numrows) ? NULL : &E->row[E->cursor_y];
|
||||
int row_len;
|
||||
switch (key) {
|
||||
case CURSOR_RIGHT:
|
||||
if (E->cursor_x != 0) {
|
||||
--E->cursor_x;
|
||||
if (row && E->cursor_x < row->size) {
|
||||
++E->cursor_x;
|
||||
} else if (row && E->cursor_x == row->size) {
|
||||
E->cursor_y++;
|
||||
E->cursor_x = 0;
|
||||
}
|
||||
break;
|
||||
case CURSOR_DOWN:
|
||||
if (E->cursor_y != E->screenrows - 1) {
|
||||
if (E->cursor_y < E->numrows) {
|
||||
++E->cursor_y;
|
||||
}
|
||||
break;
|
||||
@@ -18,11 +26,20 @@ void editorMoveCursor(struct editorConfig *E, int key) {
|
||||
}
|
||||
break;
|
||||
case CURSOR_LEFT:
|
||||
if (E->cursor_x != E->screencols - 1) {
|
||||
++E->cursor_x;
|
||||
if (E->cursor_x != 0) {
|
||||
--E->cursor_x;
|
||||
} else if (E->cursor_y > 0) {
|
||||
--E->cursor_y;
|
||||
E->cursor_x = E->row[E->cursor_y].size;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
row = (E->cursor_y >= E->numrows) ? NULL : &E->row[E->cursor_y];
|
||||
row_len = row ? row->size : 0;
|
||||
if (E->cursor_x > row_len) {
|
||||
E->cursor_x = row_len;
|
||||
}
|
||||
}
|
||||
|
||||
void editorProcessKeypress(struct editorConfig *E) {
|
||||
@@ -30,6 +47,9 @@ void editorProcessKeypress(struct editorConfig *E) {
|
||||
int times;
|
||||
|
||||
switch (c) {
|
||||
case '\r':
|
||||
/* TODO */
|
||||
break;
|
||||
case CTRL_KEY('q'):
|
||||
write(STDOUT_FILENO, "\x1b[2J", 4);
|
||||
write(STDOUT_FILENO, CURSOR_TOP_LEFT, 3);
|
||||
@@ -37,16 +57,36 @@ void editorProcessKeypress(struct editorConfig *E) {
|
||||
exit(0);
|
||||
break;
|
||||
|
||||
case CTRL_KEY('s'):
|
||||
editorSave(E);
|
||||
break;
|
||||
|
||||
case BEG_LINE:
|
||||
E->cursor_x = 0;
|
||||
break;
|
||||
|
||||
case END_LINE:
|
||||
E->cursor_x = E->screencols - 1;
|
||||
if (E->cursor_y < E->numrows) {
|
||||
E->cursor_x = E->row[E->cursor_y].size;
|
||||
}
|
||||
break;
|
||||
|
||||
case BACKSPACE:
|
||||
case CTRL_KEY('h'):
|
||||
// case DEL_KEY:
|
||||
/* TODO */
|
||||
break;
|
||||
|
||||
case PAGE_UP:
|
||||
case PAGE_DOWN: {
|
||||
if (c == PAGE_UP) {
|
||||
E->cursor_y = E->row_offset;
|
||||
} else if (c == PAGE_DOWN) {
|
||||
E->cursor_y = E->row_offset + E->screenrows - 1;
|
||||
if (E->cursor_y > E->numrows) {
|
||||
E->cursor_y = E->numrows;
|
||||
}
|
||||
}
|
||||
times = E->screenrows;
|
||||
while (--times) {
|
||||
editorMoveCursor(E, c == PAGE_UP ? CURSOR_UP : CURSOR_DOWN);
|
||||
@@ -59,5 +99,12 @@ void editorProcessKeypress(struct editorConfig *E) {
|
||||
case CURSOR_RIGHT:
|
||||
editorMoveCursor(E, c);
|
||||
break;
|
||||
|
||||
case CTRL_KEY('l'):
|
||||
case '\x1b':
|
||||
break;
|
||||
default:
|
||||
editorInsertChar(E, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+80
-6
@@ -1,4 +1,8 @@
|
||||
#include "../include/output.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
void editorDrawRows(struct editorConfig *E, struct abuf *ab) {
|
||||
int y;
|
||||
@@ -30,18 +34,77 @@ void editorDrawRows(struct editorConfig *E, struct abuf *ab) {
|
||||
abAppend(ab, "~", 1);
|
||||
}
|
||||
} else {
|
||||
len = E->row[file_row].size > E->screencols ? E->screencols
|
||||
: E->row[y].size;
|
||||
abAppend(ab, E->row[file_row].chars, len);
|
||||
len = E->row[file_row].rsize - E->col_offset;
|
||||
if (len < 0)
|
||||
len = 0;
|
||||
if (len > E->screencols)
|
||||
len = E->screencols;
|
||||
abAppend(ab, &E->row[file_row].render[E->col_offset], len);
|
||||
}
|
||||
abAppend(ab, ERASE_END_LINE, 3);
|
||||
if (y < E->screenrows - 1) {
|
||||
abAppend(ab, "\r\n", 2);
|
||||
abAppend(ab, "\r\n", 2);
|
||||
}
|
||||
}
|
||||
|
||||
void editorScroll(struct editorConfig *E) {
|
||||
E->rx = E->cursor_x;
|
||||
if (E->cursor_y < E->numrows) {
|
||||
E->rx = editorRowCxToRx(&E->row[E->cursor_y], E->cursor_x);
|
||||
}
|
||||
|
||||
if (E->cursor_y < E->row_offset) {
|
||||
E->row_offset = E->cursor_y;
|
||||
}
|
||||
if (E->cursor_y >= E->row_offset + E->screenrows) {
|
||||
E->row_offset = E->cursor_y - E->screenrows + 1;
|
||||
}
|
||||
if (E->rx < E->col_offset) {
|
||||
E->col_offset = E->rx;
|
||||
}
|
||||
if (E->rx >= E->col_offset + E->screencols) {
|
||||
E->col_offset = E->rx - E->screencols + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void editorDrawStatusBar(struct editorConfig *E, struct abuf *ab) {
|
||||
int len, render_len;
|
||||
char status[80], render_status[80];
|
||||
|
||||
abAppend(ab, "\x1b[7m", 4); // inverting colors
|
||||
len = snprintf(status, sizeof(status), "%.20s - %d lines",
|
||||
E->filename ? E->filename : "[No Name]", E->numrows);
|
||||
render_len = snprintf(render_status, sizeof(render_status), "%d/%d",
|
||||
E->cursor_y + 1, E->numrows);
|
||||
if (len > E->screencols) {
|
||||
len = E->screencols;
|
||||
}
|
||||
abAppend(ab, status, len);
|
||||
while (len < E->screencols) {
|
||||
if (E->screencols - len == render_len) {
|
||||
abAppend(ab, render_status, render_len);
|
||||
break;
|
||||
} else {
|
||||
abAppend(ab, " ", 1);
|
||||
++len;
|
||||
}
|
||||
}
|
||||
abAppend(ab, "\x1b[m", 3); // normal text mode
|
||||
abAppend(ab, "\r\n", 2);
|
||||
}
|
||||
|
||||
void editorDrawMessageBar(struct editorConfig *E, struct abuf *ab) {
|
||||
int msg_len = strlen(E->status_msg);
|
||||
abAppend(ab, ERASE_END_LINE, 3);
|
||||
if (msg_len > E->screencols) {
|
||||
msg_len = E->screencols;
|
||||
}
|
||||
if (msg_len && time(NULL) - E->status_msg_time < 5) {
|
||||
abAppend(ab, E->status_msg, msg_len);
|
||||
}
|
||||
}
|
||||
|
||||
void editorRefreshScreen(struct editorConfig *E) {
|
||||
editorScroll(E);
|
||||
struct abuf ab = ABUF_INIT;
|
||||
char buf[32];
|
||||
|
||||
@@ -49,8 +112,11 @@ void editorRefreshScreen(struct editorConfig *E) {
|
||||
abAppend(&ab, CURSOR_TOP_LEFT, 3);
|
||||
|
||||
editorDrawRows(E, &ab);
|
||||
editorDrawStatusBar(E, &ab);
|
||||
editorDrawMessageBar(E, &ab);
|
||||
|
||||
snprintf(buf, sizeof(buf), "\x1b[%d;%dH", E->cursor_y + 1, E->cursor_x + 1);
|
||||
snprintf(buf, sizeof(buf), "\x1b[%d;%dH", (E->cursor_y - E->row_offset) + 1,
|
||||
(E->rx - E->col_offset) + 1);
|
||||
abAppend(&ab, buf, strlen(buf));
|
||||
|
||||
abAppend(&ab, SHOW_CURSOR, 6);
|
||||
@@ -58,3 +124,11 @@ void editorRefreshScreen(struct editorConfig *E) {
|
||||
write(STDOUT_FILENO, ab.b, ab.len);
|
||||
abFree(&ab);
|
||||
}
|
||||
|
||||
void editorSetStatusMessage(struct editorConfig *E, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(E->status_msg, sizeof(E->status_msg), fmt, ap);
|
||||
va_end(ap);
|
||||
E->status_msg_time = time(NULL);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,56 @@
|
||||
#include "../include/row_op.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int editorRowCxToRx(erow *row, int cursor_x) {
|
||||
int render_x = 0;
|
||||
int i;
|
||||
for (i = 0; i < cursor_x; ++i) {
|
||||
if (row->chars[i] == '\t') {
|
||||
render_x += (TAB_LENGTH - 1) - (render_x % TAB_LENGTH);
|
||||
}
|
||||
render_x++;
|
||||
}
|
||||
return render_x;
|
||||
}
|
||||
|
||||
/**
|
||||
* \fn editorUpdateRow(erow *row)
|
||||
* \brief Copy content of \p row in \p row->render.
|
||||
* */
|
||||
|
||||
void editorUpdateRow(erow *row) {
|
||||
int i, i_render;
|
||||
int tabs = 0;
|
||||
|
||||
// counting number of tabs
|
||||
|
||||
for (i = 0; i < row->size; ++i) {
|
||||
tabs +=
|
||||
(row->chars[i] == '\t'); /**< increment tabs of 1 if chars[i] is one. */
|
||||
}
|
||||
|
||||
free(row->render);
|
||||
row->render = malloc(row->size + tabs * (TAB_LENGTH - 1) +
|
||||
1); /**< Tabs needs TAB_LENGTH chars so TAB_LENGTH - 1
|
||||
more than the first already counted. */
|
||||
|
||||
// end of counting
|
||||
i_render = 0;
|
||||
for (i = 0; i < row->size; ++i) {
|
||||
if (row->chars[i] == '\t') {
|
||||
row->render[i_render++] = ' ';
|
||||
while (i_render % TAB_LENGTH) {
|
||||
row->render[i_render++] =
|
||||
' '; /**< Addind the right amount of spaces for tabs */
|
||||
}
|
||||
} else {
|
||||
row->render[i_render++] = row->chars[i];
|
||||
}
|
||||
}
|
||||
row->render[i_render] = '\0'; // Don't forget the end of string character.
|
||||
row->rsize = i_render;
|
||||
}
|
||||
|
||||
void editorAppendRow(struct editorConfig *E, char *s, size_t len) {
|
||||
int at;
|
||||
@@ -9,5 +61,25 @@ void editorAppendRow(struct editorConfig *E, char *s, size_t 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* \fn editorRowInsertChar(erow *row, int at, int c)
|
||||
* \param at Index of where we want to insert the char */
|
||||
|
||||
void editorRowInsertChar(erow *row, int at, int 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);
|
||||
}
|
||||
|
||||
+2
-2
@@ -77,9 +77,9 @@ int editorReadKey() {
|
||||
case 'B':
|
||||
return CURSOR_DOWN;
|
||||
case 'C':
|
||||
return CURSOR_LEFT;
|
||||
case 'D':
|
||||
return CURSOR_RIGHT;
|
||||
case 'D':
|
||||
return CURSOR_LEFT;
|
||||
case 'H':
|
||||
return BEG_LINE;
|
||||
case 'F':
|
||||
|
||||
Reference in New Issue
Block a user