1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-02-03 07:40:05 +08:00
SpaceVim/bundle/vim-zettelkasten/lua/zettelkasten/browser.lua
2022-10-25 18:35:51 +08:00

220 lines
4.9 KiB
Lua
Vendored

local M = {}
local fn = vim.fn
local config = require('zettelkasten.config')
local ZK_FULL_TITLE_PATTERN = '# %d+-%d+-%d+-%d+-%d+-%d+ .+'
local ZK_ID_PATTERN = '%d+-%d+-%d+-%d+-%d+-%d+'
local ZK_FILE_NAME_PATTERN = '%d+-%d+-%d+-%d+-%d+-%d+.md'
local s_note_cache_with_file_path = {}
local s_note_cache_with_id = {}
local function get_files(folder)
local files = fn.split(fn.globpath(folder, '*.md'), '\\n')
files = vim.tbl_filter(function(file)
return string.match(file, ZK_FILE_NAME_PATTERN) ~= nil
end, files)
return files
end
local function extract_id_and_title(line)
local zk_id = string.match(line, ZK_FULL_TITLE_PATTERN)
if zk_id == nil then
return nil
end
zk_id = string.gsub(zk_id, '# ', '')
local note_id = string.match(zk_id, ZK_ID_PATTERN)
local title = vim.trim(string.gsub(zk_id, ZK_ID_PATTERN, ''))
return { id = note_id, title = string.gsub(title, '\r', '') }
end
local function extract_references(line, linenr)
assert(line ~= nil)
local references = {}
for ref in string.gmatch(line, '(%[%[' .. ZK_ID_PATTERN .. '%]%])') do
ref = string.gsub(ref, '%[%[', '')
ref = string.gsub(ref, '%]%]', '')
table.insert(references, { id = ref, linenr = linenr })
end
return references
end
local function extract_back_references(notes, note_id)
assert(note_id ~= nil)
local back_references = {}
for _, note in ipairs(notes) do
if note.id == note_id then
goto continue
end
local references = note.references
for _, ref in ipairs(references) do
if ref.id == note_id then
table.insert(back_references, {
id = note.id,
title = note.title,
file_name = note.file_name,
linenr = ref.linenr,
})
break
end
end
::continue::
end
return back_references
end
local function extract_tags(line, linenr)
assert(line ~= nil)
local tags = {}
for tag in string.gmatch(line, '(%#%a[%w-]+)') do
local start_pos, _ = string.find(line, tag, 1, true)
local previous_char = string.sub(line, start_pos - 1, start_pos - 1)
if previous_char == '' or previous_char == ' ' then
table.insert(tags, { linenr = linenr, name = tag })
end
end
return tags
end
local function get_note_information(file_path)
local last_modified = fn.strftime('%Y-%m-%d.%H:%M:%S', fn.getftime(file_path))
if
s_note_cache_with_file_path[file_path] ~= nil
and s_note_cache_with_file_path[file_path].last_modified == last_modified
then
return s_note_cache_with_file_path[file_path]
end
local file = io.open(file_path, 'r')
if file:read(0) == nil then
return nil
end
local info = {
id = '',
title = '',
file_name = file_path,
last_modified = last_modified,
tags = {},
references = {},
back_references = {},
}
local linenr = 0
while true do
linenr = linenr + 1
local line = file:read('*line')
if line == nil then
break
end
if line == '' then
goto continue
end
if info.id == '' then
local id_title = extract_id_and_title(line)
if id_title then
info = vim.tbl_extend('force', info, id_title)
goto continue
end
end
local refs = extract_references(line, linenr)
if refs then
vim.list_extend(info.references, refs)
end
local tags = extract_tags(line, linenr)
if tags then
vim.list_extend(info.tags, tags)
end
::continue::
end
if info.id ~= nil then
s_note_cache_with_file_path[file_path] = info
s_note_cache_with_id[info.id] = info
end
return info
end
function M.get_note(id)
if s_note_cache_with_id[id] ~= nil then
return s_note_cache_with_id[id]
end
local _ = M.get_notes()
if s_note_cache_with_id[id] ~= nil then
return s_note_cache_with_id[id]
end
return nil
end
function M.get_notes()
local folder = config.zettel_dir
local files = get_files(folder)
local all_notes = {}
for _, file in ipairs(files) do
table.insert(all_notes, get_note_information(file))
end
for _, note in ipairs(all_notes) do
if note.id == nil then
goto continue
end
local back_references = extract_back_references(all_notes, note.id)
if back_references then
-- When notes are cached, `back_references` field will have the references from before.
-- Since the files that refer to this one might have changed, we'll overwrite it here.
-- extract_back_references() already re-processes the references.
note.back_references = back_references
end
::continue::
end
return all_notes
end
function M.get_tags()
local notes = M.get_notes()
local tags = {}
for _, note in ipairs(notes) do
if #note.tags == 0 then
goto continue
end
for _, tag in ipairs(note.tags) do
table.insert(tags, {
linenr = tag.linenr,
name = tag.name,
file_name = note.file_name,
})
end
::continue::
end
return tags
end
return M