821 lines
23 KiB
Lua
821 lines
23 KiB
Lua
-- mod-version:3
|
|
local core = require "core"
|
|
local common = require "core.common"
|
|
local command = require "core.command"
|
|
local config = require "core.config"
|
|
local keymap = require "core.keymap"
|
|
local style = require "core.style"
|
|
local View = require "core.view"
|
|
local CommandView = require "core.commandview"
|
|
local DocView = require "core.docview"
|
|
|
|
local TodoTreeView = View:extend()
|
|
|
|
local SCOPES = {
|
|
ALL = "all",
|
|
FOCUSED = "focused",
|
|
}
|
|
|
|
config.plugins.todotreeview = common.merge({
|
|
todo_tags = {"TODO", "BUG", "FIX", "FIXME", "IMPROVEMENT"},
|
|
tag_colors = {
|
|
TODO = {tag=style.text, tag_hover=style.accent, text=style.text, text_hover=style.accent},
|
|
BUG = {tag=style.text, tag_hover=style.accent, text=style.text, text_hover=style.accent},
|
|
FIX = {tag=style.text, tag_hover=style.accent, text=style.text, text_hover=style.accent},
|
|
FIXME = {tag=style.text, tag_hover=style.accent, text=style.text, text_hover=style.accent},
|
|
IMPROVEMENT = {tag=style.text, tag_hover=style.accent, text=style.text, text_hover=style.accent},
|
|
},
|
|
todo_file_color = {
|
|
name=style.text,
|
|
hover=style.accent
|
|
},
|
|
-- Paths or files to be ignored
|
|
ignore_paths = {},
|
|
|
|
-- Tells if the plugin should start with the nodes expanded
|
|
todo_expanded = true,
|
|
|
|
-- 'tag' mode can be used to group the todos by tags
|
|
-- 'file' mode can be used to group the todos by files
|
|
-- 'file_tag' mode can be used to group the todos by files and then by tags inside the files
|
|
todo_mode = "tag",
|
|
|
|
treeview_size = 200 * SCALE, -- default size
|
|
|
|
-- Only used in file mode when the tag and the text are on the same line
|
|
todo_separator = " - ",
|
|
|
|
-- Text displayed when the note is empty
|
|
todo_default_text = "blank",
|
|
|
|
-- Scope of the displayed tags
|
|
-- 'all' scope to show all tags from the project all the time
|
|
-- 'focused' scope to show the tags from the currently focused file
|
|
todo_scope = SCOPES.ALL,
|
|
|
|
-- The config specification used by the settings gui
|
|
config_spec = {
|
|
name = "TodoTreeView",
|
|
{
|
|
label = "Todo Tags",
|
|
description = "List of tags to parse and show in the todo panel.",
|
|
path = "todo_tags",
|
|
type = "list_strings",
|
|
default = {"TODO", "BUG", "FIX", "FIXME", "IMPROVEMENT"},
|
|
},
|
|
{
|
|
label = "Paths to ignore",
|
|
description = "Paths to be ignored when parsing the tags.",
|
|
path = "ignore_paths",
|
|
type = "list_strings",
|
|
default = {}
|
|
},
|
|
{
|
|
label = "Groups Expanded",
|
|
description = "Defines if the groups (Tags / Files) should start expanded.",
|
|
path = "todo_expanded",
|
|
type = "toggle",
|
|
default = true,
|
|
},
|
|
{
|
|
label = "Mode for the todos",
|
|
description = "Mode for the todos to be displayed.",
|
|
path = "todo_mode",
|
|
type = "selection",
|
|
default = "tag",
|
|
values = {
|
|
{"Tag", "tag"},
|
|
{"File", "file"},
|
|
{"FileTag", "file_tag"}
|
|
}
|
|
},
|
|
{
|
|
label = "Treeview Size",
|
|
description = "Size of the todo tree view panel.",
|
|
path = "treeview_size",
|
|
type = "number",
|
|
default = 200 * SCALE,
|
|
},
|
|
{
|
|
label = "Todo separator",
|
|
description = "Separator used in file mode when the tag and the text are on the same line.",
|
|
path = "todo_separator",
|
|
type = "string",
|
|
default = " - ",
|
|
},
|
|
{
|
|
label = "Empty Default Text",
|
|
description = "Default text displayed for a note when it has no text.",
|
|
path = "todo_default_text",
|
|
type = "string",
|
|
default = " - ",
|
|
},
|
|
{
|
|
label = "Todo Scope",
|
|
description = "Scope for the notes to be picked, all notes or currently focused file.",
|
|
path = "todo_scope",
|
|
type = "selection",
|
|
default = "all",
|
|
values = {
|
|
{"All", "all"},
|
|
{"Focused", "focused"},
|
|
}
|
|
},
|
|
}
|
|
}, config.plugins.todotreeview)
|
|
|
|
local icon_small_font = style.icon_font:copy(10 * SCALE)
|
|
|
|
function TodoTreeView:new()
|
|
TodoTreeView.super.new(self)
|
|
self.scrollable = true
|
|
self.focusable = false
|
|
self.visible = true
|
|
self.times_cache = {}
|
|
self.cache = {}
|
|
self.cache_updated = false
|
|
self.init_size = true
|
|
self.focus_index = 0
|
|
self.filter = ""
|
|
self.previous_focused_file = nil
|
|
|
|
-- Items are generated from cache according to the mode
|
|
self.items = {}
|
|
end
|
|
|
|
local function is_file_ignored(filename)
|
|
for _, path in ipairs(config.plugins.todotreeview.ignore_paths) do
|
|
local s, _ = filename:find(path)
|
|
if s then
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function TodoTreeView:is_file_in_scope(filename)
|
|
if config.plugins.todotreeview.todo_scope == SCOPES.ALL then
|
|
return true
|
|
elseif config.plugins.todotreeview.todo_scope == SCOPES.FOCUSED then
|
|
if core.active_view:is(CommandView) or core.active_view:is(TodoTreeView) then
|
|
if self.previous_focused_file then
|
|
return self.previous_focused_file == filename
|
|
end
|
|
elseif core.active_view:is(DocView) then
|
|
return core.active_view.doc.filename == filename
|
|
end
|
|
return true
|
|
else
|
|
assert(false, "Unknown scope defined ("..config.plugins.todotreeview.todo_scope..")")
|
|
end
|
|
end
|
|
|
|
function TodoTreeView.get_all_files()
|
|
local all_files = {}
|
|
for _, file in ipairs(core.project_files) do
|
|
if file.filename then
|
|
all_files[file.filename] = file
|
|
end
|
|
end
|
|
for _, file in ipairs(core.docs) do
|
|
if file.filename and not all_files[file.filename] then
|
|
all_files[file.filename] = {
|
|
filename = file.filename,
|
|
type = "file"
|
|
}
|
|
end
|
|
end
|
|
return all_files
|
|
end
|
|
|
|
function TodoTreeView:refresh_cache()
|
|
local items = {}
|
|
if not next(self.items) then
|
|
items = self.items
|
|
end
|
|
self.updating_cache = true
|
|
|
|
core.add_thread(function()
|
|
for _, item in pairs(self.get_all_files()) do
|
|
local ignored = is_file_ignored(item.filename)
|
|
if not ignored and item.type == "file" then
|
|
local cached = self:get_cached(item)
|
|
|
|
if config.plugins.todotreeview.todo_mode == "file" then
|
|
items[cached.filename] = cached
|
|
elseif config.plugins.todotreeview.todo_mode == "file_tag" then
|
|
local file_t = {}
|
|
file_t.expanded = config.plugins.todotreeview.todo_expanded
|
|
file_t.type = "file"
|
|
file_t.tags = {}
|
|
file_t.todos = {}
|
|
file_t.filename = cached.filename
|
|
file_t.abs_filename = cached.abs_filename
|
|
items[cached.filename] = file_t
|
|
for _, todo in ipairs(cached.todos) do
|
|
local tag = todo.tag
|
|
if not file_t.tags[tag] then
|
|
local tag_t = {}
|
|
tag_t.expanded = config.plugins.todotreeview.todo_expanded
|
|
tag_t.type = "group"
|
|
tag_t.todos = {}
|
|
tag_t.tag = tag
|
|
file_t.tags[tag] = tag_t
|
|
end
|
|
|
|
table.insert(file_t.tags[tag].todos, todo)
|
|
end
|
|
else
|
|
for _, todo in ipairs(cached.todos) do
|
|
local tag = todo.tag
|
|
if not items[tag] then
|
|
local t = {}
|
|
t.expanded = config.plugins.todotreeview.todo_expanded
|
|
t.type = "group"
|
|
t.todos = {}
|
|
t.tag = tag
|
|
items[tag] = t
|
|
end
|
|
|
|
table.insert(items[tag].todos, todo)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Copy expanded from old items
|
|
if config.plugins.todotreeview.todo_mode == "tag" and next(self.items) then
|
|
for tag, data in pairs(self.items) do
|
|
if items[tag] then
|
|
items[tag].expanded = data.expanded
|
|
end
|
|
end
|
|
end
|
|
|
|
self.items = items
|
|
core.redraw = true
|
|
self.cache_updated = true
|
|
self.updating_cache = false
|
|
end, self)
|
|
end
|
|
|
|
|
|
local function find_file_todos(t, filename)
|
|
local fp = io.open(filename)
|
|
if not fp then return t end
|
|
local n = 1
|
|
for line in fp:lines() do
|
|
for _, todo_tag in ipairs(config.plugins.todotreeview.todo_tags) do
|
|
-- Add spaces at the start and end of line so the pattern will pick
|
|
-- tags at the start and at the end of lines
|
|
local extended_line = " "..line.." "
|
|
local match_str = "[^a-zA-Z_\"'`]"..todo_tag.."[^\"'a-zA-Z_`]+"
|
|
local s, e = extended_line:find(match_str)
|
|
if s then
|
|
local d = {}
|
|
d.type = "todo"
|
|
d.tag = todo_tag
|
|
d.filename = filename
|
|
d.text = extended_line:sub(e+1)
|
|
if d.text == "" then
|
|
d.text = config.plugins.todotreeview.todo_default_text
|
|
end
|
|
d.line = n
|
|
d.col = s
|
|
table.insert(t, d)
|
|
end
|
|
core.redraw = true
|
|
end
|
|
if n % 100 == 0 then coroutine.yield() end
|
|
n = n + 1
|
|
core.redraw = true
|
|
end
|
|
fp:close()
|
|
end
|
|
|
|
|
|
function TodoTreeView:get_cached(item)
|
|
local t = self.cache[item.filename]
|
|
if not t then
|
|
t = {}
|
|
t.expanded = config.plugins.todotreeview.todo_expanded
|
|
t.filename = item.filename
|
|
t.abs_filename = system.absolute_path(item.filename)
|
|
t.type = item.type
|
|
t.todos = {}
|
|
t.tags = {}
|
|
find_file_todos(t.todos, t.filename)
|
|
self.cache[t.filename] = t
|
|
end
|
|
return t
|
|
end
|
|
|
|
|
|
function TodoTreeView:get_name()
|
|
return "Todo Tree"
|
|
end
|
|
|
|
function TodoTreeView:set_target_size(axis, value)
|
|
if axis == "x" then
|
|
config.plugins.todotreeview.treeview_size = value
|
|
return true
|
|
end
|
|
end
|
|
|
|
function TodoTreeView:get_item_height()
|
|
return style.font:get_height() + style.padding.y
|
|
end
|
|
|
|
|
|
function TodoTreeView:get_cached_time(doc)
|
|
local t = self.times_cache[doc]
|
|
if not t then
|
|
local info = system.get_file_info(doc.filename)
|
|
if not info then return nil end
|
|
self.times_cache[doc] = info.modified
|
|
end
|
|
return t
|
|
end
|
|
|
|
|
|
function TodoTreeView:check_cache()
|
|
local existing_docs = {}
|
|
for _, doc in ipairs(core.docs) do
|
|
if doc.filename then
|
|
existing_docs[doc.filename] = true
|
|
local info = system.get_file_info(doc.filename)
|
|
local cached = self:get_cached_time(doc)
|
|
if not info and cached then
|
|
-- document deleted
|
|
self.times_cache[doc] = nil
|
|
self.cache[doc.filename] = nil
|
|
self.cache_updated = false
|
|
elseif cached and cached ~= info.modified then
|
|
-- document modified
|
|
self.times_cache[doc] = info.modified
|
|
self.cache[doc.filename] = nil
|
|
self.cache_updated = false
|
|
elseif not cached then
|
|
self.cache_updated = false
|
|
end
|
|
end
|
|
end
|
|
|
|
for _, file in ipairs(core.project_files) do
|
|
existing_docs[file.filename] = true
|
|
end
|
|
|
|
-- Check for docs in cache that may not exist anymore
|
|
-- for example: (Openend from outside of project and closed)
|
|
for filename, doc in pairs(self.cache) do
|
|
local exists = existing_docs[filename]
|
|
if not exists then
|
|
self.times_cache[doc] = nil
|
|
self.cache[filename] = nil
|
|
self.cache_updated = false
|
|
end
|
|
end
|
|
|
|
if core.project_files ~= self.last_project_files then
|
|
self.last_project_files = core.project_files
|
|
self.cache_updated = false
|
|
end
|
|
end
|
|
|
|
function TodoTreeView:each_item()
|
|
self:check_cache()
|
|
if not self.updating_cache and not self.cache_updated then
|
|
self:refresh_cache()
|
|
end
|
|
|
|
return coroutine.wrap(function()
|
|
local ox, oy = self:get_content_offset()
|
|
local y = oy + style.padding.y
|
|
local w = self.size.x
|
|
local h = self:get_item_height()
|
|
|
|
for filename, item in pairs(self.items) do
|
|
local in_scope = item.type == "group" or self:is_file_in_scope(item.filename)
|
|
if in_scope and #item.todos > 0 then
|
|
coroutine.yield(item, ox, y, w, h)
|
|
y = y + h
|
|
|
|
for _, todo in ipairs(item.todos) do
|
|
if item.expanded then
|
|
local in_todo = string.find(todo.text:lower(), self.filter:lower())
|
|
local todo_in_scope = self:is_file_in_scope(todo.filename)
|
|
if todo_in_scope and (#self.filter == 0 or in_todo) then
|
|
coroutine.yield(todo, ox, y, w, h)
|
|
y = y + h
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
if in_scope and item.tags then
|
|
local first_tag = true
|
|
for _, tag in pairs(item.tags) do
|
|
if first_tag then
|
|
coroutine.yield(item, ox, y, w, h)
|
|
y = y + h
|
|
first_tag = false
|
|
end
|
|
if item.expanded then
|
|
coroutine.yield(tag, ox, y, w, h)
|
|
y = y + h
|
|
|
|
for _, todo in ipairs(tag.todos) do
|
|
if item.expanded and tag.expanded then
|
|
local in_todo = string.find(todo.text:lower(), self.filter:lower())
|
|
local todo_in_scope = self:is_file_in_scope(todo.filename)
|
|
if todo_in_scope and (#self.filter == 0 or in_todo) then
|
|
coroutine.yield(todo, ox, y, w, h)
|
|
y = y + h
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
end)
|
|
end
|
|
|
|
|
|
function TodoTreeView:on_mouse_moved(px, py)
|
|
self.hovered_item = nil
|
|
for item, x,y,w,h in self:each_item() do
|
|
if px > x and py > y and px <= x + w and py <= y + h then
|
|
self.hovered_item = item
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
function TodoTreeView:goto_hovered_item()
|
|
if not self.hovered_item then
|
|
return
|
|
end
|
|
|
|
if self.hovered_item.type == "group" or self.hovered_item.type == "file" then
|
|
return
|
|
end
|
|
|
|
core.try(function()
|
|
local i = self.hovered_item
|
|
local dv = core.root_view:open_doc(core.open_doc(i.filename))
|
|
core.root_view.root_node:update_layout()
|
|
dv.doc:set_selection(i.line, i.col)
|
|
dv:scroll_to_line(i.line, false, true)
|
|
end)
|
|
end
|
|
|
|
function TodoTreeView:on_mouse_pressed(button, x, y)
|
|
if not self.hovered_item then
|
|
return
|
|
elseif self.hovered_item.type == "file"
|
|
or self.hovered_item.type == "group" then
|
|
self.hovered_item.expanded = not self.hovered_item.expanded
|
|
else
|
|
self:goto_hovered_item()
|
|
end
|
|
end
|
|
|
|
|
|
function TodoTreeView:update()
|
|
-- Update focus
|
|
if core.active_view:is(DocView) then
|
|
self.previous_focused_file = core.active_view.doc.filename
|
|
elseif core.active_view:is(CommandView) or core.active_view:is(TodoTreeView) then
|
|
-- Do nothing
|
|
else
|
|
self.previous_focused_file = nil
|
|
end
|
|
|
|
self.scroll.to.y = math.max(0, self.scroll.to.y)
|
|
|
|
-- update width
|
|
local dest = self.visible and config.plugins.todotreeview.treeview_size or 0
|
|
if self.init_size then
|
|
self.size.x = dest
|
|
self.init_size = false
|
|
else
|
|
self:move_towards(self.size, "x", dest)
|
|
end
|
|
|
|
TodoTreeView.super.update(self)
|
|
end
|
|
|
|
|
|
function TodoTreeView:draw()
|
|
self:draw_background(style.background2)
|
|
|
|
--local h = self:get_item_height()
|
|
local icon_width = style.icon_font:get_width("D")
|
|
local spacing = style.font:get_width(" ") * 2
|
|
local root_depth = 0
|
|
|
|
for item, x,y,w,h in self:each_item() do
|
|
local text_color = style.text
|
|
local tag_color = style.text
|
|
local file_color = config.plugins.todotreeview.todo_file_color.name or style.text
|
|
if config.plugins.todotreeview.tag_colors[item.tag] then
|
|
text_color = config.plugins.todotreeview.tag_colors[item.tag].text or style.text
|
|
tag_color = config.plugins.todotreeview.tag_colors[item.tag].tag or style.text
|
|
end
|
|
|
|
-- hovered item background
|
|
if item == self.hovered_item then
|
|
renderer.draw_rect(x, y, w, h, style.line_highlight)
|
|
text_color = style.accent
|
|
tag_color = style.accent
|
|
file_color = config.plugins.todotreeview.todo_file_color.hover or style.accent
|
|
if config.plugins.todotreeview.tag_colors[item.tag] then
|
|
text_color = config.plugins.todotreeview.tag_colors[item.tag].text_hover or style.accent
|
|
tag_color = config.plugins.todotreeview.tag_colors[item.tag].tag_hover or style.accent
|
|
end
|
|
end
|
|
|
|
-- icons
|
|
local item_depth = 0
|
|
x = x + (item_depth - root_depth) * style.padding.x + style.padding.x
|
|
if item.type == "file" then
|
|
local icon1 = item.expanded and "-" or "+"
|
|
common.draw_text(style.icon_font, file_color, icon1, nil, x, y, 0, h)
|
|
x = x + style.padding.x
|
|
common.draw_text(style.icon_font, file_color, "f", nil, x, y, 0, h)
|
|
x = x + icon_width
|
|
elseif item.type == "group" then
|
|
if config.plugins.todotreeview.todo_mode == "file_tag" then
|
|
x = x + style.padding.x * 0.75
|
|
end
|
|
|
|
if item.expanded then
|
|
common.draw_text(style.icon_font, tag_color, "-", nil, x, y, 0, h)
|
|
else
|
|
common.draw_text(icon_small_font, tag_color, ">", nil, x, y, 0, h)
|
|
end
|
|
x = x + icon_width / 2
|
|
else
|
|
if config.plugins.todotreeview.todo_mode == "tag" then
|
|
x = x + style.padding.x
|
|
else
|
|
x = x + style.padding.x * 1.5
|
|
end
|
|
common.draw_text(style.icon_font, text_color, "i", nil, x, y, 0, h)
|
|
x = x + icon_width
|
|
end
|
|
|
|
-- text
|
|
x = x + spacing
|
|
if item.type == "file" then
|
|
common.draw_text(style.font, file_color, item.filename, nil, x, y, 0, h)
|
|
elseif item.type == "group" then
|
|
common.draw_text(style.font, tag_color, item.tag, nil, x, y, 0, h)
|
|
else
|
|
if config.plugins.todotreeview.todo_mode == "file" then
|
|
common.draw_text(style.font, tag_color, item.tag, nil, x, y, 0, h)
|
|
x = x + style.font:get_width(item.tag)
|
|
common.draw_text(style.font, text_color, config.plugins.todotreeview.todo_separator..item.text, nil, x, y, 0, h)
|
|
else
|
|
common.draw_text(style.font, text_color, item.text, nil, x, y, 0, h)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function TodoTreeView:get_item_by_index(index)
|
|
local i = 0
|
|
for item in self:each_item() do
|
|
if index == i then
|
|
return item
|
|
end
|
|
i = i + 1
|
|
end
|
|
return nil
|
|
end
|
|
|
|
function TodoTreeView:get_hovered_parent_file_tag()
|
|
local file_parent = nil
|
|
local file_parent_index = 0
|
|
local group_parent = nil
|
|
local group_parent_index = 0
|
|
local i = 0
|
|
for item in self:each_item() do
|
|
if item.type == "file" then
|
|
file_parent = item
|
|
file_parent_index = i
|
|
end
|
|
if item.type == "group" then
|
|
group_parent = item
|
|
group_parent_index = i
|
|
end
|
|
if i == self.focus_index then
|
|
if item.type == "file" or item.type == "group" then
|
|
return file_parent, file_parent_index
|
|
else
|
|
return group_parent, group_parent_index
|
|
end
|
|
end
|
|
i = i + 1
|
|
end
|
|
return nil, 0
|
|
end
|
|
|
|
function TodoTreeView:get_hovered_parent()
|
|
local parent = nil
|
|
local parent_index = 0
|
|
local i = 0
|
|
for item in self:each_item() do
|
|
if item.type == "group" or item.type == "file" then
|
|
parent = item
|
|
parent_index = i
|
|
end
|
|
if i == self.focus_index then
|
|
return parent, parent_index
|
|
end
|
|
i = i + 1
|
|
end
|
|
return nil, 0
|
|
end
|
|
|
|
function TodoTreeView:update_scroll_position()
|
|
local h = self:get_item_height()
|
|
local _, min_y, _, max_y = self:get_content_bounds()
|
|
local start_row = math.floor(min_y / h)
|
|
local end_row = math.floor(max_y / h)
|
|
if self.focus_index < start_row then
|
|
self.scroll.to.y = self.focus_index * h
|
|
end
|
|
if self.focus_index + 1 > end_row then
|
|
self.scroll.to.y = (self.focus_index * h) - self.size.y + h
|
|
end
|
|
end
|
|
|
|
-- init
|
|
local view = TodoTreeView()
|
|
local node = core.root_view:get_active_node()
|
|
view.size.x = config.plugins.todotreeview.treeview_size
|
|
node:split("right", view, {x=true}, true)
|
|
|
|
core.status_view:add_item({
|
|
predicate = function()
|
|
return #view.filter > 0 and core.active_view and not core.active_view:is(CommandView)
|
|
end,
|
|
name = "todotreeview:filter",
|
|
alignment = core.status_view.Item.RIGHT,
|
|
get_item = function()
|
|
return {
|
|
style.text,
|
|
string.format("Filter: %s", view.filter)
|
|
}
|
|
end,
|
|
position = 1,
|
|
tooltip = "Todos filtered by",
|
|
separator = core.status_view.separator2
|
|
})
|
|
|
|
-- register commands and keymap
|
|
local previous_view = nil
|
|
command.add(nil, {
|
|
["todotreeview:toggle"] = function()
|
|
view.visible = not view.visible
|
|
end,
|
|
|
|
["todotreeview:expand-items"] = function()
|
|
for _, item in pairs(view.items) do
|
|
item.expanded = true
|
|
end
|
|
end,
|
|
|
|
["todotreeview:hide-items"] = function()
|
|
for _, item in pairs(view.items) do
|
|
item.expanded = false
|
|
end
|
|
end,
|
|
|
|
["todotreeview:toggle-focus"] = function()
|
|
if not core.active_view:is(TodoTreeView) then
|
|
previous_view = core.active_view
|
|
core.set_active_view(view)
|
|
view.hovered_item = view:get_item_by_index(view.focus_index)
|
|
else
|
|
command.perform("todotreeview:release-focus")
|
|
end
|
|
end,
|
|
|
|
["todotreeview:filter-notes"] = function()
|
|
local todo_view_focus = core.active_view:is(TodoTreeView)
|
|
local previous_filter = view.filter
|
|
local submit = function(text)
|
|
view.filter = text
|
|
if todo_view_focus then
|
|
view.focus_index = 0
|
|
view.hovered_item = view:get_item_by_index(view.focus_index)
|
|
view:update_scroll_position()
|
|
end
|
|
end
|
|
local suggest = function(text)
|
|
view.filter = text
|
|
end
|
|
local cancel = function(explicit)
|
|
view.filter = previous_filter
|
|
end
|
|
core.command_view:enter("Filter Notes", {
|
|
text = view.filter,
|
|
submit = submit,
|
|
suggest = suggest,
|
|
cancel = cancel
|
|
})
|
|
end,
|
|
})
|
|
|
|
command.add(
|
|
function()
|
|
return core.active_view:is(TodoTreeView)
|
|
end, {
|
|
["todotreeview:previous"] = function()
|
|
if view.focus_index > 0 then
|
|
view.focus_index = view.focus_index - 1
|
|
view.hovered_item = view:get_item_by_index(view.focus_index)
|
|
view:update_scroll_position()
|
|
end
|
|
end,
|
|
|
|
["todotreeview:next"] = function()
|
|
local next_index = view.focus_index + 1
|
|
local next_item = view:get_item_by_index(next_index)
|
|
if next_item then
|
|
view.focus_index = next_index
|
|
view.hovered_item = next_item
|
|
view:update_scroll_position()
|
|
end
|
|
end,
|
|
|
|
["todotreeview:collapse"] = function()
|
|
if not view.hovered_item then
|
|
return
|
|
end
|
|
|
|
if view.hovered_item.type == "file" then
|
|
view.hovered_item.expanded = false
|
|
else
|
|
if view.hovered_item.type == "group" and view.hovered_item.expanded then
|
|
view.hovered_item.expanded = false
|
|
else
|
|
if config.plugins.todotreeview.todo_mode == "file_tag" then
|
|
view.hovered_item, view.focus_index = view:get_hovered_parent_file_tag()
|
|
else
|
|
view.hovered_item, view.focus_index = view:get_hovered_parent()
|
|
end
|
|
|
|
view:update_scroll_position()
|
|
end
|
|
end
|
|
end,
|
|
|
|
["todotreeview:expand"] = function()
|
|
if not view.hovered_item then
|
|
return
|
|
end
|
|
|
|
if view.hovered_item.type == "file" or view.hovered_item.type == "group" then
|
|
if view.hovered_item.expanded then
|
|
command.perform("todotreeview:next")
|
|
else
|
|
view.hovered_item.expanded = true
|
|
end
|
|
end
|
|
end,
|
|
|
|
["todotreeview:open"] = function()
|
|
if not view.hovered_item then
|
|
return
|
|
end
|
|
|
|
view:goto_hovered_item()
|
|
view.hovered_item = nil
|
|
end,
|
|
|
|
["todotreeview:release-focus"] = function()
|
|
core.set_active_view(
|
|
previous_view or core.root_view:get_primary_node().active_view
|
|
)
|
|
view.hovered_item = nil
|
|
end,
|
|
})
|
|
|
|
keymap.add { ["ctrl+shift+t"] = "todotreeview:toggle" }
|
|
keymap.add { ["ctrl+shift+e"] = "todotreeview:expand-items" }
|
|
keymap.add { ["ctrl+shift+h"] = "todotreeview:hide-items" }
|
|
keymap.add { ["ctrl+shift+b"] = "todotreeview:filter-notes" }
|
|
keymap.add { ["up"] = "todotreeview:previous" }
|
|
keymap.add { ["down"] = "todotreeview:next" }
|
|
keymap.add { ["left"] = "todotreeview:collapse" }
|
|
keymap.add { ["right"] = "todotreeview:expand" }
|
|
keymap.add { ["return"] = "todotreeview:open" }
|
|
keymap.add { ["escape"] = "todotreeview:release-focus" }
|
|
|