commit 69d4cd3899845d84767fa0e2ff9c8234e9dcbf84 Author: TJ DeVries Date: Wed Jul 15 00:05:14 2020 -0400 Initial commit after stream diff --git a/README.md b/README.md new file mode 100644 index 0000000..b9fc9ad --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# telescope.nvim + +Gaze deeply into unknown regions using the power of the moon. + +## Goals + + +pipeline different objects + +"finder" + - executable: rg, git ls-files, ... + - things in lua already + - vim things + +"picker" + - fzf + - sk + - does this always need to be fuzzy? + + - you'll map what you want to do with vimscript / lua mappings + +"previewer" + - sometimes built-in + - sometimes a lua callback + + + +fzf: +- have a list of inputs +- i have a prompt/things people typed +- instantly return the stuff via stdout diff --git a/file.txt b/file.txt new file mode 100644 index 0000000..4ea4078 --- /dev/null +++ b/file.txt @@ -0,0 +1 @@ +plugin/telescope.vim diff --git a/lua/telescope/init.lua b/lua/telescope/init.lua new file mode 100644 index 0000000..32c526b --- /dev/null +++ b/lua/telescope/init.lua @@ -0,0 +1,325 @@ +package.loaded['popup'] = nil +package.loaded['popup.border'] = nil +package.loaded['popup.init'] = nil + +local a = vim.api + +local popup = require('popup') + +local telescope = {} + +local ns_telescope = a.nvim_create_namespace('telescope') +local ns_telescope_highlight = a.nvim_create_namespace('telescope_highlight') + +local Finder = {} +Finder.__index = Finder + +function Finder:new(fn_command) + -- TODO: Add config for: + -- - cwd + return setmetatable({ + fn_command = fn_command, + job_id = -1, + }, Finder) +end + +function Finder:display_results(win, bufnr, prompt) + if self.job_id > 0 then + -- Make sure we kill old jobs. + vim.fn.jobstop(self.job_id) + end + + self.job_id = vim.fn.jobstart(self.fn_command(prompt), { + -- TODO: Decide if we want this or don't want this. + stdout_buffered = true, + on_stdout = function(_, data, _) + a.nvim_buf_set_lines(bufnr, -1, -1, false, data) + end, + + on_exit = function() + -- TODO: Add possibility to easily highlight prompt within buffer + -- without having to do weird stuff and with it actually working... + if false then + vim.fn.matchadd("Type", "\\<" .. prompt .. "\\>", 1, -1, {window = win}) + end + end, + }) + + --[[ + local function get_rg_results(bufnr, search_string) + local start_time = vim.fn.reltime() + + vim.fn.jobstart(string.format('rg %s', search_string), { + cwd = '/home/tj/build/neovim', + + on_stdout = function(job_id, data, event) + vim.api.nvim_buf_set_lines(bufnr, -1, -1, false, data) + end, + + on_exit = function() + print("Finished in: ", vim.fn.reltimestr(vim.fn.reltime(start_time))) + end, + + stdout_buffer = true, + }) + end + --]] +end + +local Previewer = {} +Previewer.__index = Previewer + +function Previewer:new(fn) + return setmetatable({ + fn = fn, + }, Previewer) +end + +local picker_finders = {} + +local Picker = {} +Picker.__index = Picker + +function Picker:new(opts) + opts = opts or {} + return setmetatable({ + filter = opts.filter, + previewer = opts.previewer, + maps = opts.maps, + }, Picker) +end + +local hack_status = {} + +local previewers = {} + +function Picker:find(finder) + local prompt_string = 'MUNITER > ' + -- Create three windows: + -- 1. Prompt window + -- 2. Options window + -- 3. Preview window + + local width = 100 + local col = 10 + local prompt_line = 50 + + local result_height = 25 + local prompt_height = 1 + + -- TODO: Add back the borders after fixing some stuff in popup.nvim + local results_win = popup.create('', { + height = result_height, + minheight = result_height, + width = width, + line = prompt_line - 2 - result_height, + col = col, + -- border = {}, + enter = false, + }) + local results_bufnr = a.nvim_win_get_buf(results_win) + picker_finders[results_bufnr] = finder + + local preview_win = popup.create('', { + height = result_height + prompt_height + 4, + minheight = result_height + prompt_height + 4, + width = 100, + line = prompt_line - 2 - result_height, + col = col + width + 2, + -- border = {}, + enter = false, + highlight = false, + }) + local preview_bufnr = a.nvim_win_get_buf(preview_win) + + -- TODO: For some reason, highlighting is kind of weird on these windows. + -- It may actually be my colorscheme tho... + a.nvim_win_set_option(preview_win, 'winhl', '') + + -- TODO: We need to center this and make it prettier... + local prompt_win = popup.create('', { + height = prompt_height, + width = width, + line = prompt_line, + col = col, + border = {}, + }) + local prompt_bufnr = a.nvim_win_get_buf(prompt_win) + + a.nvim_buf_set_option(prompt_bufnr, 'buftype', 'prompt') + vim.fn.prompt_setprompt(prompt_bufnr, prompt_string) + + -- TODO: Please use the cool autocmds once you get off your lazy bottom and finish the PR ;) + local autocmd_string = string.format( + [[ autocmd TextChanged,TextChangedI :lua __TelescopeOnChange(%s, "%s", %s, %s)]], + prompt_bufnr, + prompt_string, + results_bufnr, + results_win) + + local buf_close = string.format( + [[ autocmd BufLeave :bd! %s ]], + prompt_bufnr) + + local results_close = string.format( + [[ autocmd BufLeave :call nvim_win_close(%s, v:true) ]], + results_win) + + local preview_close = string.format( + [[ autocmd BufLeave :call nvim_win_close(%s, v:true) ]], + preview_win) + + vim.cmd([[augroup PickerCommands]]) + vim.cmd([[ au!]]) + vim.cmd( autocmd_string) + vim.cmd( buf_close) + vim.cmd( results_close) + vim.cmd( preview_close) + vim.cmd([[augroup END]]) + + previewers[prompt_bufnr] = self.previewer + + -- TODO: Clear this hack status stuff when closing + hack_status[prompt_bufnr] = { + prompt_bufnr = prompt_bufnr, + prompt_win = prompt_win, + results_bufnr = results_bufnr, + results_win = results_win, + preview_bufnr = preview_bufnr, + preview_win = preview_win, + } + + local function default_mapper(map_key, table_key) + a.nvim_buf_set_keymap( + prompt_bufnr, + 'i', + map_key, + string.format( + [[:lua __TelescopeMapping(%s, %s, '%s')]], + prompt_bufnr, + results_bufnr, + table_key + ), + { + silent = true, + } + ) + end + + default_mapper('', 'control-n') + default_mapper('', 'control-p') + default_mapper('', 'enter') + + vim.cmd [[startinsert]] +end + +-- TODO: All lower case mappings +local telescope_selections = {} + +local function update_current_selection(prompt_bufnr, results_bufnr, row) + a.nvim_buf_clear_namespace(results_bufnr, ns_telescope_highlight, 0, -1) + a.nvim_buf_add_highlight( + results_bufnr, + ns_telescope_highlight, + 'Error', + row, + 0, + -1 + ) + + telescope_selections[prompt_bufnr] = row + + if previewers[prompt_bufnr] then + vim.g.got_here = true + local status = hack_status[prompt_bufnr] + previewers[prompt_bufnr].fn( + status.preview_win, + status.preview_bufnr, + status.results_bufnr, + row + ) + end +end + +local mappings = {} + +-- TODO: Refactor this to use shared code. +-- TODO: Move from top to bottom, etc. +-- TODO: It seems like doing this brings us back to the beginning of the prompt, which is not great. +mappings["control-n"] = function(prompt_bufnr, results_bufnr) + if telescope_selections[prompt_bufnr] == nil then + telescope_selections[prompt_bufnr] = 0 + end + + local row = telescope_selections[prompt_bufnr] + 1 + update_current_selection(prompt_bufnr, results_bufnr, row) +end + +mappings["control-p"] = function(prompt_bufnr, results_bufnr) + if telescope_selections[prompt_bufnr] == nil then + telescope_selections[prompt_bufnr] = 0 + end + + local row = telescope_selections[prompt_bufnr] - 1 + update_current_selection(prompt_bufnr, results_bufnr, row) +end + +mappings["enter"] = function(prompt_bufnr, results_bufnr) + local extmark = a.nvim_buf_get_extmarks( + results_bufnr, + ns_telescope_highlight, + 0, + -1, + {} + ) + + print(vim.inspect(extmark)) +end + +function __TelescopeMapping(prompt_bufnr, results_bufnr, characters) + if mappings[characters] then + mappings[characters](prompt_bufnr, results_bufnr) + end +end + +function __TelescopeOnChange(bufnr, prompt, results_bufnr, results_win) + local line = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)[1] + local prompt_input = string.sub(line, #prompt + 1) + print(string.format("|%s|", prompt_input)) + + local finder = picker_finders[results_bufnr] + a.nvim_buf_set_lines(results_bufnr, 0, -1, false, {}) + finder:display_results(results_win, results_bufnr, prompt_input) +end + +-- Uhh, finder should probably just GET the results +-- and then update some table. +-- When updating the table, we should call filter on those items +-- and then only display ones that pass the filter +local f = Finder:new(function(prompt) + return string.format('rg %s', prompt) +end) + +local p = Picker:new { + previewer = Previewer:new(function(preview_win, preview_bufnr, results_bufnr, row) + local line = a.nvim_buf_get_lines(results_bufnr, row, row + 1, false)[1] + local file_name = vim.split(line, ":")[1] + + -- print(file_name) + -- vim.fn.termopen( + -- string.format("bat --color=always --style=grid %s"), + local file_bufnr = vim.fn.bufnr(file_name, true) + -- TODO: We should probably call something like this because we're not always getting highlight and all that stuff. + -- api.nvim_command('doautocmd filetypedetect BufRead ' .. vim.fn.fnameescape(filename)) + a.nvim_win_set_buf(preview_win, file_bufnr) + end) +} +p:find(f) + +-- TODO: Make filters +-- "fzf --filter" +-- jobstart() -> | fzf --filter "input on prompt" +-- function Filter:new(command) +-- end + +return telescope diff --git a/plugin/telescope.vim b/plugin/telescope.vim new file mode 100644 index 0000000..50627bf --- /dev/null +++ b/plugin/telescope.vim @@ -0,0 +1,31 @@ +" let s:term_command = "rg preview_quit_map -l | fzf --preview 'bat --color=always --style=grid {-1}' --print0" +" let s:term_command = "rg preview_quit_map -l | fzf --preview 'bat --color=always --style=grid {-1}' > file.txt" +let s:term_command = "(rg preview_quit_map -l | fzf --preview 'bat --color=always --style=grid {-1}')" + +function! s:on_exit() abort + let g:result = readfile('file.txt') +endfunction + +function! TestFunc() abort + let g:term_output_stdout = [] + let g:term_output_stderr = [] + let g:term_output_onexit = [] + + vnew + let term_id = termopen(s:term_command, { + \ 'on_stdout': { j, d, e -> add(g:term_output_stdout, d) }, + \ 'on_stderr': { j, d, e -> add(g:term_output_stderr, d) }, + \ 'on_exit': { j, d, e -> s:on_exit() }, + \ 'stdout_buffered': v:false, + \ }) +endfunction + +function! PrintStuff() abort + echo len(g:term_output_stdout) len(g:term_output_stderr) len(g:term_output_onexit) +endfunction + +" call TestFunc() + +" echo g:term_output_stdout[-1] +" echo g:term_output_stderr +" echo g:term_output_onexit diff --git a/scratch/example.lua b/scratch/example.lua new file mode 100644 index 0000000..19952dc --- /dev/null +++ b/scratch/example.lua @@ -0,0 +1,68 @@ + +local finder = Finder:new { + 'rg %s -l', + pipeable = true, + ... +} + +local filter = Filter:new { + "fzf --filter '%s'" +} + +local lua_filter = Filter:new { + function(input, line) + if string.match(line, input) then + return true + end + + return false + end +} + + +local picker_read = Picker:new { + filter = filter, + + previewer = function(window, buffer, line) + local file = io.open(line, "r") + + local filename = vim.split(line, ':')[1] + if vim.fn.bufexists(filename) then + vim.api.nvim_win_set_buf(window, vim.fn.bufnr(filename)) + return + end + + local lines = {} + for _ = 1, 100 do + table.insert(lines, file:read("l")) + + -- TODO: Check if we're out of lines + end + + -- optionally set the filetype or whatever... + vim.api.nvim_buf_set_lines(buffer, 0, -1, false, lines) + end, + + mappings = { + [""] = function(line) + vim.cmd(string.format('e ', vim.split(line, ':'))) + end, + }, +} + +local picker = Picker:new { + filter = filter, + + -- idk + previewer = function(window, line) + vim.api.nvim_win_set_current_window(window) + + -- if is_file_loaded(line) then + -- lien_number = vim.api.nvim_... + + vim.fn.termopen(string.format( + 'bat --color=always --style=grid %s', + vim.split(line, ':')[1] + )) + end +} diff --git a/scratch/rg_lua_jobstart.lua b/scratch/rg_lua_jobstart.lua new file mode 100644 index 0000000..4b551d4 --- /dev/null +++ b/scratch/rg_lua_jobstart.lua @@ -0,0 +1,21 @@ + +local function get_rg_results(bufnr, search_string) + local start_time = vim.fn.reltime() + + vim.fn.jobstart(string.format('rg %s', search_string), { + cwd = '/home/tj/build/neovim', + + on_stdout = function(job_id, data, event) + vim.api.nvim_buf_set_lines(bufnr, -1, -1, false, data) + end, + + on_exit = function() + print("Finished in: ", vim.fn.reltimestr(vim.fn.reltime(start_time))) + end, + + stdout_buffer = true, + }) +end + +local bufnr = 14 +get_rg_results(bufnr, 'vim.api') diff --git a/scratch/slow_proc.sh b/scratch/slow_proc.sh new file mode 100755 index 0000000..6326991 --- /dev/null +++ b/scratch/slow_proc.sh @@ -0,0 +1,6 @@ + +echo "hello" +sleep 1 +echo "cool" +sleep 1 +echo "world"