+2015-01-03 Chris Allegretta <chrisa@asty.org>
+ * 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 <bensberg@justemail.net>
* src/files.c (do_lockfile): Gettextize the "File being edited"
prompt, and improve its wording.
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,
# Trailing whitespace.
color ,green "[[:space:]]+$"
+
+# Set up the formatter since spelling is probably useless...
+formatter gofmt -w
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
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 */
#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
#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);
}
#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
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"))
/* 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;
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);
#endif
#ifndef DISABLE_COLOR
void do_linter(void);
+void do_formatter(void);
#endif
#ifndef NANO_TINY
void do_wordlinechar_count(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);
endsyntax->next = NULL;
endsyntax->nmultis = 0;
endsyntax->linter = NULL;
+ endsyntax->formatter = NULL;
#ifdef DEBUG
fprintf(stderr, "Starting a new syntax type: \"%s\"\n", nameptr);
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
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);
}
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
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