From 26138f98af3735b01f2f0be6955e7a76e8971dd9 Mon Sep 17 00:00:00 2001 From: Swastik Baranwal Date: Mon, 15 Mar 2021 16:51:19 +0530 Subject: [PATCH] v doc: implement color highlighting for the stdout format, enable it by default (#9312) --- cmd/tools/vdoc/utils.v | 109 +++++++++++++++++++++++++++++++++++++++++ cmd/tools/vdoc/vdoc.v | 22 ++++++++- cmd/v/help/doc.txt | 3 ++ 3 files changed, 132 insertions(+), 2 deletions(-) diff --git a/cmd/tools/vdoc/utils.v b/cmd/tools/vdoc/utils.v index 00eb89d488..6dbb401d79 100644 --- a/cmd/tools/vdoc/utils.v +++ b/cmd/tools/vdoc/utils.v @@ -2,6 +2,12 @@ module main import os import v.doc +import term +import v.table +import v.scanner +import v.token +import strings +import v.pref [inline] fn slug(title string) string { @@ -130,3 +136,106 @@ fn gen_footer_text(d &doc.Doc, include_timestamp bool) string { time_str := '$generated_time.day $generated_time.smonth() $generated_time.year $generated_time.hhmmss()' return '$footer_text Generated on: $time_str' } + +fn color_highlight(code string, tb &table.Table) string { + builtin := ['bool', 'string', 'i8', 'i16', 'int', 'i64', 'i128', 'byte', 'u16', 'u32', 'u64', + 'u128', 'rune', 'f32', 'f64', 'int_literal', 'float_literal', 'byteptr', 'voidptr', 'any'] + highlight_code := fn (tok token.Token, typ HighlightTokenTyp) string { + lit := match typ { + .unone, .operator, .punctuation { + tok.kind.str() + } + .string { + term.yellow("'$tok.lit'") + } + .char { + term.yellow('`$tok.lit`') + } + .keyword { + term.blue(tok.lit) + } + .builtin, .symbol { + term.green(tok.lit) + } + .function { + term.cyan(tok.lit) + } + .number { + term.bright_blue(tok.lit) + } + else { + tok.lit + } + } + return lit + } + mut s := scanner.new_scanner(code, .parse_comments, &pref.Preferences{}) + mut prev := token.Token{} + mut tok := s.scan() + mut next_tok := s.scan() + mut buf := strings.new_builder(200) + mut i := 0 + for i < code.len { + if i == tok.pos { + mut tok_typ := HighlightTokenTyp.unone + match tok.kind { + .name { + if (tok.lit in builtin || tb.known_type(tok.lit)) + && (next_tok.kind != .lpar || prev.kind != .key_fn) { + tok_typ = .builtin + } else if next_tok.kind in [.lcbr, .rpar, .eof] + && (next_tok.kind != .rpar || prev.kind in [.name, .amp]) { + tok_typ = .symbol + } else if next_tok.kind in [.lpar, .lt] { + tok_typ = .function + } else { + tok_typ = .name + } + } + .comment { + tok_typ = .comment + } + .chartoken { + tok_typ = .char + } + .string { + tok_typ = .string + } + .number { + tok_typ = .number + } + .key_true, .key_false { + tok_typ = .boolean + } + .lpar, .lcbr, .rpar, .rcbr, .lsbr, .rsbr, .semicolon, .colon, .comma, .dot { + tok_typ = .punctuation + } + else { + if token.is_key(tok.lit) || token.is_decl(tok.kind) { + tok_typ = .keyword + } else if tok.kind == .decl_assign || tok.kind.is_assign() || tok.is_unary() + || tok.kind.is_relational() || tok.kind.is_infix() { + tok_typ = .operator + } + } + } + buf.write_string(highlight_code(tok, tok_typ)) + if prev.kind != .eof { + prev = tok + } else { + break + } + if next_tok.kind != .eof { + i = tok.pos + tok.len + tok = next_tok + next_tok = s.scan() + } else { + break + } + } else { + buf.write_b(code[i]) + i++ + } + } + return buf.str() +} diff --git a/cmd/tools/vdoc/vdoc.v b/cmd/tools/vdoc/vdoc.v index 9cb90983b8..1408f5754a 100644 --- a/cmd/tools/vdoc/vdoc.v +++ b/cmd/tools/vdoc/vdoc.v @@ -11,6 +11,7 @@ import v.doc import v.pref import v.vmod import json +import term const ( allowed_formats = ['md', 'markdown', 'json', 'text', 'stdout', 'html', 'htm'] @@ -44,6 +45,7 @@ struct Config { mut: pub_only bool = true show_loc bool // for plaintext + is_color bool is_multi bool is_vlib bool is_verbose bool @@ -86,7 +88,12 @@ fn (vd VDoc) gen_json(d doc.Doc) string { fn (vd VDoc) gen_plaintext(d doc.Doc) string { cfg := vd.cfg mut pw := strings.new_builder(200) - pw.writeln('$d.head.content\n') + if cfg.is_color { + content_arr := d.head.content.split(' ') + pw.writeln('${term.blue(content_arr[0])} ${term.green(content_arr[1])}\n') + } else { + pw.writeln('$d.head.content\n') + } comments := if cfg.include_examples { d.head.merge_comments() } else { @@ -103,7 +110,11 @@ fn (vd VDoc) write_plaintext_content(contents []doc.DocNode, mut pw strings.Buil cfg := vd.cfg for cn in contents { if cn.content.len > 0 { - pw.writeln(cn.content) + if cfg.is_color { + pw.writeln(color_highlight(cn.content, vd.docs[0].table)) + } else { + pw.writeln(cn.content) + } if cn.comments.len > 0 && !cfg.pub_only { comments := if cfg.include_examples { cn.merge_comments() @@ -369,6 +380,7 @@ fn (vd VDoc) vprintln(str string) { fn parse_arguments(args []string) Config { mut cfg := Config{} + cfg.is_color = term.can_show_color_on_stdout() for i := 0; i < args.len; i++ { arg := args[i] current_args := args[i..] @@ -386,6 +398,12 @@ fn parse_arguments(args []string) Config { cfg.output_type = set_output_type_from_str(format) i++ } + '-color' { + cfg.is_color = true + } + '-no-color' { + cfg.is_color = false + } '-inline-assets' { cfg.inline_assets = true } diff --git a/cmd/v/help/doc.txt b/cmd/v/help/doc.txt index bb0f921fd8..71f113a61b 100644 --- a/cmd/v/help/doc.txt +++ b/cmd/v/help/doc.txt @@ -4,6 +4,7 @@ Usage: Examples: v doc os v doc os File + v doc -no-color os v doc -o math.html math v doc -m -f html vlib/ @@ -20,6 +21,8 @@ Options: -o Specifies the output file/folder path where to store the generated docs. Set it to "stdout" to print the output instead of saving the contents to a file. + -color Force stdout colorize output. + -no-color Force plain text output, without ANSI colors. -readme Include README.md to docs if present. -v Enables verbose logging. For debugging purposes. -no-timestamp Omits the timestamp in the output file.