optimise mdcat and fix trruncated files rendering

This commit is contained in:
fatalerrors
2026-07-09 11:30:43 +02:00
parent 54da852ac3
commit e6607f07af

View File

@@ -353,7 +353,7 @@ mdcat()
local _mdcat_print_hr
_mdcat_print_hr()
{
local cols="${COLUMNS:-}"
local cols="${_mdcat_cols:-}"
if [[ -z "$cols" || ! "$cols" =~ ^[0-9]+$ || "$cols" -lt 20 ]]; then
cols=$(tput cols 2>/dev/null)
fi
@@ -376,7 +376,7 @@ mdcat()
shift
local -a lines=("$@")
local cols="${COLUMNS:-}"
local cols="${_mdcat_cols:-}"
if [[ -z "$cols" || ! "$cols" =~ ^[0-9]+$ || "$cols" -lt 20 ]]; then
cols=$(tput cols 2>/dev/null)
fi
@@ -472,11 +472,14 @@ mdcat()
local -a lines=("$@")
local -a table_rows=()
local -a col_widths=()
local i j ncols=0
local -A cell_styled=() cell_vislen=()
local i j ncols=0 orow=0
local sep=$'\x1f'
# Parse header and data rows, skipping the Markdown separator row.
# Width is computed from visible text length with ANSI escapes stripped.
# Each cell is styled exactly once here; the styled text and its visible
# width (ANSI stripped) are cached and reused when drawing, so the costly
# _mdcat_style_inline runs once per cell instead of twice.
for ((i=0; i<${#lines[@]}; ++i)); do
(( i == 1 )) && continue
local parsed
@@ -487,13 +490,15 @@ mdcat()
IFS="$sep" read -r -a row <<< "$parsed"
(( ncols < ${#row[@]} )) && ncols=${#row[@]}
for ((j=0; j<${#row[@]}; ++j)); do
local vis
vis=$(_mdcat_style_inline "${row[j]}")
vis=$(printf '%b' "$vis" | sed -E 's/\x1B\[[0-9;]*[mK]//g')
local cell_len=${#vis}
local styled visible key="$orow:$j"
styled=$(_mdcat_style_inline "${row[j]}")
visible=$(printf '%b' "$styled" | sed -E 's/\x1B\[[0-9;]*[mK]//g')
cell_styled["$key"]="$styled"
cell_vislen["$key"]=${#visible}
[[ -z "${col_widths[j]}" ]] && col_widths[j]=0
(( col_widths[j] < cell_len )) && col_widths[j]=$cell_len
(( col_widths[j] < ${#visible} )) && col_widths[j]=${#visible}
done
orow=$((orow + 1))
done
# Ensure all width slots are initialized before drawing borders.
@@ -509,17 +514,12 @@ mdcat()
done
printf "%b\n" "$IBlack$border$RESETCOL"
# Print header row
local -a header=()
IFS="$sep" read -r -a header <<< "${table_rows[0]}"
# Print header row (cached styled cells)
printf "%b|" "$IBlack"
for ((j=0; j<ncols; ++j)); do
local raw_cell="${header[j]:-}"
local styled_cell
styled_cell=$(_mdcat_style_inline "$raw_cell")
local visible
visible=$(printf '%b' "$styled_cell" | sed -E 's/\x1B\[[0-9;]*[mK]//g')
local pad=$((col_widths[j] - ${#visible}))
local hkey="0:$j"
local styled_cell="${cell_styled[$hkey]:-}"
local pad=$((col_widths[j] - ${cell_vislen[$hkey]:-0}))
(( pad < 0 )) && pad=0
printf " %b%b%*s%b |" "$BBlue" "$styled_cell" "$pad" "" "$IBlack"
done
@@ -532,18 +532,13 @@ mdcat()
done
printf "%b\n" "$RESETCOL"
# Print data rows
# Print data rows (cached styled cells)
for ((i=1; i<${#table_rows[@]}; ++i)); do
local -a row=()
IFS="$sep" read -r -a row <<< "${table_rows[i]}"
printf "%b|" "$IBlack"
for ((j=0; j<ncols; ++j)); do
local raw_cell="${row[j]:-}"
local styled_cell
styled_cell=$(_mdcat_style_inline "$raw_cell")
local visible
visible=$(printf '%b' "$styled_cell" | sed -E 's/\x1B\[[0-9;]*[mK]//g')
local pad=$((col_widths[j] - ${#visible}))
local key="$i:$j"
local styled_cell="${cell_styled[$key]:-}"
local pad=$((col_widths[j] - ${cell_vislen[$key]:-0}))
(( pad < 0 )) && pad=0
printf " %b%b%*s%b |" "$RESETCOL" "$styled_cell" "$pad" "" "$IBlack"
done
@@ -607,19 +602,42 @@ mdcat()
local -a code_lines=()
local in_table=0
local -a table_lines=()
while IFS= read -r raw || [[ -n "$raw" ]]; do
# Resolve the terminal width once for the whole render instead of shelling
# out to tput for every printed line.
local _mdcat_cols="${COLUMNS:-}"
if [[ -z "$_mdcat_cols" || ! "$_mdcat_cols" =~ ^[0-9]+$ || "$_mdcat_cols" -lt 20 ]]; then
_mdcat_cols=$(tput cols 2>/dev/null)
fi
[[ -z "$_mdcat_cols" || ! "$_mdcat_cols" =~ ^[0-9]+$ || "$_mdcat_cols" -lt 20 ]] && _mdcat_cols=80
# Single-line push-back buffer: table detection reads one line ahead, so a
# non-separator look-ahead line must be reprocessed rather than discarded.
local pending_line="" has_pending=0
while :; do
if [[ $has_pending -eq 1 ]]; then
raw="$pending_line"
has_pending=0
elif ! IFS= read -r raw; then
[[ -n "$raw" ]] || break
fi
line="${raw%$'\r'}"
# Table detection: line with |, next line with | and ---
if [[ $in_table -eq 0 && "$line" =~ ^[[:space:]]*\|.*\|[[:space:]]*$ ]]; then
local next
IFS= read -r next || true
local next="" next_ok=1
IFS= read -r next || next_ok=0
# Accept: | --- | --- | or |:---|---:| etc.
if [[ "$next" =~ ^[[:space:]]*\|[[:space:]]*:?[-]+:?([[:space:]]*\|[[:space:]]*:?[ -]+:?)*\|[[:space:]]*$ ]]; then
in_table=1
table_lines=("$line" "$next")
continue
fi
# Not a separator: push the look-ahead line back so it is not lost.
if [[ $next_ok -eq 1 || -n "$next" ]]; then
pending_line="$next"
has_pending=1
fi
fi
if [[ $in_table -eq 1 ]]; then
# Accept table row if it starts and ends with |
@@ -709,9 +727,13 @@ mdcat()
printf "%b\n" "$(_mdcat_style_inline "$line")"
done < "${input_file:-/dev/stdin}"
# Flush any block still open at end-of-input.
if [[ $in_code -eq 1 ]]; then
_mdcat_print_code_block "$code_lang" "${code_lines[@]}"
fi
if [[ $in_table -eq 1 ]]; then
_mdcat_print_table "${table_lines[@]}"
fi
}
export -f mdcat
# ------------------------------------------------------------------------------