diff --git a/lua/telescope/builtin/__files.lua b/lua/telescope/builtin/__files.lua index fc91fee..9eb5b5d 100644 --- a/lua/telescope/builtin/__files.lua +++ b/lua/telescope/builtin/__files.lua @@ -384,6 +384,7 @@ files.find_files = function(opts) pickers .new(opts, { prompt_title = "Find Files", + files_picker = true, finder = finders.new_oneshot_job(find_command, opts), previewer = conf.file_previewer(opts), sorter = conf.file_sorter(opts), diff --git a/lua/telescope/builtin/__git.lua b/lua/telescope/builtin/__git.lua index a50434f..620884f 100644 --- a/lua/telescope/builtin/__git.lua +++ b/lua/telescope/builtin/__git.lua @@ -46,6 +46,7 @@ git.files = function(opts) pickers .new(opts, { prompt_title = "Git Files", + files_picker = true, finder = finders.new_oneshot_job( vim.tbl_flatten { opts.git_command, diff --git a/lua/telescope/pickers.lua b/lua/telescope/pickers.lua index e28a451..cd596ed 100644 --- a/lua/telescope/pickers.lua +++ b/lua/telescope/pickers.lua @@ -319,6 +319,8 @@ function Picker:new(opts) cache_picker = config.resolve_table_opts(opts.cache_picker, vim.deepcopy(config.values.cache_picker)), __scrolling_limit = tonumber(vim.F.if_nil(opts.temp__scrolling_limit, 250)), + + allow_location_input = opts.files_picker or false, }, self) obj.create_layout = opts.create_layout or config.values.create_layout or default_create_layout @@ -626,6 +628,24 @@ function Picker:find() local start_time = vim.loop.hrtime() local prompt = self:_get_next_filtered_prompt() + if self.allow_location_input == true then + local filename, line_number, column_number = utils.separate_file_path_location(prompt) + + if line_number or column_number then + state.set_global_key("prompt_location", { row = line_number, col = column_number }) + self:refresh_previewer() + elseif state.get_global_key "prompt_location" then + state.set_global_key("prompt_location", nil) + self:refresh_previewer() + end + + -- it is important to continue behaving as if there is no location in prompt + prompt = filename + elseif state.get_global_key "prompt_location" then + -- in case new picker that does not support locations is opened clear the location + -- without refreshing previewer + state.set_global_key("prompt_location", nil) + end -- TODO: Entry manager should have a "bulk" setter. This can prevent a lot of redraws from display if self.cache_picker == false or self.cache_picker.is_cached ~= true then @@ -1046,6 +1066,13 @@ function Picker:set_selection(row) end local entry = self.manager:get_entry(self:get_index(row)) + + local prompt_location = state.get_global_key "prompt_location" + if entry and prompt_location then + entry.lnum = prompt_location.row or 0 + entry.col = prompt_location.col or 0 + end + state.set_global_key("selected_entry", entry) if not entry then diff --git a/lua/telescope/previewers/buffer_previewer.lua b/lua/telescope/previewers/buffer_previewer.lua index 14d4241..be865dd 100644 --- a/lua/telescope/previewers/buffer_previewer.lua +++ b/lua/telescope/previewers/buffer_previewer.lua @@ -4,6 +4,7 @@ local utils = require "telescope.utils" local putils = require "telescope.previewers.utils" local Previewer = require "telescope.previewers.previewer" local conf = require("telescope.config").values +local global_state = require "telescope.state" local pscan = require "plenary.scandir" @@ -345,8 +346,6 @@ previewers.new_buffer_previewer = function(opts) local old_bufs = {} local bufname_table = {} - - local global_state = require "telescope.state" local preview_window_id local function get_bufnr(self) @@ -489,6 +488,29 @@ end previewers.cat = defaulter(function(opts) opts = opts or {} local cwd = opts.cwd or vim.loop.cwd() + local function jump_to_line(bufnr, winid) + pcall(vim.api.nvim_buf_clear_namespace, bufnr, ns_previewer, 0, -1) + local location = global_state.get_global_key "prompt_location" + + if location and location.row > 0 then + local highlight_range = location.col and location.col > 0 and { location.col - 1, location.col } or { 0, -1 } + + pcall( + vim.api.nvim_buf_add_highlight, + bufnr, + ns_previewer, + "TelescopePreviewLine", + location.row - 1, + highlight_range[1], + highlight_range[2] + ) + + pcall(vim.api.nvim_win_set_cursor, winid, { location.row, location.col }) + vim.api.nvim_buf_call(bufnr, function() + vim.cmd "norm! zz" + end) + end + end return previewers.new_buffer_previewer { title = "File Preview", dyn_title = function(_, entry) @@ -509,6 +531,9 @@ previewers.cat = defaulter(function(opts) winid = self.state.winid, preview = opts.preview, file_encoding = opts.file_encoding, + callback = function(bufnr) + jump_to_line(bufnr, self.state.winid) + end, }) end, } diff --git a/lua/telescope/utils.lua b/lua/telescope/utils.lua index cb87c33..9b48ca4 100644 --- a/lua/telescope/utils.lua +++ b/lua/telescope/utils.lua @@ -567,4 +567,45 @@ utils.list_find = function(func, list) end end +--- Takes the path and parses optional cursor location `$file:$line:$column` +--- If line or column not present `0` returne returned. +--- @param path string +utils.separate_file_path_location = function(path) + local location_numbers = {} + -- Split the 2 last `:` separated parts and if they are numbers treat + -- as line and columnd numbers + for i = #path, 1, -1 do + if path:sub(i, i) == ":" then + -- If this is the last `:` we ignore it for a case when user + -- just entered it with intention to enter a line or column number + -- to not display "No results" immediately. + if i == #path then + path = path:sub(1, i - 1) + else + local location_value = tonumber(path:sub(i + 1)) + if location_value then + table.insert(location_numbers, location_value) + path = path:sub(1, i - 1) + + if #location_numbers == 2 then + -- There couldn't be more than 2 : separated number + break + end + end + end + end + end + + if #location_numbers == 2 then + -- because of the reverse the line number will be second + return path, location_numbers[2], location_numbers[1] + end + + if #location_numbers == 1 then + return path, location_numbers[1], 0 + end + + return path, 0, 0 +end + return utils diff --git a/lua/tests/automated/utils_spec.lua b/lua/tests/automated/utils_spec.lua index 5edf356..608f9a1 100644 --- a/lua/tests/automated/utils_spec.lua +++ b/lua/tests/automated/utils_spec.lua @@ -78,3 +78,48 @@ describe("is_uri", function() end end) end) + +describe("separates file path location", function() + local suites = { + { + input = "file.txt:12:4", + file = "file.txt", + row = 12, + col = 4, + }, + { + input = "file.txt:12", + file = "file.txt", + row = 12, + col = 0, + }, + { + input = "file:12:4", + file = "file", + row = 12, + col = 4, + }, + { + input = "file:12:", + file = "file", + row = 12, + col = 0, + }, + { + input = "file:", + file = "file", + row = 0, + col = 0, + }, + } + + for _, suite in ipairs(suites) do + it("separtates file path for " .. suite.input, function() + local file, row, col = utils.separate_file_path_location(suite.input) + + assert.are.equal(file, suite.file) + assert.are.equal(row, suite.row) + assert.are.equal(col, suite.col) + end) + end +end)