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
/* 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. */
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;
int descriptor;
@ -525,7 +526,7 @@ bool replace_buffer(const char *filename, undo_type action, bool marked)
return FALSE;
#ifndef NANO_TINY
add_undo(COUPLE_BEGIN, "spelling correction");
add_undo(COUPLE_BEGIN, operation);
#endif
/* 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);
#ifndef NANO_TINY
add_undo(COUPLE_END, "spelling correction");
add_undo(COUPLE_END, operation);
#endif
return TRUE;
}

View File

@ -693,6 +693,10 @@ void shortcut_init(void)
const char *lint_gist = N_("Invoke the linter, if available");
const char *prevlint_gist = N_("Go to previous 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 /* ENABLE_HELP */
@ -1001,9 +1005,14 @@ void shortcut_init(void)
N_("Zap Text"), WITHORSANS(zap_gist), BLANKAFTER, NOVIEW);
#ifdef ENABLE_COLOR
if (!ISSET(RESTRICTED))
if (!ISSET(RESTRICTED)) {
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
add_to_funcs(do_savefile, MMAIN,
@ -1124,6 +1133,9 @@ void shortcut_init(void)
#endif
#ifdef ENABLE_COLOR
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
add_to_sclist(MMAIN, "^C", 0, do_cursorpos_void, 0);
add_to_sclist(MMAIN, "^_", 0, do_gotolinecolumn_void, 0);
@ -1502,6 +1514,10 @@ keystruct *strtosc(const char *input)
#ifdef ENABLE_COLOR
else if (!strcasecmp(input, "linter"))
s->func = do_linter;
#ifdef ENABLE_SPELLER
else if (!strcasecmp(input, "fixer"))
s->func = do_fixer;
#endif
#endif
else if (!strcasecmp(input, "curpos"))
s->func = do_cursorpos_void;

View File

@ -231,6 +231,8 @@ typedef struct syntaxtype {
/* The list of libmagic results that this syntax applies to. */
char *linter;
/* 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;
/* What the Tab key should produce; NULL for default behavior. */
#ifdef ENABLE_COMMENT

View File

@ -273,7 +273,8 @@ void make_new_buffer(void);
void set_modified(void);
bool open_buffer(const char *filename, bool new_buffer);
#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
void prepare_for_display(void);
#ifdef ENABLE_MULTIBUFFER
@ -548,6 +549,7 @@ void do_spell(void);
#endif
#ifdef ENABLE_COLOR
void do_linter(void);
void do_fixer(void);
#endif
#ifndef NANO_TINY
void do_wordlinechar_count(void);

View File

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

View File

@ -2542,28 +2542,31 @@ const char *do_int_speller(const char *tempfile_name)
}
/* 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;
size_t current_x_save = openfile->current_x;
size_t pww_save = openfile->placewewant;
bool was_at_eol = (openfile->current->data[openfile->current_x] == '\0');
const char *msg = (spelling ? N_("spelling correction") : N_("manipulation"));
struct stat fileinfo;
time_t timestamp;
long timestamp_sec, timestamp_nsec;
static char **arguments = NULL;
pid_t thepid;
int program_status;
/* Get the timestamp and the size of the temporary file. */
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 (fileinfo.st_size == 0)
return NULL;
/* Exit from curses mode. */
endwin();
/* The spell checker needs the screen, so exit from curses mode. */
if (spelling)
endwin();
construct_argument_list(&arguments, theprogram, tempfile_name);
@ -2582,9 +2585,11 @@ const char *treat(char *tempfile_name, char *theprogram)
wait(&program_status);
block_sigwinch(FALSE);
/* Set the desired terminal state again, and reenter curses mode. */
terminal_init();
doupdate();
/* When needed, restore the terminal state and reenter curses mode. */
if (spelling) {
terminal_init();
doupdate();
}
if (!WIFEXITED(program_status) || WEXITSTATUS(program_status) != 0)
return invocation_error(theprogram);
@ -2593,7 +2598,8 @@ const char *treat(char *tempfile_name, char *theprogram)
stat(tempfile_name, &fileinfo);
/* 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;
#ifndef NANO_TINY
/* 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));
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
* 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);
} else
#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. */
goto_line_posx(lineno_save, current_x_save);
@ -2675,7 +2681,10 @@ void do_spell(void)
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);
free(temp);
@ -3039,6 +3048,49 @@ void do_linter(void)
currmenu = MMOST;
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 */
#ifndef NANO_TINY