From: Chris Allegretta Date: Sat, 3 Jan 2015 07:24:17 +0000 (+0000) Subject: 2015-01-03 Chris Allegretta X-Git-Tag: v2.3.99pre1~1 X-Git-Url: https://git.wh0rd.org/?a=commitdiff_plain;h=4b3f2771b52a638bd79f36abb268dafe5e078bda;p=nano.git 2015-01-03 Chris Allegretta * New formatter code to support syntaxes like go which have tools to automatically lint and reformat the text for you (gofmt), which is lovely. rcfile option formatter, function text.c:do_formatter() and some other calls. git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@5100 35c25a1d-7b9e-4130-9fde-d3aeb78583b8 --- diff --git a/ChangeLog b/ChangeLog index 75bdfe8a..bef2d03d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2015-01-03 Chris Allegretta + * New formatter code to support syntaxes like + go which have tools to automatically lint and reformat the text for + you (gofmt), which is lovely. rcfile option formatter, function + text.c:do_formatter() and some other calls. + 2014-12-28 Benno Schulenberg * src/files.c (do_lockfile): Gettextize the "File being edited" prompt, and improve its wording. diff --git a/doc/man/nanorc.5 b/doc/man/nanorc.5 index 7002368c..98efe6ef 100644 --- a/doc/man/nanorc.5 +++ b/doc/man/nanorc.5 @@ -270,6 +270,11 @@ For the currently defined syntax, use the given \fIprogram\fR to invoke the linter (this overrides the speller function when defined). .TP +.BI formatter " program " \fR[ "arg " \fR...] +For the currently defined syntax, use the given \fIprogram\fR +to automatically re-format text, useful in certain programming +languages (e.g. go) +.TP .BR header " [""\fIregex\fR"" ...] For the currently defined syntax, add one or more regexes which will be compared against the very first line of the file to be edited, diff --git a/doc/syntax/go.nanorc b/doc/syntax/go.nanorc index 58ef1812..0b623347 100644 --- a/doc/syntax/go.nanorc +++ b/doc/syntax/go.nanorc @@ -40,3 +40,6 @@ color brightblue start="/\*" end="\*/" # Trailing whitespace. color ,green "[[:space:]]+$" + +# Set up the formatter since spelling is probably useless... +formatter gofmt -w diff --git a/doc/syntax/nanorc.nanorc b/doc/syntax/nanorc.nanorc index c71e4920..1858f2b0 100644 --- a/doc/syntax/nanorc.nanorc +++ b/doc/syntax/nanorc.nanorc @@ -11,7 +11,7 @@ icolor yellow "^[[:space:]]*set[[:space:]]+(functioncolor|keycolor|statuscolor|t icolor brightgreen "^[[:space:]]*set[[:space:]]+(backupdir|brackets|functioncolor|keycolor|matchbrackets|operatingdir|punct|quotestr|speller|statuscolor|titlecolor|whitespace)[[:space:]]+" icolor brightgreen "^[[:space:]]*bind[[:space:]]+((\^|M-)([[:alpha:]]|space|[]]|[0-9_=+{}|;:'\",./<>\?-])|F([1-9]|1[0-6])|Ins|Del)[[:space:]]+[[:alpha:]]+[[:space:]]+[[:alpha:]]+[[:space:]]*$" icolor brightgreen "^[[:space:]]*unbind[[:space:]]+((\^|M-)([[:alpha:]]|space|[]]|[0-9_=+{}|;:'\",./<>\?-])|F([1-9]|1[0-6])|Ins|Del)[[:space:]]+[[:alpha:]]+[[:space:]]*$" -icolor brightgreen "^[[:space:]]*extendsyntax[[:space:]]+[[:alpha:]]+[[:space:]]+(i?color|header|magic|linter)[[:space:]]+.*$" +icolor brightgreen "^[[:space:]]*extendsyntax[[:space:]]+[[:alpha:]]+[[:space:]]+(i?color|header|magic|linter|formatter)[[:space:]]+.*$" icolor green "^[[:space:]]*((un)?(bind|set)|include|syntax|header|magic|linter|extendsyntax)\>" # Colors diff --git a/src/global.c b/src/global.c index 45d3e019..6f7fbb38 100644 --- a/src/global.c +++ b/src/global.c @@ -639,6 +639,7 @@ void shortcut_init(void) const char *nano_lint_msg = N_("Invoke the linter, if available"); const char *nano_prevlint_msg = N_("Go to previous linter msg"); const char *nano_nextlint_msg = N_("Go to next linter msg"); + const char *nano_formatter_msg = N_("Invoke formatter, if available"); #endif #endif /* !DISABLE_HELP */ @@ -735,6 +736,8 @@ void shortcut_init(void) #ifndef DISABLE_COLOR add_to_funcs(do_linter, MMAIN, N_("To Linter"), IFSCHELP(nano_lint_msg), BLANKAFTER, NOVIEW); + add_to_funcs(do_formatter, MMAIN, + N_("Formatter"), IFSCHELP(nano_formatter_msg), TOGETHER, NOVIEW); #endif #ifndef NANO_TINY @@ -1007,6 +1010,8 @@ void shortcut_init(void) #ifndef DISABLE_COLOR add_to_sclist(MMAIN, "^T", do_linter, 0); add_to_sclist(MMAIN, "F12", do_linter, 0); + add_to_sclist(MMAIN, "^T", do_formatter, 0); + add_to_sclist(MMAIN, "F12", do_formatter, 0); #endif #endif add_to_sclist(MMAIN, "^C", do_cursorpos_void, 0); @@ -1178,17 +1183,24 @@ void shortcut_init(void) } #ifndef DISABLE_COLOR -void set_lint_shortcuts(void) +void set_lint_or_format_shortcuts(void) { #ifndef DISABLE_SPELLER - replace_scs_for(do_spell, do_linter); + if (openfile->syntax->formatter) { + replace_scs_for(do_spell, do_formatter); + replace_scs_for(do_linter, do_formatter); + } else { + replace_scs_for(do_spell, do_linter); + replace_scs_for(do_formatter, do_linter); + } #endif } void set_spell_shortcuts(void) { #ifndef DISABLE_SPELLER - replace_scs_for(do_linter, do_spell); + replace_scs_for(do_formatter, do_spell); + replace_scs_for(do_linter, do_spell); #endif } #endif @@ -1516,7 +1528,7 @@ int strtomenu(char *input) return MHELP; #endif #ifndef DISABLE_SPELLER - else if (!strcasecmp(input, "spell")) + else if (!strcasecmp(input, "spell") || !strcasecmp(input, "formatter")) return MSPELL; #endif else if (!strcasecmp(input, "linter")) diff --git a/src/nano.h b/src/nano.h index f4f43f3b..ffd7d22d 100644 --- a/src/nano.h +++ b/src/nano.h @@ -251,6 +251,8 @@ typedef struct syntaxtype { /* The colors used in this syntax. */ char *linter; /* The command to lint this type of file. */ + char *formatter; + /* Use this formatter command (for programming lang mainly) */ int nmultis; /* How many multi-line strings this syntax has. */ struct syntaxtype *next; diff --git a/src/proto.h b/src/proto.h index 5cb280ff..2d5a803e 100644 --- a/src/proto.h +++ b/src/proto.h @@ -359,7 +359,7 @@ void assign_keyinfo(sc *s); void print_sclist(void); void shortcut_init(void); #ifndef DISABLE_COLOR -void set_lint_shortcuts(void); +void set_lint_or_format_shortcuts(void); void set_spell_shortcuts(void); #endif const subnfunc *sctofunc(sc *s); @@ -688,6 +688,7 @@ void do_spell(void); #endif #ifndef DISABLE_COLOR void do_linter(void); +void do_formatter(void); #endif #ifndef NANO_TINY void do_wordlinechar_count(void); @@ -780,7 +781,7 @@ void check_statusblank(void); char *display_string(const char *buf, size_t start_col, size_t len, bool dollars); void titlebar(const char *path); -void set_modified(void); +extern void set_modified(void); void statusbar(const char *msg, ...); void bottombars(int menu); void onekey(const char *keystroke, const char *desc, size_t len); diff --git a/src/rcfile.c b/src/rcfile.c index fe67530f..f7c897d6 100644 --- a/src/rcfile.c +++ b/src/rcfile.c @@ -328,6 +328,7 @@ void parse_syntax(char *ptr) endsyntax->next = NULL; endsyntax->nmultis = 0; endsyntax->linter = NULL; + endsyntax->formatter = NULL; #ifdef DEBUG fprintf(stderr, "Starting a new syntax type: \"%s\"\n", nameptr); @@ -995,6 +996,31 @@ void parse_linter(char *ptr) else endsyntax->linter = mallocstrcpy(syntaxes->linter, ptr); } + +void parse_formatter(char *ptr) +{ + assert(ptr != NULL); + + if (syntaxes == NULL) { + rcfile_error( + N_("Cannot add formatter without a syntax command")); + return; + } + + if (*ptr == '\0') { + rcfile_error(N_("Missing formatter command")); + return; + } + + if (endsyntax->formatter != NULL) + free(endsyntax->formatter); + + /* Let them unset the formatter by using "". */ + if (!strcmp(ptr, "\"\"")) + endsyntax->formatter = NULL; + else + endsyntax->formatter = mallocstrcpy(syntaxes->formatter, ptr); +} #endif /* !DISABLE_COLOR */ /* Check whether the user has unmapped every shortcut for a @@ -1130,6 +1156,8 @@ void parse_rcfile(FILE *rcstream parse_colors(ptr, TRUE); else if (strcasecmp(keyword, "linter") == 0) parse_linter(ptr); + else if (strcasecmp(keyword, "formatter") == 0) + parse_formatter(ptr); #endif /* !DISABLE_COLOR */ else if (strcasecmp(keyword, "bind") == 0) parse_binding(ptr, TRUE); diff --git a/src/text.c b/src/text.c index 527ed354..79a6892e 100644 --- a/src/text.c +++ b/src/text.c @@ -3225,6 +3225,160 @@ free_lints_and_return: } lint_cleanup(); } + +/* Run a formatter for the given syntax. + * Expects the formatter to be non-interactive and + * operate on a file in-place, which we'll pass it + * on the command line. Another mashuhp of the speller + * and alt_speller routines. + */ +void do_formatter(void) +{ + bool status; + FILE *temp_file; + char *temp = safe_tempfile(&temp_file); + int format_status; + size_t current_x_save = openfile->current_x; + size_t pww_save = openfile->placewewant; + ssize_t current_y_save = openfile->current_y; + ssize_t lineno_save = openfile->current->lineno; + pid_t pid_format; + char *ptr; + static int arglen = 3; + static char **formatargs = NULL; + char *finalstatus = NULL; + + /* Check whether we're using syntax highlighting + * and formatter option it set + */ + if (openfile->syntax == NULL || openfile->syntax->formatter == NULL) { + statusbar(_("Error: no linter defined")); + return; + } + + if (ISSET(RESTRICTED)) { + nano_disabled_msg(); + return; + } + + if (temp == NULL) { + statusbar(_("Error writing temp file: %s"), strerror(errno)); + return; + } + + /* we're not supporting partial formatting, oi vey */ + openfile->mark_set = FALSE; + status = write_file(temp, temp_file, TRUE, OVERWRITE, FALSE); + + if (!status) { + statusbar(_("Error writing temp file: %s"), strerror(errno)); + free(temp); + return; + } + + if (openfile->totsize == 0) { + statusbar(_("Finished")); + return; + } + + blank_bottombars(); + statusbar(_("Invoking formatter, please wait")); + doupdate(); + + endwin(); + + /* Set up an argument list to pass execvp(). */ + if (formatargs == NULL) { + formatargs = (char **)nmalloc(arglen * sizeof(char *)); + + formatargs[0] = strtok(openfile->syntax->formatter, " "); + while ((ptr = strtok(NULL, " ")) != NULL) { + arglen++; + formatargs = (char **)nrealloc(formatargs, arglen * + sizeof(char *)); + formatargs[arglen - 3] = ptr; + } + formatargs[arglen - 1] = NULL; + } + formatargs[arglen - 2] = temp; + + /* Start a new process for the formatter. */ + if ((pid_format = fork()) == 0) { + /* Start alternate format program; we are using $PATH. */ + execvp(formatargs[0], formatargs); + + /* Should not be reached, if alternate formatter is found!!! */ + exit(1); + } + + /* If we couldn't fork, get out. */ + if (pid_format < 0) { + statusbar(_("Could not fork")); + return; + } + +#ifndef NANO_TINY + /* Don't handle a pending SIGWINCH until the alternate format checker + * is finished and we've loaded the format-checked file back in. */ + allow_pending_sigwinch(FALSE); +#endif + + /* Wait for the formatter to finish. */ + wait(&format_status); + + /* Reenter curses mode. */ + doupdate(); + + /* Restore the terminal to its previous state. */ + terminal_init(); + + /* Turn the cursor back on for sure. */ + curs_set(1); + + /* The screen might have been resized. If it has, reinitialize all + * the windows based on the new screen dimensions. */ + window_init(); + + if (!WIFEXITED(format_status) || + WEXITSTATUS(format_status) != 0) { + char *format_error; + char *invoke_error = _("Error invoking \"%s\""); + + format_error = + charalloc(strlen(invoke_error) + + strlen(openfile->syntax->formatter) + 1); + sprintf(format_error, invoke_error, openfile->syntax->formatter); + finalstatus = format_error; + } else { + + /* Replace the text of the current buffer with the format-checked + * text. */ + replace_buffer(temp); + + /* Go back to the old position, and mark the file as modified. */ + do_gotopos(lineno_save, current_x_save, current_y_save, pww_save); + set_modified(); + +#ifndef NANO_TINY + /* Handle a pending SIGWINCH again. */ + allow_pending_sigwinch(TRUE); +#endif + + finalstatus = _("Finished formatting"); + } + + unlink(temp); + free(temp); + + currmenu = MMAIN; + + /* If the spell-checker printed any error messages onscreen, make + * sure that they're cleared off. */ + total_refresh(); + + statusbar(finalstatus); +} + #endif /* !DISABLE_COLOR */ #ifndef NANO_TINY diff --git a/src/winio.c b/src/winio.c index 8baff66b..8f5204ab 100644 --- a/src/winio.c +++ b/src/winio.c @@ -3323,8 +3323,9 @@ void total_refresh(void) void display_main_list(void) { #ifndef DISABLE_COLOR - if (openfile->syntax && openfile->syntax->linter) - set_lint_shortcuts(); + if (openfile->syntax + && (openfile->syntax->formatter || openfile->syntax->linter)) + set_lint_or_format_shortcuts(); else set_spell_shortcuts(); #endif