From c96bafbb4cb535b81f7bb50ae75302e6a703e531 Mon Sep 17 00:00:00 2001 From: Rishabh Dave Date: Mon, 5 Dec 2016 12:20:01 +0100 Subject: [PATCH 01/10] new feature: add a search facility to help Allow the user to search in a help text with ^W. This fulfills https://savannah.gnu.org/bugs/?28994. --- src/files.c | 20 ++++-- src/global.c | 32 +++++---- src/help.c | 215 ++++++++++++++++++++++++++++++++++++++++------------------- src/nano.c | 4 +- src/nano.h | 3 +- src/prompt.c | 5 +- src/proto.h | 5 ++ src/search.c | 3 +- src/winio.c | 21 +++--- 9 files changed, 204 insertions(+), 104 deletions(-) diff --git a/src/files.c b/src/files.c index 8c4b5b83..cd2fc6a5 100644 --- a/src/files.c +++ b/src/files.c @@ -470,7 +470,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. */ @@ -489,7 +489,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. */ @@ -590,7 +590,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. */ @@ -639,9 +640,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); @@ -908,6 +910,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 @@ -1001,7 +1007,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")); } diff --git a/src/global.c b/src/global.c index d2e5eb20..8bed7701 100644 --- a/src/global.c +++ b/src/global.c @@ -27,6 +27,11 @@ #include "assert.h" /* Global variables. */ +bool inhelp = FALSE; + /* Indicates we are displaying the help text. */ +char *title = NULL; + /* Title of the help text. */ + #ifndef NANO_TINY volatile sig_atomic_t the_window_resized = FALSE; /* Set to TRUE by the handler whenever a SIGWINCH occurs. */ @@ -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|MBROWSER|MHELP, "^W", 0, do_search, 0); + add_to_sclist(MMAIN|MBROWSER|MHELP, "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|MBROWSER|MHELP, "M-W", 0, do_research, 0); + add_to_sclist(MMAIN|MBROWSER|MHELP, "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 diff --git a/src/help.c b/src/help.c index a672d90a..e96340a7 100644 --- a/src/help.c +++ b/src/help.c @@ -24,6 +24,7 @@ #include #include #include +#include #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); - 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++; - } - } +#ifdef ENABLE_LINENUMBERS + if (ISSET(LINE_NUMBERS)) { + saved_margin = margin; + margin = 0; + UNSET(LINE_NUMBERS); + } +#endif - wnoutrefresh(edit); + /* 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); - old_line = line; + while (TRUE) { + 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); } diff --git a/src/nano.c b/src/nano.c index ae044be2..cae8a8ab 100644 --- a/src/nano.c +++ b/src/nano.c @@ -1419,7 +1419,7 @@ void do_toggle(int flag) enabled = !enabled; statusline(HUSH, "%s %s", _(flagtostr(flag)), - enabled ? _("enabled") : _("disabled")); + enabled ? _("enabled") : _("disabled")); } /* Bleh. */ @@ -1564,7 +1564,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); } diff --git a/src/nano.h b/src/nano.h index 37226adb..10f9e4bc 100644 --- a/src/nano.h +++ b/src/nano.h @@ -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 diff --git a/src/prompt.c b/src/prompt.c index add814d8..161c77af 100644 --- a/src/prompt.c +++ b/src/prompt.c @@ -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) diff --git a/src/proto.h b/src/proto.h index fa9df316..8a923eb4 100644 --- a/src/proto.h +++ b/src/proto.h @@ -24,6 +24,10 @@ #include "nano.h" /* All external variables. See global.c for their descriptions. */ + +extern bool inhelp; +extern char *title; + #ifndef NANO_TINY extern volatile sig_atomic_t the_window_resized; #endif @@ -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); diff --git a/src/search.c b/src/search.c index e6867617..f62ae138 100644 --- a/src/search.c +++ b/src/search.c @@ -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 diff --git a/src/winio.c b/src/winio.c index a7f6c931..162f8e5e 100644 --- a/src/winio.c +++ b/src/winio.c @@ -1960,10 +1960,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; @@ -1995,12 +1994,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 { @@ -3155,8 +3155,11 @@ void total_redraw(void) void total_refresh(void) { total_redraw(); - titlebar(NULL); - edit_refresh(); + titlebar(title); + if (inhelp) + display_the_help_text(TRUE); + else + edit_refresh(); bottombars(currmenu); } -- 2.12.1