Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 166 additions & 0 deletions autoload/phpactor/quickfix.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
function! phpactor#quickfix#strategy() abort
if !has_key(g:, 'PhpactorQuickfixStrategy')
return s:auto_detect_strategy()
endif
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would still prefer to define all available keys in the same place, which is here currently. Note also that options are camelCase.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this can then safely assume that g:phpactorQuickfixStrategy exists.

Also still not sure quickfix is the right term. We use quickfix to solve a problem, the problem is navigating and showing code references. So it's really a strategy for that? e.g. phpactorReferenceNavigation? For me it might make sense to say phpactorReferenceNavigation = "quickfix"

Saying that, I won't block on this, also hapy to leave it as is.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would still prefer to define all available keys in the same place, which is here currently. Note also that options are camelCase.

Can't use camel case with Funcref, it must be pascal case, otherwise vim raise an error.

Also still not sure quickfix is the right term.

From the vim doctumentation:

In Vim the quickfix commands are used more generally to find a list of positions in files.

The quickfix is just the way vim show positions in file in a way that the user can jump to those positions.

Copy link
Copy Markdown
Collaborator

@dantleech dantleech Oct 30, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The quickfix is just the way vim show positions in file in a way that the user can jump to those positions.

It's the way vim shows positions - Phpactor could use quickfix or not, it's an implementation for showing and jumping to positions. We could use FZF without using quickfix at all, but it's fine, it makes sense :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could use FZF without using quickfix at all, but it's fine, it makes sense

Absolutely, I just think we don't need a new word since we are naming a configuration variable for the vim plugin it's easier for the users to have the same language than vim. :)


let Strategy = g:PhpactorQuickfixStrategy
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not blocking: should be lowercase S ?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or this is a naming convention for functions?


if type(function('tr')) == type(Strategy)
return Strategy
endif

if type('') != type(Strategy) || 0 == count(['fzf', 'vim'], Strategy)
echoerr 'Invalid strategy provided, check the value of "g:PhpactorQuickfixStrategy"'

return s:auto_detect_strategy()
endif

return function('phpactor#quickfix#'. Strategy)
endfunction

function! s:auto_detect_strategy()
let strategy = 'vim'

if exists("*fzf#complete")
let strategy = 'fzf'
endif

return function('phpactor#quickfix#'. strategy)
endfunction

function! phpactor#quickfix#vim(entries) abort
call setqflist(a:entries)
cw
endfunction

function! phpactor#quickfix#build(entries) abort
try
let Strategy = phpactor#quickfix#strategy()
call call(Strategy, [a:entries])
catch /E117/
redraw!
echo 'The strategy "'. string(Strategy) .'" is unknown, check the value of "g:PhpactorQuickfixStrategy".'
endtry
endfunction

function! phpactor#quickfix#fzf(entries) abort
let entries = {}
for entry in a:entries
let key = s:relative_path(entry['filename'])
\ .':'. entry['lnum']
\ .':'. (entry['col'])
\ .':'. entry['text']

let entries[key] = entry
endfor

let formated = s:align_pairs(keys(entries), '^\(.\{-}:\d\+:\d\+:\)\s*\(.*\)\s*$', 100)

let tmp = copy(entries)
let results = {}
for key in keys(tmp)
let newKey = formated[key]
let results[newKey] = tmp[key]
endfor
unlet tmp

let actions = {
\ 'ctrl-t': 'tab split',
\ 'ctrl-x': 'split',
\ 'ctrl-v': 'vsplit',
\ 'ctrl-q': function('phpactor#quickfix#vim')
\ }

call fzf#run(fzf#wrap('find_references', fzf#vim#with_preview({
\ 'source': keys(results),
\ 'down': '60%',
\ '_action': actions,
\ 'sink*': function('<SID>quickfix_sink', [results, actions]),
\ 'options': [
\ '--expect='. join(keys(actions), ','),
\ '--multi',
\ '--bind=ctrl-a:select-all,ctrl-d:deselect-all',
\ '--inline-info',
\ '--header', ":: Press \x1b[35mCTRL-Q\x1b[m to open the quickfix with your selection",
\ '--delimiter=:', '--nth=1,4'
\ ]}, 'up', '?'), 1))
endfunction

function! s:quickfix_sink(results, actions, lines) abort
if 2 > len(a:lines)
return " Don't know how to handle this, should not append
endif

let actionKey = remove(a:lines, 0)
let Action = get(a:actions, actionKey, 'e')
let items = map(copy(a:lines), {key, value -> a:results[value]})

if type(function('call')) == type(Action)
return Action(items)
endif

if len(a:lines) > 1
augroup fzf_swap
autocmd SwapExists * let v:swapchoice='o' | echohl WarningMsg
\| echom 'fzf: E325: swap file exists: '. expand('<afile>')
\| echohl None
augroup END
endif

try
let empty = empty(expand('%')) && 1 == line('$') && empty(getline(1)) && !&modified
let autochdir = &autochdir
set noautochdir

for item in items
let filename = fnameescape(item.filename)
let Action = empty ? 'e' : Action " Use the current buffer if empty

execute Action '+'.item.lnum filename
execute 'normal!' item.col .'|'
normal! zz

if empty
let empty = v:false
endif

if !has('patch-8.0.0177') && !has('nvim-0.2') && exists('#BufEnter')
\ && isdirectory(item.filename)
doautocmd BufEnter
endif
endfor
catch /^Vim:Interrupt$/
finally
let &autochdir = autochdir
silent! autocmd! fzf_swap
endtry
endfunction

function! s:align_pairs(list, regexp, ...) abort
let maxlen = 0
let pairs = {}
for elem in a:list
let match = matchlist(elem, a:regexp)
let [filename, text] = match[1:2]
let maxlen = max([maxlen, len(filename)])
let pairs[elem] = [filename, text]
endfor

let args = copy(a:000)
let max = 60
if 0 < len(args) && type(v:t_number) == type(args[0])
let max = remove(args, 0)
endif

let maxlen = min([maxlen, max])

return map(pairs, "printf('%-'.maxlen.'s', v:val[0]).' '.v:val[1]")
endfunction

function! s:relative_path(absolute_path)
let l:cwd = getcwd()

return substitute(a:absolute_path, l:cwd .'/', '', '')
endfunction

" vim: et ts=4 sw=4 fdm=marker
65 changes: 65 additions & 0 deletions doc/vim-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Phpactor VIM Plugin
- [Completion](#completion)
- [Completion plugins](#completion-plugins)
- [Context Menu](#context-menu)
- [Quickfix List](#quickfix-list)
- [Extras](#extras)

Installation
------------
Expand Down Expand Up @@ -173,6 +175,7 @@ let g:phpactorPhpBin = 'php'
let g:phpactorBranch = 'master'
let g:phpactorOmniAutoClassImport = v:true
let g:phpactorInputListStrategy = 'inputlist|fzf'
let g:phpactorQuickfixStrategy = 'vim|fzf|Funcref'

" Example of implementation with vim's inputlist() function
function! InputListCustomStrategy(label, choices, ResultHandler)
Expand Down Expand Up @@ -328,3 +331,65 @@ Use `<tab>` to toggle selection and CTRL-A/CTRL-D to select all/select none.
See the
[Fzf](https://github.com/junegunn/fzf) documentation for more details.

Quickfix List
-------------

The quickfix list is used to show a list of positions in files and access them
quickly. Phpactor use it for example to show the result of `find references`.

### Strategies

This plugin provides two strategy to handle input lists:

- `vim`: Vim's basic quickfix.
- `fzf`: Use [fzf](#fzf) to show allow you to filter the results.

With the [fzf](#fzf) strategy you will still be able to get the result inside
the quickfix by selecting the elements you are interested in and pressing
`ctrl-q` to populate the quickfix with your selection and open it.

You can use your own strategy by providing a `Funcref` to the
`g:phpactorQuickfixStrategy` variable. When calling a strategy the list of
positions is given as a list that is directly usable by the function
`setqflist()`. Not all the keys are provided, depending on the context, so
you should always check if the information you want is really defined before
using it.

Extras
------

In order to get the best experience we suggest you install a few extra tools.

[fzf](https://github.com/junegunn/fzf)
--------------------------------------

This is actually not a vim plugin but a tool for the command-line.
It's shipped with a vim plugin that allows to use it inside Vim.

If you have it installed and properly configured for Vim then `Phpactor` will
use of it to provide enhance functionalities, for instance:

- [inputlist](#input-list)
- [quickfix](#quickfix-list)

[fzf.vim](https://github.com/junegunn/fzf.vim)
----------------------------------------------

This is the actual [fzf](https://github.com/junegunn/fzf) plugin for vim. It
requires you to have [fzf](https://github.com/junegunn/fzf) installed and
configured.

This plugin will allow us to use improved functionalities inside Vim. If you
want to enjoy the full possibilities of both `fzf` and `Phpactor` we strongly
recommand you to install it!

[bat](https://github.com/sharkdp/bat)
---

This is also a tool for the command-line and not a Vim plugin. It's meant to be
used instead of the command `cat` and bring a lot to the table. It can be use
as a powerful substitute for `cat` providing syntax highlighting and git
integration, among other things.

It's used by default, among other possible tools, by `fzf` to print the preview
window. Allowing you to have a preview of your files with syntaxic coloration!
62 changes: 30 additions & 32 deletions plugin/phpactor.vim
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
" ______ __ __ .______ ___ ______ .___________. ______ .______
" | _ \ | | | | | _ \ / \ / || | / __ \ | _ \
" | |_) | | |__| | | |_) | / ^ \ | ,----'`---| |----`| | | | | |_) |
" | ___/ | __ | | ___/ / /_\ \ | | | | | | | | | /
" ______ __ __ .______ ___ ______ .___________. ______ .______
" | _ \ | | | | | _ \ / \ / || | / __ \ | _ \
" | |_) | | |__| | | |_) | / ^ \ | ,----'`---| |----`| | | | | |_) |
" | ___/ | __ | | ___/ / /_\ \ | | | | | | | | | /
" | | | | | | | | / _____ \ | `----. | | | `--' | | |\ \----.
" | _| |__| |__| | _| /__/ \__\ \______| |__| \______/ | _| `._____|
"
"

if exists('g:phpactorLoaded')
finish
Expand All @@ -19,7 +19,7 @@ let g:_phpactorCompletionMeta = {}

if !exists('g:phpactorPhpBin')
let g:phpactorPhpBin = 'php'
endif
endif

if !exists('g:phpactorBranch')
let g:phpactorBranch = 'master'
Expand Down Expand Up @@ -89,8 +89,8 @@ function! phpactor#Complete(findstart, base)

if !empty(suggestions)
for suggestion in suggestions
let completion = {
\ 'word': suggestion['name'],
let completion = {
\ 'word': suggestion['name'],
\ 'abbr': phpactor#_completeTruncateLabel(suggestion['label'], g:phpactorCompleteLabelTruncateLength),
\ 'menu': suggestion['short_description'],
\ 'kind': suggestion['type'],
Expand Down Expand Up @@ -132,9 +132,9 @@ function! phpactor#_completeImportClass(completedItem)

if !empty(get(suggestion, "class_import", ""))
call phpactor#rpc("import_class", {
\ "qualified_name": suggestion['class_import'],
\ "offset": phpactor#_offset(),
\ "source": phpactor#_source(),
\ "qualified_name": suggestion['class_import'],
\ "offset": phpactor#_offset(),
\ "source": phpactor#_source(),
\ "path": expand('%:p')})
endif

Expand Down Expand Up @@ -218,21 +218,21 @@ endfunction
" RPC Proxy methods
"""""""""""""""""""""""""""
function! phpactor#_GotoDefinitionTarget(target)
call phpactor#rpc("goto_definition", {
\"offset": phpactor#_offset(),
\"source": phpactor#_source(),
\"path": expand('%:p'),
call phpactor#rpc("goto_definition", {
\"offset": phpactor#_offset(),
\"source": phpactor#_source(),
\"path": expand('%:p'),
\"target": a:target,
\'language': &ft})
endfunction
function! phpactor#GotoDefinition()
call phpactor#_GotoDefinitionTarget('focused_window')
endfunction
function! phpactor#GotoImplementations()
call phpactor#rpc("goto_implementation", {
\"offset": phpactor#_offset(),
\"source": phpactor#_source(),
\"path": expand('%:p'),
call phpactor#rpc("goto_implementation", {
\"offset": phpactor#_offset(),
\"source": phpactor#_source(),
\"path": expand('%:p'),
\"target": 'focused_window',
\'language': &ft})
endfunction
Expand Down Expand Up @@ -583,11 +583,19 @@ function! phpactor#_rpc_dispatch(actionName, parameters)

" >> file references
if a:actionName == "file_references"
let list = []
" if there is only one file, and it is the open file, don't
" bother opening the quick fix window
if len(a:parameters['file_references']) == 1
let fileRefs = a:parameters['file_references'][0]
if -1 != match(fileRefs['file'], bufname('%') . '$')
return
endif
endif

let results = []
for fileReferences in a:parameters['file_references']
for reference in fileReferences['references']
call add(list, {
call add(results, {
\ 'filename': fileReferences['file'],
\ 'lnum': reference['line_no'],
\ 'col': reference['col_no'] + 1,
Expand All @@ -596,18 +604,8 @@ function! phpactor#_rpc_dispatch(actionName, parameters)
endfor
endfor

call setqflist(list)

" if there is only one file, and it is the open file, don't
" bother opening the quick fix window
if len(a:parameters['file_references']) == 1
let fileRefs = a:parameters['file_references'][0]
if -1 != match(fileRefs['file'], bufname('%') . '$')
return
endif
endif
call phpactor#quickfix#build(results)

execute ':cwindow'
return
endif

Expand Down