aboutsummaryrefslogtreecommitdiff
path: root/window.c
diff options
context:
space:
mode:
Diffstat (limited to 'window.c')
-rw-r--r--window.c273
1 files changed, 80 insertions, 193 deletions
diff --git a/window.c b/window.c
index 47fad1a..81bb7b5 100644
--- a/window.c
+++ b/window.c
@@ -19,7 +19,6 @@
#include <ctype.h>
#include <errno.h>
#include <regex.h>
-#include <curses.h>
#include "editor.h"
#include "window.h"
#include "syntax.h"
@@ -27,29 +26,6 @@
#include "text-motions.h"
#include "util.h"
-typedef struct { /* used to calculate the display width of a character */
- char c[7]; /* utf8 encoded bytes */
- size_t len; /* number of bytes of the multibyte sequence */
- wchar_t wchar; /* equivalent converted wide character, needed for wcwidth(3) */
-} Char;
-
-typedef struct {
- unsigned char len; /* number of bytes the character displayed in this cell uses, for
- character which use more than 1 column to display, their lenght
- is stored in the leftmost cell wheras all following cells
- occupied by the same character have a length of 0. */
- char data; /* first byte of the utf8 sequence, used for white space handling */
-} Cell;
-
-typedef struct Line Line;
-struct Line { /* a line on the screen, *not* in the file */
- Line *prev, *next; /* pointer to neighbouring screen lines */
- size_t len; /* line length in terms of bytes */
- size_t lineno; /* line number from start of file */
- int width; /* zero based position of last used column cell */
- Cell cells[]; /* win->width cells storing information about the displayed characters */
-};
-
typedef struct { /* cursor position */
Filepos pos; /* in bytes from the start of the file */
int row, col; /* in terms of zero based screen coordinates */
@@ -58,19 +34,16 @@ typedef struct { /* cursor position */
struct Win { /* window showing part of a file */
Text *text; /* underlying text management */
- WINDOW *win; /* curses window for the text area */
- WINDOW *winnum; /* curses window for the line numbers area */
+ UiWin *ui;
int width, height; /* window text area size */
- int w, h, x, y; /* complete window position & dimension (including line numbers) */
Filepos start, end; /* currently displayed area [start, end] in bytes from the start of the file */
+ size_t lines_size; /* number of allocated bytes for lines (grows only) */
Line *lines; /* win->height number of lines representing window content */
Line *topline; /* top of the window, first line currently shown */
Line *lastline; /* last currently used line, always <= bottomline */
Line *bottomline; /* bottom of screen, might be unused if lastline < bottomline */
Filerange sel; /* selected text range in bytes from start of file */
Cursor cursor; /* current window cursor position */
- void (*cursor_moved)(Win*, void *); /* registered callback, fires whenever the cursor moved */
- void *cursor_moved_data; /* user supplied data, passed as second argument to the above callback */
Line *line; /* used while drawing window content, line where next char will be drawn */
int col; /* used while drawing window content, column where next char will be drawn */
Syntax *syntax; /* syntax highlighting definitions for this window or NULL */
@@ -78,7 +51,7 @@ struct Win { /* window showing part of a file */
};
static void window_clear(Win *win);
-static bool window_addch(Win *win, Char *c);
+static bool window_addch(Win *win, Cell *cell);
static size_t window_cursor_update(Win *win);
/* set/move current cursor position to a given (line, column) pair */
static size_t window_cursor_set(Win *win, Line *line, int col);
@@ -105,55 +78,22 @@ void window_selection_clear(Win *win) {
curs_set(1);
}
-static int window_numbers_width(Win *win) {
- if (!win->winnum)
- return 0;
- return snprintf(NULL, 0, "%zd", win->topline->lineno + win->height - 1) + 1;
-}
-
-static void window_numbers_draw(Win *win) {
- if (!win->winnum)
- return;
- werase(win->winnum);
- size_t prev_lineno = -1;
- int width = window_numbers_width(win), i = 0;
- char fmt[32];
- snprintf(fmt, sizeof fmt, "%%%dd", width - 1);
- for (Line *l = win->topline; l && l <= win->lastline; l = l->next, i++) {
- if (l->lineno != prev_lineno)
- mvwprintw(win->winnum, i, 0, fmt, l->lineno);
- prev_lineno = l->lineno;
- }
- mvwvline(win->winnum, 0, width-1, ACS_VLINE, win->height);
-}
-
/* reset internal window data structures (cell matrix, line offsets etc.) */
static void window_clear(Win *win) {
/* calculate line number of first line */
+ // TODO move elsewhere
win->topline = win->lines;
win->topline->lineno = text_lineno_by_pos(win->text, win->start);
win->lastline = win->topline;
- /* based on which we can calculate how much space is needed to display line numbers */
- int lineno_width = window_numbers_width(win);
- int width = win->w - lineno_width;
- if (win->winnum) {
- wresize(win->winnum, win->h, lineno_width);
- mvwin(win->winnum, win->y, win->x);
- }
-
- wresize(win->win, win->height, width);
- mvwin(win->win, win->y, win->x + lineno_width);
- win->width = width;
-
/* reset all other lines */
size_t line_size = sizeof(Line) + win->width*sizeof(Cell);
size_t end = win->height * line_size;
Line *prev = NULL;
for (size_t i = 0; i < end; i += line_size) {
Line *line = (Line*)(((char*)win->lines) + i);
- line->len = 0;
line->width = 0;
+ line->len = 0;
line->prev = prev;
if (prev)
prev->next = line;
@@ -190,15 +130,15 @@ Filerange window_viewport_get(Win *win) {
}
/* try to add another character to the window, return whether there was space left */
-static bool window_addch(Win *win, Char *c) {
+static bool window_addch(Win *win, Cell *cell) {
if (!win->line)
return false;
int width;
- Cell empty = { .len = 0, .data = '\0' };
+ static Cell empty;
size_t lineno = win->line->lineno;
- switch (c->wchar) {
+ switch (cell->data[0]) {
case '\t':
width = win->tabwidth - (win->col % win->tabwidth);
for (int w = 0; w < width; w++) {
@@ -211,68 +151,53 @@ static bool window_addch(Win *win, Char *c) {
}
if (w == 0) {
/* first cell of a tab has a length of 1 */
- win->line->cells[win->col].len = c->len;
- win->line->len += c->len;
+ win->line->cells[win->col].len = cell->len;
+ win->line->len += cell->len;
} else {
/* all remaining ones have a lenght of zero */
win->line->cells[win->col].len = 0;
}
/* but all are marked as part of a tabstop */
- win->line->cells[win->col].data = '\t';
- win->col++;
+ win->line->cells[win->col].width = 1;
+ win->line->cells[win->col].data[0] = ' ';
+ win->line->cells[win->col].data[1] = '\0';
+ win->line->cells[win->col].istab = true;
win->line->width++;
- waddch(win->win, ' ');
+ win->col++;
}
return true;
case '\n':
- width = 1;
- if (win->col + width > win->width) {
+ cell->width = 1;
+ if (win->col + cell->width > win->width) {
win->line = win->line->next;
win->col = 0;
if (!win->line)
return false;
- win->line->lineno = lineno + 1;
+ win->line->lineno = lineno;
}
- win->line->cells[win->col].len = c->len;
- win->line->len += c->len;
- win->line->cells[win->col].data = '\n';
+ win->line->cells[win->col] = *cell;
+ win->line->len += cell->len;
+ win->line->width += cell->width;
for (int i = win->col + 1; i < win->width; i++)
win->line->cells[i] = empty;
- if (win->line == win->bottomline) {
- /* XXX: curses bug? the wclrtoeol(win->win); implied by waddch(win->win, '\n')
- * doesn't seem to work on the last line!?
- *
- * Thus explicitly clear the remaining of the line.
- */
- for (int i = win->col; i < win->width; i++)
- waddch(win->win, ' ');
- } else if (win->line->width == 0) {
- /* add a single space in an otherwise empty line, makes selection cohorent */
- waddch(win->win, ' ');
- }
-
- waddch(win->win, '\n');
win->line = win->line->next;
if (win->line)
win->line->lineno = lineno + 1;
win->col = 0;
return true;
default:
- if (c->wchar < 128 && !isprint(c->wchar)) {
+ if ((unsigned char)cell->data[0] < 128 && !isprint((unsigned char)cell->data[0])) {
/* non-printable ascii char, represent it as ^(char + 64) */
- Char s = { .c = "^_", .len = 1 };
- s.c[1] = c->c[0] + 64;
- *c = s;
- width = 2;
- } else {
- if ((width = wcwidth(c->wchar)) == -1) {
- /* this should never happen */
- width = 1;
- }
+ *cell = (Cell) {
+ .data = { '^', cell->data[0] + 64, '\0' },
+ .len = 1,
+ .width = 2,
+ .istab = false,
+ };
}
- if (win->col + width > win->width) {
+ if (win->col + cell->width > win->width) {
for (int i = win->col; i < win->width; i++)
win->line->cells[i] = empty;
win->line = win->line->next;
@@ -280,16 +205,14 @@ static bool window_addch(Win *win, Char *c) {
}
if (win->line) {
- win->line->width += width;
- win->line->len += c->len;
+ win->line->width += cell->width;
+ win->line->len += cell->len;
win->line->lineno = lineno;
- win->line->cells[win->col].len = c->len;
- win->line->cells[win->col].data = c->c[0];
+ win->line->cells[win->col] = *cell;
win->col++;
/* set cells of a character which uses multiple columns */
- for (int i = 1; i < width; i++)
+ for (int i = 1; i < cell->width; i++)
win->line->cells[win->col++] = empty;
- waddstr(win->win, c->c);
return true;
}
return false;
@@ -317,9 +240,7 @@ static size_t window_cursor_update(Win *win) {
win->sel.end = cursor->pos;
window_draw(win);
}
- wmove(win->win, cursor->row, cursor->col);
- if (win->cursor_moved)
- win->cursor_moved(win, win->cursor_moved_data);
+ win->ui->cursor_to(win->ui, cursor->col, cursor->row);
return cursor->pos;
}
@@ -385,9 +306,7 @@ void window_cursor_to(Win *win, size_t pos) {
/* redraw the complete with data starting from win->start bytes into the file.
* stop once the screen is full, update win->end, win->lastline */
void window_draw(Win *win) {
- int old_width = win->width;
window_clear(win);
- wmove(win->win, 0, 0);
/* current absolute file position */
size_t pos = win->start;
/* number of bytes to read in one go */
@@ -400,8 +319,6 @@ void window_draw(Win *win) {
text[rem] = '\0';
/* current position into buffer from which to interpret a character */
char *cur = text;
- /* current 'parsed' character' */
- Char c;
/* current selection */
Filerange sel = window_selection_get(win);
/* syntax definition to use */
@@ -414,6 +331,11 @@ void window_draw(Win *win) {
while (rem > 0) {
+ /* current 'parsed' character' */
+ wchar_t wchar;
+ Cell cell;
+ memset(&cell, 0, sizeof cell);
+
if (syntax) {
if (matched && cur >= text + matched->rm_eo) {
/* end of current match */
@@ -460,13 +382,13 @@ void window_draw(Win *win) {
}
}
- size_t len = mbrtowc(&c.wchar, cur, rem, NULL);
+ size_t len = mbrtowc(&wchar, cur, rem, NULL);
if (len == (size_t)-1 && errno == EILSEQ) {
/* ok, we encountered an invalid multibyte sequence,
* replace it with the Unicode Replacement Character
* (FFFD) and skip until the start of the next utf8 char */
for (len = 1; rem > len && !ISUTF8(cur[len]); len++);
- c = (Char){ .c = "\xEF\xBF\xBD", .wchar = 0xFFFD, .len = len };
+ cell = (Cell){ .data = "\xEF\xBF\xBD", .len = len, .width = 1, .istab = false };
} else if (len == (size_t)-2) {
/* not enough bytes available to convert to a
* wide character. advance file position and read
@@ -478,74 +400,65 @@ void window_draw(Win *win) {
continue;
} else if (len == 0) {
/* NUL byte encountered, store it and continue */
- len = 1;
- c = (Char){ .c = "\x00", .wchar = 0x00, .len = len };
+ cell = (Cell){ .data = "\x00", .len = 1, .width = 0, .istab = false };
} else {
for (size_t i = 0; i < len; i++)
- c.c[i] = cur[i];
- c.c[len] = '\0';
- c.len = len;
+ cell.data[i] = cur[i];
+ cell.data[len] = '\0';
+ cell.istab = false;
+ cell.len = len;
+ cell.width = wcwidth(wchar);
+ if (cell.width == -1)
+ cell.width = 1;
}
if (cur[0] == '\r' && rem > 1 && cur[1] == '\n') {
/* convert windows style newline \r\n into a single char with len = 2 */
- len = 2;
- c = (Char){ .c = "\n", .wchar = L'\n', .len = len };
+ cell = (Cell){ .data = "\n", .len = 2, .width = 1, .istab = false };
}
+ cell.attr = attrs;
if (sel.start <= pos && pos < sel.end)
- wattrset(win->win, attrs | A_REVERSE);
- else
- wattrset(win->win, attrs);
-
- if (!window_addch(win, &c))
+ cell.attr |= A_REVERSE;
+ if (!window_addch(win, &cell))
break;
- rem -= len;
- cur += len;
- pos += len;
+ rem -= cell.len;
+ cur += cell.len;
+ pos += cell.len;
}
/* set end of viewing region */
win->end = pos;
win->lastline = win->line ? win->line : win->bottomline;
win->lastline->next = NULL;
- /* and clear the rest of the unused window */
- wclrtobot(win->win);
- /* if the text area width has changed because of the line numbers
- * we have to (re)sync the cursor->line pointer */
- if (win->width != old_width)
- window_cursor_sync(win);
- /* draw line numbers */
- window_numbers_draw(win);
+ window_cursor_sync(win);
+ win->ui->draw_text(win->ui, win->topline);
}
bool window_resize(Win *win, int width, int height) {
- // TODO: only grow memory area
- size_t line_size = sizeof(Line) + width*sizeof(Cell);
- Line *lines = calloc(height, line_size);
- if (!lines)
- return false;
- free(win->lines);
- win->w = win->width = width;
- win->h = win->height = height;
- win->lines = lines;
- window_clear(win);
+ size_t lines_size = height*(sizeof(Line) + width*sizeof(Cell));
+ if (lines_size > win->lines_size) {
+ Line *lines = malloc(lines_size);
+ if (!lines)
+ return false;
+ win->lines = lines;
+ win->lines_size = lines_size;
+ }
+ win->width = width;
+ win->height = height;
+ memset(win->lines, 0, win->lines_size);
+ window_draw(win);
return true;
}
-void window_move(Win *win, int x, int y) {
- win->x = x;
- win->y = y;
+int window_height_get(Win *win) {
+ return win->height;
}
void window_free(Win *win) {
if (!win)
return;
- if (win->winnum)
- delwin(win->winnum);
- if (win->win)
- delwin(win->win);
free(win->lines);
free(win);
}
@@ -554,27 +467,23 @@ void window_reload(Win *win, Text *text) {
win->text = text;
window_selection_clear(win);
window_cursor_to(win, 0);
+ win->ui->reload(win->ui, text);
}
-Win *window_new(Text *text) {
- if (!text)
+Win *window_new(Text *text, UiWin *ui, int width, int height) {
+ if (!text || !ui)
return NULL;
Win *win = calloc(1, sizeof(Win));
- if (!win || !(win->win = newwin(0, 0, 0, 0))) {
- window_free(win);
+ if (!win)
return NULL;
- }
win->text = text;
+ win->ui = ui;
win->tabwidth = 8;
-
- int width, height;
- getmaxyx(win->win, height, width);
if (!window_resize(win, width, height)) {
window_free(win);
return NULL;
}
-
window_selection_clear(win);
window_cursor_to(win, 0);
@@ -633,9 +542,9 @@ static size_t window_cursor_set(Win *win, Line *line, int col) {
}
/* for characters which use more than 1 column, make sure we are on the left most */
- while (col > 0 && line->cells[col].len == 0 && line->cells[col].data != '\t')
+ while (col > 0 && line->cells[col].len == 0)
col--;
- while (col < line->width && line->cells[col].data == '\t')
+ while (col < line->width && line->cells[col].istab)
col++;
/* calculate offset within the line */
@@ -845,12 +754,6 @@ size_t window_screenline_end(Win *win) {
return window_cursor_set(win, cursor->line, col >= 0 ? col : 0);
}
-void window_update(Win *win) {
- if (win->winnum)
- wnoutrefresh(win->winnum);
- wnoutrefresh(win->win);
-}
-
size_t window_delete_key(Win *win) {
Cursor *cursor = &win->cursor;
Line *line = cursor->line;
@@ -899,7 +802,7 @@ size_t window_replace_key(Win *win, const char *c, size_t len) {
Line *line = cursor->line;
size_t pos = cursor->pos;
/* do not overwrite new line which would merge the two lines */
- if (line->cells[cursor->col].data != '\n') {
+ if (line->cells[cursor->col].data[0] != '\n') {
size_t oldlen = line->cells[cursor->col].len;
text_delete(win->text, pos, oldlen);
}
@@ -939,25 +842,9 @@ Syntax *window_syntax_get(Win *win) {
return win->syntax;
}
-void window_cursor_watch(Win *win, void (*cursor_moved)(Win*, void *), void *data) {
- win->cursor_moved = cursor_moved;
- win->cursor_moved_data = data;
-}
-
size_t window_screenline_goto(Win *win, int n) {
size_t pos = win->start;
for (Line *line = win->topline; --n > 0 && line != win->lastline; line = line->next)
pos += line->len;
return pos;
}
-
-void window_line_numbers_show(Win *win, bool show) {
- if (show) {
- if (!win->winnum)
- win->winnum = newwin(0, 0, 0, 0);
- } else {
- if (win->winnum)
- delwin(win->winnum);
- win->winnum = NULL;
- }
-}