Improved formatting options

This commit is contained in:
Bruce Hill 2021-09-02 18:07:18 -07:00
parent 1cf17cf95c
commit 615d94ba68
5 changed files with 57 additions and 28 deletions

11
bp.1
View File

@ -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.

View File

@ -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.

22
bp.c
View File

@ -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] != '-') {

42
print.c
View File

@ -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;
}

View File

@ -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)))