-
Notifications
You must be signed in to change notification settings - Fork 64
Expand file tree
/
Copy pathvimcolor
More file actions
executable file
·242 lines (215 loc) · 6.05 KB
/
vimcolor
File metadata and controls
executable file
·242 lines (215 loc) · 6.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
#!/usr/bin/env bash
#
# Syntax highlight text using Vim
# Script based on work by Geoff Richards and Randy Stauner.
# This software is copyright (c) 2026 by Wolfgang Friebel.
#
# License: GPL-2.0-or-later
set -euo pipefail
usage() {
cat <<'EOF'
usage: vimcolor [-c] [-l language] [filename]
usage: vimcolor -h
usage: vimcolor -L [pattern]
This program works by running the Vim text editor and getting it to apply its
syntax highlighting (aka 'font-locking') to an input file, and mark pieces of
text according to whether it thinks they are comments, keywords, strings, etc.
The script then reads back this markup and converts it to text marked with
ANSI escape sequences based on the Vim syntax coloring of the input file.
OPTIONS:
-c report color settings at the end of the program
-h print this help and exit
-l specify the type of file Vim should expect, if not recognized correctly
-L list supported languages (file types) [that match pattern] and exit
ARGUMENT:
filename name of the file to colorize. If not given or - then STDIN is used
A color scheme set in vim is respected. You can alter the color scheme using
the VIMCOLOR_ANSI environment variable in the format of "SynGroup=color;"
For example:
VIMCOLOR_ANSI='Comment=green;Statement = magenta; '
If you want to benefit from finer-grained syntax highlighting you can
request in VIMCOLOR_ANSI additional syntax groups and chose appropriate colors.
EOF
exit 0
}
set_defaults() {
local ex=vim
command -v nvim > /dev/null && ex=nvim
if [[ "$ex" == "vim" ]]; then
VIM_OPTIONS=(--not-a-term -XZ -R -i NONE -u NONE -N -n "+set nomodeline")
else
VIM_OPTIONS=(--headless -R -i NONE -u NONE -N -n "+set nomodeline")
fi
VIM_COMMAND="$ex"
}
languages() {
local pat="${1:-}"
local cmds typefile
cmds="$(mktemp "$TDIR/cmdsXXXX")"
typefile="$(mktemp "$TDIR/typesXXXX")"
{
printf "redir! >%s|set columns=10000|echo getcompletion('%s', 'filetype')|q!\n" \
"$typefile" "$pat"
} >"$cmds"
"$VIM_COMMAND" "${VIM_OPTIONS[@]}" -S "$cmds" "$typefile" >/dev/null 2>&1 || true
{
read -r _ || true
read -r line || true
line="${line//[\[\]\',]/}" # protect '
printf '%s\n' "$line"
} <"$typefile"
rm -f "$cmds" "$typefile"
exit 0
}
# Option processing
REPORT_COLORS=0
FILETYPE=""
LIST_LANG=""
while getopts ":chl:L" opt; do
case "$opt" in
c) REPORT_COLORS=1 ;;
h) usage ;;
l) FILETYPE="$OPTARG" ;;
L) LIST_LANG=1 ;;
\?) echo "Unknown option: -$OPTARG" >&2; usage ;;
:) echo "Option -$OPTARG requires an argument." >&2; exit 1 ;;
esac
done
shift $((OPTIND - 1))
# Tempdir & Cleanup
TDIR="${TMPDIR:-/tmp}"
TDIR="$(mktemp -d "$TDIR/vimcolorXXXX")"
trap 'rm -rf "$TDIR"; printf "\n" >&2; exit 1' INT TERM HUP
trap 'rm -rf "$TDIR"' EXIT
set_defaults
if [[ -n "$LIST_LANG" ]]; then
languages "$*"
fi
FILE="${1:-}"
if [[ -z "$FILE" || "$FILE" == "-" ]]; then
FILE="$(mktemp "$TDIR/inputXXXX")"
cat >"$FILE"
fi
cmds="$(mktemp "$TDIR/cmdsXXXX")"
# nvim does not output color codes if --headless is used
colorcontent=$(vim --not-a-term -XZ +hi +qa)
#$(nvim --headless +hi +q -- > "$colorfile" 2>&1)
declare -A ATTRS
colorcontent=${colorcontent//$'\033'\[[0-9]C/ }
colorcontent=${colorcontent//$'\015'/X}
RESET=$'\033[0m'
IFS=
while [[ "$colorcontent" =~ X ]]; do
prefix="${colorcontent%%X*}"
colorcontent="${colorcontent#"$prefix"}"
key="${colorcontent%%XX*}"
key="${key%%xxx*}"
key="${key#X }"
color="${key#* }"
key="${key% "$color"}"
key="${key//X$'\n'/}"
colorcontent="${colorcontent#*X}"
[[ "$key" == [A-Z]* && -n $key && -n $color ]] || continue
ATTRS[$key]=$color
#echo :$color:$key:$RESET
done
# Vim-Skript für Markup
markup_vim_script() {
cat <<'EOF'
set report=1000000
if !strlen(&filetype)
filetype detect
endif
syn on
new
set modifiable
set paste
set isprint+=9
wincmd p
let s:end = line("$")
let s:lnum = 1
while s:lnum <= s:end
let s:line = getline(s:lnum)
let s:len = strlen(s:line)
let s:new = ""
let s:col = 1
while s:col <= s:len
let s:startcol = s:col
let s:id = synID(s:lnum, s:col, 1)
let s:col = s:col + 1
while s:col <= s:len && s:id == synID(s:lnum, s:col, 1) | let s:col = s:col + 1 | endwhile
let s:id = synIDtrans(s:id)
let s:name = synIDattr(s:id, 'name')
let s:new = s:new . '>' . s:name . '>' .
\ substitute(substitute(substitute(
\ strpart(s:line, s:startcol - 1, s:col - s:startcol),
\ '&', '\&a', 'g'),
\ '<', '\&l', 'g'),
\ '>', '\&g', 'g') .
\ '<' . s:name . '<'
if s:col > s:len
break
endif
endwhile
exe "normal \<C-W>pa" . strtrans(s:new) . "\n\e\<C-W>p"
let s:lnum = s:lnum + 1
+
endwhile
wincmd p
normal dd
EOF
}
# Markup-Datei vorbereiten
markupfile="$(mktemp "$TDIR/markupXXXX")"
markup_vim_script >"$markupfile"
# Vim-Script (Batch) für diesen Lauf
scriptfile="$(mktemp "$TDIR/scriptXXXX")"
{
# :edit vor :let, siehe Perl-Kommentar
printf ':edit %s\n' "${FILE//\"/\\\"}"
printf ':let perl_include_pod=1\n'
printf ':let b:is_bash=1\n'
printf ':filetype on\n'
if [[ -n "$FILETYPE" ]]; then
printf ':set filetype=%s\n' "$FILETYPE"
fi
printf ':source %s\n' "$markupfile"
printf ':write! %s\n' "$TDIR/vimc.out"
printf ':qall!\n'
} >"$scriptfile"
"$VIM_COMMAND" "${VIM_OPTIONS[@]}" "$FILE" -s "$scriptfile" >/dev/null 2>&1 || true
data="$(<"$TDIR/vimc.out")"
data="${data//$'\r\n'/\&nl}"
data="${data//$'\r'/\&nl;}"
TYPES_USED=()
while [[ "$data" =~ \>([^\>]*)\>([^\<]+)\< ]]; do
prefix="${data%%>*}"
data2="${data#"$prefix">}"
name="${data2%%>*}"
esc=$RESET
if [[ -n $name ]]; then
TYPES_USED+=("$name")
esc="${ATTRS[$name]:-$RESET}"
fi
data=${data//>$name>/$esc}
data=${data//<$name</$RESET}
done
data="${data//&nl;/$'\n'}"
# Unescape ampersands and pointies, restore escape
data="${data//&l/<}"
data="${data//&g/>}"
data="${data//&a/\&}"
# get start and end of file correct
[[ "$data" != *$'\n' ]] && data+="$RESET"$'\n'
printf '%s' "$data"
if (( REPORT_COLORS == 1 )); then
printf '%s\n' "$RESET"
printf '### Report color settings:\n'
for k in "${TYPES_USED[@]}"; do
if [[ -n "${ATTRS[$k]:-}" ]]; then
printf "%-20s %s%s\n" "$k" "${ATTRS[$k]}(xxx)" "$RESET"
else
printf '%-20s color not set\n' "$k"
fi
done
fi