Initial commit

This commit is contained in:
Ernst Widerberg 2021-01-28 22:51:56 +01:00
commit 9d395d316f
8 changed files with 790 additions and 0 deletions

36
README.md Normal file
View file

@ -0,0 +1,36 @@
# vim-secret
<p align='center'>
<img src="./example.svg">
</p>
## Usage
<table>
<tr>
<td><code>:Secret</code></td>
<td>Enable secret view.</td>
</tr>
<td><code>:Secret (line | word | char | none)</code></td>
<td>Enable secret view with a specific visibility setting.</td>
<tr>
<td><code>:Secret!</code></td>
<td>Disable secret view.</td>
</tr>
</table>
An area around the cursor is unhidden to enable you to see what you are typing. This can be the entire line, the current word, the current character, or it can be disabled completely.
After a duration of time without input, or invoked manually via a mapping, all characters in the buffer is hidden. For details on this and other configuration options, see [`:help secret`](./doc/secret.txt).
## FAQ
> Replacement characters have a background color, which looks weird.
This is due to how your color scheme styles the `Conceal` highlight group. To remove the background color, you could use something like the following in your `vimrc`/`init.vim`:
```vim
autocmd! VimEnter,ColorScheme * hi Conceal ctermbg=NONE guibg=NONE
```
See [`:help secret-highlight`](./doc/secret.txt#L74) for more info.

166
autoload/secret.vim Normal file
View file

@ -0,0 +1,166 @@
let s:patterns = {
\ 'char': ['\%#\S', '\S\%#'],
\ 'word': ['\S*\%#\s\@!\S*', '\S*\%#\S*'],
\ 'line': ['.*\%#.*', '.*\%#.*'],
\ 'none': ['\_.\@!', '\_.\@!'] }
function! s:pattern(name, mode)
if index(['char', 'word', 'line', 'none'], a:name) == -1
throw a:name
endif
return s:patterns[a:name][a:mode]
endfunction
" Clear augroup secret-WINID-BUFNR and remove BUFNR from w:secret_state
function! s:clear(w, b)
execute 'augroup secret-' . a:w . '-' . a:b
autocmd!
augroup END
execute 'augroup! secret-' . a:w . '-' . a:b
let active = win_getid()
if getwininfo(a:w) != []
call win_gotoid(a:w)
unlet w:secret_state[a:b]
if w:secret_state == {}
unlet w:secret_state
endif
endif
call win_gotoid(active)
endfunction
function! s:enable_mappings()
if exists('g:secret_quickhide')
execute 'nnoremap <buffer> <silent>'
\ g:secret_quickhide ':syntax clear SecretVisible<CR>'
endif
endfunction
function! s:disable_mappings()
if exists('g:secret_quickhide')
execute 'nunmap <buffer>' g:secret_quickhide
endif
endfunction
function! s:buf_win_autocmd(group, ...)
return 'autocmd ' . a:group . ' <buffer> if win_getid() == ' . win_getid()
\ . ' | ' . join(a:000, ' | ') . ' | endif'
endfunction
" Enable secret view
function! s:enable(npat, ipat, cchar)
if !exists('w:secret_state')
let w:secret_state = {}
endif
if !has_key(w:secret_state, bufnr())
let w:secret_state[bufnr()] = {}
let w:secret_state[bufnr()].syntax = &syntax
let w:secret_state[bufnr()].concealcursor = &concealcursor
let w:secret_state[bufnr()].conceallevel = &conceallevel
ownsyntax
setlocal concealcursor =nvic
setlocal conceallevel =2
endif
call s:enable_mappings()
syntax clear
execute 'syntax match SecretHidden ''\S'' conceal cchar=' . a:cchar . ' containedin=ALLBUT,SecretVisible'
execute 'syntax match SecretVisible ''' . a:npat . ''''
execute 'augroup secret-' . win_getid() . '-' . bufnr()
autocmd!
" Normal mode
execute (s:buf_win_autocmd('InsertLeave,CursorMoved',
\ 'syntax clear SecretVisible',
\ 'syntax match SecretVisible ''' . a:npat . ''''))
if g:secret_timeout_normal
execute (s:buf_win_autocmd('CursorHold', 'syntax clear SecretVisible'))
endif
" Insert mode
execute (s:buf_win_autocmd('InsertEnter,CursorMovedI',
\ 'syntax clear SecretVisible',
\ 'syntax match SecretVisible ''' . a:ipat . ''''))
if g:secret_timeout_insert
execute (s:buf_win_autocmd('CursorHoldI', 'syntax clear SecretVisible'))
endif
" Window left
execute (s:buf_win_autocmd('WinLeave',
\ 'syntax clear SecretVisible',
\ 'call s:disable_mappings()'))
" Window entered
execute (s:buf_win_autocmd('WinEnter', 'call s:enable_mappings()'))
" Window or buffer closed
execute 'autocmd WinEnter * if getwininfo(' . win_getid() . ') == []'
\ '| call s:clear(' . win_getid() . ', ' . bufnr() . ')'
\ '| call s:disable_mappings()'
\ '| endif'
execute 'autocmd BufDelete <buffer>'
\ ' call s:clear(' . win_getid() . ', ' . bufnr() . ')'
" Hidden buffer displayed
execute (s:buf_win_autocmd('BufWinEnter',
\ 'execute ''ownsyntax''',
\ 'syntax clear',
\ 'syntax match SecretHidden ''\S'' conceal cchar=' . a:cchar . ' containedin=ALLBUT,SecretVisible',
\ 'syntax match SecretVisible ''' . a:npat . ''''))
augroup END
endfunction
" Disable secret view
function! s:disable()
" If secret view not enabled for the current (window, buffer)
if !exists('w:secret_state') || !has_key(w:secret_state, bufnr())
return
endif
" Restore saved buffer state
syntax clear
execute 'set syntax =' . w:secret_state[bufnr()].syntax
execute 'setlocal concealcursor =' . w:secret_state[bufnr()].concealcursor
execute 'setlocal conceallevel =' . w:secret_state[bufnr()].conceallevel
" Clear autocommands and saved state
call s:clear(win_getid(), bufnr())
" Clear mappings
call s:disable_mappings()
endfunction
" Entry point for Secret command
function! secret#secret(enable, ...)
if a:enable
try
if a:0 == 0
call s:enable(
\ s:pattern(exists('g:secret_visibility_normal') ?
\ g:secret_visibility_normal : g:secret_visibility, 0),
\ s:pattern(exists('g:secret_visibility_insert') ?
\ g:secret_visibility_insert : g:secret_visibility, 1),
\ g:secret_cchar)
elseif a:0 == 1
call s:enable(s:pattern(a:1, 0), s:pattern(a:1, 1), g:secret_cchar)
elseif a:0 == 2
call s:enable(s:pattern(a:1, 0), s:pattern(a:2, 1), g:secret_cchar)
else
echohl WarningMsg
echo 'Secret: Too many arguments'
echohl None
endif
catch
echohl WarningMsg
echo 'Secret: Invalid argument "' . v:exception . '"'
echohl None
endtry
else
call s:disable()
endif
endfunction

91
doc/secret.txt Normal file
View file

@ -0,0 +1,91 @@
*secret.txt* *secret*
Requires |+conceal|.
==============================================================================
COMMANDS *:Secret*
:Secret Enable secret view.
:Secret! Disable secret view.
:Secret {visibility} Enable secret view with a given visibility setting.
See |secret-visibility| for possible values.
:Secret {visibility-normal} {visibility-insert}
Enable secret view with separate visibility settings
for normal and insert mode.
See |secret-visibility| for possible values.
==============================================================================
VISIBILITY SETTINGS *secret-visibility*
An area around the cursor is unhidden to enable you to see what you are
typing. This area is specified using the following values:
Value Description Example~
'line' The current line. The quick brown fox jumps over the lazy dog.
'word' The current |WORD|. ••• ••••• ••••• fox ••••• •••• ••• •••• ••••
'char' The current character. ••• ••••• ••••• ••x ••••• •••• ••• •••• ••••
'none' Do not show anything. ••• ••••• ••••• ••• ••••• •••• ••• •••• ••••
^ Cursor position~
==============================================================================
CONFIGURATION *secret-configuration*
*g:secret_cchar*
g:secret_cchar Replacement character used for hidden text.
Default: '•'
*g:secret_visibility*
g:secret_visibility Default visibility.
See |secret-visibility| for possible values.
Default: 'word'
*g:secret_visibility_normal*
g:secret_visibility_normal Default normal mode visibility.
See |secret-visibility| for possible values.
Default: |g:secret_visibility|
*g:secret_visibility_insert*
g:secret_visibility_insert Default insert mode visibility.
See |secret-visibility| for possible values.
Default: |g:secret_visibility|
*g:secret_timeout_normal*
g:secret_timeout_normal Hide entire buffer after 'updatetime' milliseconds
without input in normal mode.
Default: 1 (enabled)
*g:secret_timeout_insert*
g:secret_timeout_insert Hide entire buffer after 'updatetime' milliseconds
without input in insert mode.
Default: 0 (disabled)
==============================================================================
MAPPINGS *secret-mappings*
*g:secret_quickhide*
g:secret_quickhide Hide entire buffer until the cursor is moved. Set this
to a key sequence string: >
let g:secret_quickhide = '<Leader>q'
==============================================================================
HIGHLIGHTING *secret-highlight* *SecretVisible*
Two syntax groups are used, |Conceal| and `SecretVisible`. |Conceal| applies to
hidden text, and `SecretVisible` applies to the visible area surrounding the
cursor (see |secret-cursor|).
To extend an existing color scheme with your own highlighting for |Conceal| and
`SecretVisible`, you can for example use the following autocommand: >
autocmd! VimEnter,ColorScheme *
\ hi Conceal guifg=green |
\ hi SecretVisible guifg=red
Implementation note: It would be nice to be able to highlight hidden
characters separately from the global |Conceal| group but this is not
currently possible using Vim's conceal feature.
vim:tw=78:ft=help:norl:

208
example.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 100 KiB

20
makefile Normal file
View file

@ -0,0 +1,20 @@
VIM = vim -N -u NORC -i NONE --cmd 'set rtp=test/vim-vader packpath='
all: v nv
v: test/vim-vader
$(VIM) -c 'Vader! test/*.vader'
v-i: test/vim-vader
$(VIM) -c 'Vader test/*.vader'
nv: test/vim-vader
n$(VIM) --headless -c 'Vader! test/*.vader'
nv-i: test/vim-vader
n$(VIM) -c 'Vader test/*.vader'
test/vim-vader:
git clone https://github.com/junegunn/vader.vim test/vim-vader || ( cd test/vim-vader && git pull --rebase )
.PHONY: all v v-i nv nv-i

17
plugin/secret.vim Normal file
View file

@ -0,0 +1,17 @@
command! -bang -nargs=* Secret call secret#secret(<bang>1, <f-args>)
if !exists('g:secret_cchar')
let g:secret_cchar = '•'
endif
if !exists('g:secret_visibility')
let g:secret_visibility = 'word'
endif
if !exists('g:secret_timeout_normal')
let g:secret_timeout_normal = 1
endif
if !exists('g:secret_timeout_insert')
let g:secret_timeout_insert = 0
endif

1
test/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
vim-vader

251
test/test.vader Normal file
View file

@ -0,0 +1,251 @@
Execute (setup test environment):
set hidden
Execute (helper functions):
function! AssertSyntax(...)
for i in range(1, a:0)
let line = split(get(a:000, i-1), '\zs')
for j in range(1, len(line))
if get(line, j-1) == '•'
AssertEqual vader#helper#syntax_at(i, j), 'SecretHidden', 'Expected SecretHidden, got ' . vader#helper#syntax_at(i, j) . ' at line ' . i . ', column ' . j
elseif get(line, j-1) == ' '
AssertEqual vader#helper#syntax_at(i, j), '', 'Expected None, got ' . vader#helper#syntax_at(i, j) . ' at line ' . i . ', column ' . j
elseif get(line, j-1) == '-'
AssertEqual vader#helper#syntax_at(i, j), 'SecretVisible', 'Expected SecretVisible, got ' . vader#helper#syntax_at(i, j) . ' at line ' . i . ', column ' . j
endif
endfor
endfor
endfunction
command! -nargs=* AssertSyntax call AssertSyntax(<args>)
Before:
source plugin/secret.vim
source autoload/secret.vim
After:
Secret!
unlet! g:secret_cchar
unlet! g:secret_visibility
unlet! g:secret_visibility_normal
unlet! g:secret_visibility_insert
unlet! g:secret_timeout_normal
unlet! g:secret_timeout_insert
Given:
Lorem ipsum
dolor sit amet
Execute (enable/disable):
AssertSyntax ' ', ' '
Secret
AssertSyntax '----- •••••', '••••• ••• ••••'
Assert exists('w:secret_state')
Assert exists('#secret-' . win_getid() . '-' . bufnr())
Secret!
AssertSyntax ' ', ' '
Assert !exists('w:secret_state')
Assert !exists('#secret-' . win_getid() . '-' . bufnr())
Execute (word visibility):
Secret
AssertSyntax '----- •••••', '••••• ••• ••••'
normal jw
AssertSyntax '••••• •••••', '••••• --- ••••'
Execute (line visibility):
let g:secret_visibility = 'line'
Secret
AssertSyntax '-----------', '••••• ••• ••••'
normal j
AssertSyntax '••••• •••••', '--------------'
Execute (char visibility):
let g:secret_visibility = 'char'
Secret
AssertSyntax '-•••• •••••', '••••• ••• ••••'
normal wll
AssertSyntax '••••• ••-••', '••••• ••• ••••'
Execute (none visibility):
let g:secret_visibility = 'none'
Secret
AssertSyntax '••••• •••••', '••••• ••• ••••'
normal jww
AssertSyntax '••••• •••••', '••••• ••• ••••'
Execute (line, insert):
let g:secret_visibility_insert = 'line'
Secret
AssertSyntax '----- •••••', '••••• ••• ••••'
doautocmd InsertEnter
AssertSyntax '-----------', '••••• ••• ••••'
doautocmd InsertLeave
AssertSyntax '----- •••••', '••••• ••• ••••'
Execute (word, insert):
let g:secret_visibility_insert = 'word'
Secret
AssertSyntax '----- •••••', '••••• ••• ••••'
doautocmd InsertEnter
AssertSyntax '----- •••••', '••••• ••• ••••'
doautocmd InsertLeave
AssertSyntax '----- •••••', '••••• ••• ••••'
Execute (char, insert):
let g:secret_visibility_insert = 'char'
Secret
AssertSyntax '----- •••••', '••••• ••• ••••'
normal l
doautocmd InsertEnter
AssertSyntax '-•••• •••••', '••••• ••• ••••'
doautocmd InsertLeave
AssertSyntax '----- •••••', '••••• ••• ••••'
Execute (none, insert):
let g:secret_visibility_insert = 'none'
Secret
AssertSyntax '----- •••••', '••••• ••• ••••'
doautocmd InsertEnter
AssertSyntax '••••• •••••', '••••• ••• ••••'
doautocmd InsertLeave
AssertSyntax '----- •••••', '••••• ••• ••••'
Execute (timeout, normal):
Secret
AssertSyntax '----- •••••', '••••• ••• ••••'
doautocmd CursorHold
AssertSyntax '••••• •••••', '••••• ••• ••••'
Secret!
let g:secret_timeout_normal = 0
Secret
doautocmd CursorHold
AssertSyntax '----- •••••', '••••• ••• ••••'
Execute (timeout, insert):
" Not testable?
Execute (splits 1):
vsp
Secret
AssertSyntax '----- •••••', '••••• ••• ••••'
let w = win_getid()
let b = bufnr()
wincmd l
Assert !exists('w:secret_state')
Assert exists('#secret-' . w . '-' . b)
AssertSyntax ' ', ' '
wincmd h " Switch back to secret buffer so it is undone
Execute (splits 2):
Secret
vsp
Assert !exists('w:secret_state')
AssertSyntax ' ', ' '
wincmd l
doautocmd CursorMoved
AssertSyntax '----- •••••', '••••• ••• ••••'
Execute (leave window):
Secret
AssertSyntax '----- •••••', '••••• ••• ••••'
doautocmd WinLeave
AssertSyntax '••••• •••••', '••••• ••• ••••'
Execute (close buffer):
enew
let b = bufnr()
0put ='Lorem ipsum'
Secret
AssertSyntax '----- •••••'
bdelete!
Assert !exists('w:secret_state')
Assert !exists('#secret-' . win_getid() . '-' . b)
Execute (switching buffers in one window):
let a = bufnr()
Secret
AssertSyntax '----- •••••', '••••• ••• ••••'
enew
let b = bufnr()
Secret
0put ='Lorem ipsum'
AssertSyntax '----- •••••'
enew
let c = bufnr()
0put ='Lorem ibsum'
Secret
normal w
AssertSyntax '••••• -----'
Assert exists('#secret-' . win_getid() . '-' . a)
Assert exists('#secret-' . win_getid() . '-' . b)
Assert exists('#secret-' . win_getid() . '-' . c)
Assert has_key(w:secret_state, a)
Assert has_key(w:secret_state, b)
Assert has_key(w:secret_state, c)
execute 'b' a
AssertSyntax '----- •••••', '••••• ••• ••••'
execute 'b' b
AssertSyntax '----- •••••'
execute 'b' c
AssertSyntax '----- •••••'
execute 'b' a
execute 'bdelete!' c
Assert exists('#secret-' . win_getid() . '-' . a)
Assert exists('#secret-' . win_getid() . '-' . b)
Assert !exists('#secret-' . win_getid() . '-' . c)
Assert has_key(w:secret_state, a)
Assert has_key(w:secret_state, b)
Assert !has_key(w:secret_state, c)
execute 'bdelete!' b
Assert exists('#secret-' . win_getid() . '-' . a)
Assert !exists('#secret-' . win_getid() . '-' . b)
Assert !exists('#secret-' . win_getid() . '-' . c)
Assert has_key(w:secret_state, a)
Assert !has_key(w:secret_state, b)
Assert !has_key(w:secret_state, c)
b #
execute 'bdelete!' a
Assert !exists('#secret-' . win_getid() . '-' . a)
Assert !exists('#secret-' . win_getid() . '-' . b)
Assert !exists('#secret-' . win_getid() . '-' . c)
Assert !exists('w:secret_state')
Execute (quickhide mapping):
let g:secret_quickhide = '<C-e>'
Secret
AssertSyntax '----- •••••', '••••• ••• ••••'
normal 
AssertSyntax '••••• •••••', '••••• ••• ••••'
normal w
doautocmd CursorMoved
AssertSyntax '••••• -----', '••••• ••• ••••'
Execute (quickhide mapping, moving around):
let g:secret_quickhide = 'q'
vsp
Secret
normal q
AssertSyntax '••••• •••••', '••••• ••• ••••'
enew
0put ='Lorem ipsum'
AssertEqual maparg('q'), ''
b #
AssertEqual maparg('q'), ':syntax clear SecretVisible<CR>'
wincmd l
AssertEqual maparg('q'), ''
wincmd h
doautocmd CursorMoved
AssertSyntax '----- •••••', '••••• ••• ••••'
normal q
AssertSyntax '••••• •••••', '••••• ••• ••••'
Secret!
AssertEqual maparg('q'), ''