Gif presentation
How to use
- Type
{{tag:#tag}}
in your note. - Put a cursor on that line
- Open command palette and find “Expander:” commands (you can attach hotkeys in settings menu)
It should remove the {{tag:#tag}}
line and add notes which was found.
Notes
-
The expander do not do the current file title exclusion. Please, be careful and remove the embedded self note before running preview mode (it can cause a recursion).Was fixed in v0.2. Thanks @GreenChocho for testing.
Install
- You need Obsidian v0.9.7+
- Get Latest release
- Extract files and place them to your vault’s plugins folder: /.obsidian/plugins/
- Reload Obsidian
For version below 0.3.0
To enable the plugin run this snippet in your console:
v0.2.1
!function() {
const DELAY = 2000
const config = [
{
id: 'editor:expandEmbeds',
name: 'Expander: embed',
format: e => '![[' + e + ']]'
},
{
id: 'editor:expandLinks',
name: 'Expander: links',
format: e => '[[' + e + ']]'
},
{
id: 'editor:expandList',
name: 'Expander: list of links',
format: e => '- [[' + e + ']]'
},
{
id: 'editor:expandTODO',
name: 'Expander: list of TODO',
format: e => '- [ ] [[' + e + ']]'
},
]
function reformatLinks(links, mapFunc) {
return links.map(e => e.file.name)
.filter(e => app.workspace.activeLeaf.view.file.name !== e)
.map(mapFunc).join('\n')
}
function getLastLineNum(doc, line = 0) {
const lineNum = line === 0
? doc.getCursor().line
: line
if (doc.lineCount() === lineNum) {
return doc.getCursor().line + 1
}
return doc.getLine(lineNum) === '---'
? lineNum
: getLastLineNum(doc, lineNum + 1)
}
function initExpander(mapFunc) {
// Search files
const search = query => app.globalSearch.openGlobalSearch(query)
const getFoundFilenames = (mapFunc, callback) => {
const searchLeaf = app.workspace.getLeavesOfType('search')[0]
searchLeaf.open(searchLeaf.view)
.then(view => setTimeout(()=> {
const result = reformatLinks(view.dom.resultDoms, mapFunc)
callback(result)
}, DELAY))
}
const cmDoc = app.workspace.activeLeaf.view.sourceMode.cmEditor.doc
const hasFormulaRegexp = /^\{\{.+\}\}$/
const curNum = cmDoc.getCursor().line
const curText = cmDoc.getLine(curNum)
if (!hasFormulaRegexp.test(curText)) {
return
}
const isEmbed = cmDoc.getLine(curNum - 1) === '```expander'
&& cmDoc.getLine(curNum + 1) === '```'
const fstLineNumToReplace = isEmbed
? curNum - 1
: curNum
const lstLineNumToReplace = isEmbed
? getLastLineNum(cmDoc)
: curNum
const searchQuery = curText.replace('{{', '').replace('}}', '')
const embedFormula = '```expander\n' +
'{{' + searchQuery + '}}\n' +
'```\n'
const log = content => console.log(content) || content
const replaceLine = content => cmDoc.replaceRange(log(embedFormula + content + '\n\n---'),
{line: fstLineNumToReplace, ch: 0},
{line: lstLineNumToReplace, ch: cmDoc.getLine(lstLineNumToReplace).length}
)
search(searchQuery)
getFoundFilenames(mapFunc, replaceLine)
}
config.forEach(e => {
app.commands.addCommand({
id: e.id,
name: e.name,
callback: () => initExpander(e.format),
hotkeys: []
})
})
}()
v0.2
!function() {
const DELAY = 2000
const config = [
{
id: 'editor:expandEmbeds',
name: 'Expander: embed',
format: e => '![[' + e + ']]'
},
{
id: 'editor:expandLinks',
name: 'Expander: links',
format: e => '[[' + e + ']]'
},
{
id: 'editor:expandList',
name: 'Expander: list of links',
format: e => '- [[' + e + ']]'
},
{
id: 'editor:expandTODO',
name: 'Expander: list of TODO',
format: e => '- [ ] [[' + e + ']]'
},
]
function reformatLinks(links, mapFunc) {
return links.map(e => e.file.name)
.filter(e => app.workspace.activeLeaf.view.file.name !== e)
.map(mapFunc).join('\n')
}
function getLastLineNum(cmInst, line = 0) {
const lineNum = line === 0
? cmInst.getCursor().line
: line
if (cmInst.lastLine() === lineNum) {
return cmInst.getCursor().line + 1
}
return cmInst.getLine(lineNum) === '---'
? lineNum
: getLastLineNum(cmInst, lineNum + 1)
}
function initExpander(mapFunc) {
// Search files
const search = query => app.globalSearch.openGlobalSearch(query)
const getFoundFilenames = (mapFunc, callback) => {
const searchLeaf = app.workspace.getLeavesOfType('search')[0]
searchLeaf.open(searchLeaf.view)
.then(view => setTimeout(()=> {
const result = reformatLinks(view.dom.resultDoms, mapFunc)
callback(result)
}, DELAY))
}
const cmDoc = app.workspace.activeLeaf.view.sourceMode.cmEditor.doc
const hasFormulaRegexp = /^\{\{.+\}\}$/
const curNum = cmDoc.getCursor().line
const curText = cmDoc.getLine(curNum)
if (!hasFormulaRegexp.test(curText)) {
return
}
const isEmbed = cmDoc.getLine(curNum - 1) === '```expander'
&& cmDoc.getLine(curNum + 1) === '```'
const fstLineNumToReplace = isEmbed
? curNum - 1
: curNum
const lstLineNumToReplace = isEmbed
? getLastLineNum(cmDoc)
: curNum
const searchQuery = curText.replace('{{', '').replace('}}', '')
const embedFormula = '```expander\n' +
'{{' + searchQuery + '}}\n' +
'```\n'
const log = content => console.log(content) || content
const replaceLine = content => cmDoc.replaceRange(log(embedFormula + content + '\n\n---'),
{line: fstLineNumToReplace, ch: 0},
{line: lstLineNumToReplace, ch: cmDoc.getLine(lstLineNumToReplace).length}
)
search(searchQuery)
getFoundFilenames(mapFunc, replaceLine)
}
config.forEach(e => {
app.commands.addCommand({
id: e.id,
name: e.name,
callback: () => initExpander(e.format),
hotkeys: []
})
})
}()
v0.1
!function() {
const DELAY = 2000
const config = [
{
id: 'editor:expandEmbeds',
name: 'Expander: embed',
format: e => '![[' + e + ']]'
},
{
id: 'editor:expandLinks',
name: 'Expander: links',
format: e => '[[' + e + ']]'
},
{
id: 'editor:expandList',
name: 'Expander: list of links',
format: e => '- [[' + e + ']]'
},
{
id: 'editor:expandTODO',
name: 'Expander: list of TODO',
format: e => '- [ ] [[' + e + ']]'
},
]
function initExpander(mapFunc) {
// Search files
const search = query => app.globalSearch.openGlobalSearch(query)
// const getFoundFilenames = () => Array.from(document.querySelectorAll('.mod-global-search .search-result-file-title>span:nth-child(2)')).map(e => e.textContent)
const getFoundFilenames = (mapFunc, callback) => {
const searchLeaf = app.workspace.getLeavesOfType('search')[0]
searchLeaf.open(searchLeaf.view)
.then(view => setTimeout(()=> {
callback(view.dom.resultDoms.map(e => e.file.name).map(mapFunc).join('\n'))
}, DELAY))
}
const doc = app.workspace.activeLeaf.view.sourceMode.cmEditor.doc
const curLineNum = doc.getCursor().line
const lineContent = doc.getLine(curLineNum)
if (!/^\{\{.+\}\}$/.test(lineContent)) {
return
}
const searchQuery = lineContent.replace('{{', '').replace('}}', '')
const replaceLine = content => doc.replaceRange(content, {line: curLineNum, ch: 0}, {line: curLineNum, ch: lineContent.length})
search(searchQuery)
getFoundFilenames(mapFunc, replaceLine)
}
config.forEach(e => {
app.commands.addCommand({
id: e.id,
name: e.name,
callback: () => initExpander(e.format),
hotkeys: []
})
})
}()
What’s next
I see potential for improvements like:
-
Done in 0.2
Save snippet and update results on command trigger - Expand all snippets on page by command
- Fix hacky search implementation when Search API will be provided
Latest changes
0.3
Available for official API Alpha
0.2.1
- [FIXED] Skips check for
---
if it’s last line in file.
0.2
- Saves a formula snippet and wraps it into reusable code block
```extender
{{tag:#tag}}
```
- You can use snippet to “refresh” already added search results
- You can hide snippet code block from preview using CSS snippet below
.language-extender {
display: none;
}
- [FIXED] Remove parent note from search results
0.1
- Proof of concept added