mirror of
https://github.com/inkarkat/vim-ingo-library.git
synced 2026-05-29 11:18:51 +02:00
253 lines
10 KiB
VimL
253 lines
10 KiB
VimL
" ingo/avoidprompt.vim: Functions for echoing text without the hit-enter prompt.
|
|
"
|
|
" DESCRIPTION:
|
|
" When using the :echo or :echomsg commands with a long text, Vim will show a
|
|
" 'Hit ENTER' prompt (|hit-enter|), so that the user has a chance to actually
|
|
" read the entire text. In most cases, this is good; however, some mappings
|
|
" and custom commands just want to echo additional, secondary information
|
|
" without disrupting the user. Especially for mappings that are usually
|
|
" repeated quickly "/foo<CR>, n, n, n", a hit-enter prompt would be highly
|
|
" irritating.
|
|
" This script provides an :echo replacement which truncates lines so that the
|
|
" hit-enter prompt doesn't happen. The echoed line is too long if it is wider
|
|
" than the width of the window, minus cmdline space taken up by the ruler and
|
|
" showcmd features. The non-standard widths of <Tab>, unprintable (e.g. ^M)
|
|
" and double-width characters (e.g. Japanese Kanji) are taken into account.
|
|
|
|
" DEPENDENCIES:
|
|
" - ingo/compat.vim autoload script
|
|
" - ingo/strdisplaywidth.vim autoload script
|
|
"
|
|
" TODO:
|
|
" - Consider 'cmdheight', add argument isSingleLine.
|
|
"
|
|
" Copyright: (C) 2008-2018 Ingo Karkat
|
|
" The VIM LICENSE applies to this script; see ':help copyright'.
|
|
"
|
|
" Maintainer: Ingo Karkat <ingo@karkat.de>
|
|
|
|
function! ingo#avoidprompt#MaxLength()
|
|
let l:maxLength = &columns
|
|
|
|
" Account for space used by elements in the command-line to avoid
|
|
" 'Hit ENTER' prompts.
|
|
" If showcmd is on, it will take up 12 columns.
|
|
" If the ruler is enabled, but not displayed in the status line, it
|
|
" will in its default form take 17 columns. If the user defines
|
|
" a custom &rulerformat, they will need to specify how wide it is.
|
|
if has('cmdline_info')
|
|
if &showcmd == 1
|
|
let l:maxLength -= 12
|
|
else
|
|
let l:maxLength -= 2 " Ruler does not occupy the last cell, and there has to be one empty cell between ruler and message.
|
|
endif
|
|
if &ruler == 1 && has('statusline') && ((&laststatus == 0) || (&laststatus == 1 && winnr('$') == 1))
|
|
if &rulerformat == ''
|
|
" Default ruler is 17 chars wide.
|
|
let l:maxLength -= 17
|
|
elseif exists('g:rulerwidth')
|
|
" User specified width of custom ruler.
|
|
let l:maxLength -= g:rulerwidth
|
|
else
|
|
" Don't know width of custom ruler, make a conservative
|
|
" guess.
|
|
let l:maxLength -= &columns / 2
|
|
endif
|
|
endif
|
|
else
|
|
let l:maxLength -= 1 " Cannot occupy the last cell in the line.
|
|
endif
|
|
return l:maxLength
|
|
endfunction
|
|
|
|
if ! exists('g:IngoLibrary_TruncateEllipsis')
|
|
let g:IngoLibrary_TruncateEllipsis = (&encoding ==# 'utf-8' ? "\u2026" : '...')
|
|
endif
|
|
function! ingo#avoidprompt#TruncateTo( text, length, ... )
|
|
"*******************************************************************************
|
|
"* PURPOSE:
|
|
" Truncate a:text to a maximum of a:length virtual columns by dropping text in
|
|
" the middle of a:text if necessary. This is based on what Vim does when for
|
|
" example echoing a very long search pattern during n/N.
|
|
"* SEE ALSO:
|
|
" - ingo#strdisplaywidth#TruncateTo() does something similar, but truncates at
|
|
" the end, and doesn't account for buffer-local tabstop values.
|
|
"* ASSUMPTIONS / PRECONDITIONS:
|
|
" The ellipsis can be configured by g:IngoLibrary_TruncateEllipsis.
|
|
"* EFFECTS / POSTCONDITIONS:
|
|
" none
|
|
"* INPUTS:
|
|
" a:text Text which may be truncated to fit.
|
|
" a:length Maximum virtual columns for a:text.
|
|
" a:reservedColumns Optional number of columns that are already taken in the
|
|
" line (before a:text, this matters for tab rendering); if
|
|
" specified, a:text will be truncated to (MaxLength() -
|
|
" a:reservedColumns).
|
|
" a:truncationIndicator Optional text to be appended when truncation
|
|
" appears. a:text is further reduced to account for
|
|
" its width. Default is "..." or the single-char UTF-8
|
|
" variant if the encoding also is UTF-8.
|
|
"* RETURN VALUES:
|
|
" Truncated a:text.
|
|
"*******************************************************************************
|
|
if a:length <= 0
|
|
return ''
|
|
endif
|
|
let l:reservedColumns = (a:0 > 0 ? a:1 : 0)
|
|
let l:reservedPadding = repeat(' ', l:reservedColumns)
|
|
let l:truncationIndicator = (a:0 >= 2 ? a:2 : g:IngoLibrary_TruncateEllipsis)
|
|
|
|
" The \%<23v regexp item uses the local 'tabstop' value to determine the
|
|
" virtual column. As we want to echo with default tabstop 8, we need to
|
|
" temporarily set it up this way.
|
|
let l:save_ts = &l:tabstop
|
|
setlocal tabstop=8
|
|
|
|
let l:text = a:text
|
|
try
|
|
if ingo#strdisplaywidth#HasMoreThan(l:reservedPadding . l:text, a:length + l:reservedColumns)
|
|
let l:ellipsisLength = ingo#compat#strchars(l:truncationIndicator)
|
|
|
|
" Handle pathological cases.
|
|
if a:length == l:ellipsisLength
|
|
return l:truncationIndicator
|
|
elseif a:length < l:ellipsisLength
|
|
return ingo#compat#strcharpart(l:truncationIndicator, 0, a:length)
|
|
endif
|
|
|
|
" Consider the length of the (configurable) "..." ellipsis.
|
|
" 1 must be added because columns start at 1, not 0.
|
|
let l:length = a:length - l:ellipsisLength + 1
|
|
let l:frontCol = l:length / 2
|
|
let l:backCol = (l:length % 2 == 0 ? (l:frontCol - 1) : l:frontCol)
|
|
"**** echomsg '**** ' a:length ':' l:frontCol '-' l:backCol
|
|
while 1
|
|
let l:fullText =
|
|
\ ingo#strdisplaywidth#strleft(l:reservedPadding . l:text, l:frontCol) .
|
|
\ l:truncationIndicator .
|
|
\ ingo#strdisplaywidth#strright(l:text, l:backCol)
|
|
|
|
" The strright() cannot precisely account for the rendering of
|
|
" tab widths. Check the result, and if necessary, remove further
|
|
" characters until we go below the limit.
|
|
if ! ingo#strdisplaywidth#HasMoreThan(l:fullText, a:length + l:reservedColumns)
|
|
let l:text = strpart(l:fullText, l:reservedColumns)
|
|
break
|
|
endif
|
|
if l:backCol > 0
|
|
let l:backCol -= 1
|
|
else
|
|
let l:frontCol -= 1
|
|
endif
|
|
endwhile
|
|
endif
|
|
finally
|
|
let &l:tabstop = l:save_ts
|
|
endtry
|
|
return l:text
|
|
endfunction
|
|
function! ingo#avoidprompt#Truncate( text, ... )
|
|
"*******************************************************************************
|
|
"* PURPOSE:
|
|
" Truncate a:text so that it can be echoed to the command line without causing
|
|
" the "Hit ENTER" prompt (if desired by the user through the 'shortmess'
|
|
" option). Truncation will only happen in (the middle of) a:text.
|
|
"* ASSUMPTIONS / PRECONDITIONS:
|
|
" none
|
|
"* EFFECTS / POSTCONDITIONS:
|
|
" none
|
|
"* INPUTS:
|
|
" a:text Text which may be truncated to fit.
|
|
" a:reservedColumns Optional number of columns that are already taken in the
|
|
" line (before a:text, this matters for tab rendering); if
|
|
" specified, a:text will be truncated to (MaxLength() -
|
|
" a:reservedColumns).
|
|
" a:truncationIndicator Optional text to be appended when truncation
|
|
" appears. a:text is further reduced to account for
|
|
" its width. Default is "..." or the single-char UTF-8
|
|
" variant if the encoding also is UTF-8.
|
|
"* RETURN VALUES:
|
|
" Truncated a:text.
|
|
"*******************************************************************************
|
|
if &shortmess !~# 'T'
|
|
" People who have removed the 'T' flag from 'shortmess' want no
|
|
" truncation.
|
|
return a:text
|
|
endif
|
|
|
|
let l:reservedColumns = (a:0 > 0 ? a:1 : 0)
|
|
let l:maxLength = ingo#avoidprompt#MaxLength() - l:reservedColumns
|
|
|
|
return call('ingo#avoidprompt#TruncateTo', [a:text, l:maxLength] + a:000)
|
|
endfunction
|
|
|
|
function! ingo#avoidprompt#TranslateLineBreaks( text )
|
|
"*******************************************************************************
|
|
"* PURPOSE:
|
|
" Translate embedded line breaks in a:text into a printable characters to
|
|
" avoid that a single-line string is split into multiple lines (and thus
|
|
" broken over multiple lines or mostly obscured) by the :echo command and
|
|
" ingo#avoidprompt#Echo() functions.
|
|
"
|
|
" For the :echo command, strtrans() is not necessary; unprintable characters
|
|
" are automatically translated (and shown in a different highlighting, an
|
|
" advantage over indiscriminate preprocessing with strtrans()). However, :echo
|
|
" observes embedded line breaks (in contrast to :echomsg), which would mess up
|
|
" a single-line message that contains embedded \n = <CR> = ^M or <LF> = ^@.
|
|
"
|
|
" For the :echomsg and :echoerr commands, neither strtrans() nor this function
|
|
" are necessary; all translation is done by the built-in command.
|
|
"
|
|
"* LIMITATIONS:
|
|
" When :echo'd, the translated line breaks are not rendered with the typical
|
|
" 'SpecialKey' highlighting.
|
|
"
|
|
"* ASSUMPTIONS / PRECONDITIONS:
|
|
" ? List of any external variable, control, or other element whose state affects this procedure.
|
|
"* EFFECTS / POSTCONDITIONS:
|
|
" ? List of the procedure's effect on each external variable, control, or other element.
|
|
"* INPUTS:
|
|
" a:text Text.
|
|
"* RETURN VALUES:
|
|
" Text with translated line breaks; the text will :echo into a single line.
|
|
"*******************************************************************************
|
|
return substitute(a:text, "[\<CR>\<LF>]", '\=strtrans(submatch(0))', 'g')
|
|
endfunction
|
|
|
|
function! ingo#avoidprompt#Echo( text )
|
|
echo ingo#avoidprompt#Truncate(a:text)
|
|
endfunction
|
|
function! ingo#avoidprompt#EchoMsg( text )
|
|
"******************************************************************************
|
|
"* PURPOSE:
|
|
" Echo as much as can be viewed in the command-line area of a:text, while
|
|
" saving the full message in the message history. This way, there's no
|
|
" hit-enter prompt, but the user can still recall the message history to see
|
|
" the full message (in case important bits were truncated).
|
|
"* ASSUMPTIONS / PRECONDITIONS:
|
|
" None.
|
|
"* EFFECTS / POSTCONDITIONS:
|
|
" Echos a:text and saves it in the message history. May redraw the window.
|
|
"* INPUTS:
|
|
" a:text Text which may be truncated to fit.
|
|
"* RETURN VALUES:
|
|
" None.
|
|
"******************************************************************************
|
|
let l:truncatedText = ingo#avoidprompt#Truncate(a:text)
|
|
echomsg a:text
|
|
if l:truncatedText !=# a:text
|
|
" Need to overwrite the overly long message (it's still in full in the
|
|
" message history).
|
|
redraw " This avoids the hit-enter prompt.
|
|
echo l:truncatedText | " Use :echo because the full text already is in the message history.
|
|
endif
|
|
endfunction
|
|
function! ingo#avoidprompt#EchoAsSingleLine( text )
|
|
echo ingo#avoidprompt#Truncate(ingo#avoidprompt#TranslateLineBreaks(a:text))
|
|
endfunction
|
|
function! ingo#avoidprompt#EchoMsgAsSingleLine( text )
|
|
call ingo#avoidprompt#EchoMsg(ingo#avoidprompt#TranslateLineBreaks(a:text))
|
|
endfunction
|
|
|
|
" vim: set ts=8 sts=4 sw=4 noexpandtab ff=unix fdm=syntax :
|