obsidian-omnisearch icon indicating copy to clipboard operation
obsidian-omnisearch copied to clipboard

[Feature request] Insert link to header and block

Open jakemkc opened this issue 2 years ago • 9 comments

Is your feature request related to a problem? Please describe. Sometimes I have a longer document (Doc A), with numerous headers and each with numerous paragraphs inside.

I rely heavily on the plugin Outliner to keep it easily navigated (fold, unfold ...).

Using Omnisearch, If I am working on another document (Doc B) and want to insert a reference link to a paragraph in Doc A, I can search the keywords in the paragraph and insert a link to Doc A in Doc B.

However, since it's a long document, just inserting a link to Doc A is not that helpful.

Describe the solution you'd like In the example above, I hope Omnisearch can give us quick options to insert:

  1. header link of the target paragraph to Doc A and
  2. block link of the target paragraph to Doc A.

Describe alternatives you've considered Quick switcher ++ doesn't seem to be able to insert header link.

Now I need to search with Omnisearch first, then open the target document and remember header name. Then I use the default approach [[ to search for the target document with header.

Additional context

jakemkc avatar Nov 03 '22 01:11 jakemkc

+1 it would be a convenient feature.

fedorbass avatar Mar 19 '23 13:03 fedorbass

I really want this feature, so I think I might try to implement it. @scambier Could you please give me some advice on this idea.

theotheo avatar Oct 04 '23 14:10 theotheo

Sure thing :)

Thanks for making me look, it's probably easier than I initially thought.

1) Match an Omnisearch result with a block or a heading on the relevant note.

Fortunately, all Omnisearch results already have an offset that is used to display the relevant excerpt, and to position the cursor at the right place when opening a result.

https://github.com/scambier/obsidian-omnisearch/blob/e59f252cb4e175c259c3e86976838587632cfe50/src/globals.ts#L78-L85 https://github.com/scambier/obsidian-omnisearch/blob/e59f252cb4e175c259c3e86976838587632cfe50/src/globals.ts#L68-L71

And Obsidian's metadata for each note also lists blocks (sections) and headings, so it should be relatively trivial to match an Omnisearch result with the relevant block or heading: image

2) Create a link from the offset value of the block/heading.

Omnisearch already uses generateMarkdownLink() to create a link to a note, and you "just" need to give it a subpath: a #heading or a ^blockId.

For headings, it's quite straightforward: extract the heading text from the position.start/end values, clean it with stripHeading(), and you should be good.

For blocks, it looks harder. If the block already has an id, you'll find it directly in the metadata (see SectionCache or CachedMetadata.blocks in obsidian api). Otherwise, I haven't found anything to generate an id. I'll ask on Discord and/or the forum and see if I find something.

3) Update the UI

Now for the UI, I don't have any idea more original or cleaner than "let's add 2 shortcuts to insert a link to the closest heading or block" 😅 Probably some variations of alt ↵


You're welcome to give it a go if you wish 👍 Don't hesitate to ask for help if needed

scambier avatar Oct 04 '23 20:10 scambier

@scambier wow! Thank you so much for such a detailed description. With you help I've yet implemented header link insertion! I'll try with the block link later.

For now it's more interesting to understand about matches. Tell me please, is it possible to output each match as a separate element in the interface?

I mean, when I plan to insert a link to a file, it may not be so important for me to see a specific occurrence of a search term among the possible dozens. But when I want to insert a link to header, it is important for me to scroll through all to some very specific one. I.e. is it possible to disaggregate the matches array or in the other words how to make the matches array flat?

theotheo avatar Oct 04 '23 22:10 theotheo

Great!

The aggregation is done at the last moment, right before the results are displayed here:

https://github.com/scambier/obsidian-omnisearch/blob/e59f252cb4e175c259c3e86976838587632cfe50/src/components/ModalInFile.svelte#L64-L71 https://github.com/scambier/obsidian-omnisearch/blob/e59f252cb4e175c259c3e86976838587632cfe50/src/components/ModalInFile.svelte#L155-L174

Matches are aggregated on the same result when they're fewer than 300 chars apart; roughly when they can be displayed on the same excerpt. (Now that I think about it, aggregation could probably be reworked to group results on the same block 🤔)

If you're willing to submit a PR for this feature request, I'd like to keep this behavior. If de-aggregation is a must for you, can I suggest adding an opt-out setting to keep aggregation as the default?

scambier avatar Oct 05 '23 06:10 scambier

Thank you! I'll try to add de-aggregation.

By the way, there are a couple of questions:

  1. ) I encountered a situation where there is no heading in a note. In such cases, I believe it would be appropriate to simply reuse the InsertLink function. What are your thoughts on this?
  2. I have some doubts regarding these lines: https://github.com/scambier/obsidian-omnisearch/blob/e59f252cb4e175c259c3e86976838587632cfe50/src/components/ModalVault.svelte#L221-L225

Why is a separate behavior needed for this case? Is it possible that a link will be inserted without the file? How should these conditions be interpreted?

theotheo avatar Oct 05 '23 09:10 theotheo

  1. Yup, seems like the correct behavior

  2. The split logic here is a remnant of a removed feature: Omnisearch could index non-created files (that only "exist" as links), hence the need to handle this particular case. I removed the feature a year ago, but it could be re-added at some point (see #133 #227)

scambier avatar Oct 05 '23 15:10 scambier

There's no api to insert blockIds, but it's doable by hand.

See here for an example. (the parameter sec is the SectionCache of the block that you want to insert a link to. You can get it by something like app.metadataCache.getFileCache(file)?.sections?.find(sec => sec.position.start.line == line)) https://github.com/RyotaUshio/obsidian-math-booster/blob/24ffc2b4b984f2ef7d24629bc2932f39db44b8c9/src/utils.ts#L217

Courtesy of Ush on Discord

scambier avatar Oct 06 '23 17:10 scambier