├── LICENSE
├── lua
└── telescope
│ └── _extensions
│ ├── frecency
│ ├── util.lua
│ ├── sorter.lua
│ ├── sql_wrapper.lua
│ └── db_client.lua
│ └── frecency.lua
└── README.md
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 nvim-telescope
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/lua/telescope/_extensions/frecency/util.lua:
--------------------------------------------------------------------------------
1 | local uv = vim.loop
2 |
3 | local util = {}
4 |
5 | -- stolen from penlight
6 |
7 | -- escape any Lua 'magic' characters in a string
8 | util.escape = function(str)
9 | return (str:gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1'))
10 | end
11 |
12 | util.filemask = function(mask)
13 | mask = util.escape(mask)
14 | return '^'..mask:gsub('%%%*','.*'):gsub('%%%?','.')..'$'
15 | end
16 |
17 | util.filename_match = function(filename, pattern)
18 | return filename:find(util.filemask(pattern)) ~= nil
19 | end
20 |
21 | --
22 |
23 | util.string_isempty = function(str)
24 | return str == nil or str == ''
25 | end
26 |
27 | util.string_starts = function(str, token)
28 | return str:sub(1, str:len(token)) == token
29 | end
30 |
31 | util.string_ends = function(str, token)
32 | return str:sub(str:len() - token:len() + 1, -1) == token
33 | end
34 |
35 | util.split = function(str, delimiter)
36 | local result = {}
37 | for match in str:gmatch("[^" .. delimiter .. "]+") do
38 | table.insert(result, match)
39 | end
40 | return result
41 | end
42 |
43 | util.fs_stat = function(path)
44 | local stat = uv.fs_stat(path)
45 | local res = {}
46 | res.exists = stat and true or false -- TODO: this is silly
47 | res.isdirectory = (stat and stat.type == "directory") and true or false
48 |
49 | return res
50 | end
51 |
52 | return util
53 |
--------------------------------------------------------------------------------
/lua/telescope/_extensions/frecency/sorter.lua:
--------------------------------------------------------------------------------
1 | local sorters = require "telescope.sorters"
2 | local util = require("telescope._extensions.frecency.util")
3 |
4 | local my_sorters = {}
5 |
6 | local substr_highlighter = function(_, prompt, display)
7 | local highlights = {}
8 | display = display:lower()
9 |
10 | local search_terms = util.split(prompt, "%s")
11 | local hl_start, hl_end
12 |
13 | for _, word in pairs(search_terms) do
14 | hl_start, hl_end = display:find(word, 1, true)
15 | if hl_start then
16 | table.insert(highlights, {start = hl_start, finish = hl_end})
17 | end
18 | end
19 |
20 | return highlights
21 | end
22 |
23 | my_sorters.get_substr_matcher = function(opts)
24 | opts = opts or {}
25 |
26 | local substr = sorters:new()
27 | substr.highlighter = substr_highlighter
28 | substr.scoring_function = function(_, prompt, _, entry)
29 | local display = entry.name:lower()
30 |
31 | local search_terms = util.split(prompt, "%s")
32 | local matched = 0
33 | local total_search_terms = 0
34 | for _, word in pairs(search_terms) do
35 | total_search_terms = total_search_terms + 1
36 | if display:find(word, 1, true) then
37 | matched = matched + 1
38 | end
39 | end
40 |
41 | return matched == total_search_terms and entry.index or -1
42 | end
43 |
44 | return substr
45 | end
46 |
47 | return my_sorters
48 |
--------------------------------------------------------------------------------
/lua/telescope/_extensions/frecency.lua:
--------------------------------------------------------------------------------
1 | local has_telescope, telescope = pcall(require, "telescope")
2 |
3 | -- TODO: make dependency errors occur in a better way
4 | if not has_telescope then
5 | error("This plugin requires telescope.nvim (https://github.com/nvim-telescope/telescope.nvim)")
6 | end
7 |
8 | -- start the database client
9 | local db_client = require("telescope._extensions.frecency.db_client")
10 | db_client.init()
11 |
12 | -- finder code
13 | local conf = require('telescope.config').values
14 | local entry_display = require "telescope.pickers.entry_display"
15 | local finders = require "telescope.finders"
16 | local path = require('telescope.path')
17 | local pickers = require "telescope.pickers"
18 | local sorters = require "telescope._extensions.frecency.sorter"
19 | local utils = require('telescope.utils')
20 |
21 | local os_path_sep = vim.loop.os_uname().sysname == "Windows" and "\\" or "/"
22 | local show_scores = false
23 |
24 | local frecency = function(opts)
25 | opts = opts or {}
26 |
27 | local cwd = vim.fn.expand(opts.cwd or vim.fn.getcwd())
28 | -- opts.lsp_workspace_filter = true
29 | -- TODO: decide on how to handle cwd or lsp_workspace for pathname shorten?
30 | local results = db_client.get_file_scores(opts) -- TODO: pass `filter_workspace` option
31 |
32 | local display_cols = {}
33 | display_cols[1] = show_scores and {width = 8} or nil
34 | table.insert(display_cols, {remaining = true})
35 |
36 | local displayer = entry_display.create {
37 | separator = "",
38 | hl_chars = {[os_path_sep] = "TelescopePathSeparator"},
39 | items = display_cols
40 | }
41 |
42 | -- TODO: look into why this gets called so much
43 | local bufnr, buf_is_loaded, filename, hl_filename, display_items
44 |
45 | local make_display = function(entry)
46 | bufnr = vim.fn.bufnr
47 | buf_is_loaded = vim.api.nvim_buf_is_loaded
48 |
49 | filename = entry.name
50 | hl_filename = buf_is_loaded(bufnr(filename)) and "TelescopeBufferLoaded" or ""
51 |
52 | if opts.tail_path then
53 | filename = utils.path_tail(filename)
54 | elseif opts.shorten_path then
55 | filename = utils.path_shorten(filename)
56 | end
57 |
58 | filename = path.make_relative(filename, cwd)
59 |
60 | display_items = show_scores and {{entry.score, "Directory"}} or {}
61 | table.insert(display_items, {filename, hl_filename})
62 |
63 | return displayer(display_items)
64 | end
65 |
66 | pickers.new(opts, {
67 | prompt_title = "Frecency",
68 | finder = finders.new_table {
69 | results = results,
70 | entry_maker = function(entry)
71 | return {
72 | value = entry.filename,
73 | display = make_display,
74 | ordinal = entry.filename,
75 | name = entry.filename,
76 | score = entry.score
77 | }
78 | end,
79 | },
80 | previewer = conf.file_previewer(opts),
81 | sorter = sorters.get_substr_matcher(opts),
82 | }):find()
83 | end
84 |
85 | return telescope.register_extension {
86 | setup = function(ext_config)
87 | show_scores = ext_config.show_scores or false
88 | end,
89 | exports = {
90 | frecency = frecency,
91 | },
92 | }
93 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # THIS REPOSITORY IS ARCHIVED AND NO LONGER UPDATED
2 |
3 | # THE MAINTAINED RELEASE VERSION CAN BE FOUND [HERE](https://github.com/nvim-telescope/telescope-frecency.nvim)
4 |
5 | A [telescope.nvim](https://github.com/nvim-telescope/telescope.nvim) extension that offers intelligent prioritization when selecting files from your editing history.
6 |
7 | Using an implementation of Mozilla's [Frecency algorithm](https://developer.mozilla.org/en-US/docs/Mozilla/Tech/Places/Frecency_algorithm) (used in [Firefox's address bar](https://support.mozilla.org/en-US/kb/address-bar-autocomplete-firefox)), files edited _frecently_ are given higher precedence in the list index.
8 |
9 | As the extension learns your editing habits over time, the sorting of the list is dynamically altered to priotize the files you're likely to need.
10 |
11 |
12 |
13 | * _Scores shown in finder for demonstration purposes - disabled by default_
14 |
15 | ## Frecency: Sorting by 'frequency' _and_ 'recency'
16 |
17 | 'Frecency' is a score given to each unique file indexed in a file history database.
18 |
19 | A timestamp is recorded once per session when a file is first loaded into a buffer.
20 |
21 | The score is calculated using the age of the 10 most recent timestamps and the total amount of times that the file has been loaded:
22 |
23 | ### Recency values (per timestamp)
24 |
25 | | Timestamp age | Value |
26 | | -------- | ---------- |
27 | | 4 hours | 100 |
28 | | 1 day | 80 |
29 | | 3 days | 60 |
30 | | 1 week | 40 |
31 | | 1 month | 20 |
32 | | 90 days | 10 |
33 |
34 | ### Score calculation
35 |
36 | ```
37 | score = frequency * recency_score / max_number_of_timestamps
38 | ```
39 |
40 | ## Requirements
41 |
42 | - [telescope.nvim](https://github.com/nvim-telescope/telescope.nvim) (required)
43 | - [sql.nvim](https://github.com/tami5/sql.nvim) (required)
44 |
45 | Timestamps and file records are stored in an [SQLite3](https://www.sqlite.org/index.html) database for persistence and speed.
46 | This plugin uses `sql.nvim` to perform the database transactions.
47 |
48 | ## Installation
49 |
50 | ### [Packer.nvim](https://github.com/wbthomason/packer.nvim)
51 |
52 | ```lua
53 | use {
54 | "sunjon/telescope-frecency",
55 | config = function()
56 | require"telescope".load_extension("frecency")
57 | end
58 | }
59 | ```
60 |
61 | _TODO: add installation instructions for other package managers_
62 |
63 | If no database is found when running Neovim with the plugin installed, a new one is created and entries from `shada` `v:oldfiles` are automatically imported.
64 |
65 | ## Usage
66 |
67 | ```
68 | :Telescope frecency
69 | ```
70 | ..or to map to a key:
71 |
72 | ```lua
73 | vim.api.nvim_set_keymap("n", "", "lua require('telescope').extensions.frecency.frecency()", {noremap = true, silent = true})
74 | ```
75 |
76 | ## Configuration
77 |
78 | See [default configuration](https://github.com/nvim-telescope/telescope.nvim#telescope-defaults) for full details on configuring Telescope.
79 |
80 | - `ignore_patterns`
81 |
82 | This setting controls which files are indexed (and subsequently which you'll see in the finder results).
83 |
84 | - `show_scores`
85 |
86 | To see the scores generated by the algorithm in the results, set this to `true`.
87 |
88 |
89 | If you've not configured the extension, the following values are used:
90 |
91 | ```
92 | telescope.setup {
93 | extensions = {
94 | frecency = {
95 | show_scores = false,
96 | ignore_patterns = {"*.git/*", "*/tmp/*"},
97 | }
98 | },
99 | }
100 | ```
101 |
102 | ### Highlight Groups
103 |
104 | ```vim
105 | TelescopePathSeparator
106 | TelescopeBufferLoaded
107 | ```
108 |
109 | ## References
110 |
111 | - [Mozilla: Frecency algorithm](https://developer.mozilla.org/en-US/docs/Mozilla/Tech/Places/Frecency_algorithm)
112 |
--------------------------------------------------------------------------------
/lua/telescope/_extensions/frecency/sql_wrapper.lua:
--------------------------------------------------------------------------------
1 | local util = require("telescope._extensions.frecency.util")
2 | local vim = vim
3 |
4 | local has_sql, sql = pcall(require, "sql")
5 | if not has_sql then
6 | error("This plugin requires sql.nvim (https://github.com/tami5/sql.nvim)")
7 | end
8 |
9 | -- TODO: pass in max_timestamps from db.lua
10 | local MAX_TIMESTAMPS = 10
11 |
12 | local db_table = {}
13 | db_table.files = "files"
14 | db_table.timestamps = "timestamps"
15 | --
16 |
17 | local M = {}
18 |
19 | function M:new()
20 | local o = {}
21 | setmetatable(o, self)
22 | self.__index = self
23 | self.db = nil
24 |
25 | return o
26 | end
27 |
28 | function M:bootstrap(opts)
29 | if self.db then return end
30 |
31 | opts = opts or {}
32 | self.max_entries = opts.max_entries or 2000
33 |
34 | -- create the db if it doesn't exist
35 | local db_root = opts.docs_root or vim.fn.stdpath('data')
36 | local db_filename = db_root .. "/file_frecency.sqlite3"
37 | self.db = sql.open(db_filename)
38 | if not self.db then
39 | print("error")
40 | return
41 | end
42 |
43 | local first_run = false
44 | if not self.db:exists(db_table.files) then
45 | first_run = true
46 | -- create tables if they don't exist
47 | self.db:create(db_table.files, {
48 | id = {"INTEGER", "PRIMARY", "KEY"},
49 | count = "INTEGER",
50 | path = "TEXT"
51 | })
52 | self.db:create(db_table.timestamps, {
53 | id = {"INTEGER", "PRIMARY", "KEY"},
54 | file_id = "INTEGER",
55 | timestamp = "REAL"
56 | -- FOREIGN KEY(file_id) REFERENCES files(id)
57 | })
58 | end
59 |
60 | self.db:close()
61 | return first_run
62 | end
63 |
64 | --
65 |
66 | function M:do_transaction(t, params)
67 | -- print(vim.inspect(t))
68 | -- print(vim.inspect(params))
69 | return self.db:with_open(function(db)
70 | local case = {
71 | [1] = function() return db:select(t.cmd_data, params) end,
72 | [2] = function() return db:insert(t.cmd_data, params) end,
73 | [3] = function() return db:delete(t.cmd_data, params) end,
74 | [4] = function() return db:eval(t.cmd_data, params) end,
75 | }
76 | return case[t.cmd]()
77 | end)
78 | end
79 |
80 | local cmd = {
81 | select = 1,
82 | insert = 2,
83 | delete = 3,
84 | eval = 4,
85 | }
86 |
87 | local queries = {
88 | file_add_entry = {
89 | cmd = cmd.insert,
90 | cmd_data = db_table.files
91 | },
92 | file_delete_entry = {
93 | cmd = cmd.delete,
94 | cmd_data = db_table.files
95 | },
96 | file_get_entries = {
97 | cmd = cmd.select,
98 | cmd_data = db_table.files
99 | },
100 | file_update_counter = {
101 | cmd = cmd.eval,
102 | cmd_data = "UPDATE files SET count = count + 1 WHERE path == :path;"
103 | },
104 | timestamp_add_entry = {
105 | cmd = cmd.eval,
106 | cmd_data = "INSERT INTO timestamps (file_id, timestamp) values(:file_id, julianday('now'));"
107 | },
108 | timestamp_delete_entry = {
109 | cmd = cmd.delete,
110 | cmd_data = db_table.timestamps
111 | },
112 | timestamp_get_all_entries = {
113 | cmd = cmd.select,
114 | cmd_data = db_table.timestamps,
115 | },
116 | timestamp_get_all_entry_ages = {
117 | cmd = cmd.eval,
118 | cmd_data = "SELECT id, file_id, CAST((julianday('now') - julianday(timestamp)) * 24 * 60 AS INTEGER) AS age FROM timestamps;"
119 | },
120 | timestamp_delete_before_id = {
121 | cmd = cmd.eval,
122 | cmd_data = "DELETE FROM timestamps WHERE id < :id and file_id == :file_id;"
123 | },
124 | }
125 |
126 | M.queries = queries
127 |
128 | --
129 |
130 | local function row_id(entry)
131 | return (not vim.tbl_isempty(entry)) and entry[1].id or nil
132 | end
133 |
134 | function M:update(filepath)
135 | local filestat = util.fs_stat(filepath)
136 | if (vim.tbl_isempty(filestat) or
137 | filestat.exists == false or
138 | filestat.isdirectory == true) then
139 | return end
140 |
141 | -- create entry if it doesn't exist
142 | local file_id
143 | file_id = row_id(self:do_transaction(queries.file_get_entries, {where = {path = filepath}}))
144 | if not file_id then
145 | self:do_transaction(queries.file_add_entry, {path = filepath, count = 1})
146 | file_id = row_id(self:do_transaction(queries.file_get_entries, {where = {path = filepath}}))
147 | else
148 | -- ..or update existing entry
149 | self:do_transaction(queries.file_update_counter, {path = filepath})
150 | end
151 |
152 | -- register timestamp for this update
153 | self:do_transaction(queries.timestamp_add_entry, {file_id = file_id})
154 |
155 | -- trim timestamps to MAX_TIMESTAMPS per file (there should be up to MAX_TS + 1 at this point)
156 | local timestamps = self:do_transaction(queries.timestamp_get_all_entries, {where = {file_id = file_id}})
157 | local trim_at = timestamps[(#timestamps - MAX_TIMESTAMPS) + 1]
158 | if trim_at then
159 | self:do_transaction(queries.timestamp_delete_before_id, {id = trim_at.id, file_id = file_id})
160 | end
161 | end
162 |
163 | function M:validate()
164 | end
165 |
166 | return M
167 |
--------------------------------------------------------------------------------
/lua/telescope/_extensions/frecency/db_client.lua:
--------------------------------------------------------------------------------
1 | local sqlwrap = require("telescope._extensions.frecency.sql_wrapper")
2 | local util = require("telescope._extensions.frecency.util")
3 | local conf = require('telescope.config').values
4 |
5 | local MAX_TIMESTAMPS = 10
6 | local DB_REMOVE_SAFETY_THRESHOLD = 10
7 |
8 | -- modifier used as a weight in the recency_score calculation:
9 | local recency_modifier = {
10 | [1] = { age = 240 , value = 100 }, -- past 4 hours
11 | [2] = { age = 1440 , value = 80 }, -- past day
12 | [3] = { age = 4320 , value = 60 }, -- past 3 days
13 | [4] = { age = 10080 , value = 40 }, -- past week
14 | [5] = { age = 43200 , value = 20 }, -- past month
15 | [6] = { age = 129600, value = 10 } -- past 90 days
16 | }
17 |
18 | local default_ignore_patterns = {
19 | "*.git/*", "*/tmp/*"
20 | }
21 |
22 | local sql_wrapper = nil
23 | local ignore_patterns = {}
24 |
25 | local function import_oldfiles()
26 | local oldfiles = vim.api.nvim_get_vvar("oldfiles")
27 | for _, filepath in pairs(oldfiles) do
28 | sql_wrapper:update(filepath)
29 | end
30 | print(("Telescope-Frecency: Imported %d entries from oldfiles."):format(#oldfiles))
31 | end
32 |
33 | local function file_is_ignored(filepath)
34 | local is_ignored = false
35 | for _, pattern in pairs(ignore_patterns) do
36 | if util.filename_match(filepath, pattern) then
37 | is_ignored = true
38 | goto continue
39 | end
40 | end
41 |
42 | ::continue::
43 | return is_ignored
44 | end
45 |
46 | local function validate_db()
47 | if not sql_wrapper then return {} end
48 |
49 | local queries = sql_wrapper.queries
50 | local files = sql_wrapper:do_transaction(queries.file_get_entries, {})
51 | local pending_remove = {}
52 | for _, entry in pairs(files) do
53 | if not util.fs_stat(entry.path).exists -- file no longer exists
54 | or file_is_ignored(entry.path) then -- cleanup entries that match the _current_ ignore list
55 | table.insert(pending_remove, entry)
56 | end
57 | end
58 |
59 | -- don't allow removal of >N values from DB without confirmation
60 | local confirmed = false
61 | if #pending_remove > DB_REMOVE_SAFETY_THRESHOLD then
62 | if vim.fn.confirm("Telescope-Frecency: remove " .. #pending_remove .. " entries from SQLite3 database?", "&Yes\n&No", 2) then
63 | confirmed = true
64 | end
65 | else
66 | confirmed = true
67 | end
68 |
69 | if confirmed then
70 | for _, entry in pairs(pending_remove) do
71 | -- remove entries from file and timestamp tables
72 | print("removing entry: " .. entry.path .. "[" .. entry.id .."]")
73 | sql_wrapper:do_transaction(queries.file_delete_entry , {where = {id = entry.id }})
74 | sql_wrapper:do_transaction(queries.timestamp_delete_entry, {where = {file_id = entry.id}})
75 | end
76 | else
77 | print("TelescopeFrecency: validation aborted.")
78 | end
79 | end
80 |
81 | local function init()
82 | if sql_wrapper then return end
83 |
84 | sql_wrapper = sqlwrap:new()
85 | local first_run = sql_wrapper:bootstrap()
86 | validate_db()
87 |
88 | if first_run then
89 | -- TODO: this needs to be scheduled for after shada load
90 | vim.defer_fn(import_oldfiles, 100)
91 | end
92 |
93 | ignore_patterns = conf.file_ignore_patterns or default_ignore_patterns
94 |
95 | -- setup autocommands
96 | vim.api.nvim_command("augroup TelescopeFrecency")
97 | vim.api.nvim_command("autocmd!")
98 | vim.api.nvim_command("autocmd BufWinEnter,BufWritePost * lua require'telescope._extensions.frecency.db_client'.autocmd_handler(vim.fn.expand(''))")
99 | vim.api.nvim_command("augroup END")
100 | end
101 |
102 | local function calculate_file_score(frequency, timestamps)
103 | local recency_score = 0
104 | for _, ts in pairs(timestamps) do
105 | for _, rank in ipairs(recency_modifier) do
106 | if ts.age <= rank.age then
107 | recency_score = recency_score + rank.value
108 | goto continue
109 | end
110 | end
111 | ::continue::
112 | end
113 |
114 | return frequency * recency_score / MAX_TIMESTAMPS
115 | end
116 |
117 | local function filter_timestamps(timestamps, file_id)
118 | local res = {}
119 | for _, entry in pairs(timestamps) do
120 | if entry.file_id == file_id then
121 | table.insert(res, entry)
122 | end
123 | end
124 | return res
125 | end
126 |
127 | local function filter_workspace(filelist, workspace_path)
128 | local res = {}
129 | for _, entry in pairs(filelist) do
130 | if util.string_starts(entry.path, workspace_path) then
131 | table.insert(res, entry)
132 | end
133 | end
134 | return res
135 | end
136 |
137 | local function get_file_scores(opts)
138 | if not sql_wrapper then return {} end
139 |
140 | local queries = sql_wrapper.queries
141 | local scores = {}
142 | local files = sql_wrapper:do_transaction(queries.file_get_entries, {})
143 | local timestamp_ages = sql_wrapper:do_transaction(queries.timestamp_get_all_entry_ages, {})
144 |
145 | if vim.tbl_isempty(files) then return scores end
146 |
147 | -- filter to LSP workspace directory
148 | local buf_workspaces = opts.lsp_workspace_filter and vim.lsp.buf.list_workspace_folders() or {}
149 | if not vim.tbl_isempty(buf_workspaces) then
150 | for _, ws_path in pairs(buf_workspaces) do
151 | files = filter_workspace(files, ws_path)
152 | end
153 | end
154 |
155 | for _, file_entry in ipairs(files) do
156 | table.insert(scores, {
157 | filename = file_entry.path,
158 | score = calculate_file_score(file_entry.count, filter_timestamps(timestamp_ages, file_entry.id))
159 | })
160 | end
161 |
162 | -- sort the table
163 | table.sort(scores, function(a, b) return a.score > b.score end)
164 |
165 | return scores
166 | end
167 |
168 | local function autocmd_handler(filepath)
169 | if not sql_wrapper or util.string_isempty(filepath) then return end
170 |
171 | -- check if file is registered as loaded
172 | if not vim.b.telescope_frecency_registered then
173 | -- allow [noname] files to go unregistered until BufWritePost
174 | if not util.fs_stat(filepath).exists then return end
175 |
176 | if file_is_ignored(filepath) then return end
177 |
178 | vim.b.telescope_frecency_registered = 1
179 | sql_wrapper:update(filepath)
180 | end
181 | end
182 |
183 | return {
184 | init = init,
185 | get_file_scores = get_file_scores,
186 | autocmd_handler = autocmd_handler,
187 | }
188 |
--------------------------------------------------------------------------------