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
8585 respective duration. This can result in overlapping text when multiple
8686 scripts decide to print text at the same time.
8787
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+
88100 ``plot_perfdata``
89101 Default: yes
90102
player/lua/stats.lua+101−30
@@ -29,7 +29,12 @@ local o = { · 28 unmodified lines
2929 persistent_overlay = false, -- whether the stats can be overwritten by other output
3030 print_perfdata_passes = false, -- when true, print the full information about all passes
3131 filter_params_max_length = 100, -- a filter list longer than this many characters will be shown one filter per line instead
32+<<<<<<< HEAD
3233 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)
3338 debug = false,
3439
3540 -- Graph options and style
@@ -83,6 +88,15 @@ local o = { · 47 unmodified lines
8388 }
8489 options.read_options(o)
8590
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+
86100 local format = string.format
87101 local max = math.max
88102 local min = math.min
@@ -366,7 +380,7 @@ end · 277 unmodified lines
366380
367381 local function ellipsis(s, maxlen)
368382 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)) .. "..."
370384 end
371385
372386 -- command prefix tokens to strip - includes generic property commands
@@ -994,24 +1008,89 @@ local function eval_ass_formatting() · 621 unmodified lines
9941008 end
9951009 end
9961010
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
9971065
9981066 -- Composes the output with header and scrollable content
9991067 -- Returns string of the finished page and the actually chosen offset
10001068 --
1001−-- header : table of the header where each entry is one line
1002−-- 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
1004−local function compose_page(header, content, offset)
1005− -- up to 22 lines for the terminal - so that mpv can also print
1006− -- the status line without scrolling, and up to 40 lines for libass
1007− -- because it can put a big performance toll on libass to process
1008− -- many lines which end up outside (below) the screen.
1009− local max_content_lines = (o.use_ass and 40 or 22) - #header
1010− -- in the terminal the scrolling should stop once the last line is visible
1011− local max_offset = o.use_ass and #content or #content - max_content_lines + 1
1012− local from = max(1, min((offset or 1), max_offset))
1013− local to = min(#content, from + max_content_lines - 1)
1014− return table.concat(header) .. table.concat(content, "", from, to), from
1069+-- header : table of the header where each entry is one line
1070+-- content : table of the content where each entry is one line
1071+-- apply_scroll: scroll the content
1072+local function finalize_page(header, content, apply_scroll)
1073+ local term_width = o.term_width_limit or 80
1074+ local term_height = o.term_height_limit or 24
1075+ local from, to = 1, #content
1076+ if apply_scroll and term_height > 0 then
1077+ -- Up to 40 lines for libass because it can put a big performance toll on
1078+ -- libass to process many lines which end up outside (below) the screen.
1079+ -- In the terminal reduce height by 2 for the status line (can be more then one line)
1080+ local max_content_lines = (o.use_ass and 40 or term_height - 2) - #header
1081+ -- in the terminal the scrolling should stop once the last line is visible
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
10151094 end
10161095
10171096 -- Returns an ASS string with "normal" stats
@@ -1023,7 +1102,7 @@ local function default_stats() · 5 unmodified lines
10231102 add_video_out(stats)
10241103 add_video(stats)
10251104 add_audio(stats)
1026− return table.concat(stats)
1105+ return finalize_page({}, stats, false)
10271106 end
10281107
10291108 -- Returns an ASS string with extended VO stats
@@ -1033,11 +1112,7 @@ local function vo_stats() · 3 unmodified lines
10331112 add_header(header)
10341113 append_perfdata(header, content, true, true)
10351114 header = {table.concat(header)}
1036−
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
1115+ return finalize_page(header, content, false)
10411116 end
10421117
10431118 local kbinfo_lines = nil
@@ -1052,12 +1127,10 @@ local function keybinding_info(after_scroll) · 8 unmodified lines
10521127 header = {table.concat(header)}
10531128
10541129 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)
10561131 end
10571132
1058− local res = nil
1059− res, page.offset = compose_page(header, kbinfo_lines, page.offset)
1060− return res
1133+ return finalize_page(header, kbinfo_lines, true)
10611134 end
10621135
10631136 local function perf_stats()
@@ -1068,9 +1141,7 @@ local function perf_stats() · 4 unmodified lines
10681141 append(header, "", {prefix=page.desc .. ":", nl="", indent=""})
10691142 append_general_perfdata(content)
10701143 header = {table.concat(header)}
1071− local res = nil
1072− res, page.offset = compose_page(header, content, page.offset)
1073− return res
1144+ return finalize_page(header, content, true)
10741145 end
10751146
10761147 local function opt_time(t)
@@ -1091,7 +1162,7 @@ local function cache_stats() · 14 unmodified lines
10911162 local info = mp.get_property_native("demuxer-cache-state")
10921163 if info == nil then
10931164 append(stats, "Unavailable.", {})
1094− return table.concat(stats)
1165+ return finalize_page({}, stats, false)
10951166 end
10961167
10971168 local a = info["reader-pts"]
@@ -1169,7 +1240,7 @@ local function cache_stats() · 71 unmodified lines
11691240 {prefix = format("Range %s:", n)})
11701241 end
11711242
1172− return table.concat(stats)
1243+ return finalize_page({}, stats, false)
11731244 end
11741245
11751246 -- 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