WIP: Actually get the UI to work and add some tests

This commit is contained in:
TJ DeVries
2020-08-20 23:41:53 -04:00
parent 96cac0a8c8
commit cfddae42f5
9 changed files with 270 additions and 146 deletions

View File

@@ -4,6 +4,13 @@ Gaze deeply into unknown regions using the power of the moon.
![Example](./media/simple_rg_v1.gif)
## Installation
```vim
Plug 'nvim-lua/plenary.nvim'
Plug 'nvim-lua/telescope.nvim'
```
## Goals
@@ -26,6 +33,9 @@ Gaze deeply into unknown regions using the power of the moon.
- sometimes built-in
- sometimes a lua callback
As an example, you could pipe your inputs into fzf, and then it can sort them for you.
fzf:
- have a list of inputs
- i have a prompt/things people typed

View File

@@ -29,6 +29,7 @@ function Finder:new(opts)
-- list
-- ...
return setmetatable({
results = opts.results,
fn_command = opts.fn_command,
static = opts.static,
state = {},
@@ -46,6 +47,16 @@ end
-- do_your_job
-- process_plz
function Finder:_find(prompt, process_result, process_complete)
if self.results then
assert(type(self.results) == 'table', "self.results must be a table")
for _, v in ipairs(self.results) do
process_result(v)
end
process_complete()
return
end
if (self.state.job_id or 0) > 0 then
vim.fn.jobstop(self.job_id)
end

View File

@@ -1,83 +1,4 @@
-- https://raw.githubusercontent.com/rxi/log.lua/master/log.lua
-- log.lua
--
-- Copyright (c) 2016 rxi
--
-- This library is free software; you can redistribute it and/or modify it
-- under the terms of the MIT license. See LICENSE for details.
--
local log = { _version = "0.1.0" }
log.usecolor = true
log.outfile = vim.fn.stdpath('data') .. '/telescope.log'
log.console = false
log.level = "trace"
local modes = {
{ name = "trace", color = "\27[34m", },
{ name = "debug", color = "\27[36m", },
{ name = "info", color = "\27[32m", },
{ name = "warn", color = "\27[33m", },
{ name = "error", color = "\27[31m", },
{ name = "fatal", color = "\27[35m", },
return require('plenary.log').new {
plugin = 'telescope',
level = 'debug',
}
local levels = {}
for i, v in ipairs(modes) do
levels[v.name] = i
end
local round = function(x, increment)
increment = increment or 1
x = x / increment
return (x > 0 and math.floor(x + .5) or math.ceil(x - .5)) * increment
end
for i, x in ipairs(modes) do
local nameupper = x.name:upper()
log[x.name] = function(...)
-- Return early if we're below the log level
if i < levels[log.level] then
return
end
local passed = {...}
local fmt = table.remove(passed, 1)
local inspected = {}
for _, v in ipairs(passed) do
table.insert(inspected, vim.inspect(v))
end
local msg = string.format(fmt, unpack(inspected))
local info = debug.getinfo(2, "Sl")
local lineinfo = info.short_src .. ":" .. info.currentline
-- Output to console
if log.console then
print(string.format("%s[%-6s%s]%s %s: %s",
log.usecolor and x.color or "",
nameupper,
os.date("%H:%M:%S"),
log.usecolor and "\27[0m" or "",
lineinfo,
msg))
end
-- Output to log file
if log.outfile then
local fp = io.open(log.outfile, "a")
local str = string.format("[%-6s%s] %s: %s\n",
nameupper, os.date(), lineinfo, msg)
fp:write(str)
fp:close()
end
end
end
log.info("Logger Succesfully Loaded")
return log

View File

@@ -1,11 +1,7 @@
local a = vim.api
local fun = require('fun')
local popup = require('popup')
local zip = fun.zip
local tomap = fun.tomap
local log = require('telescope.log')
local mappings = require('telescope.mappings')
local state = require('telescope.state')
@@ -13,6 +9,8 @@ local utils = require('telescope.utils')
local pickers = {}
--- Picker is the main UI that shows up to interact w/ your results.
-- Takes a filter & a previewr
local Picker = {}
Picker.__index = Picker
@@ -133,98 +131,77 @@ function Picker:find(opts)
local on_lines = function(_, _, _, first_line, last_line)
local prompt = vim.api.nvim_buf_get_lines(prompt_bufnr, first_line, last_line, false)[1]
-- Create a closure that has all the data we need
-- We pass a function called "newResult" to get_results
-- get_results calles "newResult" every time it gets a new result
-- picker then (if available) calls sorter
-- and then appropriately places new result in the buffer.
-- Sorted table by scores.
-- Lowest score gets lowest index.
self.line_scores = {}
-- TODO: We need to fix the sorting
-- TODO: We should provide a simple fuzzy matcher in Lua for people
-- TODO: We should get all the stuff on the bottom line directly, not floating around
-- TODO: We need to handle huge lists in a good way, cause currently we'll just put too much stuff in the buffer
-- TODO: Stop having things crash if we have an error.
local replace_line = function(score, row, line)
log.trace("Replacing @ %s w/ text '%s' (%s)", row, line, score)
vim.api.nvim_buf_set_lines(results_bufnr, row, row + 1, false, {line})
end
local insert_line = function(score, row, line)
log.trace("Inserting @ %s w/ text '%s' (%s)", row, line, score)
vim.api.nvim_buf_set_lines(results_bufnr, row, row, false, {line})
end
local line_manager = pickers.line_manager(
self.max_results,
function(index, line)
local row = self.max_results - index + 1
log.trace("Setting row", row, "with value", line)
vim.api.nvim_buf_set_lines(results_bufnr, row, row + 1, false, {line})
end
)
local process_result = function(line)
if vim.trim(line) == "" then
return
end
log.trace("Processing result... ", line)
local sort_score = 0
if sorter then
local sort_score = sorter:score(prompt, line)
sort_score = sorter:score(prompt, line)
if sort_score == -1 then
log.trace("Filtering out result: ", line)
return
end
-- { 7, 3, 1, 1 }
-- 2
for row, row_score in utils.reversed_ipairs(self.line_scores) do
if row_score > sort_score then
-- Insert line at row
insert_line(sort_score, self.max_results - row, line)
-- Insert current score in the table
table.insert(self.line_scores, row + 1, sort_score)
-- All done :)
return
end
-- Don't keep inserting stuff
if row > self.max_results then
return
end
end
-- Worst score so far, so add to end
-- example: 5 max results, 8
local worst_line = self.max_results - #self.line_scores
replace_line(sort_score, worst_line, line)
table.insert(self.line_scores, sort_score)
else
-- Just always append to the end of the buffer if this is all you got.
vim.api.nvim_buf_set_lines(results_bufnr, -1, -1, false, {line})
end
line_manager:add_result(sort_score, line)
end
local process_complete = function()
local worst_line = self.max_results - #self.line_scores
local empty_lines = {}
for _ = 1, worst_line do table.insert(empty_lines, "") end
vim.api.nvim_buf_set_lines(results_bufnr, 0, worst_line, false, empty_lines)
local worst_line = self.max_results - line_manager.num_results()
local empty_lines = utils.repeated_table(worst_line, "")
-- vim.api.nvim_buf_set_lines(results_bufnr, 0, worst_line + 1, false, empty_lines)
log.info("Worst Line after process_complete: %s", worst_line)
log.trace("%s", tomap(zip(
a.nvim_buf_get_lines(results_bufnr, worst_line, self.max_results, false),
self.line_scores
)))
log.debug("Worst Line after process_complete: %s", worst_line, results_bufnr)
-- local fun = require('fun')
-- local zip = fun.zip
-- local tomap = fun.tomap
-- log.trace("%s", tomap(zip(
-- a.nvim_buf_get_lines(results_bufnr, worst_line, self.max_results, false),
-- self.line_scores
-- )))
end
pcall(function()
local ok, msg = pcall(function()
return finder(prompt, process_result, process_complete)
end)
if not ok then
log.warn("Failed with msg: ", msg)
end
end
-- Call this once to pre-populate if it makes sense
-- vim.schedule_wrap(on_lines(nil, nil, nil, 0, 1))
-- TODO: Uncomment
vim.schedule(function()
on_lines(nil, nil, nil, 0, 1)
end)
-- Register attach
vim.api.nvim_buf_attach(prompt_bufnr, true, {
@@ -359,4 +336,82 @@ pickers.new = function(...)
return Picker:new(...)
end
-- TODO: We should consider adding `process_bulk` or `bulk_line_manager` for things
-- that we always know the items and can score quickly, so as to avoid drawing so much.
pickers.line_manager = function(max_results, set_line)
log.debug("Creating line_manager...")
-- state contains list of
-- {
-- score = ...
-- line = ...
-- metadata ? ...
-- }
local state = {}
set_line = set_line or function() end
return setmetatable({
add_result = function(self, score, line)
score = score or 0
for index, item in ipairs(state) do
if item.score > score then
return self:insert(index, {
score = score,
line = line,
})
end
-- Don't add results that are too bad.
if index >= max_results then
return self
end
end
return self:insert({
score = score,
line = line,
})
end,
insert = function(self, index, item)
if item == nil then
item = index
index = #state + 1
end
-- To insert something, we place at the next available index (or specified index)
-- and then shift all the corresponding items one place.
local next_item
repeat
next_item = state[index]
set_line(index, item.line)
state[index] = item
index = index + 1
item = next_item
until not next_item
end,
num_results = function()
return #state
end,
_get_state = function()
return state
end,
}, {
-- insert =
-- __index = function(_, line)
-- end,
-- __newindex = function(_, index, line)
-- end,
})
end
return pickers

View File

@@ -29,7 +29,7 @@ previewers.vim_buffer = previewers.new {
end
local file_name = vim.split(line, ":")[1]
log.info("Previewing File: %s", file_name)
log.trace("Previewing File: %s", file_name)
-- vim.fn.termopen(
-- string.format("bat --color=always --style=grid %s"),

View File

@@ -1,3 +1,4 @@
local log = require('telescope.log')
local util = require('telescope.utils')
local sorters = {}
@@ -12,7 +13,7 @@ Sorter.__index = Sorter
---
--- Lower number is better (because it's like a closer match)
--- But, any number below 0 means you want that line filtered out.
--- @param scoring_function function Function that has the interface:
--- @field scoring_function function Function that has the interface:
-- (sorter, prompt, line): number
function Sorter:new(opts)
opts = opts or {}
@@ -59,4 +60,14 @@ sorters.get_ngram_sorter = function()
}
end
sorters.get_levenshtein_sorter = function()
return Sorter:new {
scoring_function = function(_, prompt, line)
local result = require('telescope.algos.string_distance')(prompt, line)
log.info("Sorting result for", prompt, line, " = ", result)
return result
end
}
end
return sorters

View File

@@ -19,6 +19,15 @@ utils.default_table_mt = {
end
}
utils.repeated_table = function(n, val)
local empty_lines = {}
for _ = 1, n do
table.insert(empty_lines, val)
end
return empty_lines
end
local NGram = {}
NGram.__index = NGram

View File

@@ -1,5 +1,6 @@
require('plenary.test_harness'):setup_busted()
local pickers = require('telescope.pickers')
local utils = require('telescope.utils')
--[[
@@ -13,6 +14,81 @@ describe('Picker', function()
end)
end)
describe('process_result', function()
it('works with one entry', function()
local lines_manager = pickers.line_manager(5, nil)
lines_manager:add_result(1, "hello")
assert.are.same(1, lines_manager:_get_state()[1].score)
end)
it('works with two entries', function()
local lines_manager = pickers.line_manager(5, nil)
lines_manager:add_result(1, "hello")
lines_manager:add_result(2, "later")
assert.are.same("hello", lines_manager:_get_state()[1].line)
assert.are.same("later", lines_manager:_get_state()[2].line)
end)
it('calls functions when inserting', function()
local called_count = 0
local lines_manager = pickers.line_manager(5, function() called_count = called_count + 1 end)
assert(called_count == 0)
lines_manager:add_result(1, "hello")
assert(called_count == 1)
end)
it('calls functions when inserting twice', function()
local called_count = 0
local lines_manager = pickers.line_manager(5, function() called_count = called_count + 1 end)
assert(called_count == 0)
lines_manager:add_result(1, "hello")
lines_manager:add_result(2, "world")
assert(called_count == 2)
end)
it('correctly sorts lower scores', function()
local called_count = 0
local lines_manager = pickers.line_manager(5, function() called_count = called_count + 1 end)
lines_manager:add_result(5, "worse result")
lines_manager:add_result(2, "better result")
assert.are.same("better result", lines_manager:_get_state()[1].line)
assert.are.same("worse result", lines_manager:_get_state()[2].line)
-- once to insert "worse"
-- once to insert "better"
-- and then to move "worse"
assert.are.same(3, called_count)
end)
it('respects max results', function()
local called_count = 0
local lines_manager = pickers.line_manager(1, function() called_count = called_count + 1 end)
lines_manager:add_result(2, "better result")
lines_manager:add_result(5, "worse result")
assert.are.same("better result", lines_manager:_get_state()[1].line)
-- once to insert "worse"
-- once to insert "better"
-- and then to move "worse"
assert.are.same(1, called_count)
end)
-- TODO: We should decide if we want to add this or not.
-- it('should handle no scores', function()
-- local lines_manager = pickers.line_manager(5, nil)
-- lines_manager:add_result(nil,
-- end)
end)
describe('ngrams', function()
it('should capture intself in the ngram', function()
local n = utils.new_ngram()

31
scratch/simplest_test.lua Normal file
View File

@@ -0,0 +1,31 @@
require('plenary.reload').reload_module('telescope')
local telescope = require('telescope')
-- What is a finder?
-- Finders return a list of stuff that you want to fuzzy look through.
-- Finders can be static or not.
-- Static finders just return a list that never changes
-- Otherwise they return a new list on each input, you should handle them async.
local file_finder = telescope.finders.new {
static = true,
fn_command = function() return 'git ls-files' end,
}
local file_previewer = telescope.previewers.vim_buffer_or_bat
local file_picker = telescope.pickers.new {
previewer = file_previewer
}
local file_sorter = telescope.sorters.get_ngram_sorter()
-- local file_sorter = require('telescope.sorters').get_levenshtein_sorter()
file_picker:find {
prompt = 'Simple File',
finder = file_finder,
sorter = file_sorter,
}
local x = function() end