mirror of https://github.com/mpv-player/mpv
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.
This commit is contained in:
parent
344ac5501d
commit
777f69bee8
|
@ -85,6 +85,18 @@ Configurable Options
|
|||
respective duration. This can result in overlapping text when multiple
|
||||
scripts decide to print text at the same time.
|
||||
|
||||
``term_width_limit``
|
||||
Default: -1
|
||||
|
||||
Sets the terminal width.
|
||||
A value of 0 means the width is infinite, -1 means it's automatic.
|
||||
|
||||
``term_height_limit``
|
||||
Default: -1
|
||||
|
||||
Sets the terminal height.
|
||||
A value of 0 means the height is infinite, -1 means it's automatic.
|
||||
|
||||
``plot_perfdata``
|
||||
Default: yes
|
||||
|
||||
|
|
|
@ -29,7 +29,12 @@ local o = {
|
|||
persistent_overlay = false, -- whether the stats can be overwritten by other output
|
||||
print_perfdata_passes = false, -- when true, print the full information about all passes
|
||||
filter_params_max_length = 100, -- a filter list longer than this many characters will be shown one filter per line instead
|
||||
<<<<<<< HEAD
|
||||
show_frame_info = false, -- whether to show the current frame info
|
||||
=======
|
||||
term_width_limit = -1, -- overwrites the terminal width
|
||||
term_height_limit = -1, -- overwrites the terminal height
|
||||
>>>>>>> 89493c38fb (stats.lua: truncate long lines for the terminal)
|
||||
debug = false,
|
||||
|
||||
-- Graph options and style
|
||||
|
@ -83,6 +88,15 @@ local o = {
|
|||
}
|
||||
options.read_options(o)
|
||||
|
||||
o.term_width_limit = tonumber(o.term_width_limit) or -1
|
||||
o.term_height_limit = tonumber(o.term_height_limit) or -1
|
||||
if o.term_width_limit < 0 then
|
||||
o.term_width_limit = nil
|
||||
end
|
||||
if o.term_height_limit < 0 then
|
||||
o.term_height_limit = nil
|
||||
end
|
||||
|
||||
local format = string.format
|
||||
local max = math.max
|
||||
local min = math.min
|
||||
|
@ -366,7 +380,7 @@ end
|
|||
|
||||
local function ellipsis(s, maxlen)
|
||||
if not maxlen or s:len() <= maxlen then return s end
|
||||
return s:sub(1, maxlen - 3) .. "..."
|
||||
return s:sub(1, max(0, maxlen - 3)) .. "..."
|
||||
end
|
||||
|
||||
-- command prefix tokens to strip - includes generic property commands
|
||||
|
@ -994,24 +1008,89 @@ local function eval_ass_formatting()
|
|||
end
|
||||
end
|
||||
|
||||
-- assumptions:
|
||||
-- s is composed of SGR escape sequences and/or printable UTF8 sequences
|
||||
-- printable codepoints occupy one terminal cell (we don't have wcwidth)
|
||||
-- tabstops are 8, 16, 24..., and the output starts at 0 or a tabstop
|
||||
-- note: if maxwidth <= 2 and s doesn't fit: the result is "..." (more than 2)
|
||||
function term_ellipsis(s, maxwidth)
|
||||
local TAB, ESC, SGR_END = 9, 27, ("m"):byte()
|
||||
local width, ellipsis = 0, "..."
|
||||
local fit_len, in_sgr
|
||||
|
||||
for i = 1, #s do
|
||||
local x = s:byte(i)
|
||||
|
||||
if in_sgr then
|
||||
in_sgr = x ~= SGR_END
|
||||
elseif x == ESC then
|
||||
in_sgr = true
|
||||
ellipsis = "\27[0m..." -- ensure SGR reset
|
||||
elseif x < 128 or x >= 192 then -- non UTF8-continuation
|
||||
-- tab adds till the next stop, else add 1
|
||||
width = width + (x == TAB and 8 - width % 8 or 1)
|
||||
|
||||
if fit_len == nil and width > maxwidth - 3 then
|
||||
fit_len = i - 1 -- adding "..." still fits maxwidth
|
||||
end
|
||||
if width > maxwidth then
|
||||
return s:sub(1, fit_len) .. ellipsis
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return s
|
||||
end
|
||||
|
||||
local function term_ellipsis_array(arr, from, to, max_width)
|
||||
for i = from, to do
|
||||
arr[i] = term_ellipsis(arr[i], max_width)
|
||||
end
|
||||
return arr
|
||||
end
|
||||
|
||||
-- split str into a table
|
||||
-- example: local t = split(s, "\n")
|
||||
-- plain: whether pat is a plain string (default false - pat is a pattern)
|
||||
local function split(str, pat, plain)
|
||||
local init = 1
|
||||
local r, i, find, sub = {}, 1, string.find, string.sub
|
||||
repeat
|
||||
local f0, f1 = find(str, pat, init, plain)
|
||||
r[i], i = sub(str, init, f0 and f0 - 1), i+1
|
||||
init = f0 and f1 + 1
|
||||
until f0 == nil
|
||||
return r
|
||||
end
|
||||
|
||||
-- Composes the output with header and scrollable content
|
||||
-- Returns string of the finished page and the actually chosen offset
|
||||
--
|
||||
-- header : table of the header where each entry is one line
|
||||
-- content : table of the content where each entry is one line
|
||||
-- offset : the desired scroll offset of the content from the first line at the top
|
||||
local function compose_page(header, content, offset)
|
||||
-- up to 22 lines for the terminal - so that mpv can also print
|
||||
-- the status line without scrolling, and up to 40 lines for libass
|
||||
-- because it can put a big performance toll on libass to process
|
||||
-- many lines which end up outside (below) the screen.
|
||||
local max_content_lines = (o.use_ass and 40 or 22) - #header
|
||||
-- in the terminal the scrolling should stop once the last line is visible
|
||||
local max_offset = o.use_ass and #content or #content - max_content_lines + 1
|
||||
local from = max(1, min((offset or 1), max_offset))
|
||||
local to = min(#content, from + max_content_lines - 1)
|
||||
return table.concat(header) .. table.concat(content, "", from, to), from
|
||||
-- header : table of the header where each entry is one line
|
||||
-- content : table of the content where each entry is one line
|
||||
-- apply_scroll: scroll the content
|
||||
local function finalize_page(header, content, apply_scroll)
|
||||
local term_width = o.term_width_limit or 80
|
||||
local term_height = o.term_height_limit or 24
|
||||
local from, to = 1, #content
|
||||
if apply_scroll and term_height > 0 then
|
||||
-- Up to 40 lines for libass because it can put a big performance toll on
|
||||
-- libass to process many lines which end up outside (below) the screen.
|
||||
-- In the terminal reduce height by 2 for the status line (can be more then one line)
|
||||
local max_content_lines = (o.use_ass and 40 or term_height - 2) - #header
|
||||
-- in the terminal the scrolling should stop once the last line is visible
|
||||
local max_offset = o.use_ass and #content or #content - max_content_lines + 1
|
||||
from = max(1, min((pages[curr_page].offset or 1), max_offset))
|
||||
to = min(#content, from + max_content_lines - 1)
|
||||
pages[curr_page].offset = from
|
||||
end
|
||||
local output = table.concat(header) .. table.concat(content, "", from, to)
|
||||
if not o.use_ass and term_width > 0 and curr_page ~= o.key_page_4 then
|
||||
local t = split(output, "\n", true)
|
||||
-- limit width for the terminal
|
||||
output = table.concat(term_ellipsis_array(t, 1, #t, term_width), "\n")
|
||||
end
|
||||
return output, from
|
||||
end
|
||||
|
||||
-- Returns an ASS string with "normal" stats
|
||||
|
@ -1023,7 +1102,7 @@ local function default_stats()
|
|||
add_video_out(stats)
|
||||
add_video(stats)
|
||||
add_audio(stats)
|
||||
return table.concat(stats)
|
||||
return finalize_page({}, stats, false)
|
||||
end
|
||||
|
||||
-- Returns an ASS string with extended VO stats
|
||||
|
@ -1033,11 +1112,7 @@ local function vo_stats()
|
|||
add_header(header)
|
||||
append_perfdata(header, content, true, true)
|
||||
header = {table.concat(header)}
|
||||
|
||||
local page = pages[o.key_page_2]
|
||||
local res = nil
|
||||
res, page.offset = compose_page(header, content, page.offset)
|
||||
return res
|
||||
return finalize_page(header, content, false)
|
||||
end
|
||||
|
||||
local kbinfo_lines = nil
|
||||
|
@ -1052,12 +1127,10 @@ local function keybinding_info(after_scroll)
|
|||
header = {table.concat(header)}
|
||||
|
||||
if not kbinfo_lines or not after_scroll then
|
||||
kbinfo_lines = get_kbinfo_lines()
|
||||
kbinfo_lines = get_kbinfo_lines(o.term_width_limit)
|
||||
end
|
||||
|
||||
local res = nil
|
||||
res, page.offset = compose_page(header, kbinfo_lines, page.offset)
|
||||
return res
|
||||
return finalize_page(header, kbinfo_lines, true)
|
||||
end
|
||||
|
||||
local function perf_stats()
|
||||
|
@ -1068,9 +1141,7 @@ local function perf_stats()
|
|||
append(header, "", {prefix=page.desc .. ":", nl="", indent=""})
|
||||
append_general_perfdata(content)
|
||||
header = {table.concat(header)}
|
||||
local res = nil
|
||||
res, page.offset = compose_page(header, content, page.offset)
|
||||
return res
|
||||
return finalize_page(header, content, true)
|
||||
end
|
||||
|
||||
local function opt_time(t)
|
||||
|
@ -1091,7 +1162,7 @@ local function cache_stats()
|
|||
local info = mp.get_property_native("demuxer-cache-state")
|
||||
if info == nil then
|
||||
append(stats, "Unavailable.", {})
|
||||
return table.concat(stats)
|
||||
return finalize_page({}, stats, false)
|
||||
end
|
||||
|
||||
local a = info["reader-pts"]
|
||||
|
@ -1169,7 +1240,7 @@ local function cache_stats()
|
|||
{prefix = format("Range %s:", n)})
|
||||
end
|
||||
|
||||
return table.concat(stats)
|
||||
return finalize_page({}, stats, false)
|
||||
end
|
||||
|
||||
-- Record 1 sample of cache statistics.
|
||||
|
|
Loading…
Reference in New Issue