From 771726f7d6e7e96e9273e454b1c1f49168663a37 Mon Sep 17 00:00:00 2001 From: JINNOUCHI Yasushi Date: Sun, 25 Feb 2024 14:25:40 +0900 Subject: [PATCH] feat: match like 'smartcase' (#177) * feat: match like 'smartcase' Fix #36 * docs: add note for 'smartcase' * docs: emphasize requirements --- README.md | 16 +++++++++- lua/frecency/picker.lua | 4 +-- lua/frecency/sorters.lua | 63 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 lua/frecency/sorters.lua diff --git a/README.md b/README.md index 4b10ea0..836c0ba 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ directories provided by the language server. ## Requirements -- [telescope.nvim](https://github.com/nvim-telescope/telescope.nvim) (required) +- [telescope.nvim](https://github.com/nvim-telescope/telescope.nvim) **(required)** - [nvim-web-devicons](https://github.com/kyazdani42/nvim-web-devicons) (optional) - [ripgrep](https://github.com/BurntSushi/ripgrep) or [fd](https://github.com/sharkdp/fd) (optional) @@ -142,6 +142,20 @@ Filter tags are applied by typing the `:tag:` name (adding surrounding colons) in the finder query. Entering `:` will trigger omnicompletion for available tags. +### Dealing with upper case letters + +In default, the sorter always ignores upper case letters in your input string. +But when [`'smartcase'`][smartcase] is ON and input string includes one upper case letter at +least, it matches against exact the same as you input. + +| input string | `'smartcase'` is ON | `'smartcase'` is OFF | +|--------------|-----------------------------|-----------------------------| +| `abc` | matches `abc`, `ABC`, `aBc` | matches `abc`, `ABC`, `aBc` | +| `aBc` | matches `aBc` | no match | +| `ABC` | matches `ABC` | no match | + +[smartcase]: https://neovim.io/doc/user/options.html#'smartcase' + ## Configuration See [default configuration](https://github.com/nvim-telescope/telescope.nvim#telescope-defaults) for full details on configuring Telescope. diff --git a/lua/frecency/picker.lua b/lua/frecency/picker.lua index edf3f9f..79bf216 100644 --- a/lua/frecency/picker.lua +++ b/lua/frecency/picker.lua @@ -1,11 +1,11 @@ local State = require "frecency.state" local Finder = require "frecency.finder" +local sorters = require "frecency.sorters" local log = require "plenary.log" local Path = require "plenary.path" --[[@as PlenaryPath]] local actions = require "telescope.actions" local config_values = require("telescope.config").values local pickers = require "telescope.pickers" -local sorters = require "telescope.sorters" local utils = require "telescope.utils" --[[@as TelescopeUtils]] local uv = vim.loop or vim.uv @@ -110,7 +110,7 @@ function Picker:start(opts) prompt_title = "Frecency", finder = finder, previewer = config_values.file_previewer(opts), - sorter = sorters.get_substr_matcher(), + sorter = sorters.get_frecency_matcher(), on_input_filter_cb = self:on_input_filter_cb(opts), attach_mappings = function(prompt_bufnr) return self:attach_mappings(prompt_bufnr) diff --git a/lua/frecency/sorters.lua b/lua/frecency/sorters.lua new file mode 100644 index 0000000..98b8b70 --- /dev/null +++ b/lua/frecency/sorters.lua @@ -0,0 +1,63 @@ +local sorters = require "telescope.sorters" +local util = require "telescope.utils" + +local M = {} + +---@param prompt string +---@return boolean +local function has_upper_case(prompt) + return not not prompt:match "%u" +end + +---@param prompt string +---@param display string +---@return { start: integer, finish: integer }[] +local function highlighter(_, prompt, display) + ---@type { start: integer, finish: integer }[] + local highlights = {} + display = has_upper_case(prompt) and display or display:lower() + + local search_terms = util.max_split(prompt, "%s") + local hl_start, hl_end + + for _, word in ipairs(search_terms) do + hl_start, hl_end = display:find(word, 1, true) + if hl_start then + table.insert(highlights, { start = hl_start, finish = hl_end }) + end + end + + return highlights +end + +---@param prompt string +---@param entry FrecencyEntry +---@return integer +local function scoring_function(_, prompt, _, entry) + if #prompt == 0 then + return 1 + end + + local display = has_upper_case(prompt) and entry.ordinal or entry.ordinal:lower() + + local search_terms = util.max_split(prompt, "%s") + for _, word in ipairs(search_terms) do + if not display:find(word, 1, true) then + return -1 + end + end + return entry.index +end + +---This is a sorter similar to telescope.sorters.get_substr_matcher. telescope's +---one always ignore cases, but this sorter deal with them like 'smartcase' way. +function M.get_frecency_matcher() + return vim.o.smartcase + and sorters.Sorter:new { + highlighter = highlighter, + scoring_function = scoring_function, + } + or sorters.get_substr_matcher() +end + +return M