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
parent
23c36ba667
commit
34170611d3
|
@ -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;
|
||||
}
|
||||
|
|
20
src/global.c
20
src/global.c
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
76
src/text.c
76
src/text.c
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue