| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116 |
- " Filetype plugin for editing CSV files. "{{{1
- " Author: Christian Brabandt <cb@256bit.org>
- " Version: 0.26
- " Script: http://www.vim.org/scripts/script.php?script_id=2830
- " License: VIM License
- " Last Change: Wed, 25 Jul 2012 22:22:28 +0200
- " Documentation: see :help ft-csv.txt
- " GetLatestVimScripts: 2830 25 :AutoInstall: csv.vim
- "
- " Some ideas are taken from the wiki http://vim.wikia.com/wiki/VimTip667
- " though, implementation differs.
- " Plugin folklore "{{{2
- if v:version < 700 || exists('b:did_ftplugin')
- finish
- endif
- let b:did_ftplugin = 1
- let s:cpo_save = &cpo
- set cpo&vim
- " Function definitions: "{{{2
- fu! <sid>Warn(mess) "{{{3
- echohl WarningMsg
- echomsg "CSV: " . a:mess
- echohl Normal
- endfu
- fu! <sid>Init() "{{{3
- " Hilight Group for Columns
- if exists("g:csv_hiGroup")
- let s:hiGroup = g:csv_hiGroup
- else
- let s:hiGroup="WildMenu"
- endif
- if !exists("g:csv_hiHeader")
- let s:hiHeader = "Title"
- else
- let s:hiHeader = g:csv_hiHeader
- endif
- exe "hi link CSVHeaderLine" s:hiHeader
- " Determine default Delimiter
- if !exists("g:csv_delim")
- let b:delimiter=<SID>GetDelimiter()
- else
- let b:delimiter=g:csv_delim
- endif
- " Define custom commentstring
- if !exists("g:csv_comment")
- let b:csv_cmt = split(&cms, '%s')
- else
- let b:csv_cmt = split(g:csv_comment, '%s')
- endif
- if empty(b:delimiter) && !exists("b:csv_fixed_width")
- call <SID>Warn("No delimiter found. See :h csv-delimiter to set it manually!")
- " Use a sane default as delimiter:
- let b:delimiter = ','
- endif
- let s:del='\%(' . b:delimiter . '\|$\)'
- " Pattern for matching a single column
- if !exists("g:csv_strict_columns") && !exists("g:csv_col")
- \ && !exists("b:csv_fixed_width")
- " - Allow double quotes as escaped quotes only insides double quotes
- " - Allow linebreaks only, if g:csv_nl isn't set (this is
- " only allowed in double quoted strings see RFC4180), though this
- " does not work with :WhatColumn and might mess up syntax
- " highlighting.
- " - optionally allow whitespace in front of the fields (to make it
- " work with :ArrangeCol (that is actually not RFC4180 valid))
- " - Should work with most ugly solutions that are available
- let b:col='\%(\%(\%(' . (b:delimiter !~ '\s' ? '\s*' : '') .
- \ '"\%(' . (exists("g:csv_nl") ? '\_' : '' ) .
- \ '[^"]\|""\)*"\)' . s:del . '\)\|\%(' .
- \ '[^' . b:delimiter . ']*' . s:del . '\)\)'
- elseif !exists("g:csv_col") && exists("g:csv_strict_columns")
- " strict columns
- let b:col='\%([^' . b:delimiter . ']*' . s:del . '\)'
- elseif exists("b:csv_fixed_width")
- " Fixed width column
- let b:col=''
- " Check for sane default
- if b:csv_fixed_width =~? '[^0-9,]'
- call <sid>Warn("Please specify the list of character columns" .
- \ "like this: '1,3,5'. See also :h csv-fixedwidth")
- return
- endif
- let b:csv_fixed_width_cols=split(b:csv_fixed_width, ',')
- " Force evaluating as numbers
- call map(b:csv_fixed_width_cols, 'v:val+0')
- else
- " User given column definition
- let b:col = g:csv_col
- endif
-
- " set filetype specific options
- call <sid>LocalSettings('all')
- " define buffer-local commands
- call <SID>CommandDefinitions()
- " Check Header line
- " Defines which line is considered to be a header line
- call <sid>CheckHeaderLine()
- " CSV specific mappings
- call <SID>CSVMappings()
- " force reloading CSV Syntax Highlighting
- if exists("b:current_syntax")
- unlet b:current_syntax
- " Force reloading syntax file
- endif
- call <sid>DoAutoCommands()
- " enable CSV Menu
- call <sid>Menu(1)
- call <sid>DisableFolding()
- silent do Syntax
- " Remove configuration variables
- let b:undo_ftplugin .= "| unlet! b:delimiter b:col"
- \ . "| unlet! b:csv_fixed_width_cols b:csv_filter"
- \ . "| unlet! b:csv_fixed_width b:csv_list b:col_width"
- \ . "| unlet! b:csv_SplitWindow b:csv_headerline"
- \ . "| unlet! b:csv_thousands_sep b:csv_decimal_sep"
- \. " | unlet! b:browsefilter"
- " Delete all functions
- " disabled currently, because otherwise when switching ft
- " I think, all functions need to be read in again and this
- " costs time.
- "
- " let b:undo_ftplugin .= "| delf <sid>Warn | delf <sid>Init |
- " \ delf <sid>GetPat | delf <sid>SearchColumn | delf <sid>DelColumn |
- " \ delf <sid>HiCol | delf <sid>GetDelimiter | delf <sid>WColumn |
- " \ delf <sid>MaxColumns | delf <sid>ColWidth | delf <sid>ArCol |
- " \ delf <sid>PrepUnArCol | delf <sid>UnArCol |
- " \ delf <sid>CalculateColumnWidth | delf <sid>Columnize |
- " \ delf <sid>GetColPat | delf <sid>SplitHeaderLine |
- " \ delf <sid>SplitHeaderToggle | delf <sid>MoveCol |
- " \ delf <sid>SortComplete | delf <sid>SortList | delf <sid>Sort |
- " \ delf CSV_WCol | delf <sid>CopyCol | delf <sid>MoveColumn |
- " \ delf <sid>SumColumn csv#EvalColumn | delf <sid>DoForEachColumn |
- " \ delf <sid>PrepareDoForEachColumn | delf <sid>CSVMappings |
- " \ delf <sid>Map | delf <sid>EscapeValue | delf <sid>FoldValue |
- " \ delf <sid>PrepareFolding | delf <sid>OutputFilters |
- " \ delf <sid>SortFilter | delf <sid>GetColumn |
- " \ delf <sid>RemoveLastItem | delf <sid>DisableFolding |
- " \ delf <sid>GetSID | delf <sid>CheckHeaderLine |
- " \ delf <sid>AnalyzeColumn | delf <sid>Vertfold |
- " \ delf <sid>InitCSVFixedWidth | delf <sid>LocalCmd |
- " \ delf <sid>CommandDefinitions | delf <sid>NumberFormat |
- " \ delf <sid>NewRecord | delf <sid>MoveOver | delf <sid>Menu |
- " \ delf <sid>NewDelimiter | delf <sid>DuplicateRows | delf <sid>IN |
- " \ delf <sid>SaveOptions | delf <sid>CheckDuplicates |
- " \ delf <sid>CompleteColumnNr | delf <sid>CSVPat | delf <sid>Transpose |
- " \ delf <sid>LocalSettings()
- endfu
- fu! <sid>LocalSettings(type) "{{{3
- if a:type == 'all'
- " CSV local settings
- setl nostartofline tw=0 nowrap
- " undo when setting a new filetype
- let b:undo_ftplugin = "setlocal sol& tw< wrap<"
- " Set browsefilter
- if (v:version > 703 || (v:version == 703 && has("patch593")))
- \ && exists("browsefilter")
- let b:browsefilter="CSV Files (*.csv, *.dat)\t*.csv;*.dat\n".
- \ "All Files\t*.*\n"
- endif
- if has("conceal")
- setl cole=2 cocu=nc
- let b:undo_ftplugin .= '| setl cole< cocu< '
- endif
- endif
- if a:type == 'all' || a:type == 'fold'
- " Be sure to also fold away single screen lines
- setl fen fdm=expr fdl=0 fdc=2 fml=0
- let &foldtext=strlen(v:folddashes) . ' lines hidden'
- setl fillchars-=fold:-
- " undo settings:
- let b:undo_ftplugin .=
- \ "| setl fen< fdm< fdl< fdc< fml< fdt&vim fcs& fde<"
- endif
- endfu
- fu! <sid>DoAutoCommands() "{{{3
- " Highlight column, on which the cursor is?
- if exists("g:csv_highlight_column") && g:csv_highlight_column =~? 'y' &&
- \ !exists("#CSV_HI#CursorMoved")
- aug CSV_HI
- au!
- au CursorMoved <buffer> HiColumn
- aug end
- " Set highlighting for column, on which the cursor is currently
- HiColumn
- elseif exists("#CSV_HI#CursorMoved")
- aug CSV_HI
- au! CursorMoved <buffer>
- aug end
- aug! CSV_HI
- " Remove any existing highlighting
- HiColumn!
- endif
- " undo autocommand:
- let b:undo_ftplugin .= '| exe "sil! au! CSV_HI CursorMoved <buffer> "'
- let b:undo_ftplugin .= '| exe "sil! aug! CSV_HI" |exe "sil! HiColumn!"'
- " Visually arrange columns when opening a csv file
- if exists("g:csv_autocmd_arrange") &&
- \ !exists("#CSV_Edit#BufReadPost")
- aug CSV_Edit
- au!
- au BufReadPost,BufWritePost *.csv,*.dat :sil %ArrangeColumn
- au BufWritePre *.csv,*.dat :sil %UnArrangeColumn
- aug end
- elseif exists("#CSV_Edit#BufReadPost")
- aug CSV_Edit
- au!
- aug end
- aug! CSV_Edit
- endif
- " undo autocommand:
- let b:undo_ftplugin .= '| exe "sil! au! CSV_Edit BufRead,BufWritePost,BufWritePre *.csv,*.dat "'
- let b:undo_ftplugin .= '| exe "sil! aug! CSV_Edit"'
- if !exists("#CSV#ColorScheme")
- " Make sure, syntax highlighting is applied
- " after changing the colorscheme
- augroup CSV
- au!
- au ColorScheme *.csv,*.dat,*.tsv,*.tab do Syntax
- augroup end
- endif
- let b:undo_ftplugin .= '| exe "sil! au! CSV ColorScheme *.csv,*.dat "'
- let b:undo_ftplugin .= '| exe "sil! aug! CSV"'
- if has("gui_running") && !exists("#CSV_Menu#FileType")
- augroup CSV_Menu
- au!
- au FileType csv call <sid>Menu(1)
- au BufEnter <buffer> call <sid>Menu(1) " enable
- au BufLeave <buffer> call <sid>Menu(0) " disable
- au BufNewFile,BufNew * call <sid>Menu(0)
- augroup END
- "let b:undo_ftplugin .= '| sil! amenu disable CSV'
- let b:undo_ftplugin .= '| sil! call <sid>Menut(0)'
- endif
- endfu
- fu! <sid>GetPat(colnr, maxcolnr, pat) "{{{3
- if a:colnr > 1 && a:colnr < a:maxcolnr
- if !exists("b:csv_fixed_width_cols")
- return '^' . <SID>GetColPat(a:colnr-1,0) . '\%([^' .
- \ b:delimiter . ']\{-}\)\?\zs' . a:pat . '\ze' .
- \ '\%([^' . b:delimiter .']\{-}\)\?' .
- \ b:delimiter . <SID>GetColPat(a:maxcolnr - a:colnr, 0) .
- \ '$'
- else
- return '\%' . b:csv_fixed_width_cols[(a:colnr - 1)] . 'c\zs'
- \ . a:pat . '.\{-}\ze\%'
- \ . (b:csv_fixed_width_cols[a:colnr]) . 'c\ze'
- endif
- elseif a:colnr == a:maxcolnr
- if !exists("b:csv_fixed_width_cols")
- return '^' . <SID>GetColPat(a:colnr - 1,0) .
- \ '\%([^' . b:delimiter .
- \ ']\{-}\)\?\zs' . a:pat . '\ze'
- else
- return '\%' . b:csv_fixed_width_cols[-1] .
- \ 'c\zs' . a:pat . '\ze'
- endif
- else " colnr = 1
- if !exists("b:csv_fixed_width_cols")
- return '^' . '\%([^' . b:delimiter . ']\{-}\)\?\zs' . a:pat .
- \ '\ze\%([^' . b:delimiter . ']*\)\?' . b:delimiter .
- \ <SID>GetColPat(a:maxcolnr -1 , 0) . '$'
- else
- return a:pat . '\ze.\{-}\%' . b:csv_fixed_width_cols[1] . 'c'
- endif
- endif
- return ''
- endfu
- fu! <sid>SearchColumn(arg) "{{{3
- try
- let arglist=split(a:arg)
- if len(arglist) == 1
- let colnr=<SID>WColumn()
- let pat=substitute(arglist[0], '^\(.\)\(.*\)\1$', '\2', '')
- if pat == arglist[0]
- throw "E684"
- endif
- else
- " Determine whether the first word in the argument is a number
- " (of the column to search).
- let colnr = substitute( a:arg, '^\s*\(\d\+\)\s.*', '\1', '' )
- " If it is _not_ a number,
- if colnr == a:arg
- " treat the whole argument as the pattern.
- let pat = substitute(a:arg,
- \ '^\s*\(\S\)\(.*\)\1\s*$', '\2', '' )
- if pat == a:arg
- throw "E684"
- endif
- let colnr = <SID>WColumn()
- else
- " if the first word tells us the number of the column,
- " treat the rest of the argument as the pattern.
- let pat = substitute(a:arg,
- \ '^\s*\d\+\s*\(\S\)\(.*\)\1\s*$', '\2', '' )
- if pat == a:arg
- throw "E684"
- endif
- endif
- " let colnr=arglist[0]
- " let pat=substitute(arglist[1], '^\(.\)\(.*\)\1$', '\2', '')
- " if pat == arglist[1]
- " throw "E684"
- " endif
- endif
- "catch /^Vim\%((\a\+)\)\=:E684/
- catch /E684/ " catch error index out of bounds
- call <SID>Warn("Error! Usage :SearchInColumn [<colnr>] /pattern/")
- return 1
- endtry
- let maxcolnr = <SID>MaxColumns()
- if colnr > maxcolnr
- call <SID>Warn("There exists no column " . colnr)
- return 1
- endif
- let @/ = <sid>GetPat(colnr, maxcolnr, pat)
- try
- norm! n
- catch /^Vim\%((\a\+)\)\=:E486/
- " Pattern not found
- echohl Error
- echomsg "E486: Pattern not found in column " . colnr . ": " . pat
- if &vbs > 0
- echomsg substitute(v:exception, '^[^:]*:', '','')
- endif
- echohl Normal
- endtry
- endfu
- fu! <sid>DeleteColumn(arg) "{{{3
- let _wsv = winsaveview()
- if a:arg =~ '^[/]'
- let i = 0
- let pat = a:arg[1:]
- call cursor(1,1)
- while search(pat, 'cW')
- " Delete matching column
- sil call <sid>DelColumn('')
- let i+=1
- endw
- else
- let i = 1
- sil call <sid>DelColumn(a:arg)
- endif
- if i > 1
- call <sid>Warn(printf("%d columns deleted", i))
- else
- call <sid>Warn("1 column deleted")
- endif
- call winrestview(_wsv)
- endfu
- fu! <sid>DelColumn(colnr) "{{{3
- let maxcolnr = <SID>MaxColumns()
- let _p = getpos('.')
- if empty(a:colnr)
- let colnr=<SID>WColumn()
- else
- let colnr=a:colnr
- endif
- if colnr > maxcolnr
- call <SID>Warn("There exists no column " . colnr)
- return
- endif
- if colnr != '1'
- if !exists("b:csv_fixed_width_cols")
- let pat= '^' . <SID>GetColPat(colnr-1,1) . b:col
- else
- let pat= <SID>GetColPat(colnr,0)
- endif
- else
- " distinction between csv and fixed width does not matter here
- let pat= '^' . <SID>GetColPat(colnr,0)
- endif
- if &ro
- let ro = 1
- setl noro
- else
- let ro = 0
- endif
- exe ':%s/' . escape(pat, '/') . '//'
- call setpos('.', _p)
- if ro
- setl ro
- endif
- endfu
- fu! <sid>HiCol(colnr, bang) "{{{3
- if a:colnr > <SID>MaxColumns() && !a:bang
- call <SID>Warn("There exists no column " . a:colnr)
- return
- endif
- if !a:bang
- if empty(a:colnr)
- let colnr=<SID>WColumn()
- else
- let colnr=a:colnr
- endif
- if colnr==1
- let pat='^'. <SID>GetColPat(colnr,0)
- elseif !exists("b:csv_fixed_width_cols")
- let pat='^'. <SID>GetColPat(colnr-1,1) . b:col
- else
- let pat=<SID>GetColPat(colnr,0)
- endif
- endif
- if exists("*matchadd")
- if exists("s:matchid")
- " ignore errors, that come from already deleted matches
- sil! call matchdelete(s:matchid)
- endif
- " Additionally, filter all matches, that could have been used earlier
- let matchlist=getmatches()
- call filter(matchlist, 'v:val["group"] !~ s:hiGroup')
- call setmatches(matchlist)
- if a:bang
- return
- endif
- let s:matchid=matchadd(s:hiGroup, pat, 0)
- elseif !a:bang
- exe ":2match " . s:hiGroup . ' /' . pat . '/'
- endif
- endfu
- fu! <sid>GetDelimiter() "{{{3
- if !exists("b:csv_fixed_width_cols")
- let _cur = getpos('.')
- let _s = @/
- let Delim= {0: ';', 1: ',', 2: '|', 3: ' '}
- let temp = {}
- " :silent :s does not work with lazyredraw
- let _lz = &lz
- set nolz
- for i in values(Delim)
- redir => temp[i]
- exe "silent! %s/" . i . "/&/nge"
- redir END
- endfor
- let &lz = _lz
- let Delim = map(temp, 'matchstr(substitute(v:val, "\n", "", ""), "^\\d\\+")')
- let Delim = filter(temp, 'v:val=~''\d''')
- let max = max(values(temp))
- let result=[]
- call setpos('.', _cur)
- let @/ = _s
- for [key, value] in items(Delim)
- if value == max
- return key
- endif
- endfor
- return ''
- else
- " There is no delimiter for fixedwidth files
- return ''
- endif
- endfu
- fu! <sid>WColumn(...) "{{{3
- " Return on which column the cursor is
- let _cur = getpos('.')
- if !exists("b:csv_fixed_width_cols")
- let line=getline('.')
- " move cursor to end of field
- "call search(b:col, 'ec', line('.'))
- call search(b:col, 'ec')
- let end=col('.')-1
- let fields=(split(line[0:end],b:col.'\zs'))
- let ret=len(fields)
- if exists("a:1") && a:1 > 0
- " bang attribute
- let head = split(getline(1),b:col.'\zs')
- " remove preceeding whitespace
- let ret = substitute(head[ret-1], '^\s\+', '', '')
- " remove delimiter
- let ret = substitute(ret, b:delimiter. '$', '', '')
- endif
- else
- let temp=getpos('.')[2]
- let j=1
- let ret = 1
- for i in sort(b:csv_fixed_width_cols, "<sid>SortList")
- if temp >= i
- let ret = j
- endif
- let j += 1
- endfor
- endif
- call setpos('.',_cur)
- return ret
- endfu
- fu! <sid>MaxColumns(...) "{{{3
- if exists("a:0") && a:0 == 1
- let this_col = 1
- else
- let this_col = 0
- endif
- "return maximum number of columns in first 10 lines
- if !exists("b:csv_fixed_width_cols")
- if this_col
- let i = a:1
- else
- let i = 1
- endif
- while 1
- let l = getline(i, i+10)
- " Filter comments out
- let pat = '^\s*\V'. escape(b:csv_cmt[0], '\\')
- call filter(l, 'v:val !~ pat')
- if !empty(l) || this_col
- break
- else
- let i+=10
- endif
- endw
- if empty(l)
- throw 'csv:no_col'
- endif
- let fields=[]
- let result=0
- for item in l
- let temp=len(split(item, b:col.'\zs'))
- let result=(temp>result ? temp : result)
- endfor
- return result
- else
- return len(b:csv_fixed_width_cols)
- endif
- endfu
- fu! <sid>ColWidth(colnr) "{{{3
- " Return the width of a column
- " Internal function
- let width=20 "Fallback (wild guess)
- let tlist=[]
- if !exists("b:csv_fixed_width_cols")
- if !exists("b:csv_list")
- let b:csv_list=getline(1,'$')
- let pat = '^\s*\V'. escape(b:csv_cmt[0], '\\')
- call filter(b:csv_list, 'v:val !~ pat')
- call map(b:csv_list, 'split(v:val, b:col.''\zs'')')
- endif
- try
- for item in b:csv_list
- call add(tlist, item[a:colnr-1])
- endfor
- " we have a list of the first 10 rows
- " Now transform it to a list of field a:colnr
- " and then return the maximum strlen
- " That could be done in 1 line, but that would look ugly
- "call map(list, 'split(v:val, b:col."\\zs")[a:colnr-1]')
- call map(tlist, 'substitute(v:val, ".", "x", "g")')
- call map(tlist, 'strlen(v:val)')
- return max(tlist)
- catch
- throw "ColWidth-error"
- return width
- endtry
- else
- let cols = len(b:csv_fixed_width_cols)
- if a:colnr == cols
- return strlen(substitute(getline('$'), '.', 'x', 'g')) -
- \ b:csv_fixed_width_cols[cols-1] + 1
- elseif a:colnr < cols && a:colnr > 0
- return b:csv_fixed_width_cols[a:colnr] -
- \ b:csv_fixed_width_cols[(a:colnr - 1)]
- else
- throw "ColWidth-error"
- return 0
- endif
- endif
- endfu
- fu! <sid>ArrangeCol(first, last, bang) range "{{{3
- "TODO: Why doesn't that work?
- " is this because of the range flag?
- " It's because of the way, Vim works with
- " a:firstline and a:lastline parameter, therefore
- " explicitly give the range as argument to the function
- if exists("b:csv_fixed_width_cols")
- " Nothing to do
- call <sid>Warn("ArrangeColumn does not work with fixed width column!")
- return
- endif
- let cur=winsaveview()
- if a:bang || !exists("b:col_width")
- " Force recalculation of Column width
- call <sid>CalculateColumnWidth()
- endif
- if &ro
- " Just in case, to prevent the Warning
- " Warning: W10: Changing read-only file
- let ro = 1
- setl noro
- else
- let ro = 0
- endif
- exe a:first . ',' . a:last .'s/' . (b:col) .
- \ '/\=<SID>Columnize(submatch(0))/' . (&gd ? '' : 'g')
- " Clean up variables, that were only needed for <sid>Columnize() function
- unlet! s:columnize_count s:max_cols s:prev_line
- if ro
- setl ro
- unlet ro
- endif
- call winrestview(cur)
- endfu
- fu! <sid>PrepUnArrangeCol(first, last) "{{{3
- " Because of the way, Vim works with
- " a:firstline and a:lastline parameter,
- " explicitly give the range as argument to the function
- if exists("b:csv_fixed_width_cols")
- " Nothing to do
- call <sid>Warn("UnArrangeColumn does not work with fixed width column!")
- return
- endif
- let cur=winsaveview()
- if &ro
- " Just in case, to prevent the Warning
- " Warning: W10: Changing read-only file
- setl noro
- endif
- exe a:first . ',' . a:last .'s/' . (b:col) .
- \ '/\=<SID>UnArrangeCol(submatch(0))/' . (&gd ? '' : 'g')
- " Clean up variables, that were only needed for <sid>Columnize() function
- call winrestview(cur)
- endfu
- fu! <sid>UnArrangeCol(match) "{{{3
- " Strip leading white space, also trims empty records:
- return substitute(a:match, '^\s\+', '', '')
- " only strip leading white space, if a non-white space follows:
- "return substitute(a:match, '^\s\+\ze\S', '', '')
- endfu
- fu! <sid>CalculateColumnWidth() "{{{3
- " Internal function, not called from external,
- " does not work with fixed width columns
- let b:col_width=[]
- " Force recalculating the Column width
- unlet! b:csv_list
- let s:max_cols=<SID>MaxColumns()
- try
- for i in range(1,s:max_cols)
- call add(b:col_width, <SID>ColWidth(i))
- endfor
- catch /ColWidth/
- call <sid>Warn("Error: getting Column Width, using default!")
- endtry
- " delete buffer content in variable b:csv_list,
- " this was only necessary for calculating the max width
- unlet! b:csv_list
- endfu
- fu! <sid>Columnize(field) "{{{3
- " Internal function, not called from external,
- " does not work with fixed width columns
- if !exists("s:columnize_count")
- let s:columnize_count = 0
- endif
- if !exists("s:max_cols")
- let s:max_cols = len(b:col_width)
- endif
- if exists("s:prev_line") && s:prev_line != line('.')
- let s:columnize_count = 0
- endif
- let s:prev_line = line('.')
- " convert zero based indexed list to 1 based indexed list,
- " Default: 20 width, in case that column width isn't defined
- " Careful: Keep this fast! Using
- " let width=get(b:col_width,<SID>WColumn()-1,20)
- " is too slow, so we are using:
- let width=get(b:col_width, (s:columnize_count % s:max_cols), 20)
- let s:columnize_count += 1
- if !exists("g:csv_no_multibyte") &&
- \ match(a:field, '[^ -~]') != -1
- " match characters outside the ascii range
- let a = split(a:field, '\zs')
- let add = eval(join(map(a, 'len(v:val)'), '+'))
- let add -= len(a)
- else
- let add = 0
- endif
- " Add one for the frame
- " plus additional width for multibyte chars,
- " since printf(%*s..) uses byte width!
- let width = width + add + 1
- if width == strlen(a:field)
- " Column has correct length, don't use printf()
- return a:field
- else
- return printf("%*s", width , a:field)
- endif
- endfun
- fu! <sid>GetColPat(colnr, zs_flag) "{{{3
- " Return Pattern for given column
- if a:colnr > 1
- if !exists("b:csv_fixed_width_cols")
- let pat=b:col . '\{' . (a:colnr) . '\}'
- else
- if a:colnr >= len(b:csv_fixed_width_cols)
- " Get last column
- let pat='\%' . b:csv_fixed_width_cols[-1] . 'c.*'
- else
- let pat='\%' . b:csv_fixed_width_cols[(a:colnr - 1)] .
- \ 'c.\{-}\%' . b:csv_fixed_width_cols[a:colnr] . 'c'
- endif
- endif
- elseif !exists("b:csv_fixed_width_cols")
- let pat=b:col
- else
- let pat='\%' . b:csv_fixed_width_cols[0] . 'c.\{-}' .
- \ (len(b:csv_fixed_width_cols) > 1 ?
- \ '\%' . b:csv_fixed_width_cols[1] . 'c' :
- \ '')
- endif
- return pat . (a:zs_flag ? '\zs' : '')
- endfu
- fu! <sid>SplitHeaderLine(lines, bang, hor) "{{{3
- if exists("b:csv_fixed_width_cols")
- call <sid>Warn("Header does not work with fixed width column!")
- return
- endif
- " Check that there exists a header line
- call <sid>CheckHeaderLine()
- if !a:bang
- " A Split Header Window already exists,
- " first close the already existing Window
- if exists("b:csv_SplitWindow")
- call <sid>SplitHeaderLine(a:lines, 1, a:hor)
- endif
- " Split Window
- let _stl = &l:stl
- let _sbo = &sbo
- let a = []
- let b=b:col
- if a:hor
- setl scrollopt=hor scrollbind
- let lines = empty(a:lines) ? s:csv_fold_headerline : a:lines
- let a = getline(1,lines)
- " Does it make sense to use the preview window?
- " sil! pedit %
- sp +enew
- call setline(1, a)
- " Needed for syntax highlighting
- "let b:col=b
- "setl syntax=csv
- sil! doautocmd FileType csv
- 1
- exe "resize" . lines
- setl scrollopt=hor winfixheight nowrap
- "let &l:stl=repeat(' ', winwidth(0))
- let &l:stl="%#Normal#".repeat(' ',winwidth(0))
- else
- setl scrollopt=ver scrollbind
- 0
- let a=<sid>CopyCol('',1)
- " Force recalculating columns width
- unlet! b:csv_list
- try
- let width = <sid>ColWidth(1)
- catch /ColWidth/
- call <sid>Warn("Error: getting Column Width, using default!")
- endtry
- " Does it make sense to use the preview window?
- "vert sil! pedit |wincmd w | enew!
- abo vsp +enew
- call append(0, a)
- $d _
- sil %s/.*/\=printf("%.*s", width, submatch(0))/eg
- 0
- exe "vert res" width
- let b:col=b
- call matchadd("CSVHeaderLine", b:col)
- setl scrollopt=ver winfixwidth
- endif
- let win = winnr()
- setl scrollbind buftype=nowrite bufhidden=wipe noswapfile nobuflisted
- wincmd p
- let b:csv_SplitWindow = win
- aug CSV_Preview
- au!
- au BufWinLeave <buffer> call <sid>SplitHeaderLine(0, 1, 0)
- aug END
- else
- " Close split window
- if !exists("b:csv_SplitWindow")
- return
- endif
- exe b:csv_SplitWindow . "wincmd w"
- if exists("_stl")
- let &l:stl = _stl
- endif
- if exists("_sbo")
- let &sbo = _sbo
- endif
- setl noscrollbind
- wincmd c
- "pclose!
- unlet! b:csv_SplitWindow
- aug CSV_Preview
- au!
- aug END
- aug! CSV_Preview
- endif
- endfu
- fu! <sid>SplitHeaderToggle(hor) "{{{3
- if !exists("b:csv_SplitWindow")
- :call <sid>SplitHeaderLine(1,0,a:hor)
- else
- :call <sid>SplitHeaderLine(1,1,a:hor)
- endif
- endfu
- " TODO: from here on add logic for fixed-width csv files!
- fu! <sid>MoveCol(forward, line) "{{{3
- " Move cursor position upwards/downwards left/right
- let colnr=<SID>WColumn()
- let maxcol=<SID>MaxColumns()
- let cpos=getpos('.')[2]
- if !exists("b:csv_fixed_width_cols")
- call search(b:col, 'bc', line('.'))
- endif
- let spos=getpos('.')[2]
- " Check for valid column
- " a:forward == 1 : search next col
- " a:forward == -1: search prev col
- " a:forward == 0 : stay in col
- if colnr - v:count1 >= 1 && a:forward == -1
- let colnr -= v:count1
- elseif colnr - v:count1 < 1 && a:forward == -1
- let colnr = 0
- elseif colnr + v:count1 <= maxcol && a:forward == 1
- let colnr += v:count1
- elseif colnr + v:count1 > maxcol && a:forward == 1
- let colnr = maxcol + 1
- endif
- let line=a:line
- if line < 1
- let line=1
- elseif line > line('$')
- let line=line('$')
- endif
- " Generate search pattern
- if colnr == 1
- let pat = '^' . <SID>GetColPat(colnr-1,0)
- "let pat = pat . '\%' . line . 'l'
- elseif (colnr == 0) || (colnr == maxcol + 1)
- if !exists("b:csv_fixed_width_cols")
- let pat=b:col
- else
- if a:forward > 0
- " Move forwards
- let pat=<sid>GetColPat(1, 0)
- else
- " Move backwards
- let pat=<sid>GetColPat(maxcol, 0)
- endif
- endif
- else
- if !exists("b:csv_fixed_width_cols")
- let pat='^'. <SID>GetColPat(colnr-1,1) . b:col
- else
- let pat=<SID>GetColPat(colnr,0)
- endif
- "let pat = pat . '\%' . line . 'l'
- endif
- " Search
- " move left/right
- if a:forward > 0
- call search(pat, 'W')
- elseif a:forward < 0
- call search(pat, 'bWe')
- " Moving upwards/downwards
- elseif line >= line('.')
- call search(pat . '\%' . line . 'l', '', line)
- " Move to the correct screen column
- " This is a best effort approach, we might still
- " leave the column (if the next column is shorter)
- if !exists("b:csv_fixed_width_cols")
- let a = getpos('.')
- let a[2]+= cpos-spos
- else
- let a = getpos('.')
- let a[2] = cpos
- endif
- call setpos('.', a)
- elseif line < line('.')
- call search(pat . '\%' . line . 'l', 'b', line)
- " Move to the correct screen column
- if !exists("b:csv_fixed_width_cols")
- let a = getpos('.')
- let a[2]+= cpos-spos
- else
- let a = getpos('.')
- let a[2] = cpos
- endif
- call setpos('.', a)
- endif
- endfun
- fu! <sid>SortComplete(A,L,P) "{{{3
- return join(range(1,<sid>MaxColumns()),"\n")
- endfun
- fu! <sid>SortList(a1, a2) "{{{3
- return a:a1+0 == a:a2+0 ? 0 : a:a1+0 > a:a2+0 ? 1 : -1
- endfu
- fu! <sid>Sort(bang, line1, line2, colnr) range "{{{3
- let wsv=winsaveview()
- if a:colnr =~? 'n'
- let numeric = 1
- else
- let numeric = 0
- endif
- let col = (empty(a:colnr) || a:colnr !~? '\d\+') ? <sid>WColumn() : a:colnr+0
- if col != 1
- if !exists("b:csv_fixed_width_cols")
- let pat= '^' . <SID>GetColPat(col-1,1) . b:col
- else
- let pat= '^' . <SID>GetColPat(col,0)
- endif
- else
- let pat= '^' . <SID>GetColPat(col,0)
- endif
- exe a:line1 ',' a:line2 . "sort" . (a:bang ? '!' : '') .
- \' r ' . (numeric ? 'n' : '') . ' /' . pat . '/'
- call winrestview(wsv)
- endfun
- fu! CSV_WCol(...) "{{{3
- try
- if exists("a:1") && (a:1 == 'Name' || a:1 == 1)
- return printf("%s", <sid>WColumn(1))
- else
- return printf(" %d/%d", <SID>WColumn(), <SID>MaxColumns())
- endif
- catch
- return ''
- endtry
- endfun
- fu! <sid>CopyCol(reg, col) "{{{3
- " Return Specified Column into register reg
- let col = a:col == "0" ? <sid>WColumn() : a:col+0
- let mcol = <sid>MaxColumns()
- if col == '$' || col > mcol
- let col = mcol
- endif
- let a = []
- " Don't get lines, that are currently filtered away
- if !exists("b:csv_filter") || empty(b:csv_filter)
- let a=getline(1, '$')
- else
- for line in range(1, line('$'))
- if foldlevel(line)
- continue
- else
- call add(a, getline(line))
- endif
- endfor
- endif
- " Filter comments out
- let pat = '^\s*\V'. escape(b:csv_cmt[0], '\\')
- call filter(a, 'v:val !~ pat')
- if !exists("b:csv_fixed_width_cols")
- call map(a, 'split(v:val, ''^'' . b:col . ''\zs'')[col-1]')
- else
- call map(a, 'matchstr(v:val, <sid>GetColPat(col, 0))')
- endif
- if a:reg =~ '[-"0-9a-zA-Z*+]'
- "exe ':let @' . a:reg . ' = "' . join(a, "\n") . '"'
- " set the register to blockwise mode
- call setreg(a:reg, join(a, "\n"), 'b')
- else
- return a
- endif
- endfu
- fu! <sid>MoveColumn(start, stop, ...) range "{{{3
- " Move column behind dest
- " Explicitly give the range as argument,
- " cause otherwise, Vim would move the cursor
- let wsv = winsaveview()
- let col = <sid>WColumn()
- let max = <sid>MaxColumns()
- " If no argument is given, move current column after last column
- let source=(exists("a:1") && a:1 > 0 && a:1 <= max ? a:1 : col)
- let dest =(exists("a:2") && a:2 > 0 && a:2 <= max ? a:2 : max)
- " translate 1 based columns into zero based list index
- let source -= 1
- let dest -= 1
- if source >= dest
- call <sid>Warn("Destination column before source column, aborting!")
- return
- endif
- " Swap line by line, instead of reading the whole range into memory
- for i in range(a:start, a:stop)
- let content = getline(i)
- if content =~ '^\s*\V'. escape(b:csv_cmt[0], '\\')
- " skip comments
- continue
- endif
- if !exists("b:csv_fixed_width_cols")
- let fields=split(content, b:col . '\zs')
- else
- let fields=[]
- for j in range(1, max, 1)
- call add(fields, matchstr(content, <sid>GetColPat(j,0)))
- endfor
- endif
- " Add delimiter to destination column, in case there was none,
- " remove delimiter from source, in case destination did not have one
- if matchstr(fields[dest], '.$') !~? b:delimiter
- let fields[dest] = fields[dest] . b:delimiter
- if matchstr(fields[source], '.$') =~? b:delimiter
- let fields[source] = substitute(fields[source],
- \ '^\(.*\).$', '\1', '')
- endif
- endif
- let fields= (source == 0 ? [] : fields[0 : (source-1)])
- \ + fields[ (source+1) : dest ]
- \ + [ fields[source] ] + fields[(dest+1):]
- call setline(i, join(fields, ''))
- endfor
- call winrestview(wsv)
- endfu
- fu! <sid>SumColumn(list) "{{{3
- " Sum a list of values, but only consider the digits within each value
- " parses the digits according to the given format (if none has been
- " specified, assume POSIX format (without thousand separator) If Vim has
- " does not support floats, simply sum up only the integer part
- if empty(a:list)
- return 0
- else
- let sum = has("float") ? 0.0 : 0
- for item in a:list
- if empty(item)
- continue
- endif
- let nr = matchstr(item, '\d\(.*\d\)\?$')
- let format1 = '^\d\+\zs\V' . s:nr_format[0] . '\m\ze\d'
- let format2 = '\d\+\zs\V' . s:nr_format[1] . '\m\ze\d'
- try
- let nr = substitute(nr, format1, '', '')
- if has("float") && s:nr_format[1] != '.'
- let nr = substitute(nr, format2, '.', '')
- endif
- catch
- let nr = 0
- endtry
- let sum += (has("float") ? str2float(nr) : (nr + 0))
- endfor
- if has("float")
- if float2nr(sum) == sum
- return float2nr(sum)
- else
- return printf("%.2f", sum)
- endif
- endif
- return sum
- endif
- endfu
- fu! csv#EvalColumn(nr, func, first, last) range "{{{3
- let save = winsaveview()
- call <sid>CheckHeaderLine()
- let nr = matchstr(a:nr, '^\d\+')
- let col = (empty(nr) ? <sid>WColumn() : nr)
- " don't take the header line into consideration
- let start = a:first - 1 + s:csv_fold_headerline
- let stop = a:last - 1 + s:csv_fold_headerline
- let column = <sid>CopyCol('', col)[start : stop]
- " Delete delimiter
- call map(column, 'substitute(v:val, b:delimiter . "$", "", "g")')
- call map(column, 'substitute(v:val, ''^\s\+$'', "", "g")')
- " Delete empty values
- " Leave this up to the function that does something
- " with each value
- "call filter(column, '!empty(v:val)')
-
- " parse the optional number format
- let format = matchstr(a:nr, '/[^/]*/')
- call <sid>NumberFormat()
- if !empty(format)
- try
- let s = []
- " parse the optional number format
- let str = matchstr(format, '/\zs[^/]*\ze/', 0, start)
- let s = matchlist(str, '\(.\)\?:\(.\)\?')[1:2]
- if len(s) == 0
- " Number format wrong
- call <sid>Warn("Numberformat wrong, needs to be /x:y/!")
- return ''
- endif
- if !empty(s[0])
- let s:nr_format[0] = s[0]
- endif
- if !empty(s[1])
- let s:nr_format[1] = s[1]
- endif
- endtry
- endif
- try
- let result=call(function(a:func), [column])
- return result
- catch
- " Evaluation of expression failed
- echohl Title
- echomsg "Evaluating" matchstr(a:func, '[a-zA-Z]\+$')
- \ "failed for column" col . "!"
- echohl Normal
- return ''
- finally
- call winrestview(save)
- endtry
- endfu
- fu! <sid>DoForEachColumn(start, stop, bang) range "{{{3
- " Do something for each column,
- " e.g. generate SQL-Statements, convert to HTML,
- " something like this
- " TODO: Define the function
- " needs a csv_pre_convert variable
- " csv_post_convert variable
- " csv_convert variable
- " result contains converted buffer content
- let result = []
- if !exists("g:csv_convert")
- call <sid>Warn("You need to define how to convert your data using" .
- \ "the g:csv_convert variable, see :h csv-convert")
- return
- endif
- if exists("g:csv_pre_convert") && !empty(g:csv_pre_convert)
- call add(result, g:csv_pre_convert)
- endif
- for item in range(a:start, a:stop, 1)
- let t = g:csv_convert
- let line = getline(item)
- if line =~ '^\s*\V'. escape(b:csv_cmt[0], '\\')
- " Filter comments out
- call add(result, line)
- continue
- endif
- let context = split(g:csv_convert, '%s')
- let columns = len(context)
- if columns > <sid>MaxColumns()
- let columns = <sid>MaxColumns()
- elseif columns == 1
- call <sid>Warn("No Columns defined in your g:csv_convert variable, Aborting")
- return
- endif
- if !exists("b:csv_fixed_width_cols")
- let fields=split(line, b:col . '\zs')
- if a:bang
- call map(fields, 'substitute(v:val, b:delimiter .
- \ ''\?$'' , "", "")')
- endif
- else
- let fields=[]
- for j in range(1, columns, 1)
- call add(fields, matchstr(line, <sid>GetColPat(j,0)))
- endfor
- endif
- for j in range(1, columns, 1)
- let t=substitute(t, '%s', fields[j-1], '')
- endfor
- call add(result, t)
- endfor
- if exists("g:csv_post_convert") && !empty(g:csv_post_convert)
- call add(result, g:csv_post_convert)
- endif
- new
- call append('$', result)
- 1d _
- endfun
- fu! <sid>PrepareDoForEachColumn(start, stop, bang) range"{{{3
- let pre = exists("g:csv_pre_convert") ? g:csv_pre_convert : ''
- let g:csv_pre_convert=input('Pre convert text: ', pre)
- let post = exists("g:csv_post_convert") ? g:csv_post_convert : ''
- let g:csv_post_convert=input('Post convert text: ', post)
- let convert = exists("g:csv_convert") ? g:csv_convert : ''
- let g:csv_convert=input("Converted text, use %s for column input:\n", convert)
- call <sid>DoForEachColumn(a:start, a:stop, a:bang)
- endfun
- fu! <sid>EscapeValue(val) "{{{3
- return '\V' . escape(a:val, '\')
- endfu
- fu! <sid>FoldValue(lnum, filter) "{{{3
- call <sid>CheckHeaderLine()
- if (a:lnum == s:csv_fold_headerline)
- " Don't fold away the header line
- return 0
- endif
- let result = 0
- for item in values(a:filter)
- " always fold comments away
- let content = getline(a:lnum)
- if content =~ '^\s*\V'. escape(b:csv_cmt[0], '\\')
- return 1
- elseif eval('content' . (item.match ? '!~' : '=~') . 'item.pat')
- let result += 1
- endif
- endfor
- return (result > 0)
- endfu
- fu! <sid>PrepareFolding(add, match) "{{{3
- if !has("folding")
- return
- endif
- " Move folded-parts away?
- if exists("g:csv_move_folds")
- let s:csv_move_folds = g:csv_move_folds
- else
- let s:csv_move_folds = 0
- endif
- if !exists("b:csv_filter")
- let b:csv_filter = {}
- endif
- if !exists("s:filter_count") || s:filter_count < 1
- let s:filter_count = 0
- endif
- let cpos = winsaveview()
- if !a:add
- " remove last added item from filter
- if len(b:csv_filter) > 0
- call <sid>RemoveLastItem(s:filter_count)
- let s:filter_count -= 1
- if len(b:csv_filter) == 0
- call <sid>DisableFolding()
- return
- endif
- else
- " Disable folding, if no pattern available
- call <sid>DisableFolding()
- return
- endif
- else
- let col = <sid>WColumn()
- let max = <sid>MaxColumns()
- let a = <sid>GetColumn(line('.'), col)
- if !exists("b:csv_fixed_width")
- try
- " strip leading whitespace
- if (a =~ '\s\+'. b:delimiter . '$')
- let b = split(a, '^\s\+\ze[^' . b:delimiter. ']\+')[0]
- else
- let b = a
- endif
- catch /^Vim\%((\a\+)\)\=:E684/
- " empty pattern - should match only empty columns
- let b = a
- endtry
- " strip trailing delimiter
- try
- let a = split(b, b:delimiter . '$')[0]
- catch /^Vim\%((\a\+)\)\=:E684/
- let a = b
- endtry
- if a == b:delimiter
- try
- let a=repeat(' ', <sid>ColWidth(col))
- catch
- " no-op
- endtry
- endif
- endif
- " Make a column pattern
- let b= '\%(' .
- \ (exists("b:csv_fixed_width") ? '.*' : '') .
- \ <sid>GetPat(col, max, <sid>EscapeValue(a) . '\m') .
- \ '\)'
- let s:filter_count += 1
- let b:csv_filter[s:filter_count] = { 'pat': b, 'id': s:filter_count,
- \ 'col': col, 'orig': a, 'match': a:match}
- endif
- " Put the pattern into the search register, so they will also
- " be highlighted
- " let @/ = ''
- " for val in sort(values(b:csv_filter), '<sid>SortFilter')
- " let @/ .= val.pat . (val.id == s:filter_count ? '' : '\&')
- " endfor
- let sid = <sid>GetSID()
- " Fold settings:
- call <sid>LocalSettings('fold')
- " Don't put spaces between the arguments!
- exe 'setl foldexpr=<snr>' . sid . '_FoldValue(v:lnum,b:csv_filter)'
- " Move folded area to the bottom, so there is only on consecutive
- " non-folded area
- if exists("s:csv_move_folds") && s:csv_move_folds
- \ && !&l:ro && &l:ma
- folddoclosed m$
- let cpos.lnum = s:csv_fold_headerline + 1
- endif
- call winrestview(cpos)
- endfu
- fu! <sid>OutputFilters(bang) "{{{3
- if !a:bang
- call <sid>CheckHeaderLine()
- if s:csv_fold_headerline
- let title="Nr\tMatch\tCol\t Name\tValue"
- else
- let title="Nr\tMatch\tCol\tValue"
- endif
- echohl "Title"
- echo printf("%s", title)
- echo printf("%s", repeat("=",strdisplaywidth(title)))
- echohl "Normal"
- if !exists("b:csv_filter") || len(b:csv_filter) == 0
- echo printf("%s", "No active filter")
- else
- let items = values(b:csv_filter)
- call sort(items, "<sid>SortFilter")
- for item in items
- if s:csv_fold_headerline
- echo printf("%02d\t% 2s\t%02d\t%10.10s\t%s",
- \ item.id, (item.match ? '+' : '-'), item.col,
- \ substitute(<sid>GetColumn(1, item.col),
- \ b:col.'$', '', ''), item.orig)
- else
- echo printf("%02d\t% 2s\t%02d\t%s",
- \ item.id, (item.match ? '+' : '-'),
- \ item.col, item.orig)
- endif
- endfor
- endif
- else
- " Reapply filter again
- if !exists("b:csv_filter") || len(b:csv_filter) == 0
- call <sid>Warn("No filters defined currently!")
- return
- else
- let sid = <sid>GetSID()
- exe 'setl foldexpr=<snr>' . sid . '_FoldValue(v:lnum,b:csv_filter)'
- endif
- endif
- endfu
- fu! <sid>SortFilter(a, b) "{{{3
- return a:a.id == a:b.id ? 0 :
- \ a:a.id > a:b.id ? 1 : -1
- endfu
- fu! <sid>GetColumn(line, col) "{{{3
- " Return Column content at a:line, a:col
- let a=getline(a:line)
- " Filter comments out
- if a =~ '^\s*\V'. escape(b:csv_cmt[0], '\\')
- return ''
- endif
- if !exists("b:csv_fixed_width_cols")
- try
- let a = split(a, '^' . b:col . '\zs')[a:col - 1]
- catch
- " index out of range
- let a = ''
- endtry
- else
- let a = matchstr(a, <sid>GetColPat(a:col, 0))
- endif
- return substitute(a, '^\s\+\|\s\+$', '', 'g')
- endfu
- fu! <sid>RemoveLastItem(count) "{{{3
- for [key,value] in items(b:csv_filter)
- if value.id == a:count
- call remove(b:csv_filter, key)
- endif
- endfor
- endfu
- fu! <sid>DisableFolding() "{{{3
- setl nofen fdm=manual fdc=0 fdl=0 fillchars+=fold:-
- endfu
- fu! <sid>GetSID() "{{{3
- if v:version > 703 || v:version == 703 && has("patch032")
- return maparg('W', "", "", 1).sid
- else
- "return substitute(maparg('W'), '\(<SNR>\d\+\)_', '\1', '')
- return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze_GetSID$')
- endif
- endfu
- fu! <sid>NumberFormat() "{{{3
- let s:nr_format = [',', '.']
- if exists("b:csv_thousands_sep")
- let s:nr_format[0] = b:csv_thousands_sep
- endif
- if exists("b:csv_decimal_sep")
- let s:nr_format[1] = b:csv_decimal_sep
- endif
- endfu
- fu! <sid>CheckHeaderLine() "{{{3
- if !exists("b:csv_headerline")
- let s:csv_fold_headerline = 1
- else
- let s:csv_fold_headerline = b:csv_headerline
- endif
- endfu
- fu! <sid>AnalyzeColumn(...) "{{{3
- let maxcolnr = <SID>MaxColumns()
- if len(a:000) == 1
- let colnr = a:1
- else
- let colnr = <sid>WColumn()
- endif
- if colnr > maxcolnr
- call <SID>Warn("There exists no column " . colnr)
- return 1
- endif
- " Initialize s:fold_headerline
- call <sid>CheckHeaderLine()
- let data = <sid>CopyCol('', colnr)[s:csv_fold_headerline : -1]
- let qty = len(data)
- let res = {}
- for item in data
- if !get(res, item)
- let res[item] = 0
- endif
- let res[item]+=1
- endfor
- let max_items = reverse(sort(values(res)))
- if len(max_items) > 5
- call remove(max_items, 5, -1)
- call filter(res, 'v:val =~ ''^''.join(max_items, ''\|'').''$''')
- endif
- if has("float")
- let title="Nr\tCount\t % \tValue"
- else
- let title="Nr\tCount\tValue"
- endif
- echohl "Title"
- echo printf("%s", title)
- echohl "Normal"
- echo printf("%s", repeat('=', strdisplaywidth(title)))
- let i=1
- for val in max_items
- for key in keys(res)
- if res[key] == val && i <= len(max_items)
- if !empty(b:delimiter)
- let k = substitute(key, b:delimiter . '\?$', '', '')
- else
- let k = key
- endif
- if has("float")
- echo printf("%02d\t%02d\t%2.0f%%\t%.50s", i, res[key],
- \ ((res[key] + 0.0)/qty)*100, k)
- else
- echo printf("%02d\t%02d\t%.50s", i, res[key], k)
- endif
- call remove(res,key)
- let i+=1
- else
- continue
- endif
- endfor
- endfor
- unlet max_items
- endfunc
- fu! <sid>Vertfold(bang, col) "{{{3
- if a:bang
- do Syntax
- return
- endif
- if !has("conceal")
- call <sid>Warn("Concealing not supported in your Vim")
- return
- endif
- if empty(b:delimiter) && !exists("b:csv_fixed_width_cols")
- call <sid>Warn("There are no columns defined, can't hide away anything!")
- return
- endif
- if empty(a:col)
- let colnr=<SID>WColumn()
- else
- let colnr=a:col
- endif
- let pat=<sid>GetPat(colnr, <sid>MaxColumns(), '.*')
- if exists("b:csv_fixed_width_cols") &&
- \ pat !~ '^\^\.\*'
- " Make the pattern implicitly start at line start,
- " so it will be applied by syntax highlighting (:h :syn-priority)
- let pat='^.*' . pat
- endif
- let pat=substitute(pat, '\\zs\(\.\*\)\@=', '', '')
- if !empty(pat)
- exe "syn match CSVFold /" . pat . "/ conceal cchar=+"
- endif
- endfu
- fu! <sid>InitCSVFixedWidth() "{{{3
- if !exists("+cc")
- " TODO: make this work with a custom matchadd() command for older
- " Vims, that don't have 'colorcolumn'
- call <sid>Warn("'colorcolumn' option not available")
- return
- endif
- " Turn off syntax highlighting
- syn clear
- let _cc = &l:cc
- let &l:cc = 1
- redraw!
- let list = []
- let tcc = &l:cc
- echo "<Cursor>, <Space>, <ESC>, <BS>, <CR>..."
- let char=getchar()
- while 1
- if char == "\<Left>" || char == "\<Right>"
- let tcc = eval('tcc'.(char=="\<Left>" ? '-' : '+').'1')
- elseif char == "\<Space>" || char == 32 " Space
- call add(list, tcc)
- elseif char == "\<BS>" || char == 127
- call remove(list, -1)
- elseif char == "\<ESC>" || char == 27
- let &l:cc=_cc
- redraw!
- return
- else
- break
- endif
- let &l:cc=tcc . (!empty(list)? ',' . join(list, ','):'')
- redraw!
- echo "<Cursor>, <Space>, <ESC>, <BS>, <CR>..."
- let char=getchar()
- endw
- if tcc > 0
- call add(list,tcc)
- endif
- let b:csv_fixed_width_cols=[]
- let tcc=0
- if !empty(list)
- call Break()
- " Remove duplicate entries
- for val in sort(list, "<sid>SortList")
- if val==tcc
- continue
- endif
- call add(b:csv_fixed_width_cols, val)
- let tcc=val
- endfor
- let b:csv_fixed_width=join(sort(b:csv_fixed_width_cols,
- \ "<sid>SortList"), ',')
- call <sid>Init()
- endif
- let &l:cc=_cc
- redraw!
- endfu
- fu! Break()
- return
- endfu
- fu! <sid>NewRecord(line1, line2, count) "{{{3
- if a:count =~ "\D"
- call <sid>WarningMsg("Invalid count specified")
- return
- endif
- let cnt = (empty(a:count) ? 1 : a:count)
- let record = ""
- for item in range(1,<sid>MaxColumns())
- if !exists("b:col_width")
- " Best guess width
- if exists("b:csv_fixed_width_cols")
- let record .= printf("%*s", <sid>ColWidth(item),
- \ b:delimiter)
- else
- let record .= printf("%20s", b:delimiter)
- endif
- else
- let record .= printf("%*s", b:col_width[item-1]+1, b:delimiter)
- endif
- endfor
- if getline(1)[-1:] != b:delimiter
- let record = record[0:-2] . " "
- endif
- let line = []
- for item in range(cnt)
- call add(line, record)
- endfor
- for nr in range(a:line1, a:line2)
- call append(nr, line)
- endfor
- endfu
- fu! <sid>MoveOver(outer) "{{{3
- " Move over a field
- " a:outer means include the delimiter
- let last = 0
- let mode = a:outer
- if <sid>WColumn() == <sid>MaxColumns()
- let last = 1
- if !mode && getline('.')[-1:] != b:delimiter
- " No trailing delimiter, so inner == outer
- let mode = 1
- endif
- endif
- " Use the mapped key
- exe ":sil! norm E"
- let _s = @/
- if last
- exe "sil! norm! /" . b:col . "\<cr>v$h" . (mode ? "" : "\<Left>")
- else
- exe "sil! norm! /" . b:col . "\<cr>vn\<Left>" . (mode ? "" : "\<Left>")
- endif
- let @/ = _s
- endfu
- fu! <sid>CSVMappings() "{{{3
- call <sid>Map('noremap', 'W', ':<C-U>call <SID>MoveCol(1, line("."))<CR>')
- call <sid>Map('noremap', 'E', ':<C-U>call <SID>MoveCol(-1, line("."))<CR>')
- call <sid>Map('noremap', 'K', ':<C-U>call <SID>MoveCol(0,
- \ line(".")-v:count1)<CR>')
- call <sid>Map('noremap', 'J', ':<C-U>call <SID>MoveCol(0,
- \ line(".")+v:count1)<CR>')
- call <sid>Map('nnoremap', '<CR>', ':<C-U>call <SID>PrepareFolding(1,
- \ 1)<CR>')
- call <sid>Map('nnoremap', '<Space>', ':<C-U>call <SID>PrepareFolding(1,
- \ 0)<CR>')
- call <sid>Map('nnoremap', '<BS>', ':<C-U>call <SID>PrepareFolding(0,
- \ 1)<CR>')
- " Text object: Field
- call <sid>Map('vnoremap', 'if', ':<C-U>call <sid>MoveOver(0)<CR>')
- call <sid>Map('vnoremap', 'af', ':<C-U>call <sid>MoveOver(1)<CR>')
- call <sid>Map('omap', 'af', ':norm vaf<cr>')
- call <sid>Map('omap', 'if', ':norm vif<cr>')
- " Remap <CR> original values to a sane backup
- call <sid>Map('noremap', '<LocalLeader>J', 'J')
- call <sid>Map('noremap', '<LocalLeader>K', 'K')
- call <sid>Map('vnoremap', '<LocalLeader>W', 'W')
- call <sid>Map('vnoremap', '<LocalLeader>E', 'E')
- call <sid>Map('noremap', '<LocalLeader>H', 'H')
- call <sid>Map('noremap', '<LocalLeader>L', 'L')
- call <sid>Map('nnoremap', '<LocalLeader><CR>', '<CR>')
- call <sid>Map('nnoremap', '<LocalLeader><Space>', '<Space>')
- call <sid>Map('nnoremap', '<LocalLeader><BS>', '<BS>')
- call <sid>Map('map', '<C-Right>', 'W')
- call <sid>Map('map', '<C-Left>', 'E')
- call <sid>Map('map', 'H', 'E')
- call <sid>Map('map', 'L', 'W')
- call <sid>Map('map', '<Up>', 'K')
- call <sid>Map('map', '<Down>', 'J')
- endfu
- fu! <sid>CommandDefinitions() "{{{3
- call <sid>LocalCmd("WhatColumn", ':echo <sid>WColumn(<bang>0)',
- \ '-bang')
- call <sid>LocalCmd("NrColumns", ':call <sid>NrColumns(<q-bang>)', '-bang')
- call <sid>LocalCmd("HiColumn", ':call <sid>HiCol(<q-args>,<bang>0)',
- \ '-bang -nargs=?')
- call <sid>LocalCmd("SearchInColumn",
- \ ':call <sid>SearchColumn(<q-args>)', '-nargs=*')
- call <sid>LocalCmd("DeleteColumn", ':call <sid>DeleteColumn(<q-args>)',
- \ '-nargs=? -complete=custom,<sid>SortComplete')
- call <sid>LocalCmd("ArrangeColumn",
- \ ':call <sid>ArrangeCol(<line1>, <line2>, <bang>0)',
- \ '-range -bang')
- call <sid>LocalCmd("UnArrangeColumn",
- \':call <sid>PrepUnArrangeCol(<line1>, <line2>)',
- \ '-range')
- call <sid>LocalCmd("InitCSV", ':call <sid>Init()', '')
- call <sid>LocalCmd('Header',
- \ ':call <sid>SplitHeaderLine(<q-args>,<bang>0,1)',
- \ '-nargs=? -bang')
- call <sid>LocalCmd('VHeader',
- \ ':call <sid>SplitHeaderLine(<q-args>,<bang>0,0)',
- \ '-nargs=? -bang')
- call <sid>LocalCmd("HeaderToggle",
- \ ':call <sid>SplitHeaderToggle(1)', '')
- call <sid>LocalCmd("VHeaderToggle",
- \ ':call <sid>SplitHeaderToggle(0)', '')
- call <sid>LocalCmd("Sort",
- \ ':call <sid>Sort(<bang>0, <line1>,<line2>,<q-args>)',
- \ '-nargs=* -bang -range=% -complete=custom,<sid>SortComplete')
- call <sid>LocalCmd("Column",
- \ ':call <sid>CopyCol(empty(<q-reg>)?''"'':<q-reg>,<q-count>)',
- \ '-count -register')
- call <sid>LocalCmd("MoveColumn",
- \ ':call <sid>MoveColumn(<line1>,<line2>,<f-args>)',
- \ '-range=% -nargs=* -complete=custom,<sid>SortComplete')
- call <sid>LocalCmd("SumCol",
- \ ':echo csv#EvalColumn(<q-args>, "<sid>SumColumn", <line1>,<line2>)',
- \ '-nargs=? -range=% -complete=custom,<sid>SortComplete')
- call <sid>LocalCmd("ConvertData",
- \ ':call <sid>PrepareDoForEachColumn(<line1>,<line2>,<bang>0)',
- \ '-bang -nargs=? -range=%')
- call <sid>LocalCmd("Filters", ':call <sid>OutputFilters(<bang>0)',
- \ '-nargs=0 -bang')
- call <sid>LocalCmd("Analyze", ':call <sid>AnalyzeColumn(<args>)',
- \ '-nargs=?')
- call <sid>LocalCmd("VertFold", ':call <sid>Vertfold(<bang>0,<q-args>)',
- \ '-bang -nargs=? -range=% -complete=custom,<sid>SortComplete')
- call <sid>LocalCmd("CSVFixed", ':call <sid>InitCSVFixedWidth()', '')
- call <sid>LocalCmd("NewRecord", ':call <sid>NewRecord(<line1>,
- \ <line2>, <q-args>)', '-nargs=? -range')
- call <sid>LocalCmd("NewDelimiter", ':call <sid>NewDelimiter(<q-args>)',
- \ '-nargs=1')
- call <sid>LocalCmd("Duplicates", ':call <sid>CheckDuplicates(<q-args>)',
- \ '-nargs=1 -complete=custom,<sid>CompleteColumnNr')
- call <sid>LocalCmd('Transpose', ':call <sid>Transpose(<line1>, <line2>)',
- \ '-range=%')
- call <sid>LocalCmd('Tabularize', ':call <sid>Tabularize()','')
- endfu
- fu! <sid>Map(map, name, definition) "{{{3
- " All mappings are buffer local
- exe a:map "<buffer> <silent>" a:name a:definition
- " should already exists
- if a:map == 'nnoremap'
- let unmap = 'nunmap'
- elseif a:map == 'noremap' || a:map == 'map'
- let unmap = 'unmap'
- elseif a:map == 'vnoremap'
- let unmap = 'vunmap'
- elseif a:map == 'omap'
- let unmap = 'ounmap'
- endif
- let b:undo_ftplugin .= "| " . unmap . " <buffer> " . a:name
- endfu
- fu! <sid>LocalCmd(name, definition, args) "{{{3
- if !exists(':'.a:name)
- exe "com! -buffer " a:args a:name a:definition
- let b:undo_ftplugin .= "| sil! delc " . a:name
- endif
- endfu
- fu! <sid>Menu(enable) "{{{3
- if a:enable
- " Make a menu for the graphical vim
- amenu CSV.&Init\ Plugin :InitCSV<cr>
- amenu CSV.SetUp\ &fixedwidth\ Cols :CSVFixed<cr>
- amenu CSV.-sep1- <nul>
- amenu &CSV.&Column.&Number :WhatColumn<cr>
- amenu CSV.Column.N&ame :WhatColumn!<cr>
- amenu CSV.Column.&Highlight\ column :HiColumn<cr>
- amenu CSV.Column.&Remove\ highlight :HiColumn!<cr>
- amenu CSV.Column.&Delete :DeleteColumn<cr>
- amenu CSV.Column.&Sort :%Sort<cr>
- amenu CSV.Column.&Copy :Column<cr>
- amenu CSV.Column.&Move :%MoveColumn<cr>
- amenu CSV.Column.S&um :%SumCol<cr>
- amenu CSV.Column.Analy&ze :Analyze<cr>
- amenu CSV.Column.&Arrange :%ArrangeCol<cr>
- amenu CSV.Column.&UnArrange :%UnArrangeCol<cr>
- amenu CSV.-sep2- <nul>
- amenu CSV.&Toggle\ Header :HeaderToggle<cr>
- amenu CSV.&ConvertData :ConvertData<cr>
- amenu CSV.Filters :Filters<cr>
- amenu CSV.Hide\ C&olumn :VertFold<cr>
- amenu CSV.&New\ Record :NewRecord<cr>
- else
- " just in case the Menu wasn't defined properly
- sil! amenu disable CSV
- endif
- endfu
- fu! <sid>SaveOptions(list) "{{{3
- let save = {}
- for item in a:list
- exe "let save.". item. " = &l:". item
- endfor
- return save
- endfu
- fu! <sid>NewDelimiter(newdelimiter) "{{{3
- let save = <sid>SaveOptions(['ro', 'ma'])
- if exists("b:csv_fixed_width_cols")
- call <sid>Warn("NewDelimiter does not work with fixed width column!")
- return
- endif
- if !&l:ma
- setl ma
- endif
- if &l:ro
- setl noro
- endif
- let line=1
- while line <= line('$')
- " Don't change delimiter for comments
- if getline(line) =~ '^\s*\V'. escape(b:csv_cmt[0], '\\')
- let line+=1
- continue
- endif
- let fields=split(getline(line), b:col . '\zs')
- " Remove field delimiter
- call map(fields, 'substitute(v:val, b:delimiter .
- \ ''\?$'' , "", "")')
- call setline(line, join(fields, a:newdelimiter))
- let line+=1
- endwhile
- " reset local buffer options
- for [key, value] in items(save)
- call setbufvar('', '&'. key, value)
- endfor
- "reinitialize the plugin
- call <sid>Init()
- endfu
- fu! <sid>IN(list, value) "{{{3
- for item in a:list
- if item == a:value
- return 1
- endif
- endfor
- return 0
- endfu
- fu! <sid>DuplicateRows(columnlist) "{{{3
- let duplicates = {}
- let cnt = 0
- let line = 1
- while line <= line('$')
- let key = ""
- let i = 1
- let content = getline(line)
- " Skip comments
- if content =~ '^\s*\V'. escape(b:csv_cmt[0], '\\')
- continue
- endif
- let cols = split(content, b:col. '\zs')
- for column in cols
- if <sid>IN(a:columnlist, i)
- let key .= column
- endif
- let i += 1
- endfor
- if has_key(duplicates, key) && cnt < 10
- call <sid>Warn("Duplicate Row ". line)
- let cnt += 1
- elseif has_key(duplicates, key)
- call <sid>Warn("More duplicate Rows after: ". line)
- call <sid>Warn("Aborting...")
- return
- else
- let duplicates[key] = 1
- endif
- let line += 1
- endwhile
- if cnt == 0
- call <sid>Warn("No Duplicate Row found!")
- endif
- endfu
- fu! <sid>CompleteColumnNr(A,L,P) "{{{3
- return join(range(1,<sid>MaxColumns()), "\n")
- endfu
- fu! <sid>CheckDuplicates(list) "{{{3
- let string = a:list
- if string =~ '\d\s\?-\s\?\d'
- let string = substitute(string, '\(\d\+\)\s\?-\s\?\(\d\+\)',
- \ '\=join(range(submatch(1),submatch(2)), ",")', '')
- endif
- let list=split(string, ',')
- call <sid>DuplicateRows(list)
- endfu
- fu! <sid>Transpose(line1, line2) "{{{3
- " Note: - Comments will be deleted.
- " - Does not work with fixed-width columns
- if exists("b:csv_fixed_width")
- call <sid>Warn("Transposing does not work with fixed-width columns!")
- return
- endif
- let _wsv = winsaveview()
- let TrailingDelim = 0
- if line('$') > 1
- let TrailingDelim = getline(1) =~ b:delimiter.'$'
- endif
-
- let pat = '^\s*\V'. escape(b:csv_cmt[0], '\\')
- try
- let columns = <sid>MaxColumns(a:line1)
- catch
- " No column, probably because of comment or empty line
- " so use the number of columns from the beginning of the file
- let columns = <sid>MaxColumns()
- endtry
- let matrix = []
- for line in range(a:line1, a:line2)
- " Filter comments out
- if getline(line) =~ pat
- continue
- endif
- let r = []
- for row in range(1,columns)
- let field = <sid>GetColumn(line, row)
- call add(r, field)
- endfor
- call add(matrix, r)
- endfor
- unlet row
- " create new transposed matrix
- let transposed = []
- for row in matrix
- let i = 0
- for val in row
- if get(transposed, i, []) == []
- call add(transposed, [])
- endif
- if val[-1:] != b:delimiter
- let val .= b:delimiter
- endif
- call add(transposed[i], val)
- let i+=1
- endfor
- endfor
- " Save memory
- unlet! matrix
- call map(transposed, 'join(v:val, '''')')
- if !TrailingDelim
- call map(transposed, 'substitute(v:val, b:delimiter.''\?$'', "", "")')
- endif
- " filter out empty records
- call filter(transposed, 'v:val != b:delimiter')
- " Insert transposed data
- let delete_last_line = 0
- if a:line1 == 1 && a:line2 == line('$')
- let delete_last_line = 1
- endif
- exe a:line1. ",". a:line2. "d _"
- let first = (a:line1 > 0 ? (a:line1 - 1) : 0)
- call append(first, transposed)
- if delete_last_line
- sil $d _
- endif
- " save memory
- unlet! transposed
- call winrestview(_wsv)
- endfu
- fu! <sid>NrColumns(bang) "{{{3
- if !empty(a:bang)
- try
- let cols = <sid>MaxColumns(line('.'))
- catch
- " No column or comment line
- call <sid>Warn("No valid CSV Column!")
- endtry
- else
- let cols = <sid>MaxColumns()
- endif
- echo cols
- endfu
- fu! <sid>Tabularize() "{{{3
- let _c = winsaveview()
- let _ma = &l:ma
- setl ma
- call <sid>CheckHeaderLine()
- if exists("b:csv_fixed_width_cols")
- let cols=copy(b:csv_fixed_width_cols)
- let pat = join(map(cols, ' ''\(\%''. v:val. ''c\)'' '), '\|')
- let colwidth = strlen(substitute(getline('$'), '.', 'x', 'g'))
- else
- sil call <sid>ArrangeCol(1, line('$'), 1)
- endif
- if s:csv_fold_headerline > 0
- call <sid>NewRecord(s:csv_fold_headerline, s:csv_fold_headerline, 1)
- exe 'sil '. (s:csv_fold_headerline+1).
- \ 's/\s\+/\=repeat("-", strlen(submatch(0)))/g'
- endif
- if exists("b:csv_fixed_width_cols")
- " There is no delimiter:
- exe 'sil %s/'. pat. '/|/ge'
- else
- exe 'sil %s/'. b:delimiter. '/|/ge'
- endif
- " Add vertical bar in first column, if there isn't already one
- sil %s/^[^|]/|&/e
- " And add a final vertical bar, if there isn't already
- sil %s/[^|]$/&|/e
- let colwidth = strlen(substitute(getline('$'), '.', 'x', 'g'))
- for line in [0, line('$')+1]
- call append(line, repeat('-', colwidth))
- endfor
- syn clear
- let &l:ma = _ma
- call winrestview(_c)
- endfu
- fu! CSVPat(colnr, ...) "{{{3
- " Make sure, we are working in a csv file
- if &ft != 'csv'
- return ''
- endif
- " encapsulates GetPat(), that returns the search pattern for a
- " given column and tries to set the cursor at the specific position
- let pat = <sid>GetPat(a:colnr, <SID>MaxColumns(), a:0 ? a:1 : '.*')
- "let pos = match(pat, '.*\\ze') + 1
- " Try to set the cursor at the beginning of the pattern
- " does not work
- "call setcmdpos(pos)
- return pat
- endfu
- " Initialize Plugin "{{{2
- call <SID>Init()
- let &cpo = s:cpo_save
- unlet s:cpo_save
- " Vim Modeline " {{{2
- " vim: set foldmethod=marker et:
|