Generate Link @ Cursor Position

Related to I wonder if SilverBullet could take advantage of the “URL text fragment” standard 🤔 and Getting the start of line where cursor is?

I assume there are a lot of people who have secretly implemented this? hhhh… Are there many people like me who love to goto anywhere in their documents? XD

command.define {
  name = "Cursor: Copy Reference",
  key = "Shift-Alt-c",
  run = function()
    local pageName = editor.getCurrentPage()
    local pos = editor.getCursor()
    local ref = string.format("[[%s@%d]]", pageName, pos)
    editor.copyToClipboard(ref)
    editor.flashNotification("Copied reference: " .. ref, "info")
  end
}

Compared to the alternative of planting one immutable (but invisible) anchor and jumping to it, the current technique streamlines operations but raises maintenance concerns if the document is edited (pos-word map might change).

Hence, a smarter method — like the one referenced in I wonder if SilverBullet could take advantage of the “URL text fragment” standard 🤔
or an equivalent implementation — may be warranted (from SB directly?) :

SilverBullet’s backlinks effectively re-evaluate cursor positions so they update when the source moves (However, it seems that when [[A Page]] appears at the end of B Page, the backlink from A Page to B Page does likely not return to the correct position: v2.1.9 tiny bug - the cursor does, but the view does not: view always at the top. emmmmmmmmm, Now I cannot replicate this bug -_-||).

In essence, there are three precise forms of reference, with stability typically increasing in that order:

  1. addressing a specific character position or line;
  2. referencing a given piece of text (or a textual pattern);
  3. referencing a visible or invisible label/anchor.

I previously implemented an automated backlinks-enabled, yet LaTeX-based version of strategy 3. Is anyone interested in achieving a similar effect (i.e. Cursor-level fine-grained forwardLink with Automatic BackLinks) in SilverBullet?

1 Like

An analogy to help @zef and those intrigued by this intellectual exercise understand:

At present, the Linked Mentions and Linked Tasks widgets of a given [[Page A]] are essentially Cursor-level precise backlinks — each pointing from the [[Page A]] to a specific cursor position within other[[Page ?]]s.

Now, imagine that these 2 backlink widgets are no longer fixed at the top or bottom of a [[Page A]], but can instead float freely.
In that case, they no longer belong to a particular Page Object [[Page A]], but rather to a designated Anchor Object [[Page A@pos]].

This instantly yields automatic backlinks for “floating” [[Page A@pos]] (rather than “fixed” [[Page A]]), since it reuses the precise backlinks mechanism already implemented in Library/Std/Widgets/Widgets.

Furthermore, the former forward/outward links (from [[Page ?]]s) to [[Page A]] would now all become links to [[Page A@pos]]. Note that this simply requires extending the index to also record and update the pos and corresponding [[Page A@pos]] name — which is quite feasible, given that Library/Std/Widgets/Widgets already employs a similar mechanism in Auto-Backlinks.

Beyond this, nothing more is required — it is essentially a fine-grained Cursor-level extension of the existing Page-level system. And although it may appear somewhat recursive, there is no actual loop involved.

Believe me, this will be the next killer feature of SB v2, second only to Linked Tasks: fully automated, cursor-level precise for/out-ward and back/in-ward linking — something almost no other note-taking software/website/app has achieved.

I have already implemented this exact functionality in the supplementary materials of my short paper as well as in my PhD dissertation. Since it can be realized in the LaTeX ecosystem, it is certainly achievable in SilverBullet.

1/2 of the journey remains:

manual Forwardlink to a [[Certain Page]]
auto Backlink s to [[Other Pages@pos]] s with auto-updated pos s from a [[Certain Page]]
auto Backlink s to [[Other Pages@pos_O]] s with auto-updated pos_O s from a [[Certain Page@pos_C]]
manual Forwardlink to a [[Certain Page@pos_C]] with auto-updated pos_C (without s !)

Note that once the 4 points above are implemented, the referencing positions of [[Other Pages@pos_O]] s may themselves reside within the [[Certain Page]] which contains the anchor [[Certain Page@pos_C]] that is cited. This signifies a complete transition away from the Page-based paradigm.

Keep going, @zef — this “will definitely make SB great again”!

In essence, what we are doing is reinventing Ted_Nelson
’s concept of bidirectional HTML.

It feels somewhat akin to a “block web” (similar to “block chain”).

I have a faint yet precise intuition that: SilverBullet serves as an excellent demonstrative platform for this philosophy.

1 Like

Forester? :slight_smile:

1 Like

Almost there!
Very impressive and Close enough!

But… we need more precise links!
How precise should it be?

Pretty much like the feeling when you Ctrl + Enter or Click a [[Page]],

you can precisely Alt + ← jumping back (~ AutoBackLink) to where you quoted/cited/refered at Cursor level,

and then Alt + → jumping forward (~ ManualForwardLink) once again at Cursor level.

Update: When the cursor is located inside a header, produce the format [[pageName#headerName]] rather than [[pageName@pos]].

command.define {
  name = "Cursor: Copy Reference",
  key = "Shift-Alt-c",
  run = function()
    local currentLine = editor.getCurrentLine().textWithCursor:gsub("|%^|", "")
    local pageName = editor.getCurrentPage()
    local pos = editor.getCursor()
    local headerMarks, headerName = string.match(currentLine, "^(#+) +(.+)$")
    -- editor.flashNotification(headerMarks .. headerName)
    local ref
    if headerMarks and headerName and headerName:match("%S") then
      headerName = headerName:match("^%s*(.+)")
      ref = string.format("[[%s#%s]]", pageName, headerName)
      editor.flashNotification("Copied header reference: " .. ref, "info")
    else
      ref = string.format("[[%s@%d]]", pageName, pos)
      editor.flashNotification("Copied cursor reference: " .. ref, "info")
    end

    editor.copyToClipboard(ref)
  end
}

Borrowed some tech editor.getCurrentLine().textWithCursor:gsub("|%^|", "") (see editor) from Copy the nearest Format around Cursor - #2 by ChenZhu-Xie

Copy (as) External Link version

-- [[Page#Header]] -> http(s)://host/Page#Header
-- [[Page@pos]]    -> http(s)://host/Page@pos

local function replace_space_with_percent20(s)
  local parts = {}
  for i = 1, #s do
    local c = s:sub(i, i)
    if c == " " then
      parts[#parts+1] = "%20"
    else
      parts[#parts+1] = c
    end
  end
  return table.concat(parts)
end

-- your address:   "https://your-domain"
local BASE_URL = "http://127.0.0.1:3000"

local function build_page_url(pageName)
  local path = replace_space_with_percent20(pageName)
  return string.format("%s/%s", BASE_URL, path)
end

command.define {
  name = "Cursor: Copy Link",
  key = "Ctrl-Shift-c",
  run = function()
    local currentLine = editor.getCurrentLine().text
    local pageName = editor.getCurrentPage()
    local pos = editor.getCursor()
    local headerMarks, headerName = string.match(currentLine, "^(#+) +(.+)$")
    
    local pageUrl = build_page_url(pageName)
    local out
    if headerMarks and headerName and headerName:match("%S") then
      headerName = headerName:match("^%s*(.+)")
      headerName = replace_space_with_percent20(headerName)
      out = string.format("%s#%s", pageUrl, headerName)
      editor.flashNotification("Copied header external link: " .. out, "info")
    else
      out = string.format("%s@%d", pageUrl, pos)
      editor.flashNotification("Copied cursor external link: " .. out, "info")
    end
  
    editor.copyToClipboard(out)
  end
}