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 local _mdcat_print_hr
_mdcat_print_hr() _mdcat_print_hr()
{ {
local cols="${COLUMNS:-}" local cols="${_mdcat_cols:-}"
if [[ -z "$cols" || ! "$cols" =~ ^[0-9]+$ || "$cols" -lt 20 ]]; then if [[ -z "$cols" || ! "$cols" =~ ^[0-9]+$ || "$cols" -lt 20 ]]; then
cols=$(tput cols 2>/dev/null) cols=$(tput cols 2>/dev/null)
fi fi
@@ -376,7 +376,7 @@ mdcat()
shift shift
local -a lines=("$@") local -a lines=("$@")
local cols="${COLUMNS:-}" local cols="${_mdcat_cols:-}"
if [[ -z "$cols" || ! "$cols" =~ ^[0-9]+$ || "$cols" -lt 20 ]]; then if [[ -z "$cols" || ! "$cols" =~ ^[0-9]+$ || "$cols" -lt 20 ]]; then
cols=$(tput cols 2>/dev/null) cols=$(tput cols 2>/dev/null)
fi fi
@@ -472,11 +472,14 @@ mdcat()
local -a lines=("$@") local -a lines=("$@")
local -a table_rows=() local -a table_rows=()
local -a col_widths=() 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' local sep=$'\x1f'
# Parse header and data rows, skipping the Markdown separator row. # 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 for ((i=0; i<${#lines[@]}; ++i)); do
(( i == 1 )) && continue (( i == 1 )) && continue
local parsed local parsed
@@ -487,13 +490,15 @@ mdcat()
IFS="$sep" read -r -a row <<< "$parsed" IFS="$sep" read -r -a row <<< "$parsed"
(( ncols < ${#row[@]} )) && ncols=${#row[@]} (( ncols < ${#row[@]} )) && ncols=${#row[@]}
for ((j=0; j<${#row[@]}; ++j)); do for ((j=0; j<${#row[@]}; ++j)); do
local vis local styled visible key="$orow:$j"
vis=$(_mdcat_style_inline "${row[j]}") styled=$(_mdcat_style_inline "${row[j]}")
vis=$(printf '%b' "$vis" | sed -E 's/\x1B\[[0-9;]*[mK]//g') visible=$(printf '%b' "$styled" | sed -E 's/\x1B\[[0-9;]*[mK]//g')
local cell_len=${#vis} cell_styled["$key"]="$styled"
cell_vislen["$key"]=${#visible}
[[ -z "${col_widths[j]}" ]] && col_widths[j]=0 [[ -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 done
orow=$((orow + 1))
done done
# Ensure all width slots are initialized before drawing borders. # Ensure all width slots are initialized before drawing borders.
@@ -509,17 +514,12 @@ mdcat()
done done
printf "%b\n" "$IBlack$border$RESETCOL" printf "%b\n" "$IBlack$border$RESETCOL"
# Print header row # Print header row (cached styled cells)
local -a header=()
IFS="$sep" read -r -a header <<< "${table_rows[0]}"
printf "%b|" "$IBlack" printf "%b|" "$IBlack"
for ((j=0; j<ncols; ++j)); do for ((j=0; j<ncols; ++j)); do
local raw_cell="${header[j]:-}" local hkey="0:$j"
local styled_cell local styled_cell="${cell_styled[$hkey]:-}"
styled_cell=$(_mdcat_style_inline "$raw_cell") local pad=$((col_widths[j] - ${cell_vislen[$hkey]:-0}))
local visible
visible=$(printf '%b' "$styled_cell" | sed -E 's/\x1B\[[0-9;]*[mK]//g')
local pad=$((col_widths[j] - ${#visible}))
(( pad < 0 )) && pad=0 (( pad < 0 )) && pad=0
printf " %b%b%*s%b |" "$BBlue" "$styled_cell" "$pad" "" "$IBlack" printf " %b%b%*s%b |" "$BBlue" "$styled_cell" "$pad" "" "$IBlack"
done done
@@ -532,18 +532,13 @@ mdcat()
done done
printf "%b\n" "$RESETCOL" printf "%b\n" "$RESETCOL"
# Print data rows # Print data rows (cached styled cells)
for ((i=1; i<${#table_rows[@]}; ++i)); do for ((i=1; i<${#table_rows[@]}; ++i)); do
local -a row=()
IFS="$sep" read -r -a row <<< "${table_rows[i]}"
printf "%b|" "$IBlack" printf "%b|" "$IBlack"
for ((j=0; j<ncols; ++j)); do for ((j=0; j<ncols; ++j)); do
local raw_cell="${row[j]:-}" local key="$i:$j"
local styled_cell local styled_cell="${cell_styled[$key]:-}"
styled_cell=$(_mdcat_style_inline "$raw_cell") local pad=$((col_widths[j] - ${cell_vislen[$key]:-0}))
local visible
visible=$(printf '%b' "$styled_cell" | sed -E 's/\x1B\[[0-9;]*[mK]//g')
local pad=$((col_widths[j] - ${#visible}))
(( pad < 0 )) && pad=0 (( pad < 0 )) && pad=0
printf " %b%b%*s%b |" "$RESETCOL" "$styled_cell" "$pad" "" "$IBlack" printf " %b%b%*s%b |" "$RESETCOL" "$styled_cell" "$pad" "" "$IBlack"
done done
@@ -607,19 +602,42 @@ mdcat()
local -a code_lines=() local -a code_lines=()
local in_table=0 local in_table=0
local -a table_lines=() 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'}" line="${raw%$'\r'}"
# Table detection: line with |, next line with | and --- # Table detection: line with |, next line with | and ---
if [[ $in_table -eq 0 && "$line" =~ ^[[:space:]]*\|.*\|[[:space:]]*$ ]]; then if [[ $in_table -eq 0 && "$line" =~ ^[[:space:]]*\|.*\|[[:space:]]*$ ]]; then
local next local next="" next_ok=1
IFS= read -r next || true IFS= read -r next || next_ok=0
# Accept: | --- | --- | or |:---|---:| etc. # Accept: | --- | --- | or |:---|---:| etc.
if [[ "$next" =~ ^[[:space:]]*\|[[:space:]]*:?[-]+:?([[:space:]]*\|[[:space:]]*:?[ -]+:?)*\|[[:space:]]*$ ]]; then if [[ "$next" =~ ^[[:space:]]*\|[[:space:]]*:?[-]+:?([[:space:]]*\|[[:space:]]*:?[ -]+:?)*\|[[:space:]]*$ ]]; then
in_table=1 in_table=1
table_lines=("$line" "$next") table_lines=("$line" "$next")
continue continue
fi 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 fi
if [[ $in_table -eq 1 ]]; then if [[ $in_table -eq 1 ]]; then
# Accept table row if it starts and ends with | # Accept table row if it starts and ends with |
@@ -709,9 +727,13 @@ mdcat()
printf "%b\n" "$(_mdcat_style_inline "$line")" printf "%b\n" "$(_mdcat_style_inline "$line")"
done < "${input_file:-/dev/stdin}" done < "${input_file:-/dev/stdin}"
# Flush any block still open at end-of-input.
if [[ $in_code -eq 1 ]]; then if [[ $in_code -eq 1 ]]; then
_mdcat_print_code_block "$code_lang" "${code_lines[@]}" _mdcat_print_code_block "$code_lang" "${code_lines[@]}"
fi fi
if [[ $in_table -eq 1 ]]; then
_mdcat_print_table "${table_lines[@]}"
fi
} }
export -f mdcat export -f mdcat
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------