History

stats.lua: truncate long lines for the terminal

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.

Christoph Heinrich committed 3 years ago

Commit 777f69b1 parent 344ac552 files · +113 −30Browse files

Unified · Split

2 files
  1. DOCS/man/stats.rst +12 −0
  2. player/lua/stats.lua +101 −30
DOCS/man/stats.rst+12−0
@@ -85,6 +85,18 @@ Configurable Options · 84 unmodified lines
85 respective duration. This can result in overlapping text when multiple85 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: yes101 Default: yes
90 102
player/lua/stats.lua+101−30
@@ -29,7 +29,12 @@ local o = { · 28 unmodified lines
29 persistent_overlay = false, -- whether the stats can be overwritten by other output29 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 passes30 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 instead31 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 info33 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 style40 -- Graph options and style
@@ -83,6 +88,15 @@ local o = { · 47 unmodified lines
83}88}
84options.read_options(o)89options.read_options(o)
85 90
91o.term_width_limit = tonumber(o.term_width_limit) or -1
92o.term_height_limit = tonumber(o.term_height_limit) or -1
93if o.term_width_limit < 0 then
94 o.term_width_limit = nil
95end
96if o.term_height_limit < 0 then
97 o.term_height_limit = nil
98end
99
86local format = string.format100local format = string.format
87local max = math.max101local max = math.max
88local min = math.min102local min = math.min
@@ -366,7 +380,7 @@ end · 277 unmodified lines
366 380
367local function ellipsis(s, maxlen)381local function ellipsis(s, maxlen)
368 if not maxlen or s:len() <= maxlen then return s end382 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)) .. "..."
370end384end
371 385
372-- command prefix tokens to strip - includes generic property commands386-- command prefix tokens to strip - includes generic property commands
@@ -994,24 +1008,89 @@ local function eval_ass_formatting() · 621 unmodified lines
994 end1008 end
995end1009end
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)
1016function 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
1043end
1044
1045local 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
1050end
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)
1055local 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
1064end
997 1065
998-- Composes the output with header and scrollable content1066-- Composes the output with header and scrollable content
999-- Returns string of the finished page and the actually chosen offset1067-- Returns string of the finished page and the actually chosen offset
1000--1068--
1001-- header : table of the header where each entry is one line1069-- header : table of the header where each entry is one line
1002-- content : table of the content where each entry is one line1070-- 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 top1071-- apply_scroll: scroll the content
1004local function compose_page(header, content, offset)1072local function finalize_page(header, content, apply_scroll)
1005 -- up to 22 lines for the terminal - so that mpv can also print1073 local term_width = o.term_width_limit or 80
1006 -- the status line without scrolling, and up to 40 lines for libass1074 local term_height = o.term_height_limit or 24
1007 -- because it can put a big performance toll on libass to process1075 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) - #header1077 -- 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 visible1078 -- 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 + 11079 -- 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), from1082 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
1015end1094end
1016 1095
1017-- Returns an ASS string with "normal" stats1096-- 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)
1027end1106end
1028 1107
1029-- Returns an ASS string with extended VO stats1108-- 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
1041end1116end
1042 1117
1043local kbinfo_lines = nil1118local 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 then1129 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 end1131 end
1057 1132
1058 local res = nil1133 return finalize_page(header, kbinfo_lines, true)
1059 res, page.offset = compose_page(header, kbinfo_lines, page.offset)
1060 return res
1061end1134end
1062 1135
1063local function perf_stats()1136local 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 = nil1144 return finalize_page(header, content, true)
1072 res, page.offset = compose_page(header, content, page.offset)
1073 return res
1074end1145end
1075 1146
1076local function opt_time(t)1147local 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 then1163 if info == nil then
1093 append(stats, "Unavailable.", {})1164 append(stats, "Unavailable.", {})
1094 return table.concat(stats)1165 return finalize_page({}, stats, false)
1095 end1166 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 end1241 end
1171 1242
1172 return table.concat(stats)1243 return finalize_page({}, stats, false)
1173end1244end
1174 1245
1175-- Record 1 sample of cache statistics.1246-- Record 1 sample of cache statistics.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t