Refactor file listing and zettel picking
This commit is contained in:
parent
94ff554d8d
commit
5228a3b49e
3 changed files with 72 additions and 39 deletions
|
@ -63,6 +63,8 @@ next up:
|
||||||
* [ ] recognize duplicate anchors (in directory, when listing, etc)
|
* [ ] recognize duplicate anchors (in directory, when listing, etc)
|
||||||
* [ ] provide option to rename and automatically change backlinks
|
* [ ] provide option to rename and automatically change backlinks
|
||||||
* [ ] zettel 'lens' (preview first headline + content of linked zettel through floating window etc, on keypress)
|
* [ ] zettel 'lens' (preview first headline + content of linked zettel through floating window etc, on keypress)
|
||||||
|
* [ ] support *both* md-style and wiki-style links at the same time
|
||||||
|
* [ ] file/directory exception list for gathering files, which will be ignored
|
||||||
|
|
||||||
## TODO: maintenance
|
## TODO: maintenance
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
local ls = {}
|
local ls = {}
|
||||||
|
-- TODO rename to files.lua? since it's the only module interacting solely w/ the file system
|
||||||
|
|
||||||
local o = require 'zettelkasten.options'
|
local o = require 'zettelkasten.options'
|
||||||
|
|
||||||
|
@ -7,19 +8,40 @@ local function isDirectory(ftype)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function isFile(ftype)
|
||||||
|
if ftype == 'file' then return true end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
local function cleanPath(path)
|
local function cleanPath(path)
|
||||||
if path:match("^~") then path = os.getenv("HOME") .. path:sub(2) end
|
if path:match("^~") then path = os.getenv("HOME") .. path:sub(2) end
|
||||||
return path
|
return path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Returns a set of valid zettels in the form
|
||||||
|
-- { anchor = fullpathname }.
|
||||||
|
-- Takes a (flat) set of files to iterate over in the form that
|
||||||
|
-- get_all_files produces.
|
||||||
-- TODO transform paths:
|
-- TODO transform paths:
|
||||||
-- * to ensure / at the end (or no /) gets taken into account
|
-- * to ensure / at the end (or no /) gets taken into account
|
||||||
function ls.get_anchors_and_paths(path, recursive)
|
function ls.get_anchors_and_paths(fileset)
|
||||||
-- TODO check for duplicates and warn user
|
-- TODO check for duplicates and warn user
|
||||||
local zettel = {}
|
local zettel = {}
|
||||||
local anchorreg = '^.*/?(' .. o.anchor().regex .. ')[^/]*%' ..
|
local anchorreg = '^.*/?(' .. o.anchor().regex .. ')[^/]*%' ..
|
||||||
o.zettel().extension .. '$'
|
o.zettel().extension .. '$'
|
||||||
|
|
||||||
|
for full_path, name in pairs(fileset) do
|
||||||
|
local anchor = string.match(name, anchorreg)
|
||||||
|
if anchor then zettel[tostring(anchor)] = full_path end
|
||||||
|
end
|
||||||
|
return zettel
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns a set of all files at the target directory, as well
|
||||||
|
-- as subdirectories if the recursive argument is set to true.
|
||||||
|
-- Set has the form { "full/path/name.md" = "name.md" }
|
||||||
|
function ls.get_all_files(path, recursive)
|
||||||
|
local f = {}
|
||||||
path = cleanPath(path)
|
path = cleanPath(path)
|
||||||
|
|
||||||
local handle = vim.loop.fs_scandir(path)
|
local handle = vim.loop.fs_scandir(path)
|
||||||
|
@ -28,24 +50,26 @@ function ls.get_anchors_and_paths(path, recursive)
|
||||||
if not name then break end
|
if not name then break end
|
||||||
|
|
||||||
if isDirectory(ftype) and recursive then
|
if isDirectory(ftype) and recursive then
|
||||||
local subdir = ls.get_anchors_and_paths(path .. "/" .. name, true)
|
local subdir = ls.get_all_files(path .. "/" .. name, true)
|
||||||
for k, v in pairs(subdir) do zettel[tostring(k)] = v end
|
for k, v in pairs(subdir) do f[tostring(k)] = v end
|
||||||
end
|
end
|
||||||
|
|
||||||
local anchor = string.match(name, anchorreg)
|
if isFile(ftype) then f[tostring(path .. "/" .. name)] = name end
|
||||||
if anchor then zettel[tostring(anchor)] = path .. "/" .. name end
|
|
||||||
end
|
end
|
||||||
return zettel
|
|
||||||
|
return f
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Returns the path to the zettel defined by the anchor argument.
|
-- Returns the path to the zettel defined by the anchor argument.
|
||||||
-- Take a list of zettel as an optional variable, without which
|
-- Take a list of zettel as an optional variable, without which
|
||||||
-- it will use the (recursive) results of the zettel_root directory.
|
-- it will use the (recursive) results of the zettel_root directory.
|
||||||
function ls.get_zettel(anchor, all)
|
function ls.get_zettel(anchor, all)
|
||||||
all = all or ls.get_anchors_and_paths(o.zettel().rootdir, true)
|
local zettels = all or
|
||||||
if not all then return end
|
ls.get_anchors_and_paths(
|
||||||
|
ls.get_all_files(o.zettel().rootdir, true))
|
||||||
|
if not zettels then return end
|
||||||
|
|
||||||
return all[anchor]
|
return zettels[anchor]
|
||||||
end
|
end
|
||||||
|
|
||||||
return ls
|
return ls
|
||||||
|
|
|
@ -24,20 +24,24 @@ describe("get_anchors_and_paths", function()
|
||||||
|
|
||||||
it("should return anchor-keyed table pointing to filename of zettel",
|
it("should return anchor-keyed table pointing to filename of zettel",
|
||||||
function()
|
function()
|
||||||
local file_list = {"1910291645 this-is-a-testfile.md"}
|
local file_list = {}
|
||||||
|
file_list["someDir/1910291645 this-is-a-testfile.md"] =
|
||||||
|
"1910291645 this-is-a-testfile.md"
|
||||||
_G.vim = get_api_mock(file_list)
|
_G.vim = get_api_mock(file_list)
|
||||||
|
|
||||||
local expected = {
|
local expected = {
|
||||||
["1910291645"] = "someDir/1910291645 this-is-a-testfile.md"
|
["1910291645"] = "someDir/1910291645 this-is-a-testfile.md"
|
||||||
}
|
}
|
||||||
assert.same(expected, ls.get_anchors_and_paths("someDir"))
|
assert.same(expected, ls.get_anchors_and_paths(file_list))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("should ignore any malformed files", function()
|
it("should ignore any malformed files", function()
|
||||||
local file_list = {
|
local file_list = {
|
||||||
"2010261208 this-should-be-picked-up.md",
|
["someDir/2010261208 this-should-be-picked-up.md"] = "2010261208 this-should-be-picked-up.md",
|
||||||
"1910291645 this-is-a-testfile.md", "this-is-not-a-testfile.md",
|
["someDir/1910291645 this-is-a-testfile.md"] = "1910291645 this-is-a-testfile.md",
|
||||||
"1910271456 this-is-wrong-extension.txt", "1812 this-is-ignored.md"
|
["someDir/this-is-not-a-testfile.md"] = "this-is-not-a-testfile.md",
|
||||||
|
["1910271456 this-is-wrong-extension.txt"] = "1910271456 this-is-wrong-extension.txt",
|
||||||
|
["1812 this-is-ignored.md"] = "1812 this-is-ignored.md"
|
||||||
}
|
}
|
||||||
_G.vim = get_api_mock(file_list)
|
_G.vim = get_api_mock(file_list)
|
||||||
|
|
||||||
|
@ -45,9 +49,28 @@ describe("get_anchors_and_paths", function()
|
||||||
["1910291645"] = "someDir/1910291645 this-is-a-testfile.md",
|
["1910291645"] = "someDir/1910291645 this-is-a-testfile.md",
|
||||||
["2010261208"] = "someDir/2010261208 this-should-be-picked-up.md"
|
["2010261208"] = "someDir/2010261208 this-should-be-picked-up.md"
|
||||||
}
|
}
|
||||||
assert.same(expected, ls.get_anchors_and_paths("someDir"))
|
assert.same(expected, ls.get_anchors_and_paths(file_list))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it("should adhere to the zettel extension defined in options", function()
|
||||||
|
local file_list = {
|
||||||
|
["mydirectory/1910291645 myfile.wiki"] = "1910291645 myfile.wiki",
|
||||||
|
["mydirectory/2345678901 another.wiki"] = "2345678901 another.wiki"
|
||||||
|
}
|
||||||
|
_G.vim = get_api_mock(file_list)
|
||||||
|
vim.g['zettel_extension'] = '.wiki'
|
||||||
|
|
||||||
|
local expected = {
|
||||||
|
["1910291645"] = "mydirectory/1910291645 myfile.wiki",
|
||||||
|
["2345678901"] = "mydirectory/2345678901 another.wiki"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.same(expected, ls.get_anchors_and_paths(file_list, false, vim.g))
|
||||||
|
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("get_all_files", function()
|
||||||
it("should recurse into directories if recursive argument passed in ",
|
it("should recurse into directories if recursive argument passed in ",
|
||||||
function()
|
function()
|
||||||
local files = {
|
local files = {
|
||||||
|
@ -75,15 +98,14 @@ describe("get_anchors_and_paths", function()
|
||||||
}
|
}
|
||||||
_G.vim = vim_api_mock
|
_G.vim = vim_api_mock
|
||||||
|
|
||||||
ls.get_anchors_and_paths("path/to/startingdir", true)
|
ls.get_all_files("path/to/startingdir", true)
|
||||||
|
|
||||||
assert.spy(vim_api_mock.loop.fs_scandir).was_called(2)
|
assert.spy(vim_api_mock.loop.fs_scandir).was_called(2)
|
||||||
assert.spy(vim_api_mock.loop.fs_scandir).was_called_with(
|
assert.spy(vim_api_mock.loop.fs_scandir).was_called_with(
|
||||||
"path/to/startingdir/more-notes-here")
|
"path/to/startingdir/more-notes-here")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("should append all notes found in subdirectories when recursing",
|
it("should add all files found in subdirectories when recursing", function()
|
||||||
function()
|
|
||||||
local outer_files = {
|
local outer_files = {
|
||||||
"subdir", "1234567890 myfile.md", "2345678901 another.md"
|
"subdir", "1234567890 myfile.md", "2345678901 another.md"
|
||||||
}
|
}
|
||||||
|
@ -114,27 +136,12 @@ describe("get_anchors_and_paths", function()
|
||||||
}
|
}
|
||||||
_G.vim = vim_api_mock
|
_G.vim = vim_api_mock
|
||||||
local expected = {
|
local expected = {
|
||||||
["1234567890"] = "mydirectory/1234567890 myfile.md",
|
["mydirectory/1234567890 myfile.md"] = "1234567890 myfile.md",
|
||||||
["2345678901"] = "mydirectory/2345678901 another.md",
|
["mydirectory/2345678901 another.md"] = "2345678901 another.md",
|
||||||
["2222222222"] = "mydirectory/subdir/2222222222 should-be-present.md",
|
["mydirectory/subdir/2222222222 should-be-present.md"] = "2222222222 should-be-present.md",
|
||||||
["3333333333"] = "mydirectory/subdir/3333333333 should-also-be-present.md"
|
["mydirectory/subdir/3333333333 should-also-be-present.md"] = "3333333333 should-also-be-present.md"
|
||||||
}
|
}
|
||||||
assert.same(expected, ls.get_anchors_and_paths('mydirectory', true))
|
assert.same(expected, ls.get_all_files('mydirectory', true))
|
||||||
end)
|
|
||||||
|
|
||||||
it("should adhere to the zettel extension defined in options", function()
|
|
||||||
local file_list = {"1910291645 myfile.wiki", "2345678901 another.wiki"}
|
|
||||||
_G.vim = get_api_mock(file_list)
|
|
||||||
vim.g['zettel_extension'] = '.wiki'
|
|
||||||
|
|
||||||
local expected = {
|
|
||||||
["1910291645"] = "mydirectory/1910291645 myfile.wiki",
|
|
||||||
["2345678901"] = "mydirectory/2345678901 another.wiki"
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.same(expected,
|
|
||||||
ls.get_anchors_and_paths('mydirectory', false, vim.g))
|
|
||||||
|
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -154,7 +161,7 @@ describe("get_zettel", function()
|
||||||
assert.is_not_error(function() ls.get_zettel("myanchor") end)
|
assert.is_not_error(function() ls.get_zettel("myanchor") end)
|
||||||
end)
|
end)
|
||||||
it("should default to the zettel root dir if no list passed in", function()
|
it("should default to the zettel root dir if no list passed in", function()
|
||||||
local fc = stub(ls, "get_anchors_and_paths")
|
local fc = stub(ls, "get_all_files")
|
||||||
local expected = require'zettelkasten.options'.zettel().rootdir
|
local expected = require'zettelkasten.options'.zettel().rootdir
|
||||||
|
|
||||||
ls.get_zettel(expected)
|
ls.get_zettel(expected)
|
||||||
|
|
Loading…
Reference in a new issue