├── LICENSE
├── README.md
├── doc
├── poptools.txt
└── tags
├── lib
├── funcs.vim
└── unused.vim
├── plugin
└── vim_poptools.vim
└── vim_poptools.gif
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2023, Ubaldo Tiberi.
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | * Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vim-poptools
2 |
3 | Exploit popups as much as you can!
4 |
5 |
6 |
7 |
8 |
9 |
10 | * Vim-poptools *
11 |
12 |
13 | Poptools aims to scale your productivity by conveniently using popups for a
14 | multitude of tasks, from finding files and directories, to setting your
15 | favorite colorscheme.
16 | Once a list of results is slammed into a popup menu, you can filter it in an
17 | fuzzy or exact fashion.
18 |
19 | Poptools is more essential compared to similar plugins such as [fzf][0],
20 | [fuzzyy][1] or [scope][2] and differently from them, external programs are
21 | called _synchronously_, although things may change in the future. :)
22 |
23 | Nevertheless, I personally like the interface and how it displays all the
24 | results at once. Additionally, I find the opportunity of saving the last
25 | search very handy. The configuration is also fairly straightforward.
26 |
27 | ### Commands
28 |
29 | The following is what you can search and show in popups.
30 | The commands are self-explanatory:
31 |
32 | ```
33 | :PoptoolsFindFile
34 | :PoptoolsFindFileInPath # Takes into account the setting of :h 'path'.
35 | :PoptoolsFindDir # Search from the current directory downwards
36 | :PoptoolsBuffers
37 | :PoptoolsRecentFiles
38 | :PoptoolsCmdHistory
39 | :PoptoolsKill # When something goes wrong, you can clear all the Poptools popups
40 | :PoptoolsColorscheme # The displayed colors depends on the value of :h 'background'
41 | :PoptoolsGrepInBuffer # Find pattern in the current buffer
42 | :PoptoolsGrep # External grep. Grep command is displayed.
43 | :PoptoolsVimgrep # Vimgrep, show results in the quickfix-list instead of a popup.
44 | :PoptoolsLastSearch # Show the last search results
45 | ```
46 |
47 | ... and if you are curious, the following is how I mapped them in my `.vimrc`:
48 |
49 | ```
50 | nnoremap PoptoolsFindFile
51 | nnoremap l PoptoolsLastSearch
52 | nnoremap PoptoolsBuffers
53 | nnoremap o PoptoolsRecentFiles
54 | ```
55 | ## Configuration
56 |
57 | If you don't like the default behavior, there is room for some customization.
58 | The process is very easy. All you have to do is to set some entries in
59 | the `g:poptools_config` dictionary.
60 |
61 | However, keep in mind that you may also change the plugin behavior by through
62 | Vim the options `:h 'wildignore'`, `:h 'wildoptions'` and `:h 'path'`.
63 |
64 | ### Preview window
65 |
66 | You may not want the preview window in every case. For example, you want it
67 | when you _grep_ but not when you open recent files. If that is the case, do as
68 | it follows
69 |
70 | ```
71 | g:poptools_config = {}
72 | g:poptools_config['preview_grep'] = true
73 | g:poptools_config['preview_recent_files'] = false,
74 | ```
75 |
76 | ### Syntax highlight in the preview window
77 |
78 | Syntax highlight in the preview window can be handy, but it may slow down the
79 | user experience. You can avoid using syntax highlight in the preview window by
80 | setting `g:poptools_config['preview_syntax'] = false`. This is useful in case
81 | you are encountering troubles when using the preview window. The match are
82 | still highlighted.
83 |
84 |
85 | ### To fuzzy or not to fuzzy?
86 |
87 | You can filter the results either in a fuzzy or in an exact fashion. You choose
88 | it by setting `g:poptools_config['fuzzy_search']` to `true` or to `false`.
89 |
90 | It follows an example of configuration:
91 |
92 | ```
93 | g:poptools_config = {}
94 | g:poptools_config['preview_syntax'] = false
95 | g:poptools_config['preview_recent_files'] = false
96 | g:poptools_config['fuzzy_search'] = false
97 | ```
98 | To see the whole list of keys allowed in the `g:poptools_config` dictionary,
99 | take a look at `:h poptools.txt`.
100 |
101 | ## Some notes on files/patterns search
102 |
103 | ### `PoptoolsFindFile` and `PoptoolsFindInPath`
104 |
105 | These commands take into account the setting of `:h 'wildignore'`,
106 | `:h 'wildoptions'` and `:h 'path'` options, so if you want to include/exclude
107 | some search path, you must adjust such options.
108 |
109 | By default, hidden files are excluded. If you want to find them, then you must
110 | add `.` at the beginning of the search pattern, e.g. use `.git*` to get e.g.
111 | `.gitignore`.
112 |
113 | Hidden files are searched in non-hidden folders. To find files in a hidden
114 | folder, you must first `cd` into such a folder. For example, `cd ~/.vim`
115 | followed by `PopupFindFiles` will search files inside the `.vim` folder.
116 |
117 | ### `PoptoolsGrep` and `PoptoolsVimgrep`
118 |
119 | They use the internal `:h vimgrep` and the external `:h grep`. However, The
120 | user interface is the same. The results appear both in the quickfix-list and in
121 | the popup.
122 |
123 | By default, the option `'grepprg'` is set, as it follows:
124 |
125 | ```
126 | # Windows
127 | &grepprg =
128 | 'powershell -NoProfile -ExecutionPolicy Bypass -Command '
129 | .. '"& {Set-Location -LiteralPath ''' .. search_dir .. '''; findstr /C:'''
130 | .. what .. ''' /N /S ''' .. items .. '''}"'
131 |
132 | # *nix
133 | &grepprg = 'grep -nrH --include="{items}" "{what}" {search_dir}'
134 | ```
135 |
136 | where the values of `{what}`,`{files}` and `{search_dir}` are replaced by
137 | user input.
138 |
139 | You can also configure your own `'grepprg'` command through the keys `grep_cmd_win`
140 | and `grep_cmd_nix` of the `g:poptools_config` dictionary and you can use the
141 | placeholders `{search_dir}, {items}` and `{what}`. For example, you could set
142 | the following:
143 |
144 | ```
145 | g:poptools_config['grep_cmd_win'] =
146 | 'powershell -NoProfile -ExecutionPolicy Bypass -Command '
147 | .. '"& { findstr /C:''' .. what .. ''' /N /S '''
148 | .. fnamemodify($'{search_dir}\{items}', ':p') .. ''' }"'
149 | ```
150 |
151 | If you define your own `'grepprg'`, then the elements in the quickfix shall
152 | contain the buffer number, the line number and the text. You can verify it by
153 | running `:echo getqflist()`. The returned dictionaries must have the keys
154 | `bufnr`, `lnum` and `text`. If that is not the case, then you need to set
155 | `'grepformat'` adequately. See `:h 'grepformat'` for more info.
156 |
157 | ### `PopupFindDir`
158 |
159 | To find hidden folders with `PopupFindDir` command, just add a `.` in front of
160 | the search pattern, e.g. `.git*`. That will return e.g. `.git/, .github/`,
161 | etc.
162 |
163 |
164 | [0]: https://github.com/junegunn/fzf.vim
165 | [1]: https://github.com/Donaldttt/fuzzyy
166 | [2]: https://github.com/girishji/scope.vim
167 |
--------------------------------------------------------------------------------
/doc/poptools.txt:
--------------------------------------------------------------------------------
1 | *poptools.txt*
2 |
3 | Author: ubaldot (ubaldo DOT tiberi AT gmail DOT com)
4 | For Vim version 9.0 and above
5 |
6 | ==============================================================================
7 | CONTENTS *poptools-contents*
8 |
9 | 1. Introduction ......... |poptools-introduction|
10 | 2. Requirements ......... |poptools-requirements|
11 | 3. Commands ............. |poptools-commands|
12 | 4. Grep and Vimgrep ..... |poptools-grep|
13 | 5. Configuration ........ |poptools-configuration|
14 | 6. License .............. |poptools-license|
15 |
16 | ==============================================================================
17 | INTRODUCTION *poptools-introduction*
18 |
19 | Poptools aims to scale your productivity by conveniently using popups for a
20 | multitude of tasks, from finding files and directories, to setting your
21 | favorite colorscheme.
22 |
23 | Once a list of results is slammed into a popup menu, you can filter it in a
24 | fuzzy or in an exact way.
25 |
26 | Poptools is more essential compared to similar plugins, but differently
27 | from them, external programs are called _synchronously_, although things may
28 | change in the future. :)
29 |
30 | Nevertheless, I personally like the interface and how it displays all the
31 | results at once. Additionally, I find the opportunity of saving the last
32 | search very handy. The configuration is also fairly straightforward.
33 |
34 | ==============================================================================
35 | REQUIREMENTS *poptools-requirements*
36 |
37 | Vim 9.0 is required.
38 |
39 | ==============================================================================
40 | COMMANDS *poptools-commands*
41 |
42 | It follows the list of available commads: >
43 |
44 | :PoptoolsFindFile
45 | :PoptoolsFindFileInPath # Takes into account the setting of :h 'path'.
46 | :PoptoolsFindDir # Search from the current directory downwards
47 | :PoptoolsBuffers
48 | :PoptoolsRecentFiles
49 | :PoptoolsCmdHistory
50 | # When something goes wrong, you can clear all the Poptools popups
51 | :PoptoolsKill
52 | # The displayed colors depends on the value of :h 'background'
53 | :PoptoolsColorscheme
54 | :PoptoolsGrepInBuffer # Find pattern in the current buffer
55 | :PoptoolsGrep # External grep. Grep command is displayed.
56 | # Vimgrep, show results in the quickfix-list instead of a popup.
57 | :PoptoolsVimgrep
58 | :PoptoolsLastSearch # Show the last search results
59 | <
60 |
61 | ==============================================================================
62 | GREP AND VIMGREP *poptools-grep* *poptools-vimgrep*
63 |
64 | `PopupToolsVimGrep` uses |vimgrep| whereas `PopupToolsGrep` uses |grep|.
65 | However, the user interface is the same.
66 | The results appear both in the |quickfix-list| and in the popup window.
67 |
68 | By default, |grepprg| is set,as it follows:
69 |
70 | >
71 | # Windows
72 | 'powershell -NoProfile -ExecutionPolicy Bypass -Command '
73 | .. '"& { findstr /C:''' .. what .. ''' /N /S '''
74 | .. fnamemodify($'{search_dir}\{items}', ':p') .. ''' }"'
75 |
76 | # *nix
77 | &grepprg = 'grep -nrH --include="{items}" "{what}" {search_dir}'
78 | <
79 |
80 | where the values of `{what}`,`{files}` and `{search_dir}` are replaced by
81 | user input values.
82 |
83 | You can configure your own |grepprg| command through the keys
84 | `grep_cmd_win` and `grep_cmd_nix` of the |g:poptools_config| dictionary and
85 | you can use the placeholders `{search_dir}, {items}` and `{what}`. See below
86 | for more info.
87 |
88 | The grep command sent to the shell can be retrieved through |:messages|. See
89 | below for more info.
90 |
91 | TIP: Once you have performed a search, all the results can be accessed at any
92 | time through `:PopupToolsLastSearch` without the need of performing a new
93 | search.
94 |
95 | ==============================================================================
96 | CONFIGURATION *poptools-configuration* *g:poptools_config*
97 |
98 | The plug configuration is done through `g:poptools_config` dictionary.
99 | Be sure to create the empty dictionary `g:poptools_config = {}` before
100 | filling it with he various keys.
101 |
102 | Available keys:
103 | "preview_file" Set to `true` if you want a preview window when
104 | searching for files.
105 | Default: `false`.
106 | "preview_file_in_path" Set to `true` if you want a preview window when
107 | searching for files in path.
108 | Default: `false`.
109 | "preview_recent_files" Set to `true` if you want a preview window when
110 | searching for recently opened files.
111 | Default: `false`.
112 | "preview_buffers" Set to `true` if you want a preview window when
113 | searching for buffers.
114 | Default: `false`.
115 | "preview_grep" Set to `true` if you want a preview window when
116 | using poptools grep functions.
117 | Default: `false`.
118 | "preview_vimgrep" Set to `true` if you want a preview window when
119 | using poptools vimgrep functions.
120 | Default: `false`.
121 | "fuzzy_search" Set to `true` if you want a fuzzy filtering of the
122 | results, or to `false` if you want to filter based
123 | on exact match. Default: `true`.
124 | "preview_syntax" Set to `true` if you want syntax highlighting in the
125 | preview window. Note that syntax highlighting may
126 | slow down the user-experience. Default: `true`.
127 | "grep_inc_search" Set to `false` if you don't want to incrementally
128 | search in the current buffer when using `grep`
129 | and `vimgrep`. Default: `true`.
130 | "grep_cmd_win" Value of |grepprg| for Windows.
131 | Default:
132 | >
133 | 'powershell -NoProfile -ExecutionPolicy Bypass -Command '
134 | .. '"& { findstr /C:''' .. what .. ''' /N /S '''
135 | .. fnamemodify($'{search_dir}\{items}', ':p') .. ''' }"'
136 | <
137 |
138 | "grep_cmd_nix" Value of |grepprg| for *nix systems.
139 | Default:
140 | >
141 | grep -nrH --include="{items}" "{what}" {search_dir}
142 | <
143 | If you define your own |grepprg|, then the elements in the quickfix shall
144 | contain the buffer number, the line number and the text. You can verify it by
145 | running `:echo getqflist()`. The returned dictionaries must have the keys
146 | `bufnr`, `lnum` and `text`. If that is not the case, then you need to set
147 | |grepformat| adequately. See |grepformat| for more info.
148 |
149 | ==============================================================================
150 | LICENSE *poptools-license*
151 |
152 | BSD 3-Clause License
153 |
154 | Copyright (c) 2025, Ubaldo Tiberi.
155 | All rights reserved.
156 |
157 | Redistribution and use in source and binary forms, with or without
158 | modification, are permitted provided that the following conditions are met:
159 |
160 | * Redistributions of source code must retain the above copyright notice, this
161 | list of conditions and the following disclaimer.
162 |
163 | * Redistributions in binary form must reproduce the above copyright notice,
164 | this list of conditions and the following disclaimer in the documentation
165 | and/or other materials provided with the distribution.
166 |
167 | * Neither the name of the copyright holder nor the names of its
168 | contributors may be used to endorse or promote products derived from
169 | this software without specific prior written permission.
170 |
171 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
172 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
173 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
174 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
175 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
176 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
177 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
178 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
179 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
180 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
181 |
--------------------------------------------------------------------------------
/doc/tags:
--------------------------------------------------------------------------------
1 | g:poptools_config poptools.txt /*g:poptools_config*
2 | poptools-commands poptools.txt /*poptools-commands*
3 | poptools-configuration poptools.txt /*poptools-configuration*
4 | poptools-contents poptools.txt /*poptools-contents*
5 | poptools-grep poptools.txt /*poptools-grep*
6 | poptools-introduction poptools.txt /*poptools-introduction*
7 | poptools-license poptools.txt /*poptools-license*
8 | poptools-requirements poptools.txt /*poptools-requirements*
9 | poptools-vimgrep poptools.txt /*poptools-vimgrep*
10 | poptools.txt poptools.txt /*poptools.txt*
11 |
--------------------------------------------------------------------------------
/lib/funcs.vim:
--------------------------------------------------------------------------------
1 | vim9script
2 |
3 | # TODO Exclude 'wildignore' paths in Grep (it uses an external program)
4 |
5 | # This must be persistent across different calls and therefore we explicitly
6 | # assign a number
7 | var last_results = []
8 | var last_title = ''
9 | var last_search_type = ''
10 | var last_what = ''
11 |
12 | var main_id: number
13 | var prompt_id: number
14 | var preview_id: number
15 |
16 | var prompt_cursor: string
17 | var prompt_sign: string
18 | var prompt_text: string
19 |
20 | # User defined settings through g:poptools_config
21 | var fuzzy_search: bool
22 | var preview_syntax: bool
23 |
24 | var what: string
25 | var items: string
26 | var search_dir: string
27 |
28 | var grep_inc_search: bool
29 | # Hide cursor when operating in the popups
30 | var gui_cursor: list>
31 |
32 | def Echoerr(msg: string)
33 | echohl ErrorMsg | echom $"[poptools] {msg}" | echohl None
34 | enddef
35 |
36 | def Echowarn(msg: string)
37 | echohl WarningMsg | echom $"[poptools] {msg}" | echohl None
38 | enddef
39 |
40 | def InitScriptLocalVars()
41 | # Set script-local variables
42 |
43 | main_id = -1
44 | prompt_id = -1
45 | preview_id = -1
46 |
47 | prompt_cursor = '▏'
48 | prompt_sign = '> '
49 | prompt_text = ''
50 |
51 | what = ''
52 | items = ''
53 | search_dir = ''
54 |
55 | if exists('g:poptools_config') && has_key(g:poptools_config,
56 | 'preview_syntax')
57 | preview_syntax = g:poptools_config['preview_syntax']
58 | else
59 | preview_syntax = true
60 | endif
61 |
62 | if exists('g:poptools_config') && has_key(g:poptools_config,
63 | 'grep_inc_search')
64 | grep_inc_search = g:poptools_config['grep_inc_search']
65 | else
66 | grep_inc_search = true
67 | endif
68 |
69 | if exists('g:poptools_config') && has_key(g:poptools_config, 'fuzzy_search')
70 | fuzzy_search = g:poptools_config['fuzzy_search']
71 | else
72 | fuzzy_search = true
73 | endif
74 |
75 | if empty(prop_type_get('PopupToolsMatched'))
76 | prop_type_add('PopupToolsMatched', {highlight: 'WarningMsg'})
77 | endif
78 | enddef
79 |
80 | def RestoreCursor()
81 | set t_ve&
82 | if hlget("Cursor")[0]->get('cleared', false)
83 | hlset(gui_cursor)
84 | endif
85 | enddef
86 |
87 | # ----- Callback functions ------------------------
88 | def PopupCallbackGrep(id: number, idx: number)
89 | if idx > 0
90 | popup_close(prompt_id, -1)
91 | if preview_id != -1
92 | popup_close(preview_id, -1)
93 | endif
94 |
95 | var selection = getbufline(winbufnr(main_id), idx)[0]
96 | # grep return format is 'file.xyz:76: ...'
97 | # You must extract the filename and the line number.
98 | # However, the name is not full, and you must reconstruct. The easiest
99 | # way is to fetch it from the popup title
100 | #
101 | # OBS! You could use split(selection, ':') to separate filename from line
102 | # number, but what if a filename is 'foo:bar'?
103 | var filename = selection->matchstr('^.*\ze:\d')
104 | var line = selection->matchstr('^.\{-}:\zs\d*\ze:')
105 |
106 | var path = split(popup_getoptions(id).title)[0]
107 | try
108 | if getcwd() == path
109 | exe $'edit {path}/{filename}'
110 | else
111 | exe $'edit {filename}'
112 | endif
113 | catch
114 | ClosePopups()
115 | Echoerr($'Cannot open {filename}')
116 | endtry
117 |
118 | cursor(str2nr(line), 1)
119 | RestoreCursor()
120 | endif
121 | enddef
122 |
123 | def PopupCallbackFileBuffer(id: number, idx: number)
124 | if idx > 0
125 | popup_close(prompt_id, -1)
126 | if preview_id != -1
127 | popup_close(preview_id, -1)
128 | endif
129 | echo ""
130 | var selection = getbufline(winbufnr(main_id), idx)[0]
131 | exe $'edit {selection}'
132 | RestoreCursor()
133 | endif
134 | enddef
135 |
136 | def PopupCallbackHistory(id: number, idx: number)
137 | if idx > 0
138 | popup_close(prompt_id, -1)
139 | if preview_id != -1
140 | popup_close(preview_id, -1)
141 | endif
142 | var cmd = getbufline(winbufnr(main_id), idx)[0]
143 | feedkeys(cmd)
144 | RestoreCursor()
145 | endif
146 | enddef
147 |
148 | def PopupCallbackDir(id: number, idx: number)
149 | if idx > 0
150 | var dir = getbufline(winbufnr(main_id), idx)[0]
151 | exe $'cd {dir}'
152 | pwd
153 | popup_close(prompt_id, -1)
154 | RestoreCursor()
155 | endif
156 | enddef
157 |
158 | def PopupCallbackColorscheme(id: number, idx: number)
159 | if idx > 0
160 | var scheme = getbufline(winbufnr(main_id), idx)[0]
161 | noa exe $'colorscheme {scheme}'
162 | popup_close(prompt_id, -1)
163 | RestoreCursor()
164 | endif
165 | enddef
166 |
167 | def UpdateFilePreview(search_type: string)
168 | # You may use external programs to count the lines if 'readfile()' is too
169 | # slow, e.g.
170 | # var file_length = has('win32') ? str2nr(system('...')) :
171 | # str2nr(system($'wc
172 | # -l {filename}')->matchstr('\s*\zs\d*'))
173 | # var buf_lines = has('win32')
174 | # ? systemlist($'powershell -c "Get-Content {filename} | Select-Object
175 | # -Skip
176 | # ({firstline} - 1) -First ({lastline} - {firstline} + 1)"')
177 | # : systemlist($'sed -n "{firstline},{lastline}p" {filename}')
178 | #
179 | # For the syntax highlight, you may use the 'GetFiletypeByFilename()'
180 | # function, which is now in unused.vim
181 | #
182 | # Parse the highlighted line on the main popup
183 | var idx = line('.', main_id)
184 |
185 | # This 'if' is needed because the filter is called on anyways
186 | if idx > 0
187 | # The lines of main_id may be of the form 'filenames'
188 | # or 'filenames:lines:text'
189 | var filename = index(['grep', 'vimgrep'], search_type) != -1
190 | ? getbufline(winbufnr(main_id), idx)[0]->matchstr('^.*\ze:\d')
191 | : getbufline(winbufnr(main_id), idx)[0]
192 |
193 | var line_nr = index(['grep', 'vimgrep'], search_type) != -1
194 | ? str2nr(getbufline(winbufnr(main_id), idx)[0]->matchstr(':\zs\d*\ze:'))
195 | : popup_getpos(main_id).core_height / 2
196 |
197 | # We split the fullname so that we can show it nicely in the popup.
198 | # However, when showing the preview or during the callback, it is safer to
199 | # have the fullname. The path is in the popup title.
200 | # In case of Buffers or Recent files, that is not needed
201 | if index(['file', 'file_in_path', 'grep', 'vimgrep'], search_type) != -1
202 | var path = split(popup_getoptions(main_id).title)[0]
203 | if getcwd() == path
204 | filename = $'{path}/{filename}'
205 | endif
206 | endif
207 |
208 | var file_content = []
209 | if bufexists(filename)
210 | # The quickfix list load the buffers, but they have no content yet
211 | if empty(getqflist())
212 | file_content = getbufline(filename, 1, '$')
213 | else
214 | file_content = readfile($'{filename}')
215 | endif
216 | elseif filereadable($'{expand(filename)}')
217 | file_content = readfile($'{expand(filename)}')
218 | else
219 | file_content = ["Can't preview the file!"]
220 | endif
221 |
222 | # Set options
223 | win_execute(preview_id, $'setlocal number')
224 | # TODO: the wrap thing may be selectable through g:poptools_config?
225 | # win_execute(preview_id, '&wrap = false')
226 |
227 | # clean the preview
228 | popup_settext(preview_id, repeat([""], popup_getpos(main_id).core_height))
229 | # populate the preview
230 | setwinvar(preview_id, 'buf_lines', file_content)
231 | win_execute(preview_id, 'append(0, w:buf_lines)')
232 | # Unfold stuff
233 | win_execute(preview_id, 'norm! zR')
234 |
235 | # Syntax highlight if it creates problems, disable it. It is not
236 | # bulletproof
237 | if preview_syntax
238 | # set 'synmaxcol' for avoiding crashing if some readable file has
239 | # embedded figures.
240 | # Figure generate lines with >80000 columns and the internal engine
241 | # to figure out the syntax will fail.
242 | var old_synmaxcol = &synmaxcol
243 | &synmaxcol = 300
244 | var buf_extension = $'{fnamemodify(filename, ":e")}'
245 | var found_filetypedetect_cmd =
246 | autocmd_get({group: 'filetypedetect'})
247 | ->filter($'v:val.pattern =~ "*\\.{buf_extension}$"')
248 | var set_filetype_cmd = ''
249 | if empty(found_filetypedetect_cmd)
250 | if index([$"{$HOME}/.vimrc", $"{$HOME}/.gvimrc"], expand(filename)) != -1
251 | set_filetype_cmd = '&filetype = "vim"'
252 | else
253 | set_filetype_cmd = '&filetype = ""'
254 | endif
255 | else
256 | set_filetype_cmd = found_filetypedetect_cmd[0].cmd
257 | endif
258 | win_execute(preview_id, set_filetype_cmd)
259 | &synmaxcol = old_synmaxcol
260 | endif
261 |
262 | # Highlight grep matches
263 | if !empty(what)
264 | win_execute(preview_id, $'normal! {line_nr}gg')
265 | win_execute(preview_id, 'setlocal cursorline')
266 | win_execute(preview_id, $'match Search /{what}/')
267 | endif
268 |
269 | # Set preview ID title
270 | var preview_id_opts = popup_getoptions(preview_id)
271 | preview_id_opts.title = $' {fnamemodify(filename, ':t')} '
272 | popup_setoptions(preview_id, preview_id_opts)
273 | endif
274 | enddef
275 |
276 | export def ClosePopups()
277 | # This function tear down everything
278 | if preview_id != -1
279 | popup_close(preview_id, -1)
280 | endif
281 | popup_close(main_id, -1)
282 | popup_close(prompt_id, -1)
283 | RestoreCursor()
284 | prop_type_delete('PopupToolsMatched')
285 | enddef
286 |
287 | def PopupFilter(id: number,
288 | key: string,
289 | results: list,
290 | search_type: string,
291 | current_colorscheme: string,
292 | current_background: string,
293 | ): bool
294 |
295 | # Save for last search
296 | if index(['file', 'file_in_path', 'grep', 'vimgrep'], search_type) != -1
297 | last_results = getbufline(winbufnr(main_id), 1, '$')
298 | last_title = popup_getoptions(main_id).title
299 | last_search_type = search_type
300 | last_what = what
301 | endif
302 |
303 | var maxheight = popup_getoptions(main_id).maxheight
304 |
305 | if key == "\"
306 | if search_type == 'colorscheme'
307 | exe $'colorscheme {current_colorscheme}'
308 | endif
309 | ClosePopups()
310 | return true
311 | endif
312 |
313 | # For debugging
314 | # echo 'Pressed key: ' .. key
315 | echo ''
316 | var preview_update = true
317 | # You never know what the user can type... Let's use a try-catch
318 | try
319 | if key == "\"
320 | popup_close(main_id, getcurpos(main_id)[1])
321 | ClosePopups()
322 | elseif index(["\", "\"], key) != -1
323 | win_execute(main_id, 'normal! ' .. maxheight .. "\")
324 | elseif index(["\", "\"], key) != -1
325 | win_execute(main_id, 'normal! ' .. maxheight .. "\")
326 | elseif key == "\"
327 | win_execute(main_id, "normal! gg")
328 | elseif key == "\"
329 | win_execute(main_id, "normal! G")
330 | elseif index(["\", "\", "\", "\"], key)
331 | != -1
332 | var ln = getcurpos(main_id)[1]
333 | win_execute(main_id, "normal! j")
334 | if ln == getcurpos(main_id)[1]
335 | win_execute(main_id, "normal! gg")
336 | endif
337 | elseif index(["\", "\", "\", "\"], key) !=
338 | -1
339 | var ln = getcurpos(main_id)[1]
340 | win_execute(main_id, "normal! k")
341 | if ln == getcurpos(main_id)[1]
342 | win_execute(main_id, "normal! G")
343 | endif
344 | # Scroll preview window
345 | elseif preview_id != -1 && key == "\"
346 | win_execute(preview_id, "normal! \")
347 | preview_update = false
348 | elseif preview_id != -1 && key == "\"
349 | win_execute(preview_id, "normal! \")
350 | preview_update = false
351 | elseif preview_id != -1 && key == "\"
352 | # Echowarn("FOO")
353 | win_execute(preview_id, "normal! \")
354 | preview_update = false
355 | elseif preview_id != -1 && key == "\"
356 | win_execute(preview_id, "normal! \")
357 | preview_update = false
358 | # The real deal: take a single, printable character
359 | elseif key =~ '^\p$' || keytrans(key) ==# "" || key == "\"
360 | if key =~ '^\p$'
361 | prompt_text ..= key
362 | elseif keytrans(key) ==# ""
363 | if len(prompt_text) > 0
364 | prompt_text = prompt_text[: -2]
365 | endif
366 | elseif key == "\"
367 | prompt_text = ""
368 | endif
369 |
370 | popup_settext(prompt_id, $'{prompt_sign}{prompt_text}{prompt_cursor}')
371 |
372 | # What you pass to popup_settext(main_id, ...) is a list of strings with
373 | # text properties attached, e.g.
374 | #
375 | # [
376 | # { "text": "filename.txt",
377 | # "props": [ {"col": 2, "length": 1, "type": "PopupToolsMatched"},
378 | # ... ]
379 | # },
380 | # { "text": "another_file.txt",
381 | # "props": [ {"col": 1, "length": 1, "type": "PopupToolsMatched"},
382 | # ... ]
383 | # },
384 | # ...
385 | # ]
386 | #
387 | var filtered_results_full = []
388 | var filtered_results: list>
389 |
390 | if !empty(prompt_text)
391 | if fuzzy_search
392 | filtered_results_full = results->matchfuzzypos(prompt_text)
393 | var pos = filtered_results_full[1]
394 | filtered_results = filtered_results_full[0]
395 | ->map((ii, match) => ({
396 | text: match,
397 | props: pos[ii]->copy()->map((_, col) => ({
398 | col: col + 1,
399 | length: 1,
400 | type: 'PopupToolsMatched'
401 | }))}))
402 | else
403 | filtered_results_full = copy(results)
404 | ->map((_, text) => matchstrpos(text,
405 | \ '\V' .. $"{escape(prompt_text, '\')}"))
406 | ->map((idx, match_info) => [results[idx], match_info[1],
407 | match_info[2]])
408 |
409 | filtered_results = copy(filtered_results_full)
410 | ->map((_, val) => ({
411 | text: val[0],
412 | props: val[1] >= 0 && val[2] >= 0
413 | ? [{
414 | type: 'PopupToolsMatched',
415 | col: val[1] + 1,
416 | end_col: val[2] + 1
417 | }]
418 | : []
419 | }))
420 | ->filter("!empty(v:val.props)")
421 | endif
422 | endif
423 |
424 | var opts = popup_getoptions(prompt_id)
425 | var num_hits = !empty(filtered_results)
426 | ? len(filtered_results)
427 | : len(results)
428 | var base_title = trim(opts.title->matchstr('.*\ze('))
429 | opts.title = $' {base_title} ({num_hits}) '
430 | popup_setoptions(prompt_id, opts)
431 |
432 | if !empty(prompt_text)
433 | popup_settext(main_id, filtered_results)
434 | else
435 | popup_settext(main_id, results)
436 | endif
437 | else
438 | Echowarn('Unknown key')
439 | endif
440 | catch
441 | ClosePopups()
442 | Echoerr('Internal error')
443 | endtry
444 |
445 | if preview_id != -1 && preview_update
446 | UpdateFilePreview(search_type)
447 | endif
448 |
449 | if search_type == 'colorscheme'
450 | ShowColorscheme(current_background)
451 | endif
452 |
453 | return true
454 | enddef
455 |
456 | def ShowColorscheme(current_background: string)
457 | # Circular selection
458 | var idx = line('.', main_id) % (line('$', main_id) + 1)
459 | # TODO: check
460 | # I need this check because when user makes a selection with this
461 | # function is called anyways and idx will be 0
462 | if idx > 0
463 | var scheme = getbufline(winbufnr(main_id), idx)[0]
464 | exe $'colorscheme {scheme}'
465 | &background = current_background
466 | hi link PopupSelected PmenuSel
467 | endif
468 | enddef
469 |
470 | def ShowPromptPopup(results: list,
471 | search_type: string)
472 | # This is the UI thing
473 | var main_id_core_line = popup_getpos(main_id).core_line
474 | var main_id_core_col = popup_getpos(main_id).core_col
475 | var main_id_core_width = popup_getpos(main_id).core_width
476 |
477 | var base_title = $'{search_type}:'
478 | var opts = {
479 | minwidth: main_id_core_width,
480 | maxwidth: main_id_core_width,
481 | line: main_id_core_line - 3,
482 | col: main_id_core_col - 1,
483 | borderchars: ['─', '│', '─', '│', '╭', '╮', '╯', '╰'],
484 | border: [1, 1, 0, 1],
485 | mapping: 0,
486 | scrollbar: 0,
487 | wrap: 0,
488 | drag: 0,
489 | }
490 |
491 | # Filter
492 | var current_colorscheme = execute('colorscheme')->substitute('\n', '', 'g')
493 | var current_background = &background
494 | opts.filter = (id, key) => PopupFilter(id, key, results, search_type,
495 | current_colorscheme, current_background)
496 |
497 | var num_hits = len(getbufline(winbufnr(main_id), 1, "$"))
498 | if empty(what)
499 | opts.title = $' {base_title} ({num_hits}) '
500 | else
501 | opts.title = $' {base_title} "{what}" ({num_hits}) '
502 | endif
503 |
504 | prompt_text = ""
505 | prompt_id = popup_create([prompt_sign .. prompt_cursor], opts)
506 |
507 | enddef
508 |
509 | # ----- MAIN -----
510 | def ShowPopup(results: list,
511 | search_type: string,
512 | title: string = '')
513 | # This function is regarded as main function. It is called once
514 | # the 'results' list is ready.
515 | # Clean up the command line to avoid 'Press Enter' otherwise the popups will
516 | # not show up
517 | redraw
518 |
519 | # For some reason you get ^@ (=newline)
520 | var current_colorscheme = execute('colorscheme')->substitute('\n', '', 'g')
521 | var current_background = &background
522 | hi link PopupSelected PmenuSel
523 |
524 | # hide cursor
525 | set t_ve=
526 | gui_cursor = hlget("Cursor")
527 | hlset([{name: 'Cursor', cleared: true}])
528 |
529 | # Assuming no-preview
530 | var popup_width = (&columns * 2) / 3
531 | var popup_height = &lines / 2
532 |
533 | # Standard options
534 | var opts = {
535 | pos: 'center',
536 | border: [1, 1, 1, 1],
537 | borderchars: ['─', '│', '─', '│', '├', '┤', '╯', '╰'],
538 | maxheight: popup_height,
539 | minheight: popup_height,
540 | minwidth: popup_width,
541 | maxwidth: popup_width,
542 | scrollbar: 0,
543 | cursorline: 1,
544 | mapping: 0,
545 | wrap: 0,
546 | drag: 0,
547 | }
548 |
549 | if !empty(title)
550 | opts.title = title
551 | endif
552 |
553 | main_id = popup_create(results, opts)
554 |
555 | # Preview handling
556 | var show_preview = false
557 | if search_type == 'file'
558 | if exists('g:poptools_config') && has_key(g:poptools_config,
559 | 'preview_file')
560 | show_preview = g:poptools_config['preview_file']
561 | endif
562 | elseif search_type == 'file_in_path'
563 | if exists('g:poptools_config')
564 | && has_key(g:poptools_config, 'preview_file_in_path')
565 | show_preview = g:poptools_config['preview_file_in_path']
566 | endif
567 | elseif search_type == 'recent_files'
568 | if exists('g:poptools_config')
569 | && has_key(g:poptools_config, 'preview_recent_files')
570 | show_preview = g:poptools_config['preview_recent_files']
571 | endif
572 | elseif search_type == 'buffer'
573 | if exists('g:poptools_config')
574 | && has_key(g:poptools_config, 'preview_buffers')
575 | show_preview = g:poptools_config['preview_buffers']
576 | endif
577 | elseif search_type == 'grep'
578 | if exists('g:poptools_config') && has_key(g:poptools_config,
579 | 'preview_grep')
580 | show_preview = g:poptools_config['preview_grep']
581 | endif
582 | elseif search_type == 'vimgrep'
583 | if exists('g:poptools_config') && has_key(g:poptools_config,
584 | 'preview_vimgrep')
585 | show_preview = g:poptools_config['preview_vimgrep']
586 | endif
587 | endif
588 |
589 | if show_preview
590 | # Some geometry: we want more room in case of preview
591 | popup_width = (&columns * 9) / 10
592 | popup_height = (&lines * 7) / 10
593 | var left_margin = 8
594 |
595 | # Adjustments for the main popup
596 | opts.pos = 'topleft'
597 | opts.minwidth = float2nr(0.4 * popup_width)
598 | opts.maxwidth = float2nr(0.4 * popup_width)
599 | opts.maxheight = popup_height
600 | opts.minheight = popup_height
601 | opts.line = 6
602 | # opts.col = &columns / 2 - opts.minwidth - 1
603 | opts.col = left_margin
604 |
605 | # Adjustments for the preview popup
606 | var preview_opts = copy(opts)
607 | preview_opts.pos = 'topleft'
608 | preview_opts.line = opts.line - 2
609 | preview_opts.col = left_margin + opts.maxwidth + 2
610 | preview_opts.minwidth = float2nr(0.6 * popup_width) - 1
611 | preview_opts.maxwidth = float2nr(0.6 * popup_width) - 1
612 | preview_opts.maxheight = opts.minheight + 2
613 | preview_opts.minheight = opts.minheight + 2
614 | preview_opts.cursorline = 0
615 | preview_opts.scrollbar = 1
616 |
617 | preview_opts.borderchars = ['─', '│', '─', '│', '╭', '╮', '╯', '╰']
618 | preview_id = popup_create("Something went wrong."
619 | .. "Run :call popup_clear() to close.", preview_opts)
620 |
621 | UpdateFilePreview(search_type)
622 | endif
623 |
624 | if search_type == 'colorscheme'
625 | var init_highlight_location = index(results, current_colorscheme)
626 | win_execute(main_id, $'norm {init_highlight_location }j')
627 | endif
628 |
629 | # Callback switch for main_id
630 | var PopupCallback: func
631 | if index(['file', 'file_in_path', 'recent_files', 'buffer'],
632 | \ search_type) != -1
633 | PopupCallback = PopupCallbackFileBuffer
634 | elseif search_type == 'dir'
635 | PopupCallback = PopupCallbackDir
636 | elseif search_type == 'history'
637 | PopupCallback = PopupCallbackHistory
638 | elseif index(['grep', 'vimgrep'], search_type) != -1
639 | PopupCallback = PopupCallbackGrep
640 | elseif search_type == 'colorscheme'
641 | PopupCallback = PopupCallbackColorscheme
642 | endif
643 |
644 | opts.callback = PopupCallback
645 | popup_setoptions(main_id, opts)
646 |
647 | ShowPromptPopup(results, search_type)
648 | enddef
649 |
650 | # ---- API ------------
651 | # The following functions are associated to commands in the plugin file.
652 | # They are used to generate the 'results' list to pass to ShowPopup()
653 | export def FindFile(search_type: string)
654 | InitScriptLocalVars()
655 | # Guard
656 | if (search_type == 'file' || search_type == 'file_in_path')
657 | \ && getcwd() == expand('~')
658 | Echoerr("You are in your home folder. Too many results.")
659 | return
660 | endif
661 |
662 | # Main
663 | what = input($"current folder: '{fnamemodify(getcwd(), ':~')}'\nFile name to
664 | \ search ('enter' for all): ")
665 | var hidden = what[0] == '.' ? '' : '*'
666 |
667 | search_dir = '.'
668 | if (search_type == 'file')
669 | var current_wildmenu = &wildmenu
670 | set nowildmenu
671 | search_dir = input($"\nin which folder (you can also use 'tab'): ",
672 | './**', 'dir')
673 | if empty(search_dir) || search_dir == './'
674 | search_dir = getcwd()
675 | endif
676 | &wildmenu = current_wildmenu
677 | endif
678 | var results = getcompletion($'{search_dir}/{hidden}{what}',
679 | \ search_type, true)
680 | # echo "\n[poptools] If the search takes too long hit CTRL-C few times and
681 | # try to
682 | # \ narrow down your search."
683 |
684 | if empty(results)
685 | echo $"'{what}' pattern not found!"
686 | else
687 | # OBS: the title MUST have filepath followed by \s because it is used to
688 | # reconstruct the full path filename
689 | var title = $" {fnamemodify(getcwd(), ':~')} - Files '{what}': "
690 | if empty(what)
691 | title = $" {fnamemodify(getcwd(), ':~')}: "
692 | endif
693 |
694 | results ->filter('v:val !~ "\/$"')
695 | ->filter((_, val) => filereadable(expand(val)))
696 | ->map((_, val) => fnamemodify(val, ':.'))
697 | ShowPopup(results, search_type, title)
698 | endif
699 | enddef
700 |
701 | export def FindDir()
702 | InitScriptLocalVars()
703 | if getcwd() == expand('~')
704 | Echoerr("You are in your home folder. Too many results.")
705 | return
706 | endif
707 |
708 | what = input($"current folder: '{fnamemodify(getcwd(), ':~')}'\n
709 | \Folder name to search ('enter' for all): ")
710 | var hidden = what[0] == '.' ? '' : '*'
711 |
712 | var results = getcompletion($'**/{hidden}{what}', 'dir', true)
713 |
714 | if empty(results)
715 | echo $"'{what}' pattern not found!"
716 | else
717 | # OBS: the title MUST have filepath followed by \s because it is used to
718 | # reconstruct the full path filename
719 | var title = $" {fnamemodify(getcwd(), ':~')} - Directories '{what}': "
720 | if empty(what)
721 | title = $" {fnamemodify(getcwd(), ':~')}: "
722 | endif
723 | ShowPopup(results, 'dir', title)
724 | endif
725 | enddef
726 |
727 | def Qf2Results(): list
728 | var qf_results = getqflist()
729 | var results = qf_results
730 | ->mapnew((_, val) => ($'{fnamemodify(bufname(val.bufnr), ':p')}'
731 | .. $':{val.lnum}:{val.text}'))
732 | return results
733 | enddef
734 |
735 | export def Vimgrep()
736 | InitScriptLocalVars()
737 | # Guard
738 | if getcwd() == expand('~')
739 | Echoerr("You are in your home folder. Too many results.")
740 | return
741 | endif
742 |
743 | # Main
744 | if grep_inc_search
745 | GrepInBufferHighlight()
746 | endif
747 | what = input($"current folder: '{fnamemodify(getcwd(), ':~')}'\n
748 | \String to find: ")
749 | GrepInBufferHighlightClear()
750 | if empty(what)
751 | return
752 | endif
753 |
754 | items = input($"\nin which files ('empty' for current file,
755 | \ '*' for all files): ", '*.')
756 | if empty(items)
757 | items = '%'
758 | search_dir = ''
759 | else
760 | # vimgrep does not like non-escaped spaces. We also have to remove the
761 | # final \ or / in case the user type a folder with a final / or \,
762 | # e.g. '~\Saved Games\' shall be replaced with '~\Saved\ Games'
763 | var current_wildmenu = &wildmenu
764 | set nowildmenu
765 | search_dir = input($"\nin which folder(s) (you can use 'tab'): ", './**',
766 | 'dir')
767 | ->escape(' ')
768 | ->substitute('\v(\\|/)$', '', '')
769 | &wildmenu = current_wildmenu
770 | endif
771 |
772 | var vimgrep_options = input("\nVimgrep options (g = every match, "
773 | .. "f = fuzzy): ", 'g')
774 | if empty(vimgrep_options)
775 | return
776 | endif
777 |
778 | # 'j' is to avoid jumping on the first match
779 | var cmd = $'vimgrep /{what}/j{vimgrep_options} {search_dir}/{items}'
780 | exe cmd
781 | var results = Qf2Results()->mapnew((_, val) => fnamemodify(val, ':.'))
782 | var title = $" {fnamemodify(getcwd(), ':~')}: "
783 | ShowPopup(results, 'vimgrep', title)
784 | enddef
785 |
786 | # These two functions are used to mimic the in_search feature for
787 | # GrepInBuffer() function.
788 | var match_id = 0
789 | def GrepInBufferHighlight()
790 | augroup SEARCH_HI | autocmd!
791 | autocmd CmdlineChanged @ {
792 | if match_id > 0
793 | matchdelete(match_id)
794 | endif
795 | # cursor(1, 1)
796 | var line_nr = search(getcmdline(), 'w')
797 | var pattern = getcmdline()
798 | # Highlight only the current line
799 | var what_to_match = $'\%{line_nr}l{pattern}'
800 | # var what_to_match = $'{pattern}'
801 | match_id = matchadd('IncSearch', what_to_match)
802 | # Highlight all the matches instead or the current line
803 | # match_id = matchadd('Search', getcmdline())
804 | redraw
805 | }
806 | autocmd CmdlineLeave @ {
807 | if match_id > 0
808 | matchdelete(match_id)
809 | match_id = 0
810 | endif
811 | }
812 | augroup END
813 | enddef
814 |
815 | def GrepInBufferHighlightClear()
816 | if exists("#SEARCH_HI")
817 | autocmd! SEARCH_HI
818 | augroup! SEARCH_HI
819 | endif
820 | if match_id > 0
821 | matchdelete(match_id)
822 | endif
823 | enddef
824 |
825 | export def GrepInBuffer(what_user: string = '')
826 | InitScriptLocalVars()
827 | # The format is like grep, i.e. filename:linenumber:
828 | if empty(what_user)
829 | GrepInBufferHighlight()
830 | what = input("Find in current buffer: ")
831 | GrepInBufferHighlightClear()
832 | if empty(what)
833 | return
834 | endif
835 | else
836 | what = what_user
837 | endif
838 |
839 | var initial_pos = getcursorcharpos()
840 | cursor(1, 1)
841 | var curr_line = line('.')
842 | var results = []
843 | while curr_line != 0
844 | curr_line = search(what, 'W')
845 | add(results, $'{expand("%:.")}:{curr_line}:')
846 | endwhile
847 | remove(results, -1)
848 | setcursorcharpos(initial_pos[1], initial_pos[2], initial_pos[3])
849 |
850 | var title = $" {fnamemodify(getcwd(), ':~')}: "
851 | ShowPopup(results, 'grep', title)
852 | enddef
853 |
854 |
855 | export def Grep()
856 | InitScriptLocalVars()
857 | # Guard
858 | if getcwd() == expand('~')
859 | Echoerr("You are in your home folder. Too many results.")
860 | return
861 | endif
862 |
863 | # Main
864 | if grep_inc_search
865 | GrepInBufferHighlight()
866 | endif
867 | what = input($"current folder: '{fnamemodify(getcwd(), ':~')}'\n
868 | \String to find: ")
869 |
870 | GrepInBufferHighlightClear()
871 | if empty(what)
872 | return
873 | endif
874 |
875 | items = input($"\nin which files ('*' for all files): ", '*.')
876 | var current_wildmenu = &wildmenu
877 | set nowildmenu
878 | search_dir = input($"\nin which folder (you can use 'tab'): ",
879 | \ './', 'dir')->substitute('\v(\\|/)$', '', '')
880 | if empty(search_dir) || search_dir == './'
881 | search_dir = getcwd()
882 | endif
883 | &wildmenu = current_wildmenu
884 |
885 | # Windows default
886 | var cmd_win_default = 'powershell -NoProfile -ExecutionPolicy Bypass '
887 | .. '-Command "& { findstr /C:''' .. what .. ''' /N /S '''
888 | .. fnamemodify($'{search_dir}\{items}', ':p') .. ''' }"'
889 |
890 | # *nix default
891 | var cmd_nix_default = $'grep -nrH --include="{items}" "{what}" {search_dir}'
892 |
893 | var cmd_win = cmd_win_default
894 | if exists('g:poptools_config') && has_key(g:poptools_config, 'grep_cmd_win')
895 | var search_dir_escaped = escape(search_dir, '\')
896 | cmd_win = g:poptools_config['grep_cmd_win']
897 | ->substitute("{search_dir}", search_dir_escaped, 'g')
898 | ->substitute("{items}", items, 'g')
899 | ->substitute("{what}", what, 'g')
900 | endif
901 |
902 | var cmd_nix = cmd_nix_default
903 | if exists('g:poptools_config') && has_key(g:poptools_config, 'grep_cmd_nix')
904 | cmd_nix = g:poptools_config['grep_cmd_nix']
905 | ->substitute("{search_dir}", search_dir, 'g')
906 | ->substitute("{items}", items, 'g')
907 | ->substitute("{what}", what, 'g')
908 | endif
909 |
910 | # clean up the command-line: needed!
911 | redraw
912 |
913 | # Get results
914 | var saved_grepprg = &grepprg
915 | if has('win32')
916 | &grepprg = cmd_win
917 | grep!
918 | echom getqflist({'title': 0}).title
919 | else
920 | &grepprg = cmd_nix
921 | grep!
922 | echom getqflist({'title': 0}).title
923 | endif
924 | &grepprg = saved_grepprg
925 |
926 | var qf_results = getqflist()
927 | var results = Qf2Results()->mapnew((_, val) => fnamemodify(val, ':.'))
928 | var title = $" {fnamemodify(getcwd(), ':~')}: "
929 | ShowPopup(results, 'grep', title)
930 | enddef
931 |
932 | export def Buffers()
933 | InitScriptLocalVars()
934 | var results = getcompletion('', 'buffer', true)
935 | ->map((_, val) => fnamemodify(val, ':.'))
936 | # var title = " Buffers: "
937 | ShowPopup(results, 'buffer')
938 | enddef
939 |
940 | export def Colorscheme()
941 | InitScriptLocalVars()
942 | hi link PopupSelected PmenuSel
943 | var results = getcompletion('', 'color', true)
944 | ShowPopup(results, 'colorscheme')
945 | enddef
946 |
947 | export def RecentFiles()
948 | InitScriptLocalVars()
949 | var results = copy(v:oldfiles)
950 | ->filter((_, val) => filereadable(expand(val)))
951 | ->map((_, val) => fnamemodify(val, ':.'))
952 | ShowPopup(results, 'recent_files')
953 | enddef
954 |
955 | export def CmdHistory()
956 | InitScriptLocalVars()
957 | var results = split(execute('history :'), '\n')
958 | for ii in range(0, len(results) - 1)
959 | results[ii] = substitute(results[ii], '\v^\>?\s*\d*\s*(\w*)', ':\1', 'g')
960 | endfor
961 | ShowPopup(reverse(results[1 : ]), 'history')
962 | enddef
963 |
964 | export def LastSearch()
965 | if empty(last_results)
966 | Echoerr('No last search results available!')
967 | else
968 | ShowPopup(last_results, last_search_type, last_title)
969 | endif
970 | enddef
971 |
--------------------------------------------------------------------------------
/lib/unused.vim:
--------------------------------------------------------------------------------
1 | vim9script
2 |
3 | # --- NOT USED but useful
4 | def GetFiletypeByFilename(fname: string): string
5 | # NOT USED
6 | # Pretend to load a buffer and detect its filetype manually
7 | # Used in UpdatePreviewAlternative()
8 |
9 | # just return &filetype if buffer was already loaded
10 | if bufloaded(fname)
11 | return getbufvar(fname, '&filetype')
12 | endif
13 |
14 | new
15 | try
16 | # the `file' command never reads file data from disk
17 | # (but may read path/directory contents)
18 | # however the detection will become less accurate
19 | # as some types cannot be recognized for empty files
20 | noautocmd silent! execute 'file' fnameescape(fname)
21 | filetype detect
22 | return &filetype
23 | finally
24 | bwipeout!
25 | endtry
26 | return ''
27 | enddef
28 |
29 | def UpdatePreviewAlternative(main_id: number, preview_id: number, search_type: string)
30 | # NOT USED
31 | # Alternative way for setting the filetype in the preview window.
32 | # It works with GetFiletypeByFilename() and it is slower
33 | var idx = line('.', main_id)
34 | var highlighted_line = getbufline(winbufnr(main_id), idx)[0]
35 | var buf_lines = readfile(expand(highlighted_line), '', popup_height)
36 |
37 | var buf_filetype = GetFiletypeByFilename(highlighted_line)
38 |
39 | # Clean the preview
40 | popup_settext(preview_id, repeat([""], popup_height))
41 | # populate the preview
42 | # TODO readfile gives me folded content
43 | popup_settext(preview_id, buf_lines)
44 | # Syntax highlight
45 | win_execute(preview_id, $'&filetype = "{buf_filetype}"')
46 |
47 | # Set preview ID title
48 | var preview_id_opts = popup_getoptions(preview_id)
49 | preview_id_opts.title = $' {highlighted_line} '
50 | popup_setoptions(preview_id, preview_id_opts)
51 | enddef
52 | # ------------ END NOT USED -----------
53 |
--------------------------------------------------------------------------------
/plugin/vim_poptools.vim:
--------------------------------------------------------------------------------
1 | vim9script noclear
2 |
3 | # Slam stuff in a popup
4 | # Maintainer: Ubaldo Tiberi
5 | # License: BSD3-Clause
6 | #
7 | # The architecture is fairly easy
8 | # 1. API functions are used to create a "results" var
9 | # 2. Such a "results" variable is placed into a popup
10 | # 3. Depending on the type of search, the way the results are displayed into
11 | # popups and what the callback function should do may change.
12 |
13 | if !has('vim9script') || v:version < 900
14 | # Needs Vim version 9.0 and above
15 | echo "You need at least Vim 9.0"
16 | finish
17 | endif
18 |
19 | if exists('g:vim_poptools_loaded')
20 | finish
21 | endif
22 | g:vim_poptools_loaded = true
23 |
24 | import autoload "../lib/funcs.vim"
25 |
26 | command! PoptoolsFindFile funcs.FindFile('file')
27 | command! PoptoolsFindFileInPath funcs.FindFile('file_in_path')
28 | command! PoptoolsFindDir funcs.FindDir()
29 | command! PoptoolsBuffers funcs.Buffers()
30 | command! PoptoolsRecentFiles funcs.RecentFiles()
31 | command! PoptoolsCmdHistory funcs.CmdHistory()
32 | command! PoptoolsGrep funcs.Grep()
33 | command! -nargs=? PoptoolsGrepInBuffer funcs.GrepInBuffer()
34 | command! PoptoolsVimgrep funcs.Vimgrep()
35 | command! PoptoolsColorscheme funcs.Colorscheme()
36 | command! PoptoolsLastSearch funcs.LastSearch()
37 | command! PoptoolsKill funcs.ClosePopups()
38 |
--------------------------------------------------------------------------------
/vim_poptools.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ubaldot/vim-poptools/9378c10bf5fc6a9d6eba1f49fb9b44e6a176ba86/vim_poptools.gif
--------------------------------------------------------------------------------