Last active
June 1, 2021 02:58
-
-
Save UltraInstinct05/53acd9f84bb06e5a0d0ff37dead10c58 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sublime, sublime_plugin | |
class ReferenceLinkInputHandler(sublime_plugin.TextInputHandler): | |
def placeholder(self): | |
return "Type a link" | |
class ReferenceLinkCommand(sublime_plugin.TextCommand): | |
def run(self, edit, reference_link): | |
link_number = 1 | |
ref_links = self.view.find_by_selector("meta.link.reference.def.markdown") | |
pos = self.view.sel()[0] | |
entered_link = "https://" + reference_link | |
selected_text = self.view.substr(self.view.sel()[0]) | |
# If there is no reference link. | |
if len(ref_links) == 0: | |
self.view.replace(edit, pos, self.link_template(selected_text, link_number)) | |
self.view.insert(edit, self.view.size(), "\n\n" + self.reference_template(link_number, entered_link)) | |
return | |
# Find the largest reference link number. | |
max_link_num = 0 | |
for i in ref_links: | |
region = sublime.Region(i.begin(), i.end()) | |
link_num = self.view.substr(region).split(":", 1)[0][1] | |
if int(link_num) > int(max_link_num): | |
max_link_num = int(link_num) | |
# If the reference link is same as one already present. | |
for i in ref_links: | |
region = sublime.Region(i.begin(), i.end()) | |
actual_link = self.view.substr(region).split(":", 1)[1].strip() | |
link_num = self.view.substr(region).split(":", 1)[0][1] | |
if actual_link == entered_link: | |
self.view.replace(edit, pos, self.link_template(selected_text, link_num)) | |
return | |
self.view.replace(edit, pos, self.link_template(selected_text, max_link_num + 1)) | |
self.view.insert(edit, self.view.size(), "\n" + self.reference_template(max_link_num + 1, entered_link)) | |
def input(self, args): | |
if args is not None: | |
return ReferenceLinkInputHandler() | |
def input_description(self): | |
return "https://" | |
@staticmethod | |
def reference_template(link_number, link): | |
return "[{}]: {}".format(link_number, link) | |
@staticmethod | |
def link_template(text, link_number): | |
if text == "": | |
return "[Enter description here][{}]".format(link_number) | |
return "[{}][{}]".format(text, link_number) |
I created a test case.
It seems in this case the scope catcher doesn't catch all cases.
OK, I tried to learn how to write a plug in and this is what I came up with:
import sublime
import sublime_plugin
import re
import math
# Definition Text: [RefID]: <Url Address> <Url Tooltip>
# See https://regex101.com/r/P9Pm9Z/latest
REG_EX_URL_ADD_DEF_TEXT = r"]:\s([^\s]*)\s?"
# Link Text: [some text][RefID]
# See https://regex101.com/r/iU4GlD/latest
REG_EX_REF_ID_DEF_TEXT = r"\[.*?\]\[([\d]*)\]"
def ExtractUrlAddressDefText(defString):
# Definition Text: [RefID]: <Url Address> <Url Tooltip>
# See https://regex101.com/r/P9Pm9Z/latest
httpRegExPattern = r"]:\s([^\s]*)\s?"
httpRegEx = re.compile(httpRegExPattern)
srchResult = httpRegEx.search(defString)
if srchResult:
return srchResult.group(1)
else:
return ""
def ExtractRefIdLinkText(linkString):
# Link Text: [some text][RefID]
# See https://regex101.com/r/iU4GlD/latest
idRegRxPattern = r"\[.*?\]\[([\d]*)\]"
idRegRx = re.compile(idRegRxPattern)
return idRegRx.search(linkString).group(1)
def ExtractRegExMatch(inputString, regExPattern):
regEx = re.compile(regExPattern)
regExSrch = regEx.search(inputString)
if regExSrch:
return regExSrch.group(1)
else:
return ""
def DefinitionTemplate(linkIdx, linkUrl, numDigits):
# See Parametrized Formats in https://pyformat.info/
return "[" + RefIdTemplate(linkIdx, numDigits) + "]" + ": {}".format(linkUrl)
# @staticmethod
def LinkTemplate(linkText, linkIdx, numDigits):
# See Parametrized Formats in https://pyformat.info/
if linkText == "":
return "[Enter Link Text]" + "[" + RefIdTemplate(linkIdx, numDigits) + "]"
return "[{}]".format(linkText) + "[" + RefIdTemplate(linkIdx, numDigits) + "]"
def RefIdTemplate(idIdx, numDigits):
# See Parametrized Formats in https://pyformat.info/
return "{:0{}}".format(idIdx, numDigits)
class ReferenceLinkInputHandler(sublime_plugin.TextInputHandler):
def placeholder(self):
return "Type a URL"
class ReferenceLinkCommand(sublime_plugin.TextCommand):
# See https://www.youtube.com/watch?v=Ohb_pYwO5wE
def run(self, edit, reference_link):
# reference_link - The input text of the user
defNumLeadingSpaces = 2
defNumDigits = 3
# ref_links = self.view.find_by_selector("meta.link.reference.def.markdown") # Doesn't catch all cases
# Catch the Definition ([Identifier]: <Link>) (Will catch inside ```block```, needs to be taken of)
# lRefDef = self.view.find_all("^[\s]{0,2}(\[[\d]{0,}\]):") # See https://regex101.com/r/iopdag/latest
lRefDef = self.view.find_all(r"^[\s]{0,2}\[([\d]*)\]:") # See https://regex101.com/r/iopdag/latest
# Catch the link ([Link Text][Identifier]) (Will catch inside ```block``` and `text`, needs to be taken of)
lRefLinks = self.view.find_all(r"\[.*?\](\[[\d]{0,}\])") # See https://regex101.com/r/L8GgbZ/latest
lRefLinksLine = []
for ii in lRefDef:
print(self.view.substr(ii))
numLinks = len(lRefDef)
maxNumDigits = 1
if(numLinks > 0):
maxNumDigits = math.ceil(math.log(numLinks, numLinks))
self.numLeadingSpaces = defNumLeadingSpaces
self.numDigits = max(maxNumDigits, defNumDigits)
addRefDef = True
linkIdx = numLinks + 1
if numLinks > 0:
for iRegion in lRefDef:
# urlAddress = ExtractUrlAddressDefText(self.view.substr(self.view.line(iRegion)))
urlAddress = ExtractRegExMatch(self.view.substr(self.view.line(iRegion)), REG_EX_URL_ADD_DEF_TEXT)
# print(urlAddress)
if urlAddress == reference_link:
# print("Found a Match")
linkIdx = int(self.view.substr(iRegion).strip(" \n\t[]:"))
addRefDef = False
lRefDef = [['001', reference_link, linkIdx]] # List of lists of the form [ID, urlText, Index]
if addRefDef:
self.WriteLinksDefinitions(edit, lRefDef, self.view.size())
selTextPoint = self.view.sel()[0]
selTextString = self.view.substr(selTextPoint)
self.view.replace(edit, selTextPoint, LinkTemplate(selTextString, linkIdx, self.numDigits))
def is_enabled(self):
# Should be disabled for non Mark Down (Or no text selection?)
return self.view.match_selector(0, "text.html.markdown") #<! Anoter option by self.view.syntax().name == "Markdown"
# def is_visible(self, text, pos, event=None):
# # Should be disabled for non Mark Down (Or no text selection?)
# return True
# def description(self, text, pos, event=None):
# # Only for dedicated menu
# return ""
# def want_event(self):
# return False
def input(self, args):
if args is not None:
return ReferenceLinkInputHandler()
# def input_description(self):
# return "https://"
def WriteLinksDefinitions(self, edit, lRefDef, writeIndex):
#
leadingSpaces = " " * self.numLeadingSpaces
for ii in range(len(lRefDef)):
insertString = "\n" + leadingSpaces + DefinitionTemplate(lRefDef[ii][2], lRefDef[ii][1], self.numDigits)
self.view.insert(edit, writeIndex, insertString)
writeIndex = writeIndex + len(insertString)
class FormatLinksCommand(sublime_plugin.TextCommand):
# See https://www.youtube.com/watch?v=Ohb_pYwO5wE
def run(self, edit):
# reference_link - The input text of the user
# Use dictionary to set the Ref ID by order {ID: Order}
numLeadingSpaces = 2
numDigits = 3
# Catch the link ([Link Text][Identifier]) (Will catch inside ```block``` and `text`, needs to be taken of)
lRefLinksReg = self.view.find_all(r"\[.*?\](\[[\d]{0,}\])") # See https://regex101.com/r/L8GgbZ/latest
lRefLinks = list()
for iRegion in lRefLinksReg:
# lRefLinks.append(ExtractRefIdLinkText(self.view.substr(iRegion)))
lRefLinks.append(ExtractRegExMatch(self.view.substr(iRegion)), REG_EX_REF_ID_DEF_TEXT)
# Reference ID by order
lRefLinksUnique = list(dict.fromkeys(lRefLinks)) #<! See https://stackoverflow.com/a/48028065
# print(lRefLinksUnique)
# Update Ref ID to the format
for iRegion in lRefLinksReg:
refIdRegion = self.view.find(r"\[[\d]*\]", iRegion.begin())
refIdRegion.a += 1
refIdRegion.b -= 1
# refIdRegion = sublime.Region(refIdRegionTmp.begin() + 1, refIdRegionTmp.end() - 1)
# print(self.view.substr(refIdRegion))
refIdIndex = lRefLinksUnique.index(self.view.substr(refIdRegion)) + 1
self.view.replace(edit, refIdRegion, RefIdTemplate(refIdIndex, numDigits))
# Extract Reference Definition
# lRefDef = self.view.find_all(r"^[\s]{0,2}\[([\d]*)\]:") # See https://regex101.com/r/iopdag/latest
# Pay attnetion that `\s` is not only whitespace but the class of whitespace (Includes newline)
lRefDef = self.view.find_all(r"^ {0,2}\[\d+\]:") # See https://regex101.com/r/yWdBFL/latest
# For cases for ID without address
lRefLinksUniqueUrl = [" \n"] * len(lRefLinksUnique) #<! For each one we'll have a Ref Def
for iRegion in lRefDef:
lineRegion = self.view.full_line(iRegion) #<! The whole line region
refIdRegion = self.view.find(r"\[[\d]*\]", iRegion.begin())
refIdRegion = sublime.Region(refIdRegion.begin() + 1, refIdRegion.end() - 1)
urlRegion = sublime.Region(iRegion.end() + 1, lineRegion.end()) #<! Everything after ":"
if self.view.substr(refIdRegion) in lRefLinksUnique:
urlIndex = lRefLinksUnique.index(self.view.substr(refIdRegion))
lRefLinksUniqueUrl[urlIndex] = self.view.substr(urlRegion)
# Delete all lines of lRefLinksReg (Use self.view.full_line())
# Must be done in reverse so regions are still valid
for iRegion in reversed(lRefDef):
self.view.erase(edit, self.view.full_line(iRegion))
# Build the Def List and append at the end of the buffer
leadingSpaces = " " * numLeadingSpaces
self.view.insert(edit, self.view.size(), "\n")
for ii, iRefUrl in enumerate(lRefLinksUniqueUrl):
insertString = leadingSpaces + DefinitionTemplate(ii + 1, iRefUrl, numDigits) #<! The "\n" at the end is already in `iRefUrl`
self.view.insert(edit, self.view.size(), insertString)
def ExtractUrlById(self, lRefId):
# Extract all
return
def is_enabled(self):
# Should be disabled for non Mark Down (Or no text selection?)
return self.view.match_selector(0, "text.html.markdown") #<! Another option by self.view.syntax().name == "Markdown"
My code can format existing list to have a proper order and style. It can also add a new link.
That's great to see ! 🙂 My example plugin was mainly to give a starting point (since I don't work with reference links in Markdown all that much).
Yep, Your code was a great starting point for me. Thank You.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For this to work, create a
Default.sublime-commands
file inUser
and add