|
| 1 | +(* This file is part of DBL, released under MIT license. |
| 2 | + * See LICENSE for details. |
| 3 | + *) |
| 4 | + |
| 5 | +(** Pretty printing of text range from Position.t *) |
| 6 | +open Position |
| 7 | + |
| 8 | +(* ========================================================================= *) |
| 9 | +(** Options for underlining specified region *) |
| 10 | +type underline_options = |
| 11 | + | NoUnderline |
| 12 | + (** Disable underlining *) |
| 13 | + |
| 14 | + | UnderlineBegining |
| 15 | + (** Point to only beginning of region *) |
| 16 | + |
| 17 | + | UnderlineIfOneLine |
| 18 | + (** Underline whole region, but only if specifies only one line *) |
| 19 | + |
| 20 | + | UnderlineAlways |
| 21 | + (** Underline whole region *) |
| 22 | + |
| 23 | +type options = { |
| 24 | + context : int; |
| 25 | + (** how many lines before region is to be printed *) |
| 26 | + |
| 27 | + underline : t -> underline_options; |
| 28 | + (** function that depending on region will select underlining option *) |
| 29 | + |
| 30 | + add_line_numbers : bool; |
| 31 | + (** should line numbers be added *) |
| 32 | +} |
| 33 | + |
| 34 | +let default_options = { |
| 35 | + context = 2; |
| 36 | + underline = (fun _ -> UnderlineIfOneLine); |
| 37 | + add_line_numbers = true; |
| 38 | +} |
| 39 | + |
| 40 | +(* ========================================================================= *) |
| 41 | +(** Printing to terminal with colors *) |
| 42 | + |
| 43 | +let keyword_color = "\027[1;34m" |
| 44 | + |
| 45 | +type printing_color = |
| 46 | + | Red |
| 47 | + | Teal |
| 48 | + | Yellow |
| 49 | + | Black |
| 50 | + | Green |
| 51 | + | Blue |
| 52 | + | Magenta |
| 53 | + | Cyan |
| 54 | + | White |
| 55 | + | Default |
| 56 | + |
| 57 | +let color_to_string = function |
| 58 | + | Black -> "\027[30m" |
| 59 | + | Red -> "\027[31m" |
| 60 | + | Green -> "\027[32m" |
| 61 | + | Yellow -> "\027[33m" |
| 62 | + | Blue -> "\027[34m" |
| 63 | + | Magenta -> "\027[35m" |
| 64 | + | Teal -> "\027[36m" |
| 65 | + | Cyan -> "\027[36m" |
| 66 | + | White -> "\027[37m" |
| 67 | + | Default -> "\027[0m" |
| 68 | + |
| 69 | +let bold_code_string = "\027[1m" |
| 70 | +let dim_code_string = "\027[2m" |
| 71 | +let italic_code_string = "\027[3m" |
| 72 | +let underline_code_string = "\027[4m" |
| 73 | +let blink_code_string = "\027[5m" |
| 74 | +let rapid_blink_code_string = "\027[6m" |
| 75 | +let reverse_code_string = "\027[7m" |
| 76 | +let hidden_code_string = "\027[8m" |
| 77 | + |
| 78 | +let reset_string = color_to_string Default |
| 79 | + |
| 80 | +let color_string color s = |
| 81 | + if not !DblConfig.display_colors |
| 82 | + then s |
| 83 | + else color_to_string color ^ s ^ reset_string |
| 84 | + |
| 85 | +let bolden_string s = |
| 86 | + if not !DblConfig.display_colors |
| 87 | + then s |
| 88 | + else bold_code_string ^ s ^ reset_string |
| 89 | + |
| 90 | +let underline_string s = |
| 91 | + if not !DblConfig.display_colors |
| 92 | + then s |
| 93 | + else underline_code_string ^ s ^ reset_string |
| 94 | + |
| 95 | +(* ========================================================================= *) |
| 96 | +(** Underlining *) |
| 97 | + |
| 98 | +let find_tabs line = |
| 99 | + String.fold_left (fun (i, acc) c -> |
| 100 | + if c = '\t' then (i+1, i::acc) else (i+1, acc)) (0, []) line |
| 101 | + |> snd |
| 102 | + |
| 103 | +let generate_underline ~color_printer start_cnum len tabs = |
| 104 | + if len <= 0 then "" else |
| 105 | + let underline = String.make len '^' |> color_printer in |
| 106 | + let padding = String.make (start_cnum - 1) ' ' in |
| 107 | + let f i _ = |
| 108 | + if List.mem i tabs |
| 109 | + then '\t' |
| 110 | + else ' ' |
| 111 | + in String.mapi f padding ^ underline |
| 112 | + |
| 113 | +let add_underline ~options ~pos ~color_printer (i, line) = |
| 114 | + match options.underline pos, i with |
| 115 | + | NoUnderline, _ -> Seq.return (i, line) |
| 116 | + | (UnderlineIfOneLine | UnderlineAlways), Some j |
| 117 | + when pos.pos_start_line = pos.pos_end_line |
| 118 | + && pos.pos_start_line = j -> |
| 119 | + let underline = generate_underline ~color_printer |
| 120 | + (start_column pos) pos.pos_length (find_tabs line) in |
| 121 | + Seq.cons (i, line) (Seq.return (None, underline)) |
| 122 | + | UnderlineIfOneLine, _ -> Seq.return (i, line) |
| 123 | + | UnderlineBegining, Some j |
| 124 | + when pos.pos_start_line = j -> |
| 125 | + let underline = generate_underline ~color_printer |
| 126 | + (start_column pos) 1 (find_tabs line) in |
| 127 | + Seq.cons (i, line) (Seq.return (None, underline)) |
| 128 | + | UnderlineBegining, _ -> Seq.return (i, line) |
| 129 | + | UnderlineAlways, Some j |
| 130 | + when j = pos.pos_start_line -> |
| 131 | + let underline = generate_underline ~color_printer |
| 132 | + (start_column pos) |
| 133 | + (String.length line - start_column pos) |
| 134 | + (find_tabs line) in |
| 135 | + Seq.cons (i, line) (Seq.return (None, underline)) |
| 136 | + | UnderlineAlways, Some j |
| 137 | + when j = pos.pos_end_line -> |
| 138 | + let underline = generate_underline ~color_printer |
| 139 | + 0 (end_column pos) (find_tabs line) in |
| 140 | + Seq.cons (i, line) (Seq.return (None, underline)) |
| 141 | + | UnderlineAlways, Some j |
| 142 | + when j > pos.pos_start_line && j < pos.pos_end_line -> |
| 143 | + let underline = generate_underline ~color_printer |
| 144 | + 0 (String.length line) (find_tabs line) in |
| 145 | + Seq.cons (i, line) (Seq.return (None, underline)) |
| 146 | + | UnderlineAlways, _ -> Seq.return (i, line) |
| 147 | + |
| 148 | +(* ========================================================================= *) |
| 149 | +(** Printing code *) |
| 150 | + |
| 151 | +let add_line_number ~options ~pos ~color_printer end_line = |
| 152 | + let align_to = Float.of_int end_line |
| 153 | + |> Float.log10 |
| 154 | + |> Float.to_int |
| 155 | + in |
| 156 | + fun (i, line) -> |
| 157 | + if not options.add_line_numbers then |
| 158 | + String.make (align_to + 3) ' ' ^ "| " ^ line |
| 159 | + else |
| 160 | + match i with |
| 161 | + | None -> String.make (align_to + 3) ' ' ^ "| " ^ line |
| 162 | + | Some i -> |
| 163 | + let separator = if i >= pos.pos_start_line && i <= pos.pos_end_line |
| 164 | + then color_printer "|>" else "| " in |
| 165 | + Printf.sprintf " %*d %s%s" (align_to + 1) i separator line |
| 166 | + |
| 167 | +let process ~pos ~options ~color_printer seq = |
| 168 | + let to_drop = pos.pos_start_line - 1 - options.context in |
| 169 | + let to_take = |
| 170 | + pos.pos_end_line - pos.pos_start_line + 1 + |
| 171 | + options.context - (Int.min 0 to_drop) in |
| 172 | + let lines = seq |
| 173 | + |> Seq.zip (Seq.ints 1 |> Seq.map Option.some) |
| 174 | + |> Seq.drop (Int.max to_drop 0) |
| 175 | + |> Seq.take to_take |
| 176 | + |> Seq.flat_map (add_underline ~options ~pos ~color_printer) |
| 177 | + |> Seq.map (add_line_number pos.pos_end_line ~options ~pos ~color_printer) |
| 178 | + in |
| 179 | + String.concat "\n" @@ List.of_seq lines |
| 180 | + |
| 181 | +let get_text_from_repl ~options ~repl_input ~color_printer pos = |
| 182 | + let lines = String.split_on_char '\n' repl_input in |
| 183 | + let options = { options with add_line_numbers=false } in |
| 184 | + let file_chunk = process ~pos ~options |
| 185 | + ~color_printer (List.to_seq lines) in |
| 186 | + Some file_chunk |
| 187 | + |
| 188 | +let get_text_from_file ~options ~color_printer pos = |
| 189 | + if Fun.negate Sys.file_exists pos.pos_fname then None else |
| 190 | + let get_line fd () = In_channel.input_line fd in |
| 191 | + let process_file fd = process ~pos ~options ~color_printer |
| 192 | + (Seq.of_dispenser (get_line fd)) in |
| 193 | + let file_chunk = In_channel.with_open_text pos.pos_fname process_file in |
| 194 | + let pp_file_name = " -> " ^ pos.pos_fname ^ "\n" in |
| 195 | + Some (pp_file_name ^ file_chunk) |
| 196 | + |
| 197 | +let get_text_range ?(options=default_options) ~repl_input ~color (pos : t) = |
| 198 | + let color_printer = color_string color in |
| 199 | + if pos.pos_fname = "<stdin>" && repl_input <> "" then |
| 200 | + get_text_from_repl ~options ~repl_input ~color_printer pos |
| 201 | + else |
| 202 | + get_text_from_file ~options ~color_printer pos |
0 commit comments