├── 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 | screenshot 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 | --------------------------------------------------------------------------------