commit de95ca68f7a2a072721e6dd690d5749a3d03eb09
parent 55878efe5d283b191b791128a989d287bfb8eb81
Author: Faissal Bensefia <faissaloo@gmail.com>
Date: Thu, 20 Oct 2016 09:44:29 +0100
new feature: the ability to show line numbers before the text
It can be activated with --linenumbers on the command line or with
'set linenumbers' in a nanorc file, and it can be toggled with M-#.
Signed-off-by: Faissal Bensefia <faissaloo@gmail.com>
Signed-off-by: Benno Schulenberg <bensberg@justemail.net>
Diffstat:
11 files changed, 172 insertions(+), 42 deletions(-)
diff --git a/configure.ac b/configure.ac
@@ -125,6 +125,19 @@ fi
AC_ARG_ENABLE(libmagic,
AS_HELP_STRING([--disable-libmagic], [Disable detection of file types via libmagic]))
+AC_ARG_ENABLE(linenumbers,
+AS_HELP_STRING([--disable-linenumbers], [Disable line numbering]))
+if test "x$enable_tiny" = xyes; then
+ if test "x$enable_linenumbers" != xyes; then
+ enable_linenumbers=no
+ fi
+fi
+if test "x$disable_linenumbers" != xyes; then
+ if test "x$enable_linenumbers" != xno; then
+ AC_DEFINE(ENABLE_LINENUMBERS, 1, [Define this to enable line numbering.])
+ fi
+fi
+
AC_ARG_ENABLE(mouse,
AS_HELP_STRING([--disable-mouse], [Disable mouse support (and -m flag)]))
if test "x$enable_mouse" = xno; then
diff --git a/doc/man/nano.1 b/doc/man/nano.1
@@ -183,6 +183,9 @@ editing source code.
Make the 'Cut Text' command (normally ^K) cut from the current cursor
position to the end of the line, instead of cutting the entire line.
.TP
+.BR \-l ", " \-\-linenumbers
+Display line numbers to the left of the text area.
+.TP
.BR \-m ", " \-\-mouse
Enable mouse support, if available for your system. When enabled, mouse
clicks can be used to place the cursor, set the mark (with a double
diff --git a/doc/man/nanorc.5 b/doc/man/nanorc.5
@@ -116,6 +116,9 @@ Specify the color combination to use for the shortcut key combos
in the two help lines at the bottom of the screen.
See \fBset titlecolor\fR for more details.
.TP
+.B set linenumbers
+Display line numbers to the left of the text area.
+.TP
.B set locking
Enable vim-style lock-files for when editing files.
.TP
diff --git a/src/global.c b/src/global.c
@@ -45,6 +45,17 @@ bool shift_held;
bool focusing = TRUE;
/* Whether an update of the edit window should center the cursor. */
+int margin = 0;
+ /* The amount of space reserved at the left for line numbers. */
+int editwincols = -1;
+ /* The number of usable columns in the edit window: COLS - margin. */
+#ifdef ENABLE_LINENUMBERS
+int last_drawn_line = 0;
+ /* The line number of the last drawn line. */
+int last_line_y;
+ /* The y coordinate of the last drawn line. */
+#endif
+
message_type lastmessage = HUSH;
/* Messages of type HUSH should not overwrite type MILD nor ALERT. */
@@ -1154,6 +1165,9 @@ void shortcut_init(void)
add_to_sclist(MMAIN, "M-O", 0, do_toggle_void, MORE_SPACE);
add_to_sclist(MMAIN, "M-S", 0, do_toggle_void, SMOOTH_SCROLL);
add_to_sclist(MMAIN, "M-$", 0, do_toggle_void, SOFTWRAP);
+#ifdef ENABLE_LINENUMBERS
+ add_to_sclist(MMAIN, "M-#", 0, do_toggle_void, LINE_NUMBERS);
+#endif
add_to_sclist(MMAIN, "M-P", 0, do_toggle_void, WHITESPACE_DISPLAY);
#ifndef DISABLE_COLOR
add_to_sclist(MMAIN, "M-Y", 0, do_toggle_void, NO_COLOR_SYNTAX);
@@ -1332,6 +1346,8 @@ const char *flagtostr(int flag)
return N_("No conversion from DOS/Mac format");
case SUSPEND:
return N_("Suspension");
+ case LINE_NUMBERS:
+ return N_("Line numbering");
default:
return "?????";
}
diff --git a/src/move.c b/src/move.c
@@ -367,7 +367,7 @@ void do_next_word_void(void)
void ensure_line_is_visible(void)
{
#ifndef NANO_TINY
- if (ISSET(SOFTWRAP) && strlenpt(openfile->current->data) / COLS +
+ if (ISSET(SOFTWRAP) && strlenpt(openfile->current->data) / editwincols +
openfile->current_y >= editwinrows) {
edit_update(ISSET(SMOOTH_SCROLL) ? FLOWING : CENTERING);
refresh_needed = TRUE;
@@ -492,13 +492,14 @@ void do_down(bool scroll_only)
#ifndef NANO_TINY
if (ISSET(SOFTWRAP)) {
/* Compute the number of lines to scroll. */
- amount = strlenpt(openfile->current->data) / COLS - xplustabs() / COLS +
- strlenpt(openfile->current->next->data) / COLS +
+ amount = strlenpt(openfile->current->data) / editwincols -
+ xplustabs() / editwincols +
+ strlenpt(openfile->current->next->data) / editwincols +
openfile->current_y - editwinrows + 2;
topline = openfile->edittop;
/* Reduce the amount when there are overlong lines at the top. */
for (enough = 1; enough < amount; enough++) {
- amount -= strlenpt(topline->data) / COLS;
+ amount -= strlenpt(topline->data) / editwincols;
if (amount > 0)
topline = topline->next;
if (amount < enough) {
diff --git a/src/nano.c b/src/nano.c
@@ -884,6 +884,9 @@ void usage(void)
print_opt("-i", "--autoindent", N_("Automatically indent new lines"));
print_opt("-k", "--cut", N_("Cut from cursor to end of line"));
#endif
+#ifdef ENABLE_LINENUMBERS
+ print_opt("-l", "--linenumbers", N_("Show line numbers in front of the text"));
+#endif
#ifndef DISABLE_MOUSE
print_opt("-m", "--mouse", N_("Enable the use of the mouse"));
#endif
@@ -962,6 +965,9 @@ void version(void)
#ifdef HAVE_LIBMAGIC
printf(" --enable-libmagic");
#endif
+#ifdef ENABLE_LINENUMBERS
+ printf(" --enable-linenumbers");
+#endif
#ifndef DISABLE_MOUSE
printf(" --enable-mouse");
#endif
@@ -1008,6 +1014,9 @@ void version(void)
#ifndef HAVE_LIBMAGIC
printf(" --disable-libmagic");
#endif
+#ifndef ENABLE_LINENUMBERS
+ printf(" --disable-linenumbers");
+#endif
#ifdef DISABLE_MOUSE
printf(" --disable-mouse");
#endif
@@ -1340,6 +1349,7 @@ void regenerate_screen(void)
COLS = win.ws_col;
LINES = win.ws_row;
#endif
+ editwincols = COLS - margin;
#ifdef USE_SLANG
/* Slang curses emulation brain damage, part 1: If we just do what
@@ -1414,6 +1424,9 @@ void do_toggle(int flag)
#ifndef DISABLE_COLOR
case NO_COLOR_SYNTAX:
#endif
+#ifdef ENABLE_LINENUMBERS
+ case LINE_NUMBERS:
+#endif
case SOFTWRAP:
edit_refresh();
break;
@@ -1992,6 +2005,9 @@ int main(int argc, char **argv)
{"constantshow", 0, NULL, 'c'},
{"rebinddelete", 0, NULL, 'd'},
{"help", 0, NULL, 'h'},
+#ifdef ENABLE_LINENUMBERS
+ {"linenumbers", 0, NULL, 'l'},
+#endif
#ifndef DISABLE_MOUSE
{"mouse", 0, NULL, 'm'},
#endif
@@ -2271,6 +2287,11 @@ int main(int argc, char **argv)
SET(SOFTWRAP);
break;
#endif
+#ifdef ENABLE_LINENUMBERS
+ case 'l':
+ SET(LINE_NUMBERS);
+ break;
+#endif
case 'h':
usage();
exit(0);
@@ -2542,6 +2563,8 @@ int main(int argc, char **argv)
* dimensions. */
window_init();
+ editwincols = COLS - margin;
+
/* Set up the signal handlers. */
signal_init();
diff --git a/src/nano.h b/src/nano.h
@@ -536,7 +536,8 @@ enum
NOREAD_MODE,
MAKE_IT_UNIX,
JUSTIFY_TRIM,
- SHOW_CURSOR
+ SHOW_CURSOR,
+ LINE_NUMBERS
};
/* Flags for the menus in which a given function should be present. */
diff --git a/src/proto.h b/src/proto.h
@@ -38,6 +38,13 @@ extern bool shift_held;
extern bool focusing;
+extern int margin;
+extern int editwincols;
+#ifdef ENABLE_LINENUMBERS
+extern int last_drawn_line;
+extern int last_line_y;
+#endif
+
extern message_type lastmessage;
extern int controlleft;
@@ -675,6 +682,7 @@ void do_verbatim_input(void);
/* All functions in utils.c. */
void get_homedir(void);
+int digits(int n);
bool parse_num(const char *str, ssize_t *val);
bool parse_line_column(const char *str, ssize_t *line, ssize_t *column);
void align(char **str);
diff --git a/src/rcfile.c b/src/rcfile.c
@@ -35,6 +35,9 @@
static const rcoption rcopts[] = {
{"boldtext", BOLD_TEXT},
+#ifdef ENABLE_LINENUMBERS
+ {"linenumbers", LINE_NUMBERS},
+#endif
#ifndef DISABLE_JUSTIFY
{"brackets", 0},
#endif
diff --git a/src/utils.c b/src/utils.c
@@ -52,6 +52,39 @@ void get_homedir(void)
}
}
+#ifdef ENABLE_LINENUMBERS
+/* Return the number of digits that the given integer n takes up. */
+int digits(int n)
+{
+ if (n < 100000) {
+ if (n < 1000) {
+ if (n < 100)
+ return 2;
+ else
+ return 3;
+ } else {
+ if (n < 10000)
+ return 4;
+ else
+ return 5;
+ }
+ } else {
+ if (n < 10000000) {
+ if (n < 1000000)
+ return 6;
+ else
+ return 7;
+ }
+ else {
+ if (n < 100000000)
+ return 8;
+ else
+ return 9;
+ }
+ }
+}
+#endif
+
/* Read a ssize_t from str, and store it in *val (if val is not NULL).
* On error, we return FALSE and don't change *val. Otherwise, we
* return TRUE. */
@@ -430,12 +463,12 @@ char *free_and_assign(char *dest, char *src)
* get_page_start(column) < COLS). */
size_t get_page_start(size_t column)
{
- if (column == 0 || column < COLS - 1)
+ if (column == 0 || column < editwincols - 1)
return 0;
- else if (COLS > 8)
- return column - 7 - (column - 7) % (COLS - 8);
+ else if (editwincols > 8)
+ return column - 7 - (column - 7) % (editwincols - 8);
else
- return column - (COLS - 2);
+ return column - (editwincols - 2);
}
/* Return the placewewant associated with current_x, i.e. the zero-based
diff --git a/src/winio.c b/src/winio.c
@@ -1534,7 +1534,7 @@ int get_mouseinput(int *mouse_x, int *mouse_y, bool allow_shortcuts)
return -1;
/* Save the screen coordinates where the mouse event took place. */
- *mouse_x = mevent.x;
+ *mouse_x = mevent.x - margin;
*mouse_y = mevent.y;
in_bottomwin = wenclose(bottomwin, *mouse_y, *mouse_x);
@@ -1564,7 +1564,7 @@ int get_mouseinput(int *mouse_x, int *mouse_y, bool allow_shortcuts)
if (*mouse_y == 0) {
/* Restore the untranslated mouse event coordinates, so
* that they're relative to the entire screen again. */
- *mouse_x = mevent.x;
+ *mouse_x = mevent.x - margin;
*mouse_y = mevent.y;
return 0;
@@ -2224,13 +2224,13 @@ void reset_cursor(void)
openfile->current_y = 0;
while (line != NULL && line != openfile->current) {
- openfile->current_y += strlenpt(line->data) / COLS + 1;
+ openfile->current_y += strlenpt(line->data) / editwincols + 1;
line = line->next;
}
- openfile->current_y += xpt / COLS;
+ openfile->current_y += xpt / editwincols;
if (openfile->current_y < editwinrows)
- wmove(edit, openfile->current_y, xpt % COLS);
+ wmove(edit, openfile->current_y, xpt % editwincols + margin);
} else
#endif
{
@@ -2238,7 +2238,7 @@ void reset_cursor(void)
openfile->edittop->lineno;
if (openfile->current_y < editwinrows)
- wmove(edit, openfile->current_y, xpt - get_page_start(xpt));
+ wmove(edit, openfile->current_y, xpt - get_page_start(xpt) + margin);
}
}
@@ -2257,7 +2257,7 @@ void edit_draw(filestruct *fileptr, const char *converted, int
size_t startpos = actual_x(fileptr->data, start);
/* The position in fileptr->data of the leftmost character
* that displays at least partially on the window. */
- size_t endpos = actual_x(fileptr->data, start + COLS - 1) + 1;
+ size_t endpos = actual_x(fileptr->data, start + editwincols - 1) + 1;
/* The position in fileptr->data of the first character that is
* completely off the window to the right.
*
@@ -2266,11 +2266,33 @@ void edit_draw(filestruct *fileptr, const char *converted, int
#endif
assert(openfile != NULL && fileptr != NULL && converted != NULL);
- assert(strlenpt(converted) <= COLS);
+ assert(strlenpt(converted) <= editwincols);
+
+#ifdef ENABLE_LINENUMBERS
+ if (ISSET(LINE_NUMBERS)) {
+ /* If the line numbers now require more room, schedule a refresh. */
+ if (digits(openfile->filebot->lineno) + 1 != margin) {
+ margin = digits(openfile->filebot->lineno) + 1;
+ editwincols = COLS - margin;
+ refresh_needed = TRUE;
+ }
+
+ /* Show the line number only for the non-softwrapped parts. */
+ wattron(edit, hilite_attribute);
+ if (last_drawn_line != fileptr->lineno || last_line_y >= line)
+ mvwprintw(edit, line, 0, "%*i", margin - 1, fileptr->lineno);
+ else
+ mvwprintw(edit, line, 0, "%*s", margin - 1, " ");
+ wattroff(edit, hilite_attribute);
+ } else {
+ margin = 0;
+ editwincols = COLS;
+ }
+#endif
/* First simply paint the line -- then we'll add colors or the
* marking highlight on just the pieces that need it. */
- mvwaddstr(edit, line, 0, converted);
+ mvwaddstr(edit, line, margin, converted);
#ifdef USING_OLD_NCURSES
/* Tell ncurses to really redraw the line without trying to optimize
@@ -2348,7 +2370,7 @@ void edit_draw(filestruct *fileptr, const char *converted, int
assert(0 <= x_start && 0 <= paintlen);
- mvwaddnstr(edit, line, x_start, converted +
+ mvwaddnstr(edit, line, x_start + margin, converted +
index, paintlen);
}
k = startmatch.rm_eo;
@@ -2365,7 +2387,7 @@ void edit_draw(filestruct *fileptr, const char *converted, int
if (fileptr->multidata[varnish->id] == CNONE)
goto tail_of_loop;
else if (fileptr->multidata[varnish->id] == CWHOLELINE) {
- mvwaddnstr(edit, line, 0, converted, -1);
+ mvwaddnstr(edit, line, margin, converted, -1);
goto tail_of_loop;
} else if (fileptr->multidata[varnish->id] == CBEGINBEFORE) {
regexec(varnish->end, fileptr->data, 1, &endmatch, 0);
@@ -2374,7 +2396,7 @@ void edit_draw(filestruct *fileptr, const char *converted, int
goto tail_of_loop;
paintlen = actual_x(converted, strnlenpt(fileptr->data,
endmatch.rm_eo) - start);
- mvwaddnstr(edit, line, 0, converted, paintlen);
+ mvwaddnstr(edit, line, margin, converted, paintlen);
goto tail_of_loop;
} if (fileptr->multidata[varnish->id] == -1)
/* Assume this until proven otherwise below. */
@@ -2470,7 +2492,7 @@ void edit_draw(filestruct *fileptr, const char *converted, int
fprintf(stderr, " Marking for id %i line %i as CBEGINBEFORE\n", varnish->id, line);
#endif
}
- mvwaddnstr(edit, line, 0, converted, paintlen);
+ mvwaddnstr(edit, line, margin, converted, paintlen);
/* If the whole line has been painted, don't bother looking
* for any more starts. */
if (paintlen < 0)
@@ -2516,9 +2538,9 @@ void edit_draw(filestruct *fileptr, const char *converted, int
strnlenpt(fileptr->data,
endmatch.rm_eo) - start - x_start);
- assert(0 <= x_start && x_start < COLS);
+ assert(0 <= x_start && x_start < editwincols);
- mvwaddnstr(edit, line, x_start,
+ mvwaddnstr(edit, line, x_start + margin,
converted + index, paintlen);
if (paintlen > 0) {
fileptr->multidata[varnish->id] = CSTARTENDHERE;
@@ -2545,10 +2567,10 @@ void edit_draw(filestruct *fileptr, const char *converted, int
if (end_line == NULL)
break;
- assert(0 <= x_start && x_start < COLS);
+ assert(0 <= x_start && x_start < editwincols);
/* Paint the rest of the line. */
- mvwaddnstr(edit, line, x_start, converted + index, -1);
+ mvwaddnstr(edit, line, x_start + margin, converted + index, -1);
fileptr->multidata[varnish->id] = CENDAFTER;
#ifdef DEBUG
fprintf(stderr, " Marking for id %i line %i as CENDAFTER\n", varnish->id, line);
@@ -2626,11 +2648,15 @@ void edit_draw(filestruct *fileptr, const char *converted, int
paintlen = actual_x(converted + index, paintlen);
wattron(edit, hilite_attribute);
- mvwaddnstr(edit, line, x_start, converted + index, paintlen);
+ mvwaddnstr(edit, line, x_start + margin, converted + index, paintlen);
wattroff(edit, hilite_attribute);
}
}
#endif /* !NANO_TINY */
+#ifdef ENABLE_LINENUMBERS
+ last_drawn_line = fileptr->lineno;
+ last_line_y = line;
+#endif
}
/* Just update one line in the edit buffer. This is basically a wrapper
@@ -2654,7 +2680,7 @@ int update_line(filestruct *fileptr, size_t index)
filestruct *tmp;
for (tmp = openfile->edittop; tmp && tmp != fileptr; tmp = tmp->next)
- line += (strlenpt(tmp->data) / COLS) + 1;
+ line += (strlenpt(tmp->data) / editwincols) + 1;
} else
#endif
line = fileptr->lineno - openfile->edittop->lineno;
@@ -2678,11 +2704,11 @@ int update_line(filestruct *fileptr, size_t index)
/* Expand the line, replacing tabs with spaces, and control
* characters with their displayed forms. */
#ifdef NANO_TINY
- converted = display_string(fileptr->data, page_start, COLS, TRUE);
+ converted = display_string(fileptr->data, page_start, editwincols, TRUE);
#else
- converted = display_string(fileptr->data, page_start, COLS, !ISSET(SOFTWRAP));
+ converted = display_string(fileptr->data, page_start, editwincols, !ISSET(SOFTWRAP));
#ifdef DEBUG
- if (ISSET(SOFTWRAP) && strlen(converted) >= COLS - 2)
+ if (ISSET(SOFTWRAP) && strlen(converted) >= editwincols - 2)
fprintf(stderr, "update_line(): converted(1) line = %s\n", converted);
#endif
#endif /* !NANO_TINY */
@@ -2695,13 +2721,13 @@ int update_line(filestruct *fileptr, size_t index)
if (!ISSET(SOFTWRAP)) {
#endif
if (page_start > 0)
- mvwaddch(edit, line, 0, '$');
- if (strlenpt(fileptr->data) > page_start + COLS)
+ mvwaddch(edit, line, margin, '$');
+ if (strlenpt(fileptr->data) > page_start + editwincols)
mvwaddch(edit, line, COLS - 1, '$');
#ifndef NANO_TINY
} else {
size_t full_length = strlenpt(fileptr->data);
- for (index += COLS; index <= full_length && line < editwinrows - 1; index += COLS) {
+ for (index += editwincols; index <= full_length && line < editwinrows - 1; index += editwincols) {
line++;
#ifdef DEBUG
fprintf(stderr, "update_line(): softwrap code, moving to %d index %lu\n", line, (unsigned long)index);
@@ -2710,9 +2736,9 @@ int update_line(filestruct *fileptr, size_t index)
/* Expand the line, replacing tabs with spaces, and control
* characters with their displayed forms. */
- converted = display_string(fileptr->data, index, COLS, !ISSET(SOFTWRAP));
+ converted = display_string(fileptr->data, index, editwincols, !ISSET(SOFTWRAP));
#ifdef DEBUG
- if (ISSET(SOFTWRAP) && strlen(converted) >= COLS - 2)
+ if (ISSET(SOFTWRAP) && strlen(converted) >= editwincols - 2)
fprintf(stderr, "update_line(): converted(2) line = %s\n", converted);
#endif
@@ -2753,7 +2779,7 @@ void compute_maxrows(void)
maxrows = 0;
for (n = 0; n < editwinrows && foo; n++) {
maxrows++;
- n += strlenpt(foo->data) / COLS;
+ n += strlenpt(foo->data) / editwincols;
foo = foo->next;
}
@@ -2798,7 +2824,7 @@ void edit_scroll(scroll_dir direction, ssize_t nlines)
#ifndef NANO_TINY
/* Don't over-scroll on long lines. */
if (ISSET(SOFTWRAP) && direction == UPWARD) {
- ssize_t len = strlenpt(openfile->edittop->data) / COLS;
+ ssize_t len = strlenpt(openfile->edittop->data) / editwincols;
i -= len;
if (len > 0)
refresh_needed = TRUE;
@@ -2879,7 +2905,7 @@ void edit_redraw(filestruct *old_current)
if (openfile->current->lineno >= openfile->edittop->lineno + maxrows ||
#ifndef NANO_TINY
(openfile->current->lineno == openfile->edittop->lineno + maxrows - 1 &&
- ISSET(SOFTWRAP) && strlenpt(openfile->current->data) >= COLS) ||
+ ISSET(SOFTWRAP) && strlenpt(openfile->current->data) >= editwincols) ||
#endif
openfile->current->lineno < openfile->edittop->lineno) {
edit_update((focusing || !ISSET(SMOOTH_SCROLL)) ? CENTERING : FLOWING);
@@ -2978,7 +3004,7 @@ void edit_update(update_type manner)
goal = editwinrows - 1;
#ifndef NANO_TINY
if (ISSET(SOFTWRAP))
- goal -= strlenpt(openfile->current->data) / COLS ;
+ goal -= strlenpt(openfile->current->data) / editwincols;
#endif
}
} else {
@@ -2996,7 +3022,7 @@ void edit_update(update_type manner)
goal --;
#ifndef NANO_TINY
if (ISSET(SOFTWRAP)) {
- goal -= strlenpt(openfile->edittop->data) / COLS;
+ goal -= strlenpt(openfile->edittop->data) / editwincols;
if (goal < 0)
openfile->edittop = openfile->edittop->next;
}
@@ -3118,7 +3144,7 @@ void spotlight(bool active, const char *word)
size_t word_len = strlenpt(word), room;
/* Compute the number of columns that are available for the word. */
- room = COLS + get_page_start(xplustabs()) - xplustabs();
+ room = editwincols + get_page_start(xplustabs()) - xplustabs();
assert(room > 0);