The terminal is assumed to be 80x24 in size, the new options `term_width_limit` and `term_height_limit` can be used to overwrite that. Lines longer then the terminal width cause problems with scrolling pages and need to be shortened. The algorithm used for shortening can deal with tabs and escape sequences, has rudimentary support for UTF-8 and runs in O(n). avih helped in the creation of the term_ellipsis() function and split() is also from him.
Unified · Split
| @@ -85,6 +85,18 @@ Configurable Options · 84 unmodified lines | |||
| 85 | respective duration. This can result in overlapping text when multiple | 85 | respective duration. This can result in overlapping text when multiple |
| 86 | scripts decide to print text at the same time. | 86 | scripts decide to print text at the same time. |
| 87 | 87 | ||
| 88 | ``term_width_limit`` | ||
| 89 | Default: -1 | ||
| 90 | |||
| 91 | Sets the terminal width. | ||
| 92 | A value of 0 means the width is infinite, -1 means it's automatic. | ||
| 93 | |||
| 94 | ``term_height_limit`` | ||
| 95 | Default: -1 | ||
| 96 | |||
| 97 | Sets the terminal height. | ||
| 98 | A value of 0 means the height is infinite, -1 means it's automatic. | ||
| 99 | |||
| 88 | ``plot_perfdata`` | 100 | ``plot_perfdata`` |
| 89 | Default: yes | 101 | Default: yes |
| 90 | 102 | ||
| @@ -29,7 +29,12 @@ local o = { · 28 unmodified lines | |||
| 29 | persistent_overlay = false, -- whether the stats can be overwritten by other output | 29 | persistent_overlay = false, -- whether the stats can be overwritten by other output |
| 30 | print_perfdata_passes = false, -- when true, print the full information about all passes | 30 | print_perfdata_passes = false, -- when true, print the full information about all passes |
| 31 | filter_params_max_length = 100, -- a filter list longer than this many characters will be shown one filter per line instead | 31 | filter_params_max_length = 100, -- a filter list longer than this many characters will be shown one filter per line instead |
| 32 | <<<<<<< HEAD | ||
| 32 | show_frame_info = false, -- whether to show the current frame info | 33 | show_frame_info = false, -- whether to show the current frame info |
| 34 | ======= | ||
| 35 | term_width_limit = -1, -- overwrites the terminal width | ||
| 36 | term_height_limit = -1, -- overwrites the terminal height | ||
| 37 | >>>>>>> 89493c38fb (stats.lua: truncate long lines for the terminal) | ||
| 33 | debug = false, | 38 | debug = false, |
| 34 | 39 | ||
| 35 | -- Graph options and style | 40 | -- Graph options and style |
| @@ -83,6 +88,15 @@ local o = { · 47 unmodified lines | |||
| 83 | } | 88 | } |
| 84 | options.read_options(o) | 89 | options.read_options(o) |
| 85 | 90 | ||
| 91 | o.term_width_limit = tonumber(o.term_width_limit) or -1 | ||
| 92 | o.term_height_limit = tonumber(o.term_height_limit) or -1 | ||
| 93 | if o.term_width_limit < 0 then | ||
| 94 | o.term_width_limit = nil | ||
| 95 | end | ||
| 96 | if o.term_height_limit < 0 then | ||
| 97 | o.term_height_limit = nil | ||
| 98 | end | ||
| 99 | |||
| 86 | local format = string.format | 100 | local format = string.format |
| 87 | local max = math.max | 101 | local max = math.max |
| 88 | local min = math.min | 102 | local min = math.min |
| @@ -366,7 +380,7 @@ end · 277 unmodified lines | |||
| 366 | 380 | ||
| 367 | local function ellipsis(s, maxlen) | 381 | local function ellipsis(s, maxlen) |
| 368 | if not maxlen or s:len() <= maxlen then return s end | 382 | if not maxlen or s:len() <= maxlen then return s end |
| 369 | return s:sub(1, maxlen - 3) .. "..." | 383 | return s:sub(1, max(0, maxlen - 3)) .. "..." |
| 370 | end | 384 | end |
| 371 | 385 | ||
| 372 | -- command prefix tokens to strip - includes generic property commands | 386 | -- command prefix tokens to strip - includes generic property commands |
| @@ -994,24 +1008,89 @@ local function eval_ass_formatting() · 621 unmodified lines | |||
| 994 | end | 1008 | end |
| 995 | end | 1009 | end |
| 996 | 1010 | ||
| 1011 | -- assumptions: | ||
| 1012 | -- s is composed of SGR escape sequences and/or printable UTF8 sequences | ||
| 1013 | -- printable codepoints occupy one terminal cell (we don't have wcwidth) | ||
| 1014 | -- tabstops are 8, 16, 24..., and the output starts at 0 or a tabstop | ||
| 1015 | -- note: if maxwidth <= 2 and s doesn't fit: the result is "..." (more than 2) | ||
| 1016 | function term_ellipsis(s, maxwidth) | ||
| 1017 | local TAB, ESC, SGR_END = 9, 27, ("m"):byte() | ||
| 1018 | local width, ellipsis = 0, "..." | ||
| 1019 | local fit_len, in_sgr | ||
| 1020 | |||
| 1021 | for i = 1, #s do | ||
| 1022 | local x = s:byte(i) | ||
| 1023 | |||
| 1024 | if in_sgr then | ||
| 1025 | in_sgr = x ~= SGR_END | ||
| 1026 | elseif x == ESC then | ||
| 1027 | in_sgr = true | ||
| 1028 | ellipsis = "\27[0m..." -- ensure SGR reset | ||
| 1029 | elseif x < 128 or x >= 192 then -- non UTF8-continuation | ||
| 1030 | -- tab adds till the next stop, else add 1 | ||
| 1031 | width = width + (x == TAB and 8 - width % 8 or 1) | ||
| 1032 | |||
| 1033 | if fit_len == nil and width > maxwidth - 3 then | ||
| 1034 | fit_len = i - 1 -- adding "..." still fits maxwidth | ||
| 1035 | end | ||
| 1036 | if width > maxwidth then | ||
| 1037 | return s:sub(1, fit_len) .. ellipsis | ||
| 1038 | end | ||
| 1039 | end | ||
| 1040 | end | ||
| 1041 | |||
| 1042 | return s | ||
| 1043 | end | ||
| 1044 | |||
| 1045 | local function term_ellipsis_array(arr, from, to, max_width) | ||
| 1046 | for i = from, to do | ||
| 1047 | arr[i] = term_ellipsis(arr[i], max_width) | ||
| 1048 | end | ||
| 1049 | return arr | ||
| 1050 | end | ||
| 1051 | |||
| 1052 | -- split str into a table | ||
| 1053 | -- example: local t = split(s, "\n") | ||
| 1054 | -- plain: whether pat is a plain string (default false - pat is a pattern) | ||
| 1055 | local function split(str, pat, plain) | ||
| 1056 | local init = 1 | ||
| 1057 | local r, i, find, sub = {}, 1, string.find, string.sub | ||
| 1058 | repeat | ||
| 1059 | local f0, f1 = find(str, pat, init, plain) | ||
| 1060 | r[i], i = sub(str, init, f0 and f0 - 1), i+1 | ||
| 1061 | init = f0 and f1 + 1 | ||
| 1062 | until f0 == nil | ||
| 1063 | return r | ||
| 1064 | end | ||
| 997 | 1065 | ||
| 998 | -- Composes the output with header and scrollable content | 1066 | -- Composes the output with header and scrollable content |
| 999 | -- Returns string of the finished page and the actually chosen offset | 1067 | -- Returns string of the finished page and the actually chosen offset |
| 1000 | -- | 1068 | -- |
| 1001 | -- header : table of the header where each entry is one line | 1069 | -- header : table of the header where each entry is one line |
| 1002 | -- content : table of the content where each entry is one line | 1070 | -- content : table of the content where each entry is one line |
| 1003 | -- offset : the desired scroll offset of the content from the first line at the top | 1071 | -- apply_scroll: scroll the content |
| 1004 | local function compose_page(header, content, offset) | 1072 | local function finalize_page(header, content, apply_scroll) |
| 1005 | -- up to 22 lines for the terminal - so that mpv can also print | 1073 | local term_width = o.term_width_limit or 80 |
| 1006 | -- the status line without scrolling, and up to 40 lines for libass | 1074 | local term_height = o.term_height_limit or 24 |
| 1007 | -- because it can put a big performance toll on libass to process | 1075 | local from, to = 1, #content |
| 1008 | -- many lines which end up outside (below) the screen. | 1076 | if apply_scroll and term_height > 0 then |
| 1009 | local max_content_lines = (o.use_ass and 40 or 22) - #header | 1077 | -- Up to 40 lines for libass because it can put a big performance toll on |
| 1010 | -- in the terminal the scrolling should stop once the last line is visible | 1078 | -- libass to process many lines which end up outside (below) the screen. |
| 1011 | local max_offset = o.use_ass and #content or #content - max_content_lines + 1 | 1079 | -- In the terminal reduce height by 2 for the status line (can be more then one line) |
| 1012 | local from = max(1, min((offset or 1), max_offset)) | 1080 | local max_content_lines = (o.use_ass and 40 or term_height - 2) - #header |
| 1013 | local to = min(#content, from + max_content_lines - 1) | 1081 | -- in the terminal the scrolling should stop once the last line is visible |
| 1014 | return table.concat(header) .. table.concat(content, "", from, to), from | 1082 | local max_offset = o.use_ass and #content or #content - max_content_lines + 1 |
| 1083 | from = max(1, min((pages[curr_page].offset or 1), max_offset)) | ||
| 1084 | to = min(#content, from + max_content_lines - 1) | ||
| 1085 | pages[curr_page].offset = from | ||
| 1086 | end | ||
| 1087 | local output = table.concat(header) .. table.concat(content, "", from, to) | ||
| 1088 | if not o.use_ass and term_width > 0 and curr_page ~= o.key_page_4 then | ||
| 1089 | local t = split(output, "\n", true) | ||
| 1090 | -- limit width for the terminal | ||
| 1091 | output = table.concat(term_ellipsis_array(t, 1, #t, term_width), "\n") | ||
| 1092 | end | ||
| 1093 | return output, from | ||
| 1015 | end | 1094 | end |
| 1016 | 1095 | ||
| 1017 | -- Returns an ASS string with "normal" stats | 1096 | -- Returns an ASS string with "normal" stats |
| @@ -1023,7 +1102,7 @@ local function default_stats() · 5 unmodified lines | |||
| 1023 | add_video_out(stats) | 1102 | add_video_out(stats) |
| 1024 | add_video(stats) | 1103 | add_video(stats) |
| 1025 | add_audio(stats) | 1104 | add_audio(stats) |
| 1026 | return table.concat(stats) | 1105 | return finalize_page({}, stats, false) |
| 1027 | end | 1106 | end |
| 1028 | 1107 | ||
| 1029 | -- Returns an ASS string with extended VO stats | 1108 | -- Returns an ASS string with extended VO stats |
| @@ -1033,11 +1112,7 @@ local function vo_stats() · 3 unmodified lines | |||
| 1033 | add_header(header) | 1112 | add_header(header) |
| 1034 | append_perfdata(header, content, true, true) | 1113 | append_perfdata(header, content, true, true) |
| 1035 | header = {table.concat(header)} | 1114 | header = {table.concat(header)} |
| 1036 | 1115 | return finalize_page(header, content, false) | |
| 1037 | local page = pages[o.key_page_2] | ||
| 1038 | local res = nil | ||
| 1039 | res, page.offset = compose_page(header, content, page.offset) | ||
| 1040 | return res | ||
| 1041 | end | 1116 | end |
| 1042 | 1117 | ||
| 1043 | local kbinfo_lines = nil | 1118 | local kbinfo_lines = nil |
| @@ -1052,12 +1127,10 @@ local function keybinding_info(after_scroll) · 8 unmodified lines | |||
| 1052 | header = {table.concat(header)} | 1127 | header = {table.concat(header)} |
| 1053 | 1128 | ||
| 1054 | if not kbinfo_lines or not after_scroll then | 1129 | if not kbinfo_lines or not after_scroll then |
| 1055 | kbinfo_lines = get_kbinfo_lines() | 1130 | kbinfo_lines = get_kbinfo_lines(o.term_width_limit) |
| 1056 | end | 1131 | end |
| 1057 | 1132 | ||
| 1058 | local res = nil | 1133 | return finalize_page(header, kbinfo_lines, true) |
| 1059 | res, page.offset = compose_page(header, kbinfo_lines, page.offset) | ||
| 1060 | return res | ||
| 1061 | end | 1134 | end |
| 1062 | 1135 | ||
| 1063 | local function perf_stats() | 1136 | local function perf_stats() |
| @@ -1068,9 +1141,7 @@ local function perf_stats() · 4 unmodified lines | |||
| 1068 | append(header, "", {prefix=page.desc .. ":", nl="", indent=""}) | 1141 | append(header, "", {prefix=page.desc .. ":", nl="", indent=""}) |
| 1069 | append_general_perfdata(content) | 1142 | append_general_perfdata(content) |
| 1070 | header = {table.concat(header)} | 1143 | header = {table.concat(header)} |
| 1071 | local res = nil | 1144 | return finalize_page(header, content, true) |
| 1072 | res, page.offset = compose_page(header, content, page.offset) | ||
| 1073 | return res | ||
| 1074 | end | 1145 | end |
| 1075 | 1146 | ||
| 1076 | local function opt_time(t) | 1147 | local function opt_time(t) |
| @@ -1091,7 +1162,7 @@ local function cache_stats() · 14 unmodified lines | |||
| 1091 | local info = mp.get_property_native("demuxer-cache-state") | 1162 | local info = mp.get_property_native("demuxer-cache-state") |
| 1092 | if info == nil then | 1163 | if info == nil then |
| 1093 | append(stats, "Unavailable.", {}) | 1164 | append(stats, "Unavailable.", {}) |
| 1094 | return table.concat(stats) | 1165 | return finalize_page({}, stats, false) |
| 1095 | end | 1166 | end |
| 1096 | 1167 | ||
| 1097 | local a = info["reader-pts"] | 1168 | local a = info["reader-pts"] |
| @@ -1169,7 +1240,7 @@ local function cache_stats() · 71 unmodified lines | |||
| 1169 | {prefix = format("Range %s:", n)}) | 1240 | {prefix = format("Range %s:", n)}) |
| 1170 | end | 1241 | end |
| 1171 | 1242 | ||
| 1172 | return table.concat(stats) | 1243 | return finalize_page({}, stats, false) |
| 1173 | end | 1244 | end |
| 1174 | 1245 | ||
| 1175 | -- Record 1 sample of cache statistics. | 1246 | -- Record 1 sample of cache statistics. |