package janestreet_csv
Tools for working with CSVs on the command line
Install
Dune Dependency
Authors
Maintainers
Sources
janestreet_csv-v0.16.0.tar.gz
sha256=c039eeef15bc68460984e74a003c3068da2e4854c4dc2cdcfec6be946f2b0a65
doc/src/janestreet_csv.csv_tool_lib/pretty.ml.html
Source file pretty.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
open Core module Pad_floats = struct type t = { digits_before_dot : int ; digits_after_and_including_dot : int } let empty = { digits_before_dot = 1; digits_after_and_including_dot = 0 } let width { digits_before_dot; digits_after_and_including_dot } = digits_before_dot + digits_after_and_including_dot ;; let of_string x = match String.index x '.' with | None -> { digits_before_dot = String.length x; digits_after_and_including_dot = 0 } | Some digits_before_dot -> { digits_before_dot ; digits_after_and_including_dot = String.length x - digits_before_dot } ;; let max a b = { digits_before_dot = Int.max a.digits_before_dot b.digits_before_dot ; digits_after_and_including_dot = Int.max a.digits_after_and_including_dot b.digits_after_and_including_dot } ;; let max_length = List.fold ~init:empty ~f:(fun acc string -> max acc (of_string string)) let pad desired string = let actual = of_string string in let spaces n = String.make n ' ' in let left_padding = spaces (desired.digits_before_dot - actual.digits_before_dot) in let right_padding = spaces (desired.digits_after_and_including_dot - actual.digits_after_and_including_dot) in String.concat [ left_padding; string; right_padding ] ;; end let max_length xs = List.fold xs ~init:1 ~f:(fun w x -> Int.max w (String.length x)) let string_padding x w = let w = Int.max 1 w in let n = String.length x in if w > n then Some (String.make (w - n) ' ') else None ;; let pad_right x ~width = match string_padding x width with | None -> x | Some pad -> x ^ pad ;; let matches of_string x = match of_string x with | _ -> true | exception _ -> false ;; let may_be_numeric = function | "" -> true | n -> let n = String.chop_prefix_if_exists ~prefix:"$" n in matches Int.of_string n || matches Float.of_string n ;; let col_type col = if List.for_all col ~f:may_be_numeric then `Number else `String type t = { header_lines : string list ; row_lines : string list } let lines t = t.header_lines @ t.row_lines let print t = List.iter ~f:print_endline t.header_lines; List.iter ~f:print_endline t.row_lines ;; let prettify_internal ~space ~suppress_header csv = let sep_width = space in let sep = String.make space ' ' in let header_block columns = List.rev (let hd, tl = List.fold_right columns ~init:("", []) ~f:(fun (header, col_type) (base_row, header_rows) -> let bar_place, column_width = match col_type with | `String width -> 0, width | `Number (pad_float : Pad_floats.t) -> pad_float.digits_before_dot - 1, Pad_floats.width pad_float in let header_width = String.length header in let header_offset = Int.max 0 (bar_place + 1 - header_width) in let pad = String.make column_width ' ' in let bar_pad = String.mapi pad ~f:(fun i c -> if i = bar_place then '|' else c) in let rec add = function | [] -> [ String.make header_offset ' ' ^ header, 0 ] | (text, num_blanks) :: rest -> if column_width + num_blanks >= header_width then ( let pad text = pad ^ sep ^ text in let text = Bytes.of_string (pad text) in Bytes.From_string.blit ~src:header ~src_pos:0 ~dst:text ~dst_pos:header_offset ~len:header_width; (Bytes.to_string text, 0) :: List.map rest ~f:(fun (text, num_blanks) -> pad text, column_width + sep_width + num_blanks)) else (bar_pad ^ sep ^ text, 0) :: add rest in let base_row = if String.equal base_row "" then String.rstrip bar_pad else bar_pad ^ sep ^ base_row in base_row, add header_rows) in hd :: List.map ~f:fst tl) in match csv |> Csv_shape.create_verbose |> Csv_shape.to_error_string with | Error csv -> Error csv | Ok () -> let cols = csv |> List.transpose_exn |> List.map ~f:(function | [] -> assert false | header :: values -> let col_type = col_type values in let width, pad = match col_type with | `String -> let width = max_length values in `String width, pad_right ~width | `Number -> let pad_floats = Pad_floats.max_length values in `Number pad_floats, Pad_floats.pad pad_floats in let header_info = header, width in header_info, List.map values ~f:pad) in let header_lines = if suppress_header then [] else header_block (List.map cols ~f:fst) in let row_lines = List.map ~f:(String.concat ~sep) (List.transpose_exn (List.map cols ~f:snd)) in Ok { header_lines; row_lines } ;; (* bootstrap *) let prettify ~space ~suppress_header csv = match prettify_internal ~space ~suppress_header csv with | Ok result -> Ok result | Error error_csv -> let error_csv_lines = match prettify_internal ~space:2 ~suppress_header:false error_csv with | Ok t -> lines t | Error _ -> List.map error_csv ~f:(fun row -> String.concat ~sep:"," row) in Error (String.concat ~sep:"\n" ("Error: lines with different numbers of columns" :: "" :: error_csv_lines)) ;;
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>