author: Chenzhu-Xie name: Library/xczphysics/CONFIG/Nearest_Pattern/Duplicate tags: meta/library
-- pattern def:{ name, pattern, priority }
-- use % to escape special characters
-- you can add your own patterns
INLINE_PATTERNS = {
{ "Wiki Link", "%[%[^\n%](^\n%)+%]%]", 100 }, -- [...](...) 或 [...|...](...|...)
{ "Fields", "%[^\n%](^\n%)+:[^\n%]]+%]", 95 }, -- [key:value]
{ "Image", "!%[^\n%](^\n%)-%]%([^\n)]+%)", 90 }, -- 
{ "Markdown Link", "%[^\n%](^\n%)+%]%([^\n)]+%)", 85 }, -- [text](url)
{ "Color Func", "%${[A-Za-z0-9]*%([\"\'][^\n}]*[\"\']%)}", 80 }, -- ${Color("...")}
{ "Bold", "%*%*[^\n%*]+%*%*", 70 }, -- **bold**
{ "Italic", "_[^\n_]+_", 60 }, -- _italic_
{ "Sup", "%^[^ \n%^]+%^", 55 }, -- ^sup^
{ "Strikethrough", "~~[^\n]+~~", 50 }, -- ~~?~~
{ "Sub", "~[^ \n~]+~", 45 }, -- ^sub^
{ "Tag", "#[^\n, %?%.:%|\\{}%)%(%*&%^%%%$#@!]+", 40 }, -- #tag
{ "Marker", "==[^\n]+==", 35 }, -- ==?==
{ "Inline Code", "`[^\n`]+`", 30 }, -- ``?``
{ "Header", "(#+ [^\n]+)\n", 25 }, -- # header
{ "Lua Block", "```(lua)+[^`]+```", 21 }, -- ```...```
{ "Script Block", "```(script)+[^`]+```", 20 }, -- ```...```
}
function SelectiondistanceToCursor(startPos, endPos, cursorPos)
if cursorPos < startPos then return startPos - cursorPos end
if cursorPos > endPos then return cursorPos - endPos end
return 0
end
function getCursorPos()
local cur = editor.getCursor()
local cursor_pos = (type(cur) == "table" and cur.pos) or cur
return cursor_pos
end
function findNearestInlinePattern()
local pageText = editor.getText()
local curPos = getCursorPos()
local nearest = nil
for _, pat in ipairs(INLINE_PATTERNS) do
local name, pattern, prio = pat[1], pat[2], pat[3]
local init = 1
local ok, err = pcall(function()
while true do
local s, e = pageText:find(pattern, init)
if not s then break end
local dist = SelectiondistanceToCursor(s, e, curPos)
local score = dist * 1001 + (1000 - prio * 10)
if not nearest or score < nearest.score then
nearest = { name = name, start = s, stop = e, text = pageText:sub(s, e), score = score }
end
init = (e >= init) and (e + 1) or (init + 1)
end
end)
if not ok then
editor.flashNotification(("[Pattern error] %s: %s"):format(name, tostring(err)))
end
end
return nearest
end
command.define{
name = "Cursor: Copy Nearest Pattern",
description = "Copy the nearest and highest-priority formatted structure around the cursor",
key = "Alt-c",
run = function()
local match = findNearestInlinePattern()
if not match then
editor.flashNotification("No pattern matched.")
return
end
editor.copyToClipboard(match.text)
editor.insertAtCursor(match.text, false)
-- editor.flashNotification("Copied: " .. match.name .. " → " .. match.text)
editor.flashNotification(match.name .. " ✅")
if not match.name == "Inline Code" then
editor.flashNotification(match.text)
end
end
}
-- pattern def:{ name, pattern, priority }
-- use % to escape special characters
-- you can add your own patterns
local PATTERNS = {
{ "Wiki Link", "%[%[^%](^%)+%]%]", 100 }, -- [...](...) 或 [...|...](...|...)
{ "Fields", "%[^%](^%)+:[^%]]+%]", 95 }, -- [key:value]
{ "Image", "!%[^%](^%)-%]%([^)]+%)", 90 }, -- 
{ "Markdown Link", "%[^%](^%)+%]%([^)]+%)", 85 }, -- [text](url)
{ "Color Func", "%${[A-Za-z0-9]*%([\"\'][^}]*[\"\']%)}", 80 }, -- ${Color("...")}
{ "Bold", "%*%*[^%*]+%*%*", 70 }, -- **bold**
{ "Italic", "_[^_]+_", 60 }, -- _italic_
{ "Sup", "%^[^ %^]+%^", 55 }, -- ^sup^
{ "Strikethrough", "~~[^]+~~", 50 }, -- ~~?~~
{ "Sub", "~[^ ~]+~", 45 }, -- ^sub^
{ "Tag", "#[^, <>%?%.:|\\{}%)%(%*&%^%%%$#@!]+", 40 }, -- #tag
{ "Marker", "==[^]+==", 35 }, -- ==?==
{ "Inline Code", "`[^`]+`", 30 }, -- ``?``
}
local function distanceToCursor(startPos, endPos, cursorPos)
if cursorPos < startPos then return startPos - cursorPos end
if cursorPos > endPos then return cursorPos - endPos end
return 0
end
function getCursorPos()
local cur = editor.getCursor()
local cursor_pos = (type(cur) == "table" and cur.pos) or cur
return cursor_pos
end
function getCursor_LineStart()
local textBeforeCursor = editor.getText():sub(1, getCursorPos())
local cursorLineStart = textBeforeCursor:reverse():find("\n", 1, true)
-- editor.flashNotification(cursorLineStart)
return cursorLineStart
end
function getPos_LineStart()
local posLineStart = getCursorPos() - getCursor_LineStart() + 2
-- editor.flashNotification(posLineStart)
return posLineStart
end
local function findNearestPattern()
-- local currentLine = editor.getCurrentLine().textWithCursor:gsub("|%^|", "")
local currentLine = editor.getCurrentLine().text
-- https://silverbullet.md/API/editor#editor.getCurrentLine()
-- editor.flashNotification(currentLine)
local curPos_LineStart = getCursor_LineStart()
-- getPos_LineStart()
local nearest = nil
for _, pat in ipairs(PATTERNS) do
local name, pattern, prio = pat[1], pat[2], pat[3]
local init = 1
local ok, err = pcall(function()
while true do
local s, e = currentLine:find(pattern, init)
if not s then break end
local dist = distanceToCursor(s, e, curPos_LineStart)
local score = dist * 1001 + (1000 - prio * 10)
if not nearest or score < nearest.score then
nearest = { name = name, start = s, stop = e, text = currentLine:sub(s, e), score = score }
end
init = (e >= init) and (e + 1) or (init + 1)
end
end)
if not ok then
editor.flashNotification(("[Pattern error] %s: %s"):format(name, tostring(err)))
end
end
return nearest
end
command.define{
name = "Cursor: Copy Nearest Pattern",
description = "Copy the nearest and highest-priority formatted structure around the cursor",
key = "Alt-c",
run = function()
local match = findNearestPattern()
if not match then
editor.flashNotification("No pattern matched.")
return
end
editor.copyToClipboard(match.text)
editor.flashNotification("Copied: " .. match.name .. " → " .. match.text)
end
}