diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..adbdb1b
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,3 @@
+[*]
+indent_style = space
+indent_size = 4
diff --git a/README.md b/README.md
index 653fc7e..cd8c588 100644
--- a/README.md
+++ b/README.md
@@ -41,6 +41,21 @@ With `Telescope neorg search_headings` you can search through all the headings i
+### Search File and Heading Backlinks
+- `Telescope neorg find_backlinks` - find every line in your workspace that links^* to the current file
+- `Telescope neorg find_header_backlinks` - same but with links to the current file _and_ heading
+
+These are limited to workspace relative links (ie.
+`{:$/worspace/relative/path:}`) for the sake of simplicity. Both exact
+(`{:$/path:** lvl 2 heading}`) and fuzzy (`{:$/path:# heading}`) links are
+found.
+
+
+ Demo
+
+![search backlink](https://github.com/nvim-neorg/neorg-telescope/assets/56943754/37a5b68f-29b3-43ae-a679-9656cfa646db)
+
+
## Gtd Pickers
### Those pickers are all broken since gtd was removed in core
diff --git a/lua/neorg/modules/core/integrations/telescope/module.lua b/lua/neorg/modules/core/integrations/telescope/module.lua
index 82a7cec..afe51f2 100644
--- a/lua/neorg/modules/core/integrations/telescope/module.lua
+++ b/lua/neorg/modules/core/integrations/telescope/module.lua
@@ -28,6 +28,8 @@ module.load = function()
"find_aof_tasks",
"find_context_tasks",
"switch_workspace",
+ "find_backlinks",
+ "find_header_backlinks",
})
end
@@ -42,6 +44,8 @@ module.public = {
find_aof_tasks = require("telescope._extensions.neorg.find_aof_tasks"),
find_aof_project_tasks = require("telescope._extensions.neorg.find_aof_project_tasks"),
switch_workspace = require("telescope._extensions.neorg.switch_workspace"),
+ find_backlinks = require("telescope._extensions.neorg.backlinks.file_backlinks"),
+ find_header_backlinks = require("telescope._extensions.neorg.backlinks.header_backlinks"),
}
module.on_event = function(event)
@@ -65,6 +69,10 @@ module.on_event = function(event)
module.public.find_context_tasks()
elseif event.split_type[2] == "core.integrations.telescope.switch_workspace" then
module.public.switch_workspace()
+ elseif event.split_type[2] == "core.integrations.telescope.find_backlinks" then
+ module.public.find_backlinks()
+ elseif event.split_type[2] == "core.integrations.telescope.find_header_backlinks" then
+ module.public.find_header_backlinks()
end
end
@@ -80,6 +88,8 @@ module.events.subscribed = {
["core.integrations.telescope.find_aof_tasks"] = true,
["core.integrations.telescope.find_aof_project_tasks"] = true,
["core.integrations.telescope.switch_workspace"] = true,
+ ["core.integrations.telescope.find_backlinks"] = true,
+ ["core.integrations.telescope.find_header_backlinks"] = true,
},
}
diff --git a/lua/neorg/telescope_utils.lua b/lua/neorg/telescope_utils.lua
index 6bd5c74..20f5e5f 100644
--- a/lua/neorg/telescope_utils.lua
+++ b/lua/neorg/telescope_utils.lua
@@ -152,4 +152,15 @@ utils.get_project_tasks = function()
return projects_tasks
end
+---Gets the full path to the current workspace
+---@return string?
+utils.get_current_workspace = function()
+ local dirman = neorg.modules.get_module("core.dirman")
+ if dirman then
+ local current_workspace = dirman.get_current_workspace()[2]
+ return current_workspace
+ end
+ return nil
+end
+
return utils
diff --git a/lua/telescope/_extensions/neorg.lua b/lua/telescope/_extensions/neorg.lua
index ea5fe1d..5861243 100644
--- a/lua/telescope/_extensions/neorg.lua
+++ b/lua/telescope/_extensions/neorg.lua
@@ -10,5 +10,7 @@ return require("telescope").register_extension({
find_aof_tasks = require("neorg.modules.core.integrations.telescope.module").public.find_aof_tasks,
find_aof_project_tasks = require("neorg.modules.core.integrations.telescope.module").public.find_aof_project_tasks,
switch_workspace = require("neorg.modules.core.integrations.telescope.module").public.switch_workspace,
+ find_backlinks = require("neorg.modules.core.integrations.telescope.module").public.find_backlinks,
+ find_header_backlinks = require("neorg.modules.core.integrations.telescope.module").public.find_header_backlinks,
},
})
diff --git a/lua/telescope/_extensions/neorg/backlinks/common.lua b/lua/telescope/_extensions/neorg/backlinks/common.lua
new file mode 100644
index 0000000..65153ed
--- /dev/null
+++ b/lua/telescope/_extensions/neorg/backlinks/common.lua
@@ -0,0 +1,27 @@
+local M = {}
+
+---produce the regular expression used to find workspace relative paths to the given file.
+---Optionally takes a header that should exist in the current file
+---@param workspace_path string "/abs/path/to/workspace"
+---@param current_file string "test.norg"
+---@param heading string? "** heading"
+---@return string
+M.build_backlink_regex = function(workspace_path, current_file, heading)
+ current_file = vim.api.nvim_buf_get_name(0)
+ current_file = current_file:gsub("%.norg$", "")
+ current_file = current_file:gsub("^" .. workspace_path .. "/", "")
+
+ if not heading then
+ return ([[\{:\$/%s:.*\}]]):format(current_file) -- {:$/workspace_path:}
+ end
+
+ local heading_prefix = heading:match("^%**")
+ if heading_prefix then
+ heading_prefix = heading_prefix:gsub("%*", "\\*")
+ end
+ local heading_text = heading:gsub("^%** ", "")
+ heading_text = heading_text:gsub("^%(.%)%s?", "")
+ return ([[\{:\$/%s:(#|%s) %s\}]]):format(current_file, heading_prefix, heading_text) -- {:$/workspace_path:(# heading or ** heading)}
+end
+
+return M
diff --git a/lua/telescope/_extensions/neorg/backlinks/file_backlinks.lua b/lua/telescope/_extensions/neorg/backlinks/file_backlinks.lua
new file mode 100644
index 0000000..77e5a4b
--- /dev/null
+++ b/lua/telescope/_extensions/neorg/backlinks/file_backlinks.lua
@@ -0,0 +1,19 @@
+local common = require("telescope._extensions.neorg.backlinks.common")
+local utils = require("neorg.telescope_utils")
+
+return function()
+ local current_workspace = utils.get_current_workspace()
+
+ if not current_workspace then
+ return
+ end
+
+ local current_file = vim.api.nvim_buf_get_name(0)
+
+ require("telescope.builtin").grep_string({
+ search = common.build_backlink_regex(current_workspace, current_file),
+ use_regex = true,
+ search_dirs = { current_workspace },
+ prompt_title = "File Backlinks",
+ })
+end
diff --git a/lua/telescope/_extensions/neorg/backlinks/header_backlinks.lua b/lua/telescope/_extensions/neorg/backlinks/header_backlinks.lua
new file mode 100644
index 0000000..9159579
--- /dev/null
+++ b/lua/telescope/_extensions/neorg/backlinks/header_backlinks.lua
@@ -0,0 +1,38 @@
+local common = require("telescope._extensions.neorg.backlinks.common")
+local utils = require("neorg.telescope_utils")
+
+return function()
+ local current_workspace = utils.get_current_workspace()
+
+ if not current_workspace then
+ return
+ end
+
+ local current_file = vim.api.nvim_buf_get_name(0)
+ local linenr = vim.api.nvim_win_get_cursor(0)[1]
+ local lines = vim.api.nvim_buf_get_lines(0, 0, linenr, false)
+ local heading = nil
+
+ -- HACK: iterate backward (up) over lines, and use the first heading we find. We should be using
+ -- TS instead, but I'm not super familiar with how to do things like that.
+ for i = #lines, 1, -1 do
+ local line = lines[i]
+ local potential_heading = line:match("^%s*%*+ .*$")
+ if potential_heading then
+ heading = potential_heading
+ break
+ end
+ end
+
+ if not heading then
+ vim.notify("[Neorg Telescope] Couldn't find current heading", vim.log.levels.ERROR)
+ return
+ end
+
+ require("telescope.builtin").grep_string({
+ search = common.build_backlink_regex(current_workspace, current_file, heading),
+ use_regex = true,
+ search_dirs = { current_workspace },
+ prompt_title = "Header Backlinks (" .. heading .. ")",
+ })
+end
diff --git a/lua/telescope/_extensions/neorg/find_linkable.lua b/lua/telescope/_extensions/neorg/find_linkable.lua
index 45236dc..92e0d18 100644
--- a/lua/telescope/_extensions/neorg/find_linkable.lua
+++ b/lua/telescope/_extensions/neorg/find_linkable.lua
@@ -1,23 +1,9 @@
-local neorg_loaded, neorg = pcall(require, "neorg.core")
-
-assert(neorg_loaded, "Neorg is not loaded - please make sure to load Neorg first")
-
-local function get_current_workspace()
- local dirman = neorg.modules.get_module("core.dirman")
-
- if dirman then
- local current_workspace = dirman.get_current_workspace()[2]
-
- return current_workspace
- end
-
- return nil
-end
+local utils = require("neorg.telescope_utils")
return function(opts)
opts = opts or {}
- local current_workspace = get_current_workspace()
+ local current_workspace = utils.get_current_workspace()
if not current_workspace then
return