diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2021-09-02 18:07:18 -0700 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2021-09-02 18:07:18 -0700 |
| commit | 615d94ba68be75520fbe177b971c96c2e739436f (patch) | |
| tree | de0bfb5f33475f6f08b2765fd63d5a78d09ff80f | |
| parent | 1cf17cf95c75f7d02a0b9da79d9a78811e1ee9d8 (diff) | |
Improved formatting options
| -rw-r--r-- | bp.1 | 11 | ||||
| -rw-r--r-- | bp.1.md | 8 | ||||
| -rw-r--r-- | bp.c | 22 | ||||
| -rw-r--r-- | print.c | 42 | ||||
| -rw-r--r-- | print.h | 2 |
5 files changed, 57 insertions, 28 deletions
@@ -75,11 +75,14 @@ matches. If \f[I]N\f[R] is \f[B]\[lq]all\[rq]\f[R], print all text before and after each match. .TP -\f[B]-f\f[R], \f[B]--format\f[R] \f[I]auto\f[R]|\f[I]fancy\f[R]|\f[I]plain\f[R] +\f[B]-f\f[R], \f[B]--format\f[R] \f[I]fancy\f[R]|\f[I]plain\f[R]|\f[I]bare\f[R]|\f[I]file:line\f[R]|\f[I]auto\f[R] Set the output format. -\f[I]fancy\f[R] includes colors and line numbers, \f[I]plain\f[R] -includes neither, and \f[I]auto\f[R] (the default) uses \f[I]fancy\f[R] -formatting only when the output is a TTY. +\f[I]fancy\f[R] includes colors and line numbers, \f[I]plain\f[R] prints +line numbers with no coloring, \f[I]bare\f[R] prints only the match +text, \f[I]file:line\f[R] prints the filename and line number for each +match (grep-style), and \f[I]auto\f[R] (the default) uses +\f[I]fancy\f[R] formatting when the output is a TTY and \f[I]plain\f[R] +formatting otherwise. .TP \f[B]-h\f[R], \f[B]--help\f[R] Print the usage and exit. @@ -72,10 +72,12 @@ passed to `git --ls-files` instead of treated as literal files. is `none`, print only the exact text of the matches. If *N* is **"all"**, print all text before and after each match. -`-f`, `--format` *auto*\|*fancy*\|*plain* +`-f`, `--format` *fancy*\|*plain*\|*bare*\|*file:line*\|*auto* : Set the output format. *fancy* includes colors and line numbers, *plain* -includes neither, and *auto* (the default) uses *fancy* formatting only when -the output is a TTY. +prints line numbers with no coloring, *bare* prints only the match text, +*file:line* prints the filename and line number for each match (grep-style), +and *auto* (the default) uses *fancy* formatting when the output is a TTY and +*plain* formatting otherwise. `-h`, `--help` : Print the usage and exit. @@ -53,7 +53,7 @@ static const char *usage = ( " -B --context-before <n> set number of lines of context to print before the match\n" " -B --context-after <n> set number of lines of context to print after the match\n" " -C --context <context> set number of lines of context to print before and after the match\n" - " -f --format auto|fancy|plain set the output format\n" + " -f --format fancy|plain|bare|file:line set the output format\n" " -g --grammar <grammar-file> use the specified file as a grammar"); // Used as a heuristic to check if a file is binary or text: @@ -64,7 +64,7 @@ static struct { int context_before, context_after; bool ignorecase, verbose, git_mode, print_filenames; enum { MODE_NORMAL, MODE_LISTFILES, MODE_INPLACE, MODE_JSON, MODE_EXPLAIN } mode; - enum { FORMAT_AUTO, FORMAT_FANCY, FORMAT_PLAIN } format; + enum { FORMAT_AUTO, FORMAT_FANCY, FORMAT_PLAIN, FORMAT_BARE, FORMAT_FILE_LINE } format; pat_t *skip; } options = { .context_before = USE_DEFAULT_CONTEXT, @@ -77,6 +77,13 @@ static struct { .skip = NULL, }; +const char *LINE_FORMATS[] = { + [FORMAT_FANCY] = "\033[0;2m#\033(0\x78\033(B", + [FORMAT_PLAIN] = "#|", + [FORMAT_BARE] = "", + [FORMAT_FILE_LINE] = "@:#0:", +}; + // If a file is partly through being modified when the program exits, restore it from backup. static FILE *modifying_file = NULL; static file_t *backup_file; @@ -233,7 +240,7 @@ static int print_matches(FILE *out, def_t *defs, file_t *f, pat_t *pattern) .context_before = options.context_before, .context_after = options.context_after, .use_color = options.format == FORMAT_FANCY, - .print_line_numbers = options.format == FORMAT_FANCY, + .lineformat = LINE_FORMATS[options.format], }; match_t *m = NULL; @@ -483,7 +490,14 @@ int main(int argc, char *argv[]) } else if (FLAG("-A") || FLAG("--after-context")) { options.context_after = context_from_flag(flag); } else if (FLAG("-f") || FLAG("--format")) { - options.format = streq(flag, "fancy") ? FORMAT_FANCY : streq(flag, "plain") ? FORMAT_PLAIN : FORMAT_AUTO; + if (streq(flag, "fancy")) options.format = FORMAT_FANCY; + else if (streq(flag, "plain")) options.format = FORMAT_PLAIN; + else if (streq(flag, "bare")) options.format = FORMAT_BARE; + else if (streq(flag, "file:line")) { + options.format = FORMAT_FILE_LINE; + options.print_filenames = 0; + } else if (!streq(flag, "auto")) + errx(EXIT_FAILURE, "Unknown --format option: %s", flag); } else if (argv[0][0] == '-' && argv[0][1] && argv[0][1] != '-') { // single-char flags errx(EXIT_FAILURE, "Unrecognized flag: -%c\n\n%s", argv[0][1], usage); } else if (argv[0][0] != '-') { @@ -20,20 +20,30 @@ static const char *current_color = NULL; // // Print a line number, if it needs to be printed. -// line number of 0 means "just print an empty space for the number" +// In the lineformat string, replace "@" with the filename, and "#" with the line number. // __attribute__((nonnull(1,2))) -static inline void print_line_number(FILE *out, printer_t *pr, size_t line_number, const char *color) +static inline void print_line_number(FILE *out, printer_t *pr, size_t line_number, const char *color, int is_line_continued) { if (!pr->needs_line_number) return; - if (pr->print_line_numbers) { - if (line_number == 0) { - if (color) fprintf(out, "\033[0;2m \033(0\x78\033(B%s", color); - else fprintf(out, " |"); - } else { - if (color) fprintf(out, "\033[0;2m%5lu\033(0\x78\033(B%s", line_number, color); - else fprintf(out, "%5lu|", line_number); - } + for (const char *c = pr->lineformat; c && *c; c++) { + if (*c == '@') { // Print filename + fprintf(out, "%s", pr->file->filename); + } else if (*c == '#') { // Print line number + const char *after; + int space = (int)strtol(c+1, (char**)&after, 10); + if (after > c+1) c = after-1; // Width was specified + else // Otherwise default to "wide enough for every line number in this file" + for (int i = (int)pr->file->nlines; i > 0; i /= 10) ++space; + + if (is_line_continued) { + for (space = abs(space); space > 0; --space) + fputc('.', out); + } else fprintf(out, "%*lu", space, line_number); + } else fputc(*c, out); + } + if (color) { + fprintf(out, "%s", color); current_color = color; } pr->needs_line_number = 0; @@ -48,7 +58,7 @@ static void print_between(FILE *out, printer_t *pr, const char *start, const cha file_t *f = pr->file; while (start < end) { size_t line_num = get_line_number(f, start); - print_line_number(out, pr, line_num, color); + print_line_number(out, pr, line_num, color, 0); const char *eol = get_line(pr->file, line_num + 1); if (!eol || eol > end) eol = end; if (color && color != current_color) { @@ -122,7 +132,7 @@ static void _print_match(FILE *out, printer_t *pr, match_t *m) // TODO: clean up the line numbering code for (const char *r = text; r < end; ) { - print_line_number(out, pr, line > line_end ? 0 : line, pr->use_color ? color_replace : NULL); + print_line_number(out, pr, line, pr->use_color ? color_replace : NULL, line > line_end); // Capture substitution if (*r == '@' && r[1] && r[1] != '@') { @@ -151,9 +161,9 @@ static void _print_match(FILE *out, printer_t *pr, match_t *m) pr->file, get_line_number(pr->file, m->start)); char denter = line_start ? *line_start : '\t'; fputc('\n', out); - ++line; pr->needs_line_number = 1; - print_line_number(out, pr, 0, pr->use_color ? color_replace : NULL); + print_line_number(out, pr, line, pr->use_color ? color_replace : NULL, 1); + ++line; if (denter == ' ' || denter == '\t') { for (const char *p = line_start; p && *p == denter && p < m->start; ++p) fputc(denter, out); @@ -182,7 +192,7 @@ static void _print_match(FILE *out, printer_t *pr, match_t *m) continue; } } - print_line_number(out, pr, line > line_end ? 0 : line, pr->use_color ? color_normal : NULL); + print_line_number(out, pr, line, pr->use_color ? color_normal : NULL, line > line_end); } else { const char *prev = m->start; for (int i = 0; m->children && m->children[i]; i++) { @@ -218,7 +228,7 @@ void print_match(FILE *out, printer_t *pr, match_t *m) if (!first) { // When not printing context lines, print each match on its own // line instead of jamming them all together: - if (pr->context_before == NO_CONTEXT && pr->context_after == NO_CONTEXT && (!pr->needs_line_number || !pr->print_line_numbers)) { + if (pr->context_before == NO_CONTEXT && pr->context_after == NO_CONTEXT && (!pr->needs_line_number || !pr->lineformat)) { fprintf(out, "\n"); pr->needs_line_number = 1; } @@ -19,7 +19,7 @@ typedef struct { int context_before, context_after; bool needs_line_number:1; bool use_color:1; - bool print_line_numbers:1; + const char *lineformat; } printer_t; __attribute__((nonnull(1,2))) |
