" Vimball Archiver by Charles E. Campbell, Jr., Ph.D. UseVimball finish doc\eightheader.txt [[[1 273 *eightheader.txt* For Vim version 7.4 Last change: 2014. 04. 25. EIGHTHEADER~ Easily create custom (fold)headers, foldtext, toc, etc. ============= BimbaLaszlo(.co.nr|gmail.com) ============== 2014.04.30 13:54 == Copyright: (c) 2014 by BimbaLaszlo The VIM LICENSE applies to eightheader.vim and eightheader.txt (see |copyright|) except use 'EightHeader' instead of 'Vim' NO WARRANTY, EXPRESS OR IMPLIED. USE AT-YOUR-OWN-RISK. CONTENT~ Using EightHeader............................................|EightHeader| Arguments.........................................|EightHeaderArguments| Variables..............................................|EightHeaderVars| Options.............................................|EightHeaderOptions| Custom foldtext with EightHeaderFolds...................|EightHeaderFolds| Calling it from script...................................|EightHeaderCall| Examples.............................................|EightHeaderExamples| Simple headers....................................|EightHeaderExHeaders| Update last change.................................|EightHeaderExUpdate| Write vimhelp header..............................|EightHeaderExVimhelp| Formating the table of contents.......................|EightHeaderExToc| Markdown header..................................|EightHeaderExMarkdown| ============================================================================== USING EIGHTHEADER *EightHeader* The script needs 'nocompatible' mode, so put this on the beginnig of your .vimrc: > set nocompatible < To use |EightHeader| just move the cursor to the line which you want to modify (hereinafter {line}), then call it: EightHeader( {length}, {align}, {oneline}, {pattern}, {marker}, {str} ) {length} Length of the header. {align} Alignment of text. {oneline} {line} and {marker} in one line, {pattern} in another. {pattern} Pattern to fill with. {marker} Extra content after patternRightEnd. {str} Replace the content of {line} with this. See |EightHeaderArguments| for detailed list. It handles the commented lines if the g:EightHeader_comment and g:EightHeader_uncomment set (see |EightHeaderOptions|). An example with {oneline}: > call EightHeader( 78, 'center', 1, ['l ', 'pat', ' r'], ' m', \ '\=" ".s:str." "' ) l patpatpatpatpatpatpatpat TEXT IN THE LINE patpatpatpatpatpatpatpat r m < ... and whitout it: > call EightHeader( 78, 'center', 0, ['l ', 'pat', ' r'], ' m', '' ) TEXT IN THE LINE m l patpatpatpatpatpatpatpatpatpatpatpatpatpatpatpatpatpatpatpatpatpatpa r < You can do |mapping|s or |command|s to your favourite header functions to simplify the usage. For example: > noremap 1 :silent call EightHeader(...) command DoHeader call EightHeader(...) < The extra is avoiding 'Press ENTER' prompt. ARGUMENTS *EightHeaderArguments* {length} Possible values: Number The length of the whole header including the indenting. If you want to do a fixed width header (independent from the indenting) then use negative value. {align} Possible values: 'left', 'right', 'center' The alignment of the text. {oneline} Possible values: 0, 1 If 1, then all parts of the header will be in one line, otherwise the text and {marker} will be on the same line, the others will be in another. {pattern} Possible values: String, or List of 3 Strings The pattern that fills the gap between elements. If it's a String, then there's nothing special, if a List, in that case the first element will be the left end of the header, the second element is the pattern, the third element is the right end before {marker}. {marker} Possible values: String The most right end of the header if it's a {oneline}, in other case it will be on the right end of the same line where the text is on. {str} Possible values: String Replacing the text in the {line}. VARIABLES *EightHeaderVars* Expect of {oneline} the arguments can be a string that begins with '\=', the rest of the string will be evaulated. You can use some variables inside of these expressions: s:str Uncommented and blank-stripped {line}. s:strLen The |strdisplaywidth()| of s:str s:indent The indenting of s:str. s:indentLen The |strdisplaywidth()| of s:str For example you can set marker based on the length of indenting: > call EightHeader( &tw, 'left', 0, '_', '\=s:indentLen/&sw', '' ) < OPTIONS *EightHeaderOptions* g:EightHeader_comment g:EightHeader_uncomment Functions that (un)comments the current line. If the line is commented, then the script will uncomment, format and recomment it. For example you can use the NERDComment(): > let g:EightHeader_comment = 'call NERDComment( "n", "comment" )' let g:EightHeader_uncomment = 'call NERDComment( "n", "uncomment" )' < If these are not set, then the script cannot handle comments. ============================================================================== CUSTOM FOLDTEXT WITH EIGHTHEADERFOLDS *EightHeaderFolds* If you don't like the default 'foldtext' you can customize it by setting to EightHeaderFolds(). EightHeaderFolds( {length}, {align}, {pattern}, {marker}, {str} ) The arguments are the same as |EightHeader| has. There are some additional variables you can use: s:fullwidth Inner width of the current window. s:foldlines Number of lines in the fold. For builtin variables see |fold-foldtext|. If you using |sign|s (for example Syntastic plugin does) then you should decrease the s:fullwidth with 2, because the script does not know that the signs are visible or not. For example the closed folds looks like this by default: > +-- 45 lines: Fold level one +--- 67 lines: Fold level two < If you would like to change it to this kind: > Fold level one................45 lines Fold level two..............67 lines < ... then you can use this function: > let &foldtext = "EightHeaderFolds( '\\=s:fullwidth-2', 'left', \ [ repeat( ' ', v:foldlevel - 1 ), '.', '' ], \ '\\= s:foldlines . \" lines\"', '' )" < ============================================================================== CALLING IT FROM SCRIPT *EightHeaderCall* The EightHeaderCall() formats a string. EightHeaderCall( {line}, {length}, {align}, {pattern}, {marker}, {str} ) The arguments are the same as |EightHeader| has, the {line} is the string that you want to format. The function returns the formated string or -1 on error. It does not handling comment operators! ============================================================================== EXAMPLES *EightHeaderExamples* SIMPLE HEADERS *EightHeaderExHeader* Basic header examples: > call EightHeader( &tw, 'center', 0, '=', ' {'.'{{1', '' ) call EightHeader( 0-(&tw/2), 'left', 1, ['__', '_', ''], '', \ '\=" ".s:str." "' ) < If you would like to use the same |mapping| to create headers for different filetypes then you can use some kind of this: > autocmd FileType help noremap 1 \ :call EightHeader(...) autocmd FileType markdown noremap 1 \ :call EightHeader(...) < UPDATE LAST CHANGE *EightHeaderExUpdate* To keep the date of last change fresh in the header of file: > function MyHeader() call EightHeader( &tw, 'center', 1, \ ['', '=', strftime(' %Y.%m.%d %H:%M ==')], \ '', ' BY USER ' ) endfunction autocmd BufWritePre * 0 /= BY USER =/ call MyHeader() < WRITE VIMHELP HEADER *EightHeaderExVimhelp* Automatically write filename, version and last change when saving vimhelp file: > call EightHeader( 78, 'center', 1, \ ['*'.expand('%').'*', ' ', 'Last change: '.strftime('%Y. %m. %d.')], \ '', 'For Vim version 7.4' ) < ... you can do an autocommand to always update the time: > autocmd BufWritePre * if &filetype == 'help' | \ 0 call EightHeader(...) | \ endif < FORMATING THE TABLE OF CONTENTS *EightHeaderExToc* For exmaple this: > Options;options Default mappings;maps Launch nuclear strike;apocalypse < ... to this: > Options........................................................|options| Default mappings..................................................|maps| Launch nuclear strike.....................................|apocalypse| < Visually select the lines, than: > call EightHeader( 78, 'left', 1, '.', \ '\="|".matchstr(s:str, ";\\@<=.*")."|"', \ '\=matchstr(s:str, ".*;\\@=")' ) < MARKDOWN HEADER *EightHeaderExMarkdown* To underline a markdown header, for exmaple: > Hello World! ============ < Use this: > call EightHeader( '\=0-s:strLen', 'left', 0, '=', '', '' ) < vim:filetype=help plugin\eightheader.vim [[[1 328 " eightheader.vim " SCRIPT FUNCTIONS {{{1 " ============================================================================ " ISTYPE {{{2 " ____________________________________________________________________________ " " Returns true if the {var} is type of something. function! s:IsNumber( var ) return type( a:var ) == 0 endfunction function! s:IsString( var ) return type( a:var ) == 1 endfunction function! s:IsList( var ) return type( a:var ) == 3 endfunction " ERROR {{{2 " ____________________________________________________________________________ " " Echoing the error message. function! s:Error( msg ) echohl ErrorMsg | echomsg a:msg | echohl None endfunction " VALUE {{{2 " ____________________________________________________________________________ " " Returns the (evaulated) value of {var}. function! s:Value( var ) if s:IsString( a:var ) && (a:var =~ '^\\=') return eval( matchstr( a:var, '\(\\=\)\@<=.*' ) ) else return a:var endif endfunction " PATTERN {{{2 " ____________________________________________________________________________ " " Repeating a pattern, then returns with it. " " __ ARGUMENTS __________________________ " " start Number Index of first character in pattern. " len Number Length of generated pattern. " pattern String Pattern that we want to repeat. " " FIXME: " strpart() not works with multibyte characters. function! s:Pattern( start, len, pattern ) if ! a:len return '' endif let retstr = '' let len = a:len let patternLen = strdisplaywidth( a:pattern ) let index = a:start % patternLen if patternLen >= (len + index) return strpart( a:pattern, index, len ) else let retstr .= strpart( a:pattern, index, patternLen - index ) let len -= patternLen - index endif let retstr .= repeat( a:pattern, len / patternLen ) if len % patternLen let retstr .= strpart( a:pattern, 0, len % patternLen ) endif return retstr endfunction " READARGS {{{2 " ____________________________________________________________________________ " " Checking for invalid arguments, evaulating values and returns with the " Dictionary of them or '' on error. " " __ ARGUMENTS __________________________ " " line String The line that have to be formated. " " The others are the same as for EightHeader(). " " __ RETURN _____________________________ " " s:str String Blank-striped string that you want to decorate. " s:indent String Indent before s:str. " s:strLen Number Displaywidth of s:str. " s:indentLen Number Displaywidth of s:indent. " function! s:ReadArgs( line, length, align, pattern, marker, str ) let args = {} " Evaulating values. let s:indent = matchstr( a:line, '^[[:blank:]]*' ) let s:indentLen = strdisplaywidth( s:indent ) let s:str = substitute( a:line, '^[[:blank:]]\+\|[[:blank]]\+$', '', 'g' ) let s:strLen = strdisplaywidth( s:str ) let args['length'] = s:Value( a:length ) let args['align'] = s:Value( a:align ) let args['marker'] = s:Value( a:marker ) " Checking for errors. if ! s:IsNumber( args['length'] ) call s:Error( '{length} have to be number: ' . string( a:length ) ) return '' endif if a:align !~ '^\(left\|center\|right\)$' call s:Error( '{align} have to be "left", "right" or "center": ' . string( a:align ) ) return '' endif if ! ((s:IsList( a:pattern ) && (len( a:pattern ) == 3)) || s:IsString( a:pattern )) call s:Error( '{pattern} have to be string or list with 3 items: ' . string( a:pattern ) ) return '' endif " Evaulating pattern and str. if s:IsList( a:pattern ) let args['patternLeftEnd'] = s:Value( a:pattern[0] ) let args['pattern'] = s:Value( a:pattern[1] ) let args['patternRightEnd'] = s:Value( a:pattern[2] ) else let args['patternLeftEnd'] = '' let args['pattern'] = s:Value( a:pattern ) let args['patternRightEnd'] = '' endif if len( a:str ) let s:str = s:Value( a:str ) let s:strLen = strdisplaywidth( s:str ) endif return args endfunction " HEADER {{{2 " ____________________________________________________________________________ " " The main function: formats the uncommented s:str and returns with it. " The {args} is a ditionary returned by s:ReadArgs(). function! s:Header( args ) " Preparing. let length = a:args['length'] if length >= 0 let length -= s:indentLen else let length = abs( length ) endif let length -= s:strLen let length -= strdisplaywidth( a:args['patternLeftEnd'] . a:args['patternRightEnd'] ) if len( a:args['marker'] ) let length -= strdisplaywidth( a:args['marker'] ) endif if a:args['align'] == 'left' let patternLeftLen = 0 let patternRightLen = length elseif a:args['align'] == 'right' let patternLeftLen = length let patternRightLen = 0 elseif a:args['align'] == 'center' let patternLeftLen = length / 2 let patternRightLen = length / 2 + length % 2 endif " Main work. let retstr = s:indent let retstr .= a:args['patternLeftEnd'] let retstr .= s:Pattern( 0, patternLeftLen, a:args['pattern'] ) if len( s:str ) let retstr .= s:str endif let retstr .= s:Pattern( patternLeftLen + s:strLen , patternRightLen, a:args['pattern'] ) let retstr .= a:args['patternRightEnd'] let retstr .= a:args['marker'] " Remove the trailing spaces. (for example if the pattern is ' ' and no " marker set) " " FIXME: " Sometimes it's a bad idea, for example if you want to align to center: " /* stg */ -> /* stg */ return substitute( retstr, '[[:blank:]]\+$', '', '' ) endfunction " EIGHTHEADER {{{1 " ============================================================================ " " Creates a (commented) header. " " __ ARGUMENTS __________________________ " " length Value Length of the header. " align Value Alignment of text. " oneline Number {line} and {marker} in one line, {pattern} in another. " pattern Value Pattern to fill with. " marker Value Extra content after patternRightEnd. " str Value Replace the content of {line} with this. " " FIXME: " Check errors before uncommenting. function! EightHeader( length, align, oneline, pattern, marker, str ) " Preparing. let firstLine = line( '.' ) let lastLine = firstLine let line = getline( '.' ) " Uncommenting. let commentLen = 0 if exists( 'g:EightHeader_uncomment' ) && exists( 'g:EightHeader_comment' ) execute g:EightHeader_uncomment let lineUncommented = getline( '.' ) let commentLen = strdisplaywidth( line ) - strdisplaywidth( lineUncommented ) if commentLen let line = lineUncommented endif endif " Evaulating arguments. let args = s:ReadArgs( line, a:length, a:align, a:pattern, a:marker, a:str ) if ! len( args ) return -1 endif let args['length'] -= (args['length'] < 0) ? 0 : commentLen " Doing main stuff. if a:oneline call setline( '.', s:Header( args ) ) else call setline( '.', s:Header( s:ReadArgs( line, args['length'], a:align, ' ', a:marker, a:str ) ) ) call append( '.', s:Header( s:ReadArgs( s:indent, args['length'], a:align, a:pattern, '', '' ) ) ) let lastLine += 1 endif " Commenting again if necessary. Puting an 'X' after the indenting to align " the comment operators to the left. silent! foldopen! if commentLen silent execute firstLine . ',' . lastLine . ' call setline( ".", substitute( getline( "." ), "^' . s:indent . '", "&X", "" ) )' execute firstLine . ',' . lastLine . ' ' . g:EightHeader_comment silent execute firstLine . ',' . lastLine . ' call setline( ".", substitute( getline( "." ), "^\\([^X]\\+\\)X", "\\=submatch( 1 )", "" ) )' endif endfunction " EIGHTHEADERFOLDS {{{1 " ============================================================================ " " Returning whith the modified foldheader. function! EightHeaderFolds( length, align, pattern, marker, str ) let s:fullwidth = winwidth( 0 ) - (&number ? &numberwidth : 0) - &foldcolumn let s:foldlines = v:foldend - v:foldstart + 1 " Geting the text of foldheader from the original foldtext(). let args = s:ReadArgs( substitute( foldtext(), '^.\{-}: \|[[:blank:]]\+$', '', 'g' ), a:length, a:align, a:pattern, a:marker, a:str ) if ! len( args ) return -1 endif return s:Header( args ) endfunction " EIGHTHEADERCALL {{{1 " ============================================================================ " " Returning whith the modified {line} or -1 if there was an error. Does not " commenting! See EightHeader() for arguments. function! EightHeaderCall( line, length, align, pattern, marker, str ) let args = s:ReadArgs( a:line, a:length, a:align, a:pattern, a:marker, a:str ) if ! len( args ) return -1 endif return s:Header( args ) endfunction