Last active
February 22, 2022 22:44
-
-
Save GitMurf/d254ab2fefba8cc0be306d3f8fee371e 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
<%* | |
//https://gist.github.com/GitMurf/d254ab2fefba8cc0be306d3f8fee371e | |
const ignoreWords = ["the", "all", "can"]; | |
const skipString = "*None (SKIP)"; | |
function cleanWords(wordStr) { | |
wordStr = wordStr.replace(/[\.\?\,\!\:]+$/, ""); | |
return wordStr; | |
} | |
function removeSuffixes(wordStr) { | |
wordStr = wordStr.replace(/(s|ed|ing|est|er)$/i, ""); | |
return wordStr; | |
} | |
async function returnUnlinkedText(textToLink, uniq) { | |
//Loop through all links and narrow it down to just ones that match some part of the selected text from your note | |
const LcaseSelection = textToLink.toLowerCase(); | |
const filteredArray = []; | |
const filteredArraySecondary = []; | |
const filteredArrayThird = []; | |
uniq.forEach((eachLink) => { | |
const Lcase = eachLink[0].toLowerCase(); | |
if (LcaseSelection.indexOf(Lcase) > -1) { | |
filteredArray.push(eachLink); | |
} | |
//Handle plurals and other suffixes like 'ed' 'ing' etc. | |
const removeEnding = removeSuffixes(Lcase); | |
if (LcaseSelection.indexOf(removeEnding) > -1) { | |
filteredArraySecondary.push(eachLink); | |
} | |
//Handle missing just the last word, so names like John match John Doe or Microsoft Office matches Microsoft Office 365 | |
const tmpLastWord = Lcase.split(" "); | |
const tmpFirstWord = tmpLastWord[0].toLowerCase(); | |
if (LcaseSelection.indexOf(tmpFirstWord) > -1 && tmpFirstWord.length > 2) { | |
tmpLastWord.pop(); | |
const removeLastWord = tmpLastWord.join(" "); | |
if (LcaseSelection.indexOf(removeLastWord) > -1) { | |
filteredArrayThird.push(eachLink); | |
} | |
} | |
}); | |
//Get each word in the selected text | |
const words = []; | |
let lines = textToLink.split("\n"); | |
lines.forEach(eachLine => { | |
const lineWords = eachLine.split(" "); | |
words.push(...lineWords); | |
}) | |
//Loop through each word | |
let chCtr = 0; | |
let finalString = textToLink; | |
for (let i = 0; i < words.length; i++) { | |
const thisWord = words[i]; | |
const thisWordClean = cleanWords(thisWord); | |
let nextWord = ""; | |
let nextWordClean = ""; | |
let thirdWord = ""; | |
let thirdWordClean = ""; | |
if (i < (words.length - 1)) { | |
nextWord = words[i + 1]; | |
nextWordClean = cleanWords(nextWord); | |
} | |
if (i < (words.length - 2)) { | |
thirdWord = words[i + 2]; | |
thirdWordClean = cleanWords(thirdWord); | |
} | |
let twoWords = ""; | |
let twoWordsClean = ""; | |
let threeWords = ""; | |
let threeWordsClean = ""; | |
if (nextWord) { | |
twoWords = `${thisWord}${nextWord ? (" " + nextWord) : ""}`; | |
twoWordsClean = `${thisWordClean}${nextWord ? (" " + nextWordClean) : ""}`; | |
} | |
if (thirdWord) { | |
threeWords = `${thisWord}${nextWord ? (" " + nextWord) : ""}${thirdWord ? (" " + thirdWord) : ""}`; | |
threeWordsClean = `${thisWordClean}${nextWord ? (" " + nextWordClean) : ""}${thirdWord ? (" " + thirdWordClean) : ""}`; | |
} | |
//Loop each filteredArray and match exactly to three, two or single words | |
let oneFound = []; | |
let twoFound = []; | |
let threeFound = []; | |
filteredArray.forEach(eachFilter => { | |
let foundObj = { | |
link: eachFilter[1], | |
filterIteration: 1, | |
oneTwoThree: null, | |
wordsClean: null, | |
words: null, | |
alias: eachFilter[1] | |
} | |
if (eachFilter[0].toLowerCase() === threeWordsClean.toLowerCase()) { | |
foundObj.oneTwoThree = 3; | |
foundObj.wordsClean = threeWordsClean; | |
foundObj.words = threeWords; | |
threeFound.push(foundObj); | |
} else if (eachFilter[0].toLowerCase() === twoWordsClean.toLowerCase()) { | |
foundObj.oneTwoThree = 2; | |
foundObj.wordsClean = twoWordsClean; | |
foundObj.words = twoWords; | |
twoFound.push(foundObj); | |
} else if (eachFilter[0].toLowerCase() === thisWordClean.toLowerCase() && !ignoreWords.includes(thisWordClean.toLowerCase())) { | |
foundObj.oneTwoThree = 1; | |
foundObj.wordsClean = thisWordClean; | |
foundObj.words = thisWord; | |
oneFound.push(foundObj); | |
} | |
}) | |
//No exact match so now check for plurals and other suffixes | |
filteredArraySecondary.forEach(eachFilter => { | |
let foundObj = { | |
link: eachFilter[1], | |
filterIteration: 2, | |
oneTwoThree: null, | |
wordsClean: null, | |
words: null, | |
alias: eachFilter[1] | |
} | |
if (eachFilter[0].substring(0, 3).toLowerCase() === thisWordClean.substring(0, 3).toLowerCase()) { | |
const removeEndingFilter = removeSuffixes(eachFilter[0]); | |
const removeEndingThree = removeSuffixes(threeWordsClean); | |
const removeEndingTwo = removeSuffixes(twoWordsClean); | |
const removeEndingOne = removeSuffixes(thisWordClean); | |
if (removeEndingFilter.toLowerCase() === removeEndingThree.toLowerCase()) { | |
foundObj.oneTwoThree = 3; | |
foundObj.wordsClean = threeWordsClean; | |
foundObj.words = threeWords; | |
threeFound.push(foundObj); | |
} else if (removeEndingFilter.toLowerCase() === removeEndingTwo.toLowerCase()) { | |
foundObj.oneTwoThree = 2; | |
foundObj.wordsClean = twoWordsClean; | |
foundObj.words = twoWords; | |
twoFound.push(foundObj); | |
} else if (removeEndingFilter.toLowerCase() === removeEndingOne.toLowerCase() && !ignoreWords.includes(thisWordClean.toLowerCase())) { | |
foundObj.oneTwoThree = 1; | |
foundObj.wordsClean = thisWordClean; | |
foundObj.words = thisWord; | |
oneFound.push(foundObj); | |
} | |
} | |
}) | |
//No exact match or suffixes match, now look for missing last word only (John match John Doe) | |
filteredArrayThird.forEach(eachFilter => { | |
let foundObj = { | |
link: eachFilter[1], | |
filterIteration: 3, | |
oneTwoThree: null, | |
wordsClean: null, | |
words: null, | |
alias: eachFilter[1] | |
} | |
if (eachFilter[0].substring(0, 3).toLowerCase() === thisWordClean.substring(0, 3).toLowerCase()) { | |
//This is specifically for when you include John D in note and want it to match John Doe page | |
const tmpLastWord = eachFilter[0].split(" "); | |
let secondWordFirstChar = ""; | |
if (tmpLastWord.length > 1) { | |
secondWordFirstChar = tmpLastWord[1] ? tmpLastWord[0] + " " + tmpLastWord[1].substring(0, 1) : eachFilter[0]; | |
tmpLastWord.pop(); | |
} else { | |
secondWordFirstChar = eachFilter[0]; | |
} | |
const removeLastWord = tmpLastWord.join(" "); | |
if (removeLastWord.toLowerCase() === threeWordsClean.toLowerCase()) { | |
foundObj.oneTwoThree = 3; | |
foundObj.wordsClean = threeWordsClean; | |
foundObj.words = threeWords; | |
threeFound.push(foundObj); | |
} else if (removeLastWord.toLowerCase() === twoWordsClean.toLowerCase()) { | |
foundObj.oneTwoThree = 2; | |
foundObj.wordsClean = twoWordsClean; | |
foundObj.words = twoWords; | |
twoFound.push(foundObj); | |
} else if (secondWordFirstChar.toLowerCase() === twoWordsClean.toLowerCase()) { | |
foundObj.oneTwoThree = 2; | |
foundObj.wordsClean = twoWordsClean; | |
foundObj.words = twoWords; | |
twoFound.push(foundObj); | |
} else if (removeLastWord.toLowerCase() === thisWordClean.toLowerCase() && !ignoreWords.includes(thisWordClean.toLowerCase())) { | |
foundObj.oneTwoThree = 1; | |
foundObj.wordsClean = thisWordClean; | |
foundObj.words = thisWord; | |
oneFound.push(foundObj); | |
} | |
} | |
}) | |
//Combine threeFound, twoFound and oneFound arrays | |
let allFoundArray = []; | |
if (threeFound.length > 0) { | |
allFoundArray = allFoundArray.concat(threeFound); | |
} | |
if (twoFound.length > 0) { | |
allFoundArray = allFoundArray.concat(twoFound); | |
} | |
if (oneFound.length > 0) { | |
allFoundArray = allFoundArray.concat(oneFound); | |
} | |
if (allFoundArray.length > 0) { | |
const tmpFoundOptions = [skipString]; | |
const tmpFoundOptionsText = [skipString]; | |
allFoundArray.forEach(eachFound => { | |
let foundObj = { | |
link: eachFound.link, | |
filterIteration: eachFound.filterIteration, | |
oneTwoThree: eachFound.oneTwoThree, | |
wordsClean: eachFound.wordsClean, | |
words: eachFound.words, | |
alias: eachFound.alias, | |
selectedItemText: null | |
} | |
if (!tmpFoundOptionsText.includes(`${foundObj.wordsClean} --> [` + `[${foundObj.link}]]`)) { | |
tmpFoundOptions.push(foundObj); | |
tmpFoundOptionsText.push(`${foundObj.wordsClean} --> [` + `[${foundObj.link}]]`); | |
foundObj.selectedItemText = `${foundObj.wordsClean} --> [` + `[${foundObj.link}]]`; | |
} | |
}) | |
//Add alias options if not an exact match | |
tmpFoundOptions.push("⬇️ LINK AS ALIAS BELOW ⬇️"); | |
tmpFoundOptionsText.push("⬇️ LINK AS ALIAS BELOW ⬇️"); | |
let bAddAlias = false; | |
allFoundArray.forEach(eachFound => { | |
let foundObj = { | |
link: eachFound.link, | |
filterIteration: eachFound.filterIteration, | |
oneTwoThree: eachFound.oneTwoThree, | |
wordsClean: eachFound.wordsClean, | |
words: eachFound.words, | |
alias: eachFound.alias, | |
selectedItemText: null | |
} | |
let aliasOpt = foundObj.link.toLowerCase() !== foundObj.wordsClean.toLowerCase() ? (foundObj.link + "|" + foundObj.wordsClean) : ""; | |
if (aliasOpt) { | |
if (!tmpFoundOptionsText.includes(`${foundObj.wordsClean} --> [` + `[${aliasOpt}]]`)) { | |
foundObj.alias = aliasOpt; | |
tmpFoundOptions.push(foundObj); | |
tmpFoundOptionsText.push(`${foundObj.wordsClean} --> [` + `[${aliasOpt}]]`); | |
foundObj.selectedItemText = `${foundObj.wordsClean} --> [` + `[${aliasOpt}]]`; | |
bAddAlias = true; | |
} | |
} | |
}) | |
if (!bAddAlias) { | |
tmpFoundOptions.pop(); | |
tmpFoundOptionsText.pop(); | |
} | |
let savedMatch = false; | |
savedSelections.forEach(eachSave => { | |
const newTmpFoundText = []; | |
tmpFoundOptionsText.forEach(eachTmpFoundText => { newTmpFoundText.push(eachTmpFoundText.toLowerCase()) }); | |
if (newTmpFoundText.includes(eachSave[0])) { | |
if (newTmpFoundText.length === eachSave[2]) { | |
savedMatch = true; | |
if (eachSave[1] === skipString) { | |
foundSelectedItem = skipString; | |
} else { | |
foundSelectedItem = tmpFoundOptions.find(x => (x.selectedItemText ? x.selectedItemText.toLowerCase() : "") === eachSave[0]); | |
} | |
} | |
} | |
}) | |
if (!savedMatch) { | |
foundSelectedItem = await tp.system.suggester(tmpFoundOptionsText, tmpFoundOptions, false); | |
if(!foundSelectedItem) { | |
//User closed out of modal (will exit below) | |
} else if (foundSelectedItem !== skipString) { | |
savedSelections.push([foundSelectedItem.selectedItemText.toLowerCase(), foundSelectedItem, tmpFoundOptionsText.length]); | |
} else { | |
if (tmpFoundOptionsText[1]) { | |
savedSelections.push([tmpFoundOptionsText[1].toLowerCase(), skipString, tmpFoundOptionsText.length]); | |
} | |
} | |
} | |
if(!foundSelectedItem) { | |
//User closed out of modal | |
return; | |
} | |
let bSelectedOption = false; | |
if (foundSelectedItem === skipString) { | |
//Skip | |
//console.log(`SKIP: ${thisWord}`); | |
} else { | |
bSelectedOption = true; | |
const startPos = chCtr; | |
const endPos = chCtr + foundSelectedItem.wordsClean.length; | |
let aliasString = ""; | |
if (foundSelectedItem.alias.indexOf("|") > -1) { | |
aliasString = "|" + foundSelectedItem.wordsClean; | |
} | |
const selectedLink = foundSelectedItem.link; | |
finalString = finalString.substring(0, startPos) + "[" + "[" + selectedLink + aliasString + "]]" + finalString.substring(endPos); | |
switch (foundSelectedItem.oneTwoThree) { | |
case 1: | |
break; | |
case 2: | |
//Skip next word in the parent loop | |
i = i + 1; | |
break; | |
case 3: | |
//Skip two words in the parent loop | |
i = i + 2; | |
break; | |
} | |
const punctuationDiff = foundSelectedItem.words.length - foundSelectedItem.wordsClean.length; | |
chCtr = chCtr + selectedLink.length + punctuationDiff + aliasString.length + 1 + 4; | |
} | |
if (bSelectedOption === false) { | |
chCtr = chCtr + thisWord.length + 1; | |
} | |
} else { | |
chCtr = chCtr + thisWord.length + 1; | |
} | |
} | |
return finalString; | |
} | |
//Get selection | |
let selectedText = tp.file.selection(); | |
if (!selectedText) { | |
const editor = app.workspace.activeLeaf.view.editor; | |
const curLine = editor.getCursor().line; | |
let getLine = editor.getLine(curLine); | |
if (!getLine) { | |
const cbText = await navigator.clipboard.readText(); | |
getLine = cbText ? cbText : null; | |
} else { | |
editor.setSelection({ line: curLine, ch: 0 }, { line: curLine, ch: getLine.length }); | |
} | |
if (!getLine) { | |
new Notice("Exiting Unlink-Finder because no text is selected, the current line is empty and there is nothing on the clipboard.", 10000); | |
return; | |
} | |
selectedText = getLine; | |
} | |
//All markdown notes | |
const files = await app.vault.getMarkdownFiles(); | |
const links = []; | |
files.forEach(file => { | |
links.push([file.basename, file.basename, "file"]); | |
const mdCache = app.metadataCache.getFileCache(file); | |
if (mdCache) { | |
if (mdCache.frontmatter) { | |
if (mdCache.frontmatter.alias) { | |
const linkAliases = mdCache.frontmatter.alias; | |
if (Array.isArray(linkAliases)) { | |
linkAliases.forEach(eachAlias => { | |
links.push([eachAlias, file.basename, "alias"]); | |
}) | |
} else { | |
links.push([linkAliases, file.basename, "alias"]); | |
} | |
} | |
} | |
} | |
}); | |
//All links (unresolved) not made into a markdown note yet | |
const unResLinks = Object.values(Object.fromEntries(Object.entries(app.metadataCache.unresolvedLinks))); | |
unResLinks.forEach((eachLink) => { | |
let theValues = Object.keys(eachLink); | |
theValues.forEach(eachVal => { | |
links.push([eachVal, eachVal, "link"]); | |
}) | |
}); | |
//let uniqLinks = Array.from(new Set(links)); | |
let uniqLinks = links; | |
const savedSelections = []; | |
//This will loop through the selected text making sure to NOT try and re-link any word(s) that are already linked in double brackets | |
const splitAlreadyLinked = selectedText.split(/(\[\[[^\[\]]*\]\])/); | |
let resultText = ""; | |
for (let i = 0; i < splitAlreadyLinked.length; i++) { | |
const eachTextString = splitAlreadyLinked[i]; | |
if(eachTextString) { | |
let newResult; | |
if (eachTextString.startsWith("[" + "[")) { | |
newResult = eachTextString; | |
} else { | |
newResult = await returnUnlinkedText(eachTextString, uniqLinks); | |
if(newResult === null || newResult === undefined) { | |
//User likely closed out of the modal | |
new Notice("Unlink-finder was exited early. No changes were applied!", 10000); | |
return; | |
} | |
} | |
resultText = resultText + newResult; | |
} | |
} | |
console.log(`\n------\n${tp.date.now("yyyy-MM-DD HH:mm.ss")}\nTEMPLATER UNLINK-FINDER ORIGINAL TEXT\n------\n\n`); | |
console.log(`${selectedText}`); | |
//tR = `${selectedText}\n\n${resultText}`; | |
tR = `${resultText}`; | |
%> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment