summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristoph Heinrich <christoph.heinrich@student.tugraz.at>2023-08-28 23:39:34 +0200
committerKacper Michajłow <kasper93@gmail.com>2024-03-21 03:08:52 +0100
commit777f69bee8aceadcd319edf046535e177ef43d48 (patch)
tree83582a96679f1774bc32b0c002e7354a38e50378
parent344ac5501de35cd90a2a79e7baf599209d0e7572 (diff)
downloadmpv-777f69bee8aceadcd319edf046535e177ef43d48.tar.bz2
mpv-777f69bee8aceadcd319edf046535e177ef43d48.tar.xz
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.
-rw-r--r--DOCS/man/stats.rst12
-rw-r--r--player/lua/stats.lua131
2 files changed, 113 insertions, 30 deletions
diff --git a/DOCS/man/stats.rst b/DOCS/man/stats.rst
index 88238bcf93..7ddf851567 100644
--- a/DOCS/man/stats.rst
+++ b/DOCS/man/stats.rst
@@ -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
diff --git a/player/lua/stats.lua b/player/lua/stats.lua
index 62a1ff295f..33380c9842 100644
--- a/player/lua/stats.lua
+++ b/player/lua/stats.lua
@@ -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.