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
|
if exists('g:polyglot_disabled') && index(g:polyglot_disabled, 'csv') != -1
finish
endif
" A simple syntax highlighting, simply alternate colors between two
" adjacent columns
" Init {{{2
let s:cpo_save = &cpo
set cpo&vim
scriptencoding utf8
if version < 600
syn clear
elseif exists("b:current_syntax")
finish
endif
" Helper functions "{{{2
fu! <sid>Warning(msg) "{{{3
" Don't redraw, so we are not overwriting messages from the ftplugin
" script
echohl WarningMsg
echomsg "CSV Syntax:" . a:msg
echohl Normal
endfu
fu! <sid>Esc(val, char) "{{{3
if empty(a:val)
return a:val
endif
return '\V'.escape(a:val, '\\'.a:char).'\m'
endfu
fu! <sid>CheckSaneSearchPattern() "{{{3
let s:del_def = ','
let s:col_def = '\%([^' . s:del_def . ']*' . s:del_def . '\|$\)'
let s:col_def_end = '\%([^' . s:del_def . ']*' . s:del_def . '\)'
" First:
" Check for filetype plugin. This syntax script relies on the filetype
" plugin, else, it won't work properly.
redir => s:a |sil filetype | redir end
let s:a=split(s:a, "\n")[0]
if match(s:a, '\cplugin:off') > 0
call <sid>Warning("No filetype support, only simple highlighting using"
\ . s:del_def . " as delimiter! See :h csv-installation")
endif
" Check Comment setting
if !exists("g:csv_comment")
let b:csv_cmt = split(&cms, '%s')
elseif match(g:csv_comment, '%s') >= 0
let b:csv_cmt = split(g:csv_comment, '%s')
else
let b:csv_cmt = [g:csv_comment]
endif
" Second: Check for sane defaults for the column pattern
" Not necessary to check for fixed width columns
if exists("b:csv_fixed_width_cols")
return
endif
" Try a simple highlighting, if the defaults from the ftplugin
" don't exist
let s:col = get(b:, 'col', s:col_def)
let s:col_end = get(b:, 'col_end', s:col_def_end)
let s:del = get(b:, 'delimiter', s:del_def)
let s:cmts = b:csv_cmt[0]
let s:cmte = len(b:csv_cmt) == 2 ? b:csv_cmt[1] : ''
" Make the file start at the first actual CSV record (issue #71)
if !exists("b:csv_headerline")
let cmts = <sid>Esc(s:cmts, '')
let pattern = '\%^\(\%('.cmts.'.*\n\)\|\%(\s*\n\)\)\+'
let start = search(pattern, 'nWe', 10)
" don't do it, on an empty file
if start > 0 && !empty(getline(start))
let b:csv_headerline = start+1
endif
endif
" escape '/' for syn match command
let s:cmts=<sid>Esc(s:cmts, '/')
let s:cmte=<sid>Esc(s:cmte, '/')
if line('$') > 1 && (!exists("b:col") || empty(b:col))
" check for invalid pattern, ftplugin hasn't been loaded yet
call <sid>Warning("Invalid column pattern, using default pattern " . s:col_def)
endif
endfu
" Syntax rules {{{2
fu! <sid>DoHighlight() "{{{3
if has("conceal") && !exists("g:csv_no_conceal") &&
\ !exists("b:csv_fixed_width_cols")
exe "syn match CSVDelimiter /" . s:col_end .
\ '/ms=e,me=e contained conceal cchar=' .
\ (&enc == "utf-8" ? "│" : '|')
hi def link CSVDelimiter Conceal
elseif !exists("b:csv_fixed_width_cols")
" The \%(.\)\@<= makes sure, the last char won't be concealed,
" if it isn't a delimiter
exe "syn match CSVDelimiter /" . s:col_end . '/ms=e,me=e contained'
if has("conceal")
hi def link CSVDelimiter Conceal
else
hi def link CSVDelimiter Ignore
endif
endif " There is no delimiter for csv fixed width columns
if !exists("b:csv_fixed_width_cols")
exe 'syn match CSVColumnEven nextgroup=CSVColumnOdd /'
\ . s:col . '/ contains=CSVDelimiter'
exe 'syn match CSVColumnOdd nextgroup=CSVColumnEven /'
\ . s:col . '/ contains=CSVDelimiter'
exe 'syn match CSVColumnHeaderEven nextgroup=CSVColumnHeaderOdd /\%<'. (get(b:, 'csv_headerline', 1)+1).'l'
\. s:col . '/ contains=CSVDelimiter'
exe 'syn match CSVColumnHeaderOdd nextgroup=CSVColumnHeaderEven /\%<'. (get(b:, 'csv_headerline', 1)+1).'l'
\. s:col . '/ contains=CSVDelimiter'
else
for i in range(len(b:csv_fixed_width_cols))
let pat = '/\%' . b:csv_fixed_width_cols[i] . 'v.*' .
\ ((i == len(b:csv_fixed_width_cols)-1) ? '/' :
\ '\%' . b:csv_fixed_width_cols[i+1] . 'v/')
let group = "CSVColumn" . (i%2 ? "Odd" : "Even" )
let ngroup = "CSVColumn" . (i%2 ? "Even" : "Odd" )
exe "syn match " group pat " nextgroup=" . ngroup
endfor
endif
" Comment regions
exe 'syn match CSVComment /'. s:cmts. '.*'.
\ (!empty(s:cmte) ? '\%('. s:cmte. '\)\?'
\: ''). '/'
hi def link CSVComment Comment
endfun
fu! <sid>HiLink(name, target) "{{{3
if !hlexists(a:name)
exe "hi def link" a:name a:target
endif
endfu
fu! <sid>DoSyntaxDefinitions() "{{{3
syn spell toplevel
" Not really needed
syn case ignore
call <sid>HiLink("CSVColumnHeaderOdd", "WarningMsg")
call <sid>HiLink("CSVColumnHeaderEven", "WarningMsg")
if get(g:, 'csv_no_column_highlight', 0)
call <sid>HiLink("CSVColumnOdd", "Normal")
call <sid>HiLink("CSVColumnEven", "Normal")
else
call <sid>HiLink("CSVColumnOdd", "String")
call <sid>HiLink("CSVColumnEven","Statement")
endif
endfun
" Main: {{{2
" Make sure, we are using a sane, valid pattern for syntax
" highlighting
call <sid>CheckSaneSearchPattern()
" Define all necessary syntax groups
call <sid>DoSyntaxDefinitions()
" Highlight the file
call <sid>DoHighlight()
" Set the syntax variable {{{2
let b:current_syntax="csv"
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: set foldmethod=marker et sw=0 sts=-1 ts=4:
|