new feature: add a search facility to the help viewer

Allow the user to search in a help text with ^W and M-W.

Achieve this by not writing the help text directly to the screen
but first writing it to a temporary file and then opening this file
in a new buffer, and treating it specially: the normal file-reading
feedback is suppressed, the titlebar shows the headline of the text,
the cursor is hidden, and the menu is limited to just the up and down
movements and searching.

This fulfills https://savannah.gnu.org/bugs/?28994.

Signed-off-by: Rishabh Dave <rishabhddave@gmail.com>
master
Rishabh Dave 2017-04-22 21:57:01 +05:30 committed by Benno Schulenberg
parent ca6d7b6a46
commit 90bd25c1bb
9 changed files with 207 additions and 105 deletions

View File

@ -465,7 +465,7 @@ bool open_buffer(const char *filename, bool undoable)
if (new_buffer) {
make_new_buffer();
if (has_valid_path(realname)) {
if (!inhelp && has_valid_path(realname)) {
#ifndef NANO_TINY
if (ISSET(LOCKING) && filename[0] != '\0') {
/* When not overriding an existing lock, discard the buffer. */
@ -484,7 +484,7 @@ bool open_buffer(const char *filename, bool undoable)
/* If the filename isn't blank, and we are not in NOREAD_MODE,
* open the file. Otherwise, treat it as a new file. */
rc = (filename[0] != '\0' && !ISSET(NOREAD_MODE)) ?
open_file(realname, new_buffer, FALSE, &f) : -2;
open_file(realname, new_buffer, inhelp, &f) : -2;
/* If we have a file, and we're loading into a new buffer, update
* the filename. */
@ -585,7 +585,8 @@ void replace_marked_buffer(const char *filename, filestruct *top, size_t top_x,
void display_buffer(void)
{
/* Update the titlebar, since the filename may have changed. */
titlebar(NULL);
if (!inhelp)
titlebar(NULL);
#ifndef DISABLE_COLOR
/* Make sure we're using the buffer's associated colors. */
@ -634,9 +635,10 @@ void switch_to_prevnext_buffer(bool to_next)
display_buffer();
/* Indicate the switch on the statusbar. */
statusline(HUSH, _("Switched to %s"),
((openfile->filename[0] == '\0') ?
_("New Buffer") : openfile->filename));
if (!inhelp)
statusline(HUSH, _("Switched to %s"),
((openfile->filename[0] == '\0') ?
_("New Buffer") : openfile->filename));
#ifdef DEBUG
dump_filestruct(openfile->current);
@ -903,6 +905,10 @@ void read_file(FILE *f, int fd, const char *filename, bool undoable,
/* Set the desired x position at the end of what was inserted. */
openfile->placewewant = xplustabs();
/* If we've read a help file, don't give any feedback. */
if (inhelp)
return;
if (!writable)
statusline(ALERT, _("File '%s' is unwritable"), filename);
#ifndef NANO_TINY
@ -996,7 +1002,7 @@ int open_file(const char *filename, bool newfie, bool quiet, FILE **f)
if (*f == NULL) {
statusline(ALERT, _("Error reading %s: %s"), filename, strerror(errno));
close(fd);
} else
} else if (!inhelp)
statusbar(_("Reading File"));
}

View File

@ -58,6 +58,11 @@ message_type lastmessage = HUSH;
filestruct *pletion_line = NULL;
/* The line where the last completion was found, if any. */
bool inhelp = FALSE;
/* Whether we are in the help viewer. */
char *title = NULL;
/* When not NULL: the title of the current help text. */
int controlleft, controlright, controlup, controldown, controlhome, controlend;
#ifndef NANO_TINY
int shiftcontrolleft, shiftcontrolright, shiftcontrolup, shiftcontroldown;
@ -711,6 +716,9 @@ void shortcut_init(void)
add_to_funcs(total_refresh, MHELP, refresh_tag, "x", 0, VIEW);
add_to_funcs(do_search, MHELP, whereis_tag, "x", 0, VIEW);
add_to_funcs(do_research, MHELP, whereis_next_tag, "x", 0, VIEW);
add_to_funcs(do_up_void, MHELP, prev_line_tag, "x", 0, VIEW);
add_to_funcs(do_down_void, MHELP, next_line_tag, "x" , 0, VIEW);
#endif
@ -1035,8 +1043,8 @@ void shortcut_init(void)
/* Start associating key combos with functions in specific menus. */
add_to_sclist(MMOST, "^G", 0, do_help_void, 0);
add_to_sclist(MMOST, "F1", 0, do_help_void, 0);
add_to_sclist(MMOST & ~MFINDINHELP, "^G", 0, do_help_void, 0);
add_to_sclist(MMOST & ~MFINDINHELP, "F1", 0, do_help_void, 0);
add_to_sclist(MMAIN|MHELP|MBROWSER, "^X", 0, do_exit, 0);
add_to_sclist(MMAIN|MHELP|MBROWSER, "F2", 0, do_exit, 0);
add_to_sclist(MMAIN, "^O", 0, do_writeout_void, 0);
@ -1044,8 +1052,8 @@ void shortcut_init(void)
add_to_sclist(MMAIN, "^R", 0, do_insertfile_void, 0);
add_to_sclist(MMAIN, "F5", 0, do_insertfile_void, 0);
add_to_sclist(MMAIN, "Ins", 0, do_insertfile_void, 0);
add_to_sclist(MMAIN|MBROWSER, "^W", 0, do_search, 0);
add_to_sclist(MMAIN|MBROWSER, "F6", 0, do_search, 0);
add_to_sclist(MMAIN|MHELP|MBROWSER, "^W", 0, do_search, 0);
add_to_sclist(MMAIN|MHELP|MBROWSER, "F6", 0, do_search, 0);
add_to_sclist(MMAIN, "^\\", 0, do_replace, 0);
add_to_sclist(MMAIN, "M-R", 0, do_replace, 0);
add_to_sclist(MMAIN, "F14", 0, do_replace, 0);
@ -1083,8 +1091,8 @@ void shortcut_init(void)
add_to_sclist(MMAIN|MHELP, "M-/", 0, do_last_line, 0);
add_to_sclist(MMAIN|MHELP, "^End", CONTROL_END, do_last_line, 0);
add_to_sclist(MMAIN|MHELP, "M-?", 0, do_last_line, 0);
add_to_sclist(MMAIN|MBROWSER, "M-W", 0, do_research, 0);
add_to_sclist(MMAIN|MBROWSER, "F16", 0, do_research, 0);
add_to_sclist(MMAIN|MHELP|MBROWSER, "M-W", 0, do_research, 0);
add_to_sclist(MMAIN|MHELP|MBROWSER, "F16", 0, do_research, 0);
#ifndef NANO_TINY
add_to_sclist(MMAIN, "M-]", 0, do_find_bracket, 0);
add_to_sclist(MMAIN, "M-A", 0, do_mark, 0);
@ -1227,17 +1235,17 @@ void shortcut_init(void)
add_to_sclist(MWHEREIS, "^T", 0, do_gotolinecolumn_void, 0);
add_to_sclist(MGOTOLINE, "^T", 0, gototext_void, 0);
#ifndef DISABLE_HISTORIES
add_to_sclist(MWHEREIS|MREPLACE|MREPLACEWITH|MWHEREISFILE, "^P", 0, get_history_older_void, 0);
add_to_sclist(MWHEREIS|MREPLACE|MREPLACEWITH|MWHEREISFILE, "^N", 0, get_history_newer_void, 0);
add_to_sclist(MWHEREIS|MREPLACE|MREPLACEWITH|MWHEREISFILE|MFINDINHELP, "^P", 0, get_history_older_void, 0);
add_to_sclist(MWHEREIS|MREPLACE|MREPLACEWITH|MWHEREISFILE|MFINDINHELP, "^N", 0, get_history_newer_void, 0);
#ifdef ENABLE_UTF8
if (using_utf8()) {
add_to_sclist(MWHEREIS|MREPLACE|MREPLACEWITH|MWHEREISFILE, "\xE2\x86\x91", KEY_UP, get_history_older_void, 0);
add_to_sclist(MWHEREIS|MREPLACE|MREPLACEWITH|MWHEREISFILE, "\xE2\x86\x93", KEY_DOWN, get_history_newer_void, 0);
add_to_sclist(MWHEREIS|MREPLACE|MREPLACEWITH|MWHEREISFILE|MFINDINHELP, "\xE2\x86\x91", KEY_UP, get_history_older_void, 0);
add_to_sclist(MWHEREIS|MREPLACE|MREPLACEWITH|MWHEREISFILE|MFINDINHELP, "\xE2\x86\x93", KEY_DOWN, get_history_newer_void, 0);
} else
#endif
{
add_to_sclist(MWHEREIS|MREPLACE|MREPLACEWITH|MWHEREISFILE, "Up", KEY_UP, get_history_older_void, 0);
add_to_sclist(MWHEREIS|MREPLACE|MREPLACEWITH|MWHEREISFILE, "Down", KEY_DOWN, get_history_newer_void, 0);
add_to_sclist(MWHEREIS|MREPLACE|MREPLACEWITH|MWHEREISFILE|MFINDINHELP, "Up", KEY_UP, get_history_older_void, 0);
add_to_sclist(MWHEREIS|MREPLACE|MREPLACEWITH|MWHEREISFILE|MFINDINHELP, "Down", KEY_DOWN, get_history_newer_void, 0);
}
#endif
#ifndef DISABLE_BROWSER

View File

@ -24,6 +24,7 @@
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#ifndef DISABLE_HELP
@ -34,31 +35,90 @@ static char *end_of_intro = NULL;
/* The point in the help text where the introductory paragraphs end
* and the shortcut descriptions begin. */
const char *beg_of_intro = NULL;
/* The point in the help text where the introductory paragraphs
* begin. */
char *tempfilename = NULL;
/* Name of the safe temporary file that we will use for wrapping
* and writing the help text. */
/* Writes the hard wrapped help text in the temp file and displays it. */
void display_the_help_text(bool redisplaying)
{
int line_size;
const char *ptr = beg_of_intro;
/* The current line of the help text. */
FILE *fp = fopen(tempfilename, "w+b");
if (fp == NULL) {
statusline(ALERT, _("Error writing temp file: %s"), strerror(errno));
return;
}
/* Wrap and copy the rest of the help_text into the temporary file. */
while (strlen(ptr) > 0) {
line_size = help_line_len(ptr);
fwrite(ptr, sizeof(char), line_size, fp);
ptr += line_size;
/* Hard wrap the lines in the help text. */
if (*ptr != '\n')
fwrite("\n", sizeof(char), 1, fp);
else
while (*ptr == '\n')
fwrite(ptr++, sizeof(char), 1, fp);
}
fclose(fp);
if (redisplaying)
close_buffer();
if (!ISSET(MULTIBUFFER)) {
SET(MULTIBUFFER);
open_buffer(tempfilename, FALSE);
UNSET(MULTIBUFFER);
} else
open_buffer(tempfilename, FALSE);
display_buffer();
}
/* Our main help-viewer function. */
void do_help(void)
{
int kbinput = ERR;
bool old_no_help = ISSET(NO_HELP);
size_t line = 0;
/* The line number in help_text of the first displayed help
* line. This variable is zero-based. */
size_t last_line = 0;
/* The line number in help_text of the last help line. This
* variable is zero-based. */
bool was_whitespace = ISSET(WHITESPACE_DISPLAY);
int oldmenu = currmenu;
/* The menu we were called from. */
const char *ptr;
/* The current line of the help text. */
size_t old_line = (size_t)-1;
/* The line we were on before the current line. */
functionptrtype func;
/* The function of the key the user typed in. */
FILE *fp;
int line_size;
int saved_margin = 0;
/* For avoiding the line numbers on the help screen. */
char *saved_answer = (answer != NULL) ? strdup(answer) : NULL;
/* Store current answer when user invokes help at the prompt. */
inhelp = TRUE;
/* Don't show a cursor in the help screen. */
curs_set(0);
blank_edit();
blank_statusbar();
/* Get a safe temporary file for displaying the help text. If we can't
* obtain one, return. */
tempfilename = safe_tempfile(&fp);
fclose(fp);
if (tempfilename == NULL) {
statusline(ALERT, _("Error writing temp file: %s"), strerror(errno));
inhelp = FALSE;
free(saved_answer);
return;
}
/* Set help_text as the string to display. */
help_init();
@ -71,61 +131,48 @@ void do_help(void)
window_init();
}
UNSET(WHITESPACE_DISPLAY);
bottombars(MHELP);
wnoutrefresh(bottomwin);
#ifdef ENABLE_LINENUMBERS
if (ISSET(LINE_NUMBERS)) {
saved_margin = margin;
margin = 0;
UNSET(LINE_NUMBERS);
}
#endif
/* Extract title from help_text and display it. */
title = charalloc(MAX_BUF_SIZE * sizeof(char));
ptr = help_text;
line_size = break_line(ptr, 74, TRUE);
strncpy(title, ptr, line_size);
title[line_size] = '\0';
titlebar(title);
/* Skip the title and point to the beginning of the introductory
* paragraphs. */
ptr += line_size;
while (*ptr == '\n')
++ptr;
beg_of_intro = ptr;
display_the_help_text(FALSE);
curs_set(0);
while (TRUE) {
size_t i;
ptr = help_text;
/* Find the line number of the last line of the help text. */
for (last_line = 0; *ptr != '\0'; last_line++) {
ptr += help_line_len(ptr);
if (*ptr == '\n')
ptr++;
}
if (last_line > 0)
last_line--;
/* Redisplay if the text was scrolled or an invalid key was pressed. */
if (line != old_line || kbinput == ERR) {
blank_edit();
ptr = help_text;
/* Advance in the text to the first line to be displayed. */
for (i = 0; i < line; i++) {
ptr += help_line_len(ptr);
if (*ptr == '\n')
ptr++;
}
/* Now display as many lines as the window will hold. */
for (i = 0; i < editwinrows && *ptr != '\0'; i++) {
size_t j = help_line_len(ptr);
mvwaddnstr(edit, i, 0, ptr, j);
ptr += j;
if (*ptr == '\n')
ptr++;
}
}
wnoutrefresh(edit);
old_line = line;
edit_refresh();
lastmessage = HUSH;
focusing = TRUE;
kbinput = get_kbinput(edit);
#ifndef NANO_TINY
if (kbinput == KEY_WINCH) {
kbinput = ERR;
continue; /* Redraw the screen. */
}
if (kbinput == KEY_WINCH)
continue;
#endif
#ifndef DISABLE_MOUSE
@ -141,31 +188,47 @@ void do_help(void)
if (func == total_refresh) {
total_redraw();
} else if (func == do_up_void) {
if (line > 0)
line--;
do_up(TRUE);
} else if (func == do_down_void) {
if (line + (editwinrows - 1) < last_line)
line++;
if (openfile->edittop->lineno + editwinrows < openfile->
filebot->lineno)
do_down(TRUE);
} else if (func == do_page_up) {
if (line > editwinrows - 2)
line -= editwinrows - 2;
else
line = 0;
do_page_up();
} else if (func == do_page_down) {
if (line + (editwinrows - 1) < last_line)
line += editwinrows - 2;
do_page_down();
} else if (func == do_first_line) {
line = 0;
do_first_line();
} else if (func == do_last_line) {
if (line + (editwinrows - 1) < last_line)
line = last_line - (editwinrows - 1);
do_last_line();
} else if (func == do_search) {
do_search();
bottombars(MHELP);
wnoutrefresh(bottomwin);
curs_set(1);
} else if (func == do_research) {
do_research();
currmenu = MHELP;
curs_set(1);
} else if (func == do_exit) {
/* Exit from the help viewer. */
close_buffer();
break;
} else
unbound_key(kbinput);
}
/* We're exiting from the help screen. So, restore the flags and the
* original menu, refresh the entire screen and deallocate the memory. */
#ifdef ENABLE_LINENUMBERS
if (saved_margin != 0) {
margin = saved_margin;
SET(LINE_NUMBERS);
}
#endif
if (old_no_help) {
blank_bottombars();
wnoutrefresh(bottomwin);
@ -175,14 +238,26 @@ void do_help(void)
} else
bottombars(oldmenu);
if (was_whitespace)
SET(WHITESPACE_DISPLAY);
free(title);
title = NULL;
inhelp = FALSE;
#ifndef DISABLE_BROWSER
if (oldmenu == MBROWSER || oldmenu == MWHEREISFILE || oldmenu == MGOTODIR)
browser_refresh();
else
#endif
edit_refresh();
total_refresh();
free(answer);
answer = saved_answer;
remove(tempfilename);
free(tempfilename);
/* We're exiting from the help screen. */
free(help_text);
}

View File

@ -1416,7 +1416,7 @@ void do_toggle(int flag)
enabled = !enabled;
statusline(HUSH, "%s %s", _(flagtostr(flag)),
enabled ? _("enabled") : _("disabled"));
enabled ? _("enabled") : _("disabled"));
}
/* Bleh. */
@ -1561,7 +1561,7 @@ void unbound_key(int code)
statusline(ALERT, _("Unbound key: M-%c"), toupper(code));
} else if (code < 0x20)
statusline(ALERT, _("Unbound key: ^%c"), code + 0x40);
else
else if (currmenu != MHELP)
statusline(ALERT, _("Unbound key: %c"), code);
}

View File

@ -538,9 +538,10 @@ enum
#define MGOTODIR (1<<12)
#define MYESNO (1<<13)
#define MLINTER (1<<14)
#define MFINDINHELP (1<<15)
/* This is an abbreviation for all menus except Help and YesNo. */
#define MMOST (MMAIN|MWHEREIS|MREPLACE|MREPLACEWITH|MGOTOLINE|MWRITEFILE|MINSERTFILE|\
MEXTCMD|MBROWSER|MWHEREISFILE|MGOTODIR|MSPELL|MLINTER)
MEXTCMD|MBROWSER|MWHEREISFILE|MGOTODIR|MFINDINHELP|MSPELL|MLINTER)
/* Basic control codes. */
#define TAB_CODE 0x09

View File

@ -611,8 +611,9 @@ int do_prompt(bool allow_tabs, bool allow_files,
int retval;
functionptrtype func = NULL;
bool listed = FALSE;
/* Save a possible current statusbar x position. */
/* Save a possible current statusbar x position and prompt. */
size_t was_statusbar_x = statusbar_x;
char *saved_prompt = prompt;
bottombars(menu);
@ -635,7 +636,7 @@ int do_prompt(bool allow_tabs, bool allow_files,
refresh_func);
free(prompt);
prompt = NULL;
prompt = saved_prompt;
#ifndef NANO_TINY
if (retval == KEY_WINCH)

View File

@ -24,6 +24,7 @@
#include "nano.h"
/* All external variables. See global.c for their descriptions. */
#ifndef NANO_TINY
extern volatile sig_atomic_t the_window_resized;
#endif
@ -46,6 +47,9 @@ extern message_type lastmessage;
extern filestruct *pletion_line;
extern bool inhelp;
extern char *title;
extern int controlleft;
extern int controlright;
extern int controlup;
@ -337,6 +341,7 @@ void thanks_for_all_the_fish(void);
/* All functions in help.c. */
#ifndef DISABLE_HELP
void display_the_help_text(bool redisplaying);
void do_help(void);
void help_init(void);
functionptrtype parse_help_input(int *kbinput);

View File

@ -143,7 +143,8 @@ int search_init(bool replacing, bool use_answer)
/* This is now one simple call. It just does a lot. */
i = do_prompt(FALSE, FALSE,
replacing ? MREPLACE : MWHEREIS, backupstring,
inhelp ? MFINDINHELP : (replacing ? MREPLACE : MWHEREIS),
backupstring,
#ifndef DISABLE_HISTORIES
&search_history,
#endif

View File

@ -1961,10 +1961,9 @@ char *display_string(const char *buf, size_t start_col, size_t span,
/* If path is NULL, we're in normal editing mode, so display the current
* version of nano, the current filename, and whether the current file
* has been modified on the titlebar. If path isn't NULL, we're in the
* file browser, and path contains the directory to start the file
* browser in, so display the current version of nano and the contents
* of path on the titlebar. */
* has been modified on the titlebar. If path isn't NULL, we're either
* in the file browser or the help viewer, so show either the current
* directory or the title of help text, that is: whatever is in path. */
void titlebar(const char *path)
{
size_t verlen, prefixlen, pathlen, statelen;
@ -1996,12 +1995,13 @@ void titlebar(const char *path)
* then sacrifice the prefix, and only then start dottifying. */
/* Figure out the path, prefix and state strings. */
if (inhelp)
state = _("Help");
#ifndef DISABLE_BROWSER
if (path != NULL)
else if (path != NULL)
prefix = _("DIR:");
else
#endif
{
else {
if (openfile->filename[0] == '\0')
path = _("New Buffer");
else {
@ -3142,8 +3142,13 @@ void total_redraw(void)
void total_refresh(void)
{
total_redraw();
titlebar(NULL);
edit_refresh();
titlebar(title);
#ifndef DISABLE_HELP
if (inhelp)
display_the_help_text(TRUE);
else
#endif
edit_refresh();
bottombars(currmenu);
}