restored feature: a per-syntax 'fixer' command that processes the buffer

The command can be used to run some kind of formatter or corrector or
arranging tool on the buffer.  By default the command is bound to M-F.
The formatter/corrector/arranging program must be non-interactive.

This addresses https://savannah.gnu.org/bugs/?55365,
and addresses https://savannah.gnu.org/bugs/?54651.
master
Benno Schulenberg 2019-10-09 19:42:22 +02:00
parent 23c36ba667
commit 34170611d3
6 changed files with 96 additions and 19 deletions

View File

@ -513,7 +513,8 @@ bool open_buffer(const char *filename, bool new_buffer)
#ifdef ENABLE_SPELLER #ifdef ENABLE_SPELLER
/* Open the specified file, and if that succeeds, remove the text of the marked /* Open the specified file, and if that succeeds, remove the text of the marked
* region or of the entire buffer and read the file contents into its place. */ * region or of the entire buffer and read the file contents into its place. */
bool replace_buffer(const char *filename, undo_type action, bool marked) bool replace_buffer(const char *filename, undo_type action, bool marked,
const char *operation)
{ {
linestruct *was_cutbuffer = cutbuffer; linestruct *was_cutbuffer = cutbuffer;
int descriptor; int descriptor;
@ -525,7 +526,7 @@ bool replace_buffer(const char *filename, undo_type action, bool marked)
return FALSE; return FALSE;
#ifndef NANO_TINY #ifndef NANO_TINY
add_undo(COUPLE_BEGIN, "spelling correction"); add_undo(COUPLE_BEGIN, operation);
#endif #endif
/* When nothing is marked, start at the top of the buffer. */ /* When nothing is marked, start at the top of the buffer. */
@ -550,7 +551,7 @@ bool replace_buffer(const char *filename, undo_type action, bool marked)
read_file(f, descriptor, filename, TRUE); read_file(f, descriptor, filename, TRUE);
#ifndef NANO_TINY #ifndef NANO_TINY
add_undo(COUPLE_END, "spelling correction"); add_undo(COUPLE_END, operation);
#endif #endif
return TRUE; return TRUE;
} }

View File

@ -693,6 +693,10 @@ void shortcut_init(void)
const char *lint_gist = N_("Invoke the linter, if available"); const char *lint_gist = N_("Invoke the linter, if available");
const char *prevlint_gist = N_("Go to previous linter msg"); const char *prevlint_gist = N_("Go to previous linter msg");
const char *nextlint_gist = N_("Go to next linter msg"); const char *nextlint_gist = N_("Go to next linter msg");
#ifdef ENABLE_SPELLER
const char *fixer_gist =
N_("Invoke a program to fix/format/arrange the buffer");
#endif
#endif #endif
#endif /* ENABLE_HELP */ #endif /* ENABLE_HELP */
@ -1001,9 +1005,14 @@ void shortcut_init(void)
N_("Zap Text"), WITHORSANS(zap_gist), BLANKAFTER, NOVIEW); N_("Zap Text"), WITHORSANS(zap_gist), BLANKAFTER, NOVIEW);
#ifdef ENABLE_COLOR #ifdef ENABLE_COLOR
if (!ISSET(RESTRICTED)) if (!ISSET(RESTRICTED)) {
add_to_funcs(do_linter, MMAIN, add_to_funcs(do_linter, MMAIN,
N_("To Linter"), WITHORSANS(lint_gist), BLANKAFTER, NOVIEW); N_("To Linter"), WITHORSANS(lint_gist), TOGETHER, NOVIEW);
#ifdef ENABLE_SPELLER
add_to_funcs(do_fixer, MMAIN,
N_("Manipulate"), WITHORSANS(fixer_gist), BLANKAFTER, NOVIEW);
#endif
}
#endif #endif
#endif #endif
add_to_funcs(do_savefile, MMAIN, add_to_funcs(do_savefile, MMAIN,
@ -1124,6 +1133,9 @@ void shortcut_init(void)
#endif #endif
#ifdef ENABLE_COLOR #ifdef ENABLE_COLOR
add_to_sclist(MMAIN, "M-B", 0, do_linter, 0); add_to_sclist(MMAIN, "M-B", 0, do_linter, 0);
#ifdef ENABLE_SPELLER
add_to_sclist(MMAIN, "M-F", 0, do_fixer, 0);
#endif
#endif #endif
add_to_sclist(MMAIN, "^C", 0, do_cursorpos_void, 0); add_to_sclist(MMAIN, "^C", 0, do_cursorpos_void, 0);
add_to_sclist(MMAIN, "^_", 0, do_gotolinecolumn_void, 0); add_to_sclist(MMAIN, "^_", 0, do_gotolinecolumn_void, 0);
@ -1502,6 +1514,10 @@ keystruct *strtosc(const char *input)
#ifdef ENABLE_COLOR #ifdef ENABLE_COLOR
else if (!strcasecmp(input, "linter")) else if (!strcasecmp(input, "linter"))
s->func = do_linter; s->func = do_linter;
#ifdef ENABLE_SPELLER
else if (!strcasecmp(input, "fixer"))
s->func = do_fixer;
#endif
#endif #endif
else if (!strcasecmp(input, "curpos")) else if (!strcasecmp(input, "curpos"))
s->func = do_cursorpos_void; s->func = do_cursorpos_void;

View File

@ -231,6 +231,8 @@ typedef struct syntaxtype {
/* The list of libmagic results that this syntax applies to. */ /* The list of libmagic results that this syntax applies to. */
char *linter; char *linter;
/* The command with which to lint this type of file. */ /* The command with which to lint this type of file. */
char *fixer;
/* The command with which to "fix"/format/modify this type of file. */
char *tab; char *tab;
/* What the Tab key should produce; NULL for default behavior. */ /* What the Tab key should produce; NULL for default behavior. */
#ifdef ENABLE_COMMENT #ifdef ENABLE_COMMENT

View File

@ -273,7 +273,8 @@ void make_new_buffer(void);
void set_modified(void); void set_modified(void);
bool open_buffer(const char *filename, bool new_buffer); bool open_buffer(const char *filename, bool new_buffer);
#ifdef ENABLE_SPELLER #ifdef ENABLE_SPELLER
bool replace_buffer(const char *filename, undo_type action, bool marked); bool replace_buffer(const char *filename, undo_type action, bool marked,
const char *operation);
#endif #endif
void prepare_for_display(void); void prepare_for_display(void);
#ifdef ENABLE_MULTIBUFFER #ifdef ENABLE_MULTIBUFFER
@ -548,6 +549,7 @@ void do_spell(void);
#endif #endif
#ifdef ENABLE_COLOR #ifdef ENABLE_COLOR
void do_linter(void); void do_linter(void);
void do_fixer(void);
#endif #endif
#ifndef NANO_TINY #ifndef NANO_TINY
void do_wordlinechar_count(void); void do_wordlinechar_count(void);

View File

@ -333,6 +333,7 @@ void begin_new_syntax(char *ptr)
live_syntax->headers = NULL; live_syntax->headers = NULL;
live_syntax->magics = NULL; live_syntax->magics = NULL;
live_syntax->linter = NULL; live_syntax->linter = NULL;
live_syntax->fixer = NULL;
live_syntax->tab = NULL; live_syntax->tab = NULL;
#ifdef ENABLE_COMMENT #ifdef ENABLE_COMMENT
live_syntax->comment = copy_of(GENERAL_COMMENT_CHARACTER); live_syntax->comment = copy_of(GENERAL_COMMENT_CHARACTER);
@ -954,6 +955,8 @@ bool parse_syntax_commands(char *keyword, char *ptr)
#endif #endif
} else if (strcasecmp(keyword, "linter") == 0) } else if (strcasecmp(keyword, "linter") == 0)
pick_up_name("linter", ptr, &live_syntax->linter); pick_up_name("linter", ptr, &live_syntax->linter);
else if (strcasecmp(keyword, "fixer") == 0)
pick_up_name("fixer", ptr, &live_syntax->fixer);
else else
return FALSE; return FALSE;
@ -1110,7 +1113,8 @@ void parse_rcfile(FILE *rcstream, bool just_syntax, bool intros_only)
strcasecmp(keyword, "icolor") == 0 || strcasecmp(keyword, "icolor") == 0 ||
strcasecmp(keyword, "comment") == 0 || strcasecmp(keyword, "comment") == 0 ||
strcasecmp(keyword, "tabgives") == 0 || strcasecmp(keyword, "tabgives") == 0 ||
strcasecmp(keyword, "linter") == 0)) { strcasecmp(keyword, "linter") == 0 ||
strcasecmp(keyword, "fixer") == 0)) {
if (!opensyntax) if (!opensyntax)
jot_error(N_("A '%s' command requires a preceding " jot_error(N_("A '%s' command requires a preceding "
"'syntax' command"), keyword); "'syntax' command"), keyword);

View File

@ -2542,27 +2542,30 @@ const char *do_int_speller(const char *tempfile_name)
} }
/* Execute the given program, with the given temp file as last argument. */ /* Execute the given program, with the given temp file as last argument. */
const char *treat(char *tempfile_name, char *theprogram) const char *treat(char *tempfile_name, char *theprogram, bool spelling)
{ {
ssize_t lineno_save = openfile->current->lineno; ssize_t lineno_save = openfile->current->lineno;
size_t current_x_save = openfile->current_x; size_t current_x_save = openfile->current_x;
size_t pww_save = openfile->placewewant; size_t pww_save = openfile->placewewant;
bool was_at_eol = (openfile->current->data[openfile->current_x] == '\0'); bool was_at_eol = (openfile->current->data[openfile->current_x] == '\0');
const char *msg = (spelling ? N_("spelling correction") : N_("manipulation"));
struct stat fileinfo; struct stat fileinfo;
time_t timestamp; long timestamp_sec, timestamp_nsec;
static char **arguments = NULL; static char **arguments = NULL;
pid_t thepid; pid_t thepid;
int program_status; int program_status;
/* Get the timestamp and the size of the temporary file. */ /* Get the timestamp and the size of the temporary file. */
stat(tempfile_name, &fileinfo); stat(tempfile_name, &fileinfo);
timestamp = fileinfo.st_mtime; timestamp_sec = (long)fileinfo.st_mtim.tv_sec;
timestamp_nsec = (long)fileinfo.st_mtim.tv_nsec;
/* If the number of bytes to check is zero, get out. */ /* If the number of bytes to check is zero, get out. */
if (fileinfo.st_size == 0) if (fileinfo.st_size == 0)
return NULL; return NULL;
/* Exit from curses mode. */ /* The spell checker needs the screen, so exit from curses mode. */
if (spelling)
endwin(); endwin();
construct_argument_list(&arguments, theprogram, tempfile_name); construct_argument_list(&arguments, theprogram, tempfile_name);
@ -2582,9 +2585,11 @@ const char *treat(char *tempfile_name, char *theprogram)
wait(&program_status); wait(&program_status);
block_sigwinch(FALSE); block_sigwinch(FALSE);
/* Set the desired terminal state again, and reenter curses mode. */ /* When needed, restore the terminal state and reenter curses mode. */
if (spelling) {
terminal_init(); terminal_init();
doupdate(); doupdate();
}
if (!WIFEXITED(program_status) || WEXITSTATUS(program_status) != 0) if (!WIFEXITED(program_status) || WEXITSTATUS(program_status) != 0)
return invocation_error(theprogram); return invocation_error(theprogram);
@ -2593,7 +2598,8 @@ const char *treat(char *tempfile_name, char *theprogram)
stat(tempfile_name, &fileinfo); stat(tempfile_name, &fileinfo);
/* Read in the temporary file only when it changed. */ /* Read in the temporary file only when it changed. */
if (fileinfo.st_mtime != timestamp) { if ((long)fileinfo.st_mtim.tv_sec != timestamp_sec ||
(long)fileinfo.st_mtim.tv_nsec != timestamp_nsec) {
bool replaced = FALSE; bool replaced = FALSE;
#ifndef NANO_TINY #ifndef NANO_TINY
/* Replace the marked text (or entire text) with the corrected text. */ /* Replace the marked text (or entire text) with the corrected text. */
@ -2603,7 +2609,7 @@ const char *treat(char *tempfile_name, char *theprogram)
openfile->mark_x < openfile->current_x)); openfile->mark_x < openfile->current_x));
ssize_t was_mark_lineno = openfile->mark->lineno; ssize_t was_mark_lineno = openfile->mark->lineno;
replaced = replace_buffer(tempfile_name, CUT, TRUE); replaced = replace_buffer(tempfile_name, CUT, TRUE, msg);
/* Adjust the end point of the marked region for any change in /* Adjust the end point of the marked region for any change in
* length of the region's last line. */ * length of the region's last line. */
@ -2616,7 +2622,7 @@ const char *treat(char *tempfile_name, char *theprogram)
openfile->mark = line_from_number(was_mark_lineno); openfile->mark = line_from_number(was_mark_lineno);
} else } else
#endif #endif
replaced = replace_buffer(tempfile_name, CUT_TO_EOF, FALSE); replaced = replace_buffer(tempfile_name, CUT_TO_EOF, FALSE, msg);
/* Go back to the old position. */ /* Go back to the old position. */
goto_line_posx(lineno_save, current_x_save); goto_line_posx(lineno_save, current_x_save);
@ -2675,7 +2681,10 @@ void do_spell(void)
blank_bottombars(); blank_bottombars();
result_msg = (alt_speller ? treat(temp, alt_speller) : do_int_speller(temp)); if (alt_speller)
result_msg = treat(temp, alt_speller, TRUE);
else
result_msg = do_int_speller(temp);
unlink(temp); unlink(temp);
free(temp); free(temp);
@ -3039,6 +3048,49 @@ void do_linter(void)
currmenu = MMOST; currmenu = MMOST;
titlebar(NULL); titlebar(NULL);
} }
#ifdef ENABLE_SPELLER
/* Run a manipulation program on the contents of the buffer. */
void do_fixer(void)
{
FILE *stream;
char *temp_name;
bool okay = FALSE;
const char *result_msg;
if (in_restricted_mode())
return;
if (!openfile->syntax || !openfile->syntax->fixer) {
statusbar(_("No fixer is defined for this type of file"));
return;
}
temp_name = safe_tempfile(&stream);
if (temp_name != NULL)
okay = write_file(temp_name, stream, TRUE, OVERWRITE, TRUE);
if (!okay) {
statusline(ALERT, _("Error writing temp file: %s"), strerror(errno));
free(temp_name);
return;
}
/* Manipulation always happens on the whole buffer. */
openfile->mark = NULL;
result_msg = treat(temp_name, openfile->syntax->fixer, FALSE);
if (result_msg != NULL)
statusline(ALERT, result_msg);
else
statusbar(_("Buffer has been processed"));
unlink(temp_name);
free(temp_name);
}
#endif /* ENABLE_SPELLER */
#endif /* ENABLE_COLOR */ #endif /* ENABLE_COLOR */
#ifndef NANO_TINY #ifndef NANO_TINY