Repository: nvim-telescope/telescope.nvim (lua)

Issue:
Add an ability to open direct location
**Is your feature request related to a problem? Please describe.**
I usually deal with direct location output from the compiler which is a path to the file plus the location marker. e.g.

```
my/directory/file.rs:24:4
```

**Describe the solution you'd like**
I want to be able to paste this directly to the telescope, press enter, and appear in the direct symbol location.

> P.S. This is possible in vs code with no additional setup for example

**Describe alternatives you've considered**
1. Open telescope search
2. Observe nothing found
3. Remove paths 
4. type :24<CR> to go to the line 

**Additional context**
This should be primarily an input feature — strip out everything that looks like location, search, and when open file focus specific symbol. That's why I think it should be a part of the telescope core and not be solved via plugins. 

If you are interested I can open PR for this

Pull request:
## Type of change

- New feature (non-breaking change which adds functionality)

## Description

Fixing the original issue: When selecting files in the telescope impossible to copy paths from the compiler, you need to clear the location from the path.

Fixes #2650

This fixes the problem by stripping out the location from the path (important: only for file pickers). And using the location to set the cursor when opening the file.

There is a new option `files_picker` for a `Picker` that needs to be set to true for all file pickers, for now, it basically enables location stripping which makes no sense in another type of pickers.

Watch the video:

https://github.com/nvim-telescope/telescope.nvim/assets/16926049/1ee3b507-4af9-4d0f-abe2-3c67254dfcd9

## Implementation notice

I didn't find another way to put the location stripping because it must happen right after the input and make sure that if the location is set the prompt = input without location. But after we calculated that during selection we can set `entry.lnum` and `entry.col` to the value and reuse all the existing functionality to jump to the cursor

## How it is different from https://github.com/nvim-telescope/telescope.nvim/pull/2312

The PR seems to be abandoned but I need this functionality so much so I updated and changed it myself, made the API closer to the current entry row/linenr selection state which is used in live_grep, implemented support for the column number, added automated tests, and highlighted the line and column in the previewer.

## How Has This Been Tested?

An added test suite that covers the algorithm of stripping the location from the path to the `utils_spec.lua` 

Manual test:

1. Open telescope to find files
2. Enter a:12:1
3. Observer that files with a in the name found
4. Open the file and observe that you are in the :12:1 position

**Configuration**:
* Neovim version (nvim --version):

NVIM v0.9.4
Build type: Release
LuaJIT 2.1.1699180677
* Operating system and version: MacOS 13

# Checklist:

- [x] My code follows the style guidelines of this project (stylua)
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I have made corresponding changes to the documentation (lua annotations)

Patch:
diff --git a/lua/telescope/builtin/__files.lua b/lua/telescope/builtin/__files.lua
index fc91fee94b..1c54261a98 100644
--- a/lua/telescope/builtin/__files.lua
+++ b/lua/telescope/builtin/__files.lua
@@ -384,8 +384,9 @@ files.find_files = function(opts)
   pickers
     .new(opts, {
       prompt_title = "Find Files",
+      __locations_input = true,
       finder = finders.new_oneshot_job(find_command, opts),
-      previewer = conf.file_previewer(opts),
+      previewer = conf.grep_previewer(opts),
       sorter = conf.file_sorter(opts),
     })
     :find()
diff --git a/lua/telescope/builtin/__git.lua b/lua/telescope/builtin/__git.lua
index 1bd3b52548..3caa454597 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",
+      __locations_input = true,
       finder = finders.new_oneshot_job(
         vim.tbl_flatten {
           opts.git_command,
@@ -54,7 +55,7 @@ git.files = function(opts)
         },
         opts
       ),
-      previewer = conf.file_previewer(opts),
+      previewer = conf.grep_previewer(opts),
       sorter = conf.file_sorter(opts),
     })
     :find()
diff --git a/lua/telescope/pickers.lua b/lua/telescope/pickers.lua
index e28a451009..0db23492d5 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)),
+
+    __locations_input = vim.F.if_nil(opts.__locations_input, false),
   }, self)
 
   obj.create_layout = opts.create_layout or config.values.create_layout or default_create_layout
@@ -626,6 +628,21 @@ function Picker:find()
       local start_time = vim.loop.hrtime()
 
       local prompt = self:_get_next_filtered_prompt()
+      if self.__locations_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 })
+        elseif state.get_global_key "prompt_location" then
+          state.set_global_key("prompt_location", nil)
+        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
+        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 +1063,19 @@ 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
+    if prompt_location.col and prompt_location.col > 0 then
+      entry.col = prompt_location.col
+      entry.colend = prompt_location.col + 1
+    else
+      entry.col = 1 -- we do + 1 here because previewer does -1
+      entry.colend = 0
+    end
+  end
+
   state.set_global_key("selected_entry", entry)
 
   if not entry then
@@ -1103,6 +1133,7 @@ function Picker:set_selection(row)
     return
   end
 
+  self:refresh_previewer()
   if old_entry == entry and self._selection_row == row then
     return
   end
@@ -1111,8 +1142,6 @@ function Picker:set_selection(row)
   self._selection_entry = entry
   self._selection_row = row
 
-  self:refresh_previewer()
-
   vim.api.nvim_win_set_cursor(self.results_win, { row + 1, 0 })
 end
 
diff --git a/lua/telescope/previewers/buffer_previewer.lua b/lua/telescope/previewers/buffer_previewer.lua
index 14d42415d3..74080f25f0 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)
diff --git a/lua/telescope/utils.lua b/lua/telescope/utils.lua
index cb87c33830..32df244600 100644
--- a/lua/telescope/utils.lua
+++ b/lua/telescope/utils.lua
@@ -567,4 +567,40 @@ 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` returned.
+--- @param path string
+utils.__separate_file_path_location = function(path)
+  local location_numbers = {}
+  for i = #path, 1, -1 do
+    if path:sub(i, i) == ":" then
+      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


Test patch:
diff --git a/lua/tests/automated/utils_spec.lua b/lua/tests/automated/utils_spec.lua
index 5edf35624c..b0f5d22c47 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)