Improved formatting options
This commit is contained in:
parent
1cf17cf95c
commit
615d94ba68
11
bp.1
11
bp.1
@ -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.
|
||||
|
8
bp.1.md
8
bp.1.md
@ -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
22
bp.c
@ -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
42
print.c
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user