aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config.def.h19
-rw-r--r--syntax.h15
-rw-r--r--ui-curses.c4
-rw-r--r--view.c94
-rw-r--r--view.h2
-rw-r--r--vis.c32
6 files changed, 149 insertions, 17 deletions
diff --git a/config.def.h b/config.def.h
index 9d491a0..dff70d5 100644
--- a/config.def.h
+++ b/config.def.h
@@ -827,6 +827,11 @@ enum {
COLOR_COMMENT = COLOR_SYNTAX7,
COLOR_IDENTIFIER = COLOR_SYNTAX8,
COLOR_TYPE = COLOR_SYNTAX9,
+ COLOR_WHITESPACE = COLOR_COMMENT,
+ COLOR_SPACES = COLOR_WHITESPACE,
+ COLOR_TABS = COLOR_WHITESPACE,
+ COLOR_EOL = COLOR_WHITESPACE,
+ COLOR_EOF = COLOR_WHITESPACE,
};
static Color colors[] = {
@@ -894,6 +899,12 @@ static Color colors[] = {
&colors[COLOR_PREPROCESSOR], \
}
+#define SYNTAX_SPACES { "\xC2\xB7", &colors[COLOR_SPACES] }
+#define SYNTAX_TABS { "\xE2\x96\xB6", &colors[COLOR_TABS] }
+#define SYNTAX_TABS_FILL { " ", &colors[COLOR_TABS] }
+#define SYNTAX_EOL { "\xE2\x8F\x8E", &colors[COLOR_EOL] }
+#define SYNTAX_EOF { "~", &colors[COLOR_EOF] }
+
/* these rules are applied top to bottom, first match wins. Therefore more 'greedy'
* rules such as for comments should be the first entries.
*
@@ -905,8 +916,16 @@ static Syntax syntaxes[] = {{
.settings = (const char*[]){
"set number",
"set autoindent",
+ "set show spaces=0 tabs=1 newlines=1",
NULL
},
+ .symbols = {
+ SYNTAX_SPACES,
+ SYNTAX_TABS,
+ SYNTAX_TABS_FILL,
+ SYNTAX_EOL,
+ SYNTAX_EOF,
+ },
.rules = {
SYNTAX_MULTILINE_COMMENT,
SYNTAX_SINGLE_LINE_COMMENT,
diff --git a/syntax.h b/syntax.h
index 13fcb8c..c28a7ea 100644
--- a/syntax.h
+++ b/syntax.h
@@ -15,12 +15,27 @@ typedef struct {
regex_t regex; /* compiled form of the above rule */
} SyntaxRule;
+typedef struct {
+ char *symbol;
+ Color *color;
+} SyntaxSymbol;
+
+enum {
+ SYNTAX_SYMBOL_SPACE,
+ SYNTAX_SYMBOL_TAB,
+ SYNTAX_SYMBOL_TAB_FILL,
+ SYNTAX_SYMBOL_EOL,
+ SYNTAX_SYMBOL_EOF,
+ SYNTAX_SYMBOL_LAST,
+};
+
typedef struct Syntax Syntax;
struct Syntax { /* a syntax definition */
char *name; /* syntax name */
char *file; /* apply to files matching this regex */
regex_t file_regex; /* compiled file name regex */
const char **settings;/* settings associated with this file type */
+ SyntaxSymbol symbols[SYNTAX_SYMBOL_LAST]; /* symbols for white space handling */
SyntaxRule rules[24]; /* all rules for this file type */
};
diff --git a/ui-curses.c b/ui-curses.c
index a999aef..5dfb299 100644
--- a/ui-curses.c
+++ b/ui-curses.c
@@ -213,7 +213,7 @@ static void ui_window_draw_sidebar(UiCursesWin *win, const Line *line) {
size_t cursor_lineno = view_cursor_getpos(win->view).line;
werase(win->winside);
for (const Line *l = line; l; l = l->next, i++) {
- if (l->lineno != prev_lineno) {
+ if (l->lineno && l->lineno != prev_lineno) {
if (win->options & UI_OPTION_LINE_NUMBERS_ABSOLUTE) {
mvwprintw(win->winside, i, 0, "%*u", sidebar_width-1, l->lineno);
} else if (win->options & UI_OPTION_LINE_NUMBERS_RELATIVE) {
@@ -376,6 +376,8 @@ static void ui_window_draw_text(UiWin *w, const Line *line) {
wattrset(win->win, l->cells[x].attr);
waddstr(win->win, l->cells[x].data);
}
+ if (l->width != win->width - win->sidebar_width)
+ waddstr(win->win, "\n");
}
wclrtoeol(win->win);
}
diff --git a/view.c b/view.c
index d49c417..a002cd5 100644
--- a/view.c
+++ b/view.c
@@ -51,9 +51,26 @@ struct View { /* viewable area, showing part of a file */
Line *line; /* used while drawing view content, line where next char will be drawn */
int col; /* used while drawing view content, column where next char will be drawn */
Syntax *syntax; /* syntax highlighting definitions for this view or NULL */
+ SyntaxSymbol *symbols[SYNTAX_SYMBOL_LAST]; /* symbols to use for white spaces etc */
int tabwidth; /* how many spaces should be used to display a tab character */
};
+static SyntaxSymbol symbols_none[] = {
+ { " " }, /* spaces */
+ { " " }, /* tab first cell */
+ { " " }, /* tab remaining cells */
+ { "" }, /* eol */
+ { "~" }, /* eof */
+};
+
+static SyntaxSymbol symbols_default[] = {
+ { "\xC2\xB7" }, /* spaces */
+ { "\xE2\x96\xB6" }, /* tab first cell */
+ { " " }, /* tab remaining cells */
+ { "\xE2\x8F\x8E" }, /* eol */
+ { "~" }, /* eof */
+};
+
static void view_clear(View *view);
static bool view_addch(View *view, Cell *cell);
static size_t view_cursor_update(View *view);
@@ -134,6 +151,8 @@ static bool view_addch(View *view, Cell *cell) {
switch (cell->data[0]) {
case '\t':
+ cell->istab = true;
+ cell->width = 1;
width = view->tabwidth - (view->col % view->tabwidth);
for (int w = 0; w < width; w++) {
if (view->col + 1 > view->width) {
@@ -143,23 +162,18 @@ static bool view_addch(View *view, Cell *cell) {
return false;
view->line->lineno = lineno;
}
- if (w == 0) {
- /* first cell of a tab has a length of 1 */
- view->line->cells[view->col].len = cell->len;
- view->line->len += cell->len;
- } else {
- /* all remaining ones have a lenght of zero */
- view->line->cells[view->col].len = 0;
- }
- /* but all are marked as part of a tabstop */
- view->line->cells[view->col].width = 1;
- view->line->cells[view->col].data[0] = ' ';
- view->line->cells[view->col].data[1] = '\0';
- view->line->cells[view->col].istab = true;
- view->line->cells[view->col].attr = cell->attr;
- view->line->width++;
+
+ cell->len = w == 0 ? 1 : 0;
+ int t = w == 0 ? SYNTAX_SYMBOL_TAB : SYNTAX_SYMBOL_TAB_FILL;
+ strncpy(cell->data, view->symbols[t]->symbol, sizeof(cell->data));
+ if (view->symbols[t]->color)
+ cell->attr = view->symbols[t]->color->attr;
+ view->line->cells[view->col] = *cell;
+ view->line->len += cell->len;
+ view->line->width += cell->width;
view->col++;
}
+ cell->len = 1;
return true;
case '\n':
cell->width = 1;
@@ -170,6 +184,11 @@ static bool view_addch(View *view, Cell *cell) {
return false;
view->line->lineno = lineno;
}
+
+ strncpy(cell->data, view->symbols[SYNTAX_SYMBOL_EOL]->symbol, sizeof(cell->data));
+ if (view->symbols[SYNTAX_SYMBOL_EOL]->color)
+ cell->attr = view->symbols[SYNTAX_SYMBOL_EOL]->color->attr;
+
view->line->cells[view->col] = *cell;
view->line->len += cell->len;
view->line->width += cell->width;
@@ -193,6 +212,12 @@ static bool view_addch(View *view, Cell *cell) {
};
}
+ if (cell->data[0] == ' ') {
+ strncpy(cell->data, view->symbols[SYNTAX_SYMBOL_SPACE]->symbol, sizeof(cell->data));
+ if (view->symbols[SYNTAX_SYMBOL_SPACE]->color)
+ cell->attr = view->symbols[SYNTAX_SYMBOL_SPACE]->color->attr;
+ }
+
if (view->col + cell->width > view->width) {
for (int i = view->col; i < view->width; i++)
view->line->cells[i] = empty;
@@ -452,7 +477,15 @@ void view_draw(View *view) {
/* set end of vieviewg region */
view->end = pos;
view->lastline = view->line ? view->line : view->bottomline;
- view->lastline->next = NULL;
+
+ for (Line *l = view->lastline->next; l; l = l->next) {
+ strncpy(l->cells[0].data, view->symbols[SYNTAX_SYMBOL_EOF]->symbol, sizeof(l->cells[0].data));
+ if (view->symbols[SYNTAX_SYMBOL_EOF]->color)
+ l->cells[0].attr =view->symbols[SYNTAX_SYMBOL_EOF]->color->attr;
+ l->width = 1;
+ l->len = 0;
+ }
+
view_cursor_sync(view);
if (view->ui)
view->ui->draw_text(view->ui, view->topline);
@@ -505,6 +538,7 @@ View *view_new(Text *text, ViewEvent *events) {
view->text = text;
view->events = events;
view->tabwidth = 8;
+ view_symbols_set(view, 0);
if (!view_resize(view, 1, 1)) {
view_free(view);
@@ -776,12 +810,40 @@ void view_selection_start(View *view) {
void view_syntax_set(View *view, Syntax *syntax) {
view->syntax = syntax;
+ for (int i = 0; i < LENGTH(view->symbols); i++) {
+ if (syntax && syntax->symbols[i].symbol)
+ view->symbols[i] = &syntax->symbols[i];
+ else
+ view->symbols[i] = &symbols_none[i];
+ }
}
Syntax *view_syntax_get(View *view) {
return view->syntax;
}
+void view_symbols_set(View *view, int flags) {
+ for (int i = 0; i < LENGTH(view->symbols); i++) {
+ if (flags & (1 << i)) {
+ if (view->syntax && view->syntax->symbols[i].symbol)
+ view->symbols[i] = &view->syntax->symbols[i];
+ else
+ view->symbols[i] = &symbols_default[i];
+ } else {
+ view->symbols[i] = &symbols_none[i];
+ }
+ }
+}
+
+int view_symbols_get(View *view) {
+ int flags = 0;
+ for (int i = 0; i < LENGTH(view->symbols); i++) {
+ if (view->symbols[i] != &symbols_none[i])
+ flags |= (1 << i);
+ }
+ return flags;
+}
+
size_t view_screenline_goto(View *view, int n) {
size_t pos = view->start;
for (Line *line = view->topline; --n > 0 && line != view->lastline; line = line->next)
diff --git a/view.h b/view.h
index 049ed4c..1d10110 100644
--- a/view.h
+++ b/view.h
@@ -115,5 +115,7 @@ bool view_viewport_down(View *view, int n);
/* associate a set of syntax highlighting rules to this window. */
void view_syntax_set(View*, Syntax*);
Syntax *view_syntax_get(View*);
+void view_symbols_set(View*, int flags);
+int view_symbols_get(View*);
#endif
diff --git a/vis.c b/vis.c
index 63c574e..6f1fb27 100644
--- a/vis.c
+++ b/vis.c
@@ -1339,6 +1339,7 @@ static bool cmd_set(Filerange *range, enum CmdOpt cmdopt, const char *argv[]) {
OPTION_EXPANDTAB,
OPTION_TABWIDTH,
OPTION_SYNTAX,
+ OPTION_SHOW,
OPTION_NUMBER,
OPTION_NUMBER_RELATIVE,
};
@@ -1349,6 +1350,7 @@ static bool cmd_set(Filerange *range, enum CmdOpt cmdopt, const char *argv[]) {
[OPTION_EXPANDTAB] = { { "expandtab", "et" }, OPTION_TYPE_BOOL },
[OPTION_TABWIDTH] = { { "tabwidth", "tw" }, OPTION_TYPE_NUMBER },
[OPTION_SYNTAX] = { { "syntax" }, OPTION_TYPE_STRING, true },
+ [OPTION_SHOW] = { { "show" }, OPTION_TYPE_STRING },
[OPTION_NUMBER] = { { "numbers", "nu" }, OPTION_TYPE_BOOL },
[OPTION_NUMBER_RELATIVE] = { { "relativenumbers", "rnu" }, OPTION_TYPE_BOOL },
};
@@ -1448,6 +1450,36 @@ static bool cmd_set(Filerange *range, enum CmdOpt cmdopt, const char *argv[]) {
else
editor_info_show(vis, "Unknown syntax definition: `%s'", argv[2]);
break;
+ case OPTION_SHOW:
+ if (!argv[2]) {
+ editor_info_show(vis, "Expecting: spaces, tabs, newlines");
+ return false;
+ }
+ char *keys[] = { "spaces", "tabs", "newlines" };
+ int values[] = {
+ 1 << SYNTAX_SYMBOL_SPACE,
+ (1 << SYNTAX_SYMBOL_TAB)|(1 << SYNTAX_SYMBOL_TAB_FILL),
+ 1 << SYNTAX_SYMBOL_EOL
+ };
+ int flags = view_symbols_get(vis->win->view);
+ for (const char **args = &argv[2]; *args; args++) {
+ for (int i = 0; i < LENGTH(keys); i++) {
+ if (strcmp(*args, keys[i]) == 0) {
+ flags |= values[i];
+ } else if (strstr(*args, keys[i]) == *args) {
+ bool show;
+ const char *v = *args + strlen(keys[i]);
+ if (*v == '=' && parse_bool(v+1, &show)) {
+ if (show)
+ flags |= values[i];
+ else
+ flags &= ~values[i];
+ }
+ }
+ }
+ }
+ view_symbols_set(vis->win->view, flags);
+ break;
case OPTION_NUMBER:
editor_window_options(vis->win, arg.b ? UI_OPTION_LINE_NUMBERS_ABSOLUTE :
UI_OPTION_LINE_NUMBERS_NONE);