mirror of
https://github.com/kepler155c/opus
synced 2024-12-29 10:00:26 +00:00
better fuzzy matching + vfs type flag in Files
This commit is contained in:
parent
b93d69c261
commit
985830fcfd
@ -77,8 +77,8 @@ local Browser = UI.Page {
|
|||||||
grid = UI.ScrollingGrid {
|
grid = UI.ScrollingGrid {
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'Name', key = 'name' },
|
{ heading = 'Name', key = 'name' },
|
||||||
{ key = 'flags', width = 2 },
|
{ key = 'flags', width = 3, textColor = 'lightGray' },
|
||||||
{ heading = 'Size', key = 'fsize', width = 5 },
|
{ heading = 'Size', key = 'fsize', width = 5, textColor = 'yellow' },
|
||||||
},
|
},
|
||||||
sortColumn = 'name',
|
sortColumn = 'name',
|
||||||
y = 2, ey = -2,
|
y = 2, ey = -2,
|
||||||
@ -211,7 +211,7 @@ function Browser:enable()
|
|||||||
self:setFocus(self.grid)
|
self:setFocus(self.grid)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Browser.menuBar:getActive(menuItem)
|
function Browser.menuBar.getActive(_, menuItem)
|
||||||
local file = Browser.grid:getSelected()
|
local file = Browser.grid:getSelected()
|
||||||
if menuItem.flags == FILE then
|
if menuItem.flags == FILE then
|
||||||
return file and not file.isDir
|
return file and not file.isDir
|
||||||
@ -223,7 +223,7 @@ function Browser:setStatus(status, ...)
|
|||||||
self.notification:info(string.format(status, ...))
|
self.notification:info(string.format(status, ...))
|
||||||
end
|
end
|
||||||
|
|
||||||
function Browser:unmarkAll()
|
function Browser.unmarkAll()
|
||||||
for _,m in pairs(marked) do
|
for _,m in pairs(marked) do
|
||||||
m.marked = false
|
m.marked = false
|
||||||
end
|
end
|
||||||
@ -263,10 +263,11 @@ function Browser:updateDirectory(dir)
|
|||||||
dir.size = #files
|
dir.size = #files
|
||||||
for _, file in pairs(files) do
|
for _, file in pairs(files) do
|
||||||
file.fullName = fs.combine(dir.name, file.name)
|
file.fullName = fs.combine(dir.name, file.name)
|
||||||
file.flags = ''
|
file.flags = file.fstype or ' '
|
||||||
if not file.isDir then
|
if not file.isDir then
|
||||||
dir.totalSize = dir.totalSize + file.size
|
dir.totalSize = dir.totalSize + file.size
|
||||||
file.fsize = formatSize(file.size)
|
file.fsize = formatSize(file.size)
|
||||||
|
file.flags = file.flags .. ' '
|
||||||
else
|
else
|
||||||
if config.showDirSizes then
|
if config.showDirSizes then
|
||||||
file.size = fs.getSize(file.fullName, true)
|
file.size = fs.getSize(file.fullName, true)
|
||||||
@ -274,11 +275,9 @@ function Browser:updateDirectory(dir)
|
|||||||
dir.totalSize = dir.totalSize + file.size
|
dir.totalSize = dir.totalSize + file.size
|
||||||
file.fsize = formatSize(file.size)
|
file.fsize = formatSize(file.size)
|
||||||
end
|
end
|
||||||
file.flags = 'D'
|
file.flags = file.flags .. 'D'
|
||||||
end
|
|
||||||
if file.isReadOnly then
|
|
||||||
file.flags = file.flags .. 'R'
|
|
||||||
end
|
end
|
||||||
|
file.flags = file.flags .. (file.isReadOnly and 'R' or ' ')
|
||||||
if config.showHidden or file.name:sub(1, 1) ~= '.' then
|
if config.showHidden or file.name:sub(1, 1) ~= '.' then
|
||||||
dir.files[file.fullName] = file
|
dir.files[file.fullName] = file
|
||||||
end
|
end
|
||||||
@ -467,7 +466,7 @@ function Browser:eventHandler(event)
|
|||||||
|
|
||||||
elseif event.type == 'paste' then
|
elseif event.type == 'paste' then
|
||||||
for _,m in pairs(copied) do
|
for _,m in pairs(copied) do
|
||||||
local s, m = pcall(function()
|
pcall(function()
|
||||||
if cutMode then
|
if cutMode then
|
||||||
fs.move(m.fullName, fs.combine(self.dir.name, m.name))
|
fs.move(m.fullName, fs.combine(self.dir.name, m.name))
|
||||||
else
|
else
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
local fuzzy = require('opus.fuzzy')
|
||||||
local UI = require('opus.ui')
|
local UI = require('opus.ui')
|
||||||
local Util = require('opus.util')
|
local Util = require('opus.util')
|
||||||
|
|
||||||
@ -42,13 +43,13 @@ UI:addPage('main', UI.Page {
|
|||||||
|
|
||||||
elseif event.type == 'text_change' then
|
elseif event.type == 'text_change' then
|
||||||
if not event.text then
|
if not event.text then
|
||||||
self.grid.values = topics
|
self.grid.sortColumn = 'lname'
|
||||||
else
|
else
|
||||||
self.grid.values = { }
|
self.grid.sortColumn = 'score'
|
||||||
for _,f in pairs(topics) do
|
self.grid.inverseSort = false
|
||||||
if string.find(f.lname, event.text:lower()) then
|
local pattern = event.text:lower()
|
||||||
table.insert(self.grid.values, f)
|
for _,v in pairs(self.grid.values) do
|
||||||
end
|
v.score = -fuzzy(v.lname, pattern)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self.grid:update()
|
self.grid:update()
|
||||||
|
@ -41,7 +41,7 @@ local page = UI.Page {
|
|||||||
},
|
},
|
||||||
description = UI.TextArea {
|
description = UI.TextArea {
|
||||||
x = 16, y = 3, ey = -5,
|
x = 16, y = 3, ey = -5,
|
||||||
marginRight = 0, marginLeft = 0,
|
marginRight = 2, marginLeft = 0,
|
||||||
},
|
},
|
||||||
action = UI.SlideOut {
|
action = UI.SlideOut {
|
||||||
titleBar = UI.TitleBar {
|
titleBar = UI.TitleBar {
|
||||||
@ -140,9 +140,9 @@ function page:eventHandler(event)
|
|||||||
elseif event.type == 'grid_focus_row' then
|
elseif event.type == 'grid_focus_row' then
|
||||||
local manifest = event.selected.manifest
|
local manifest = event.selected.manifest
|
||||||
|
|
||||||
self.description.value = string.format('%s%s\n\n%s%s',
|
self.description:setValue(string.format('%s%s\n\n%s%s',
|
||||||
Ansi.yellow, manifest.title,
|
Ansi.yellow, manifest.title,
|
||||||
Ansi.white, manifest.description)
|
Ansi.white, manifest.description))
|
||||||
self.description:draw()
|
self.description:draw()
|
||||||
self:updateSelection(event.selected)
|
self:updateSelection(event.selected)
|
||||||
|
|
||||||
|
@ -52,8 +52,8 @@ local function run(...)
|
|||||||
loadFn = loadfile
|
loadFn = loadfile
|
||||||
end
|
end
|
||||||
|
|
||||||
local funkshun, err = loadFn(path, env)
|
local O_v_O, err = loadFn(path, env)
|
||||||
if not funkshun then
|
if not O_v_O then
|
||||||
error(err, -1)
|
error(err, -1)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ local function run(...)
|
|||||||
}
|
}
|
||||||
|
|
||||||
env[ "arg" ] = { [0] = path, table.unpack(args) }
|
env[ "arg" ] = { [0] = path, table.unpack(args) }
|
||||||
local r = { funkshun(table.unpack(args)) }
|
local r = { O_v_O(table.unpack(args)) }
|
||||||
|
|
||||||
tProgramStack[#tProgramStack] = nil
|
tProgramStack[#tProgramStack] = nil
|
||||||
|
|
||||||
|
@ -166,6 +166,13 @@ function fs.complete(partial, dir, includeFiles, includeSlash)
|
|||||||
return fs.native.complete(partial, dir, includeFiles, includeSlash)
|
return fs.native.complete(partial, dir, includeFiles, includeSlash)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local displayFlags = {
|
||||||
|
urlfs = 'U',
|
||||||
|
linkfs = 'L',
|
||||||
|
ramfs = 'T',
|
||||||
|
netfs = 'N',
|
||||||
|
}
|
||||||
|
|
||||||
function fs.listEx(dir)
|
function fs.listEx(dir)
|
||||||
dir = fs.combine(dir, '')
|
dir = fs.combine(dir, '')
|
||||||
local node = getNode(dir)
|
local node = getNode(dir)
|
||||||
@ -176,20 +183,22 @@ function fs.listEx(dir)
|
|||||||
local t = { }
|
local t = { }
|
||||||
local files = node.fs.list(node, dir)
|
local files = node.fs.list(node, dir)
|
||||||
|
|
||||||
pcall(function()
|
for _,f in ipairs(files) do
|
||||||
for _,f in ipairs(files) do
|
pcall(function()
|
||||||
local fullName = fs.combine(dir, f)
|
local fullName = fs.combine(dir, f)
|
||||||
|
local n = fs.getNode(fullName)
|
||||||
local file = {
|
local file = {
|
||||||
name = f,
|
name = f,
|
||||||
isDir = fs.isDir(fullName),
|
isDir = fs.isDir(fullName),
|
||||||
isReadOnly = fs.isReadOnly(fullName),
|
isReadOnly = fs.isReadOnly(fullName),
|
||||||
|
fstype = n.mountPoint == fullName and displayFlags[n.fstype],
|
||||||
}
|
}
|
||||||
if not file.isDir then
|
if not file.isDir then
|
||||||
file.size = fs.getSize(fullName)
|
file.size = fs.getSize(fullName)
|
||||||
end
|
end
|
||||||
table.insert(t, file)
|
table.insert(t, file)
|
||||||
end
|
end)
|
||||||
end)
|
end
|
||||||
return t
|
return t
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -6,37 +6,41 @@ fs.loadTab('sys/etc/fstab')
|
|||||||
-- add some Lua compatibility functions
|
-- add some Lua compatibility functions
|
||||||
function os.remove(a)
|
function os.remove(a)
|
||||||
if fs.exists(a) then
|
if fs.exists(a) then
|
||||||
local s = pcall(fs.delete, a)
|
local s = pcall(fs.delete, a)
|
||||||
return s and true or nil, a .. ': Unable to remove file'
|
return s and true or nil, a .. ': Unable to remove file'
|
||||||
end
|
end
|
||||||
return nil, a .. ': No such file or directory'
|
return nil, a .. ': No such file or directory'
|
||||||
end
|
end
|
||||||
|
|
||||||
os.execute = function(cmd)
|
os.execute = function(cmd)
|
||||||
if not cmd then
|
local env = _G.getfenv(2)
|
||||||
return 1
|
if not cmd then
|
||||||
end
|
return env.shell and 1 or 0
|
||||||
|
end
|
||||||
|
|
||||||
local env = _G.getfenv(2)
|
if not env.shell then
|
||||||
local s, m = env.shell.run('sys/apps/shell.lua ' .. cmd)
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
if not s then
|
local s, m = env.shell.run('sys/apps/shell.lua ' .. cmd)
|
||||||
return 1, m
|
|
||||||
end
|
|
||||||
|
|
||||||
return 0
|
if not s then
|
||||||
|
return 1, m
|
||||||
|
end
|
||||||
|
|
||||||
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
os.tmpname = function()
|
os.tmpname = function()
|
||||||
local fname
|
local fname
|
||||||
repeat
|
repeat
|
||||||
fname = 'tmp/a' .. math.random(1, 32768)
|
fname = 'tmp/a' .. math.random(1, 32768)
|
||||||
until not fs.exists(fname)
|
until not fs.exists(fname)
|
||||||
|
|
||||||
return fname
|
return fname
|
||||||
end
|
end
|
||||||
|
|
||||||
-- non-standard - will raise error instead
|
-- non-standard - will raise error instead
|
||||||
os.exit = function(code)
|
os.exit = function(code)
|
||||||
error('Terminated with ' .. code)
|
error('Terminated with ' .. code)
|
||||||
end
|
end
|
||||||
|
@ -13,7 +13,13 @@ table.insert(helpPaths, '/sys/help')
|
|||||||
for name in pairs(Packages:installed()) do
|
for name in pairs(Packages:installed()) do
|
||||||
local packageDir = fs.combine('packages', name)
|
local packageDir = fs.combine('packages', name)
|
||||||
|
|
||||||
|
local fstabPath = fs.combine(packageDir, 'etc/fstab')
|
||||||
|
if fs.exists(fstabPath) then
|
||||||
|
fs.loadTab(fstabPath)
|
||||||
|
end
|
||||||
|
|
||||||
table.insert(appPaths, 1, '/' .. packageDir)
|
table.insert(appPaths, 1, '/' .. packageDir)
|
||||||
|
|
||||||
local apiPath = fs.combine(packageDir, 'apis') -- TODO: rename dir to 'modules' (someday)
|
local apiPath = fs.combine(packageDir, 'apis') -- TODO: rename dir to 'modules' (someday)
|
||||||
if fs.exists(apiPath) then
|
if fs.exists(apiPath) then
|
||||||
fs.mount(fs.combine('rom/modules/main', name), 'linkfs', apiPath)
|
fs.mount(fs.combine('rom/modules/main', name), 'linkfs', apiPath)
|
||||||
@ -23,11 +29,6 @@ for name in pairs(Packages:installed()) do
|
|||||||
if fs.exists(helpPath) then
|
if fs.exists(helpPath) then
|
||||||
table.insert(helpPaths, helpPath)
|
table.insert(helpPaths, helpPath)
|
||||||
end
|
end
|
||||||
|
|
||||||
local fstabPath = fs.combine(packageDir, 'etc/fstab')
|
|
||||||
if fs.exists(fstabPath) then
|
|
||||||
fs.loadTab(fstabPath)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
help.setPath(table.concat(helpPaths, ':'))
|
help.setPath(table.concat(helpPaths, ':'))
|
||||||
|
@ -1,21 +1,56 @@
|
|||||||
-- Based on Squid's fuzzy search
|
local find = string.find
|
||||||
-- https://github.com/SquidDev-CC/artist/blob/vnext/artist/lib/match.lua
|
local floor = math.floor
|
||||||
--
|
local min = math.min
|
||||||
-- not very fuzzy anymore
|
local max = math.max
|
||||||
|
local sub = string.sub
|
||||||
|
|
||||||
local SCORE_WEIGHT = 1000
|
-- https://rosettacode.org/wiki/Jaro_distance (ported to lua)
|
||||||
local LEADING_LETTER_PENALTY = -30
|
return function(s1, s2)
|
||||||
local LEADING_LETTER_PENALTY_MAX = -90
|
local l1, l2 = #s1, #s2;
|
||||||
|
if l1 == 0 then
|
||||||
local _find = string.find
|
return l2 == 0 and 1.0 or 0.0
|
||||||
local _max = math.max
|
|
||||||
|
|
||||||
return function(str, pattern)
|
|
||||||
local start = _find(str, pattern, 1, true)
|
|
||||||
if start then
|
|
||||||
-- All letters before the current one are considered leading, so add them to our penalty
|
|
||||||
return SCORE_WEIGHT
|
|
||||||
+ _max(LEADING_LETTER_PENALTY * (start - 1), LEADING_LETTER_PENALTY_MAX)
|
|
||||||
- (#str - #pattern)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local match_distance = max(floor(max(l1, l2) / 2) - 1, 0)
|
||||||
|
local s1_matches = { }
|
||||||
|
local s2_matches = { }
|
||||||
|
local matches = 0
|
||||||
|
|
||||||
|
for i = 1, l1 do
|
||||||
|
local _end = min(i + match_distance + 1, l2)
|
||||||
|
for k = max(1, i - match_distance), _end do
|
||||||
|
if not s2_matches[k] and sub(s1, i, i) == sub(s2, k, k) then
|
||||||
|
s1_matches[i] = true
|
||||||
|
s2_matches[k] = true
|
||||||
|
matches = matches + 1
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if matches == 0 then
|
||||||
|
return 0.0
|
||||||
|
end
|
||||||
|
|
||||||
|
local t = 0.0
|
||||||
|
local k = 1
|
||||||
|
for i = 1, l1 do
|
||||||
|
if s1_matches[i] then
|
||||||
|
while not s2_matches[k] do
|
||||||
|
k = k + 1
|
||||||
|
end
|
||||||
|
if sub(s1, i, i) ~= sub(s2, k, k) then
|
||||||
|
t = t + 0.5
|
||||||
|
end
|
||||||
|
k = k + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- provide a major boost for exact matches
|
||||||
|
local b = 0.0
|
||||||
|
if find(s1, s2, 1, true) then
|
||||||
|
b = b + .5
|
||||||
|
end
|
||||||
|
|
||||||
|
local m = matches
|
||||||
|
return (m / l1 + m / l2 + (m - t) / m) / 3.0 + b
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user