Merge pull request #11 from Mallioch/upgrade-to-general-rendering-and-settings

Upgrade to general rendering and settings
This commit is contained in:
Ryan Mellmer 2024-05-01 10:04:00 -07:00 committed by GitHub
commit 28c6365a22
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 375 additions and 82 deletions

View file

@ -2,9 +2,9 @@
![Obsidian Link Range Logo](https://user-images.githubusercontent.com/23059902/225677761-c36b01a6-6194-4d83-a130-a1d7561b8359.png)
This plugin brings wiki-link header range queries to Obsidian while retaining native backlink functionality. Supports standard wiki-links, hover preview, and embedded content wiki-links.
This plugin brings wiki-link header range queries to Obsidian while retaining native backlink functionality. Supports standard wiki-links or other user-defined patterns, hover preview, and embedded content wiki-links.
![demo](./docs/demo.gif)
![demo](./docs/demo-2.gif)
## Usage
To use, simply use the same wiki-link syntax you're used to in obsidian, but instead of specifying a single header, you can specify a header range separated by a separator string.
@ -13,9 +13,16 @@ For example, if I wanted a link to headers "Ingredients" through "Directions" in
`[[Recipe#Ingredients..Directions]]`
## Settings
| Setting | Description | Default Value |
| ------------------------------------------- | ------ | ------ |
| Heading Separator | Defines the separator to be used to define a link heading range. I.e. this is the ".." in example: `[[Note 1#h1..h2]]` | `..` |
| Alt Text Format | The alternate text format that gest shown when in read mode. Use $note for the note name placeholder, and $h1/$h2 for the header placeholders. | `$note:$h1-$h2` |
| End Inclusive | When turned on, the second header and the content following it (through the next header in the note) is shown. | true |
## Basic Settings
The default settings give you the functionality indicated above.
![default settings](./docs/default-settings.png)
## Advanced Settings Choices
If you prefer a different heading separator than the Wikilink syntax, you may change it.
Some will want to change the visual style of the heading separators. The
![advanced settings](./docs/advanced-settings.png)

BIN
docs/advanced-settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

BIN
docs/default-settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

BIN
docs/demo-2.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

View file

@ -4,7 +4,9 @@ import { ViewPlugin } from "@codemirror/view";
import { DEFAULT_SETTINGS, LinkRangeSettings, LinkRangeSettingTab } from 'src/settings';
import { linkRangePostProcessor } from 'src/markdownPostProcessor';
import { checkLink } from 'src/utils';
import { LinkRangeView } from 'src/linkRangeView';
import { LifePreviewEmbedReplacer } from 'src/livePreviewEmbedReplacer';
import { buildCMViewPlugin } from 'src/livePreviewDisplayView';
import { Prec } from "@codemirror/state";
export default class LinkRange extends Plugin {
settings: LinkRangeSettings;
@ -25,9 +27,12 @@ export default class LinkRange extends Plugin {
// wait for layout to be ready
this.app.workspace.onLayoutReady(() => {
this.registerEditorExtension(ViewPlugin.define((v) => {
return new LinkRangeView(this.settings, this.app)
return new LifePreviewEmbedReplacer(this.settings, this.app)
}));
const ext = Prec.lowest(buildCMViewPlugin(this.app, this.settings));
this.registerEditorExtension(ext);
const pagePreviewPlugin = this.app.internalPlugins.plugins["page-preview"];
console.log("LinkRange: Hooking into page-preview onHover calls")

View file

@ -1,10 +1,10 @@
{
"id": "link-range",
"name": "Link Range",
"version": "1.0.1",
"version": "2.0.0",
"minAppVersion": "0.15.0",
"description": "This Obsidian plugin brings ranged wiki-link support to Obsidian.",
"author": "Ryan Mellmer",
"authorUrl": "https://ryanmellmer.com",
"description": "This Obsidian plugin brings ranged link support to Obsidian.",
"author": "Ryan Mellmer and Eric Sowell",
"authorUrl": "https://ryanmellmer.com, https://ericsowell.com",
"isDesktopOnly": false
}

78
package-lock.json generated
View file

@ -7,6 +7,7 @@
"": {
"name": "obsidian-sample-plugin",
"version": "1.0.0",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@types/codemirror": "^5.60.7",
@ -14,6 +15,7 @@
"monkey-around": "^2.3.0"
},
"devDependencies": {
"@codemirror/language": "6.10.1",
"@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0",
@ -54,13 +56,13 @@
}
},
"node_modules/@codemirror/language": {
"version": "6.6.0",
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.6.0.tgz",
"integrity": "sha512-cwUd6lzt3MfNYOobdjf14ZkLbJcnv4WtndYaoBkbor/vF+rCNguMPK0IRtvZJG4dsWiaWPcK8x1VijhvSxnstg==",
"version": "6.10.1",
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz",
"integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==",
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
"@lezer/common": "^1.0.0",
"@codemirror/view": "^6.23.0",
"@lezer/common": "^1.1.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0",
"style-mod": "^4.0.0"
@ -87,17 +89,17 @@
}
},
"node_modules/@codemirror/state": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.2.0.tgz",
"integrity": "sha512-69QXtcrsc3RYtOtd+GsvczJ319udtBf1PTrr2KbLWM/e2CXUPnh0Nz9AUo8WfhSQ7GeL8dPVNUmhQVgpmuaNGA=="
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.0.tgz",
"integrity": "sha512-hm8XshYj5Fo30Bb922QX9hXB/bxOAVH+qaqHBzw5TKa72vOeslyGwd4X8M0c1dJ9JqxlaMceOQ8RsL9tC7gU0A=="
},
"node_modules/@codemirror/view": {
"version": "6.9.1",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.9.1.tgz",
"integrity": "sha512-bzfSjJn9dAADVpabLKWKNmMG4ibyTV2e3eOGowjElNPTdTkSbi6ixPYHm2u0ADcETfKsi2/R84Rkmi91dH9yEg==",
"version": "6.24.0",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.24.0.tgz",
"integrity": "sha512-zK6m5pNkdhdJl8idPP1gA4N8JKTiSsOz8U/Iw+C1ChMwyLG7+MLiNXnH/wFuAk6KeGEe33/adOiAh5jMqee03w==",
"dependencies": {
"@codemirror/state": "^6.1.4",
"style-mod": "^4.0.0",
"@codemirror/state": "^6.4.0",
"style-mod": "^4.1.0",
"w3c-keyname": "^2.2.4"
}
},
@ -524,9 +526,9 @@
"peer": true
},
"node_modules/@lezer/common": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.0.2.tgz",
"integrity": "sha512-SVgiGtMnMnW3ActR8SXgsDhw7a0w0ChHSYAyAUxxrOiJ1OqYWEKk/xJd84tTSPo1mo6DXLObAJALNnd0Hrv7Ng=="
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.1.tgz",
"integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ=="
},
"node_modules/@lezer/highlight": {
"version": "1.1.3",
@ -2374,9 +2376,9 @@
}
},
"node_modules/style-mod": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.0.0.tgz",
"integrity": "sha512-OPhtyEjyyN9x3nhPsu76f52yUGXiZcgvsrFVtvTkyGRQJ0XK+GPc6ov1z+lRpbeabka+MYEQxOYRnt5nF30aMw=="
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz",
"integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA=="
},
"node_modules/supports-color": {
"version": "7.2.0",
@ -2596,13 +2598,13 @@
}
},
"@codemirror/language": {
"version": "6.6.0",
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.6.0.tgz",
"integrity": "sha512-cwUd6lzt3MfNYOobdjf14ZkLbJcnv4WtndYaoBkbor/vF+rCNguMPK0IRtvZJG4dsWiaWPcK8x1VijhvSxnstg==",
"version": "6.10.1",
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz",
"integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==",
"requires": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
"@lezer/common": "^1.0.0",
"@codemirror/view": "^6.23.0",
"@lezer/common": "^1.1.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0",
"style-mod": "^4.0.0"
@ -2629,17 +2631,17 @@
}
},
"@codemirror/state": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.2.0.tgz",
"integrity": "sha512-69QXtcrsc3RYtOtd+GsvczJ319udtBf1PTrr2KbLWM/e2CXUPnh0Nz9AUo8WfhSQ7GeL8dPVNUmhQVgpmuaNGA=="
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.0.tgz",
"integrity": "sha512-hm8XshYj5Fo30Bb922QX9hXB/bxOAVH+qaqHBzw5TKa72vOeslyGwd4X8M0c1dJ9JqxlaMceOQ8RsL9tC7gU0A=="
},
"@codemirror/view": {
"version": "6.9.1",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.9.1.tgz",
"integrity": "sha512-bzfSjJn9dAADVpabLKWKNmMG4ibyTV2e3eOGowjElNPTdTkSbi6ixPYHm2u0ADcETfKsi2/R84Rkmi91dH9yEg==",
"version": "6.24.0",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.24.0.tgz",
"integrity": "sha512-zK6m5pNkdhdJl8idPP1gA4N8JKTiSsOz8U/Iw+C1ChMwyLG7+MLiNXnH/wFuAk6KeGEe33/adOiAh5jMqee03w==",
"requires": {
"@codemirror/state": "^6.1.4",
"style-mod": "^4.0.0",
"@codemirror/state": "^6.4.0",
"style-mod": "^4.1.0",
"w3c-keyname": "^2.2.4"
}
},
@ -2849,9 +2851,9 @@
"peer": true
},
"@lezer/common": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.0.2.tgz",
"integrity": "sha512-SVgiGtMnMnW3ActR8SXgsDhw7a0w0ChHSYAyAUxxrOiJ1OqYWEKk/xJd84tTSPo1mo6DXLObAJALNnd0Hrv7Ng=="
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.1.tgz",
"integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ=="
},
"@lezer/highlight": {
"version": "1.1.3",
@ -4200,9 +4202,9 @@
"peer": true
},
"style-mod": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.0.0.tgz",
"integrity": "sha512-OPhtyEjyyN9x3nhPsu76f52yUGXiZcgvsrFVtvTkyGRQJ0XK+GPc6ov1z+lRpbeabka+MYEQxOYRnt5nF30aMw=="
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz",
"integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA=="
},
"supports-color": {
"version": "7.2.0",

View file

@ -21,7 +21,8 @@
"obsidian": "latest",
"patch-package": "^6.5.1",
"tslib": "2.4.0",
"typescript": "4.7.4"
"typescript": "4.7.4",
"@codemirror/language": "6.10.1"
},
"dependencies": {
"@types/codemirror": "^5.60.7",

View file

@ -7,7 +7,8 @@ export async function replaceEmbed(app: App, embed: Node, settings: LinkRangeSet
const res = checkLink(app, embedHtml, settings, true, "src");
if (res !== null) {
const isLinkRange = res !== null && res.h2 !== undefined;
if (isLinkRange) {
const { vault } = app;
const foundNote : TFile | undefined = app.vault.getMarkdownFiles().filter(
x => x.basename == res.note

View file

@ -0,0 +1,147 @@
import { App } from 'obsidian';
import { Decoration, DecorationSet, EditorView, ViewPlugin, ViewUpdate, WidgetType } from "@codemirror/view";
import { RangeSetBuilder } from "@codemirror/state";
import { syntaxTree } from "@codemirror/language";
import { LinkRangeSettings } from "./settings";
import { findPatternForFile } from './utils';
class CharacterOverwriteWidget extends WidgetType {
private char: string;
constructor(char: string) {
super();
this.char = char;
}
toDOM() {
let el = document.createElement("span");
el.innerText = this.char;
el.style.textDecoration = 'underline';
return el;
}
}
export function buildCMViewPlugin(app: App, settings: LinkRangeSettings) {
const viewPlugin = ViewPlugin.fromClass(
class {
decorations: DecorationSet;
decoratedRanges: Array<{ from: number, to: number }>;
lastLocation: { from: number; to: number; };
constructor(view: EditorView) {
this.decorations = this.buildDecorations(view, null);
this.lastLocation = { from: 0, to: 0 };;
}
update(update: ViewUpdate) {
let currentLocation = {
from: update.state.selection.ranges[0].from,
to: update.state.selection.ranges[0].to
};
if (update.docChanged || update.viewportChanged) {
this.decorations = this.buildDecorations(update.view, currentLocation);
}
else {
// isRepeatUpdate - avoid repeated updates and renders
let isRepeatUpdate = this.lastLocation.from == currentLocation.from && this.lastLocation.to === currentLocation.to;
if (update.state.selection.ranges.length > 0 && !isRepeatUpdate) {
this.decorations = this.buildDecorations(update.view, currentLocation);
}
}
this.lastLocation = currentLocation;
}
buildDecorations(view: EditorView, location: { from: number, to: number } | null): DecorationSet {
let builder = new RangeSetBuilder<Decoration>();
const lastPassDecoratedRanges: Array<{from: number, to: number}> = this.decoratedRanges;
this.decoratedRanges = [];
const inLastPass = function(nodeStart: number, index: number | undefined): boolean {
if (index === undefined) {
return false;
}
for (let i in lastPassDecoratedRanges) {
const rng = lastPassDecoratedRanges[i];
if (rng.from == nodeStart && index >= rng.from && index <= rng.to) {
return true;
}
}
return false;
}
for (let {from, to} of view.visibleRanges) {
syntaxTree(view.state).iterate({
from,
to,
enter: (node) => {
// Big thanks to Supercharged Links. Used your code as an example! https://github.com/mdelobelle/obsidian_supercharged_links
// TODO: there used to be a different way than splitting the name but the api for this has
// changed in CodeMirror. Should look into it more.
const tokenProps = node.type.name.split('_');
if (tokenProps) {
const props = new Set(tokenProps);
const isLink = props.has("hmd-internal-link");
const isAlias = props.has("link-alias");
const isPipe = props.has("link-alias-pipe");
const isMDUrl = props.has('url');
if (isLink && !isAlias && !isPipe || isMDUrl) {
let linkText = view.state.doc.sliceString(node.from, node.to);
const indexOfHeaderMarker = linkText.indexOf('#');
const indexOfHeaderMarkerInDoc = indexOfHeaderMarker + node.from;
if (indexOfHeaderMarkerInDoc >= node.from && indexOfHeaderMarkerInDoc <= node.to) {
if (!inLastPass(node.from - 2, location?.from)) {
const fileName = linkText.substring(0, indexOfHeaderMarker);
const pattern = findPatternForFile(fileName, settings);
// Overrides the heading character ('#') to what is specified in settings
if (pattern.headingVisual !== '') {
let overrideP2HWidget = Decoration.widget({
widget: new CharacterOverwriteWidget(pattern.headingVisual),
});
builder.add(indexOfHeaderMarkerInDoc, indexOfHeaderMarkerInDoc + 1, overrideP2HWidget);
}
// Overrides the header separator character to what is specified in settings
if (pattern.headingSeparatorVisual !== '') {
const indexOfRangeMarker = linkText.indexOf(settings.headingSeparator) + node.from;
if (indexOfRangeMarker >= node.from && indexOfRangeMarker <= node.to) {
let overrideH2HWidget = Decoration.widget({
widget: new CharacterOverwriteWidget(pattern.headingSeparatorVisual),
});
builder.add(indexOfRangeMarker, indexOfRangeMarker + settings.headingSeparator.length, overrideH2HWidget);
}
}
}
this.decoratedRanges.push({
from: node.from - 2,
to: node.to + 2
});
}
}
}
}
})
}
const bufferedDecs = builder.finish();
return bufferedDecs;
}
},
{
decorations: v => v.decorations
}
);
return viewPlugin;
}

View file

@ -4,7 +4,7 @@ import { LinkRangeSettings } from "./settings";
import { RangeSetBuilder } from "@codemirror/state";
import { replaceEmbed } from "./embeds";
export class LinkRangeView implements PluginValue {
export class LifePreviewEmbedReplacer implements PluginValue {
decorations: DecorationSet = Decoration.none;
settings: LinkRangeSettings;
app: App;

View file

@ -1,17 +1,37 @@
import LinkRange from "main";
import { App, PluginSettingTab, Setting } from "obsidian";
import { App, PluginSettingTab, Setting, ButtonComponent } from "obsidian";
import { postProcessorUpdate } from "./utils";
export interface Pattern {
headingVisual: string;
headingSeparatorVisual: string;
path: string;
}
export interface LinkRangeSettings {
headingSeparator: string;
altFormat: string;
endInclusive: boolean;
altFormat: string; // This is for backwards compatibility
settingsVersion: string;
patterns: [Pattern]
getDefaultPattern() : Pattern
}
export const DEFAULT_SETTINGS: LinkRangeSettings = {
headingSeparator: '..',
altFormat: '$note:$h1-$h2',
endInclusive: true
endInclusive: true,
altFormat: '',
settingsVersion: 'v2',
patterns: [{ headingVisual: '..', headingSeparatorVisual: '-', path: '/' }],
getDefaultPattern() {
const first = this.patterns[0];
if (!first) {
return { headingVisual: ':', headingSeparatorVisual: '-', path: '/' }
}
return first;
},
}
export class LinkRangeSettingTab extends PluginSettingTab {
@ -20,6 +40,33 @@ export class LinkRangeSettingTab extends PluginSettingTab {
constructor(app: App, plugin: LinkRange) {
super(app, plugin);
this.plugin = plugin;
this.migrateOldSettings();
}
migrateOldSettings() {
const stgs = this.plugin.settings;
const hasV1Settings = stgs.altFormat != undefined && stgs.altFormat.length > 0;
if (hasV1Settings) {
// default altFormat string: `$note:$h1-$h2`
const altFormat = stgs.altFormat;
const indexOfNote = altFormat.indexOf('$note');
const indexOfH1 = altFormat.indexOf('$h1');
const indexOfH2 = altFormat.indexOf('$h2');
const formatIsValid = indexOfNote === 0 && indexOfH1 !== -1 && indexOfH2 !== -2;
if (formatIsValid) {
const firstValue = altFormat.substring('$note'.length, indexOfH1);
const secondValue = altFormat.substring(indexOfH1 + '$h1'.length, indexOfH2);
stgs.patterns = [{ headingVisual: firstValue, headingSeparatorVisual: secondValue, path: '' }]
}
stgs.altFormat = '';
this.plugin.saveSettings();
}
}
display(): void {
@ -27,11 +74,11 @@ export class LinkRangeSettingTab extends PluginSettingTab {
containerEl.empty();
containerEl.createEl('h2', {text: 'Settings for link-range plugin'});
this.createH2('Settings for link-range plugin')
new Setting(containerEl)
.setName('Heading Separator')
.setDesc('Defines the separator to be used to define a link heading range. Defaults to ".." (i.e. [[Note Name#h1..h2]])')
.setDesc('Defines the separator to be used to define a link heading range. Defaults to ".." (e.g. [[Note Name#h1..h2]])')
.addText(text => text
.setPlaceholder('Enter a separator string (defaults to ..)')
.setValue(this.plugin.settings.headingSeparator)
@ -41,18 +88,6 @@ export class LinkRangeSettingTab extends PluginSettingTab {
postProcessorUpdate(this.app)
}));
new Setting(containerEl)
.setName('Alt Text Format')
.setDesc('Defines the alternate text format that gets shown in read mode. Use $note for the note placeholder and $h1/$h2 for the header placeholders')
.addText(text => text
.setPlaceholder('Enter an alt format')
.setValue(this.plugin.settings.altFormat)
.onChange(async (value) => {
this.plugin.settings.altFormat = value;
await this.plugin.saveSettings();
postProcessorUpdate(this.app)
}));
new Setting(containerEl)
.setName('End Inclusive')
.setDesc('Whether or not the end heading should be inclusive or exclusive')
@ -63,5 +98,86 @@ export class LinkRangeSettingTab extends PluginSettingTab {
await this.plugin.saveSettings();
postProcessorUpdate(this.app)
}));
new Setting(this.containerEl)
.setName("Add a New Visual Pattern")
.setDesc("Add new pattern to match files in a directory. The first value will change the visual for the heading in a link. The second value will change the visual for separator. The third specifies the folder in which the files must be to match. The first match, starting bottom up, will be applied. Therefore, the first is the default pattern.")
.addButton((button: ButtonComponent) => {
button
.setTooltip("Add new pattern to match files in a directory.")
.setButtonText("+")
.setCta()
.onClick(() => {
this.plugin.settings.patterns.push({
headingVisual: '',
headingSeparatorVisual: '',
path: ''
});
this.plugin.saveSettings();
this.display();
});
});
this.plugin.settings.patterns.forEach(
(pattern, index) => {
const s = new Setting(this.containerEl)
.addText(text => text
.setPlaceholder('Enter a heading override')
.setValue(pattern.headingVisual)
.onChange(async (value) => {
pattern.headingVisual = value;
await this.plugin.saveSettings();
postProcessorUpdate(this.app)
}))
.addText(text => text
.setPlaceholder('Enter a separator override')
.setValue(pattern.headingSeparatorVisual)
.onChange(async (value) => {
pattern.headingSeparatorVisual = value;
await this.plugin.saveSettings();
postProcessorUpdate(this.app)
}))
.addText(text => text
.setPlaceholder(index === 0 ? '(global)' : 'Enter a path')
.setValue(pattern.path)
.setDisabled(index === 0)
.onChange(async (value) => {
pattern.path = value;
await this.plugin.saveSettings();
postProcessorUpdate(this.app)
}));
if (index === 0) {
s.addExtraButton((cb) => {
cb.setIcon("lock")
.setTooltip("This pattern is the default and cannot be completed");
});
}
else {
if (index !== 0) {
s.addExtraButton((cb) => {
cb.setIcon("cross")
.setTooltip("Delete")
.onClick(() => {
this.plugin.settings.patterns.splice(
index,
1
);
this.plugin.saveSettings();
this.display();
});
});
}
}
}
);
}
createH2(text: string) {
const {containerEl} = this;
containerEl.createEl('h2', { text: text });
}
}

View file

@ -1,5 +1,5 @@
import { App, TFile } from "obsidian";
import { LinkRangeSettings } from "./settings";
import { LinkRangeSettings, Pattern } from "./settings";
export interface ParsedLink {
note: string;
@ -26,11 +26,6 @@ export function checkLinkText(href: string, settings: LinkRangeSettings): Parsed
const header = matches[2];
const split = header.split(settings.headingSeparator);
// our ranged link format is "#h1..h2"
if (split.length < 2) {
return null;
}
const note = matches[1];
const h1 = split[0];
const h2 = split[1];
@ -41,9 +36,17 @@ export function checkLinkText(href: string, settings: LinkRangeSettings): Parsed
altText = matches[3]
}
else {
altText = settings.altFormat.replace(NOTE_PLACEHOLDER, note)
altText = altText.replace(H1_PLACEHOLDER, h1)
altText = altText.replace(H2_PLACEHOLDER, h2)
let pattern = findPatternForFile(note, settings);
let headingVisual = pattern.headingVisual === '' ? '#' : pattern.headingVisual;
let headingSeparatorVisual = pattern.headingSeparatorVisual === '' ? settings.headingSeparator : pattern.headingSeparatorVisual;
if (h2 !== undefined) {
altText = `${note}${headingVisual}${h1}${headingSeparatorVisual}${h2}`
}
else {
altText = `${note}${headingVisual}${h1}`
}
}
return { note, h1, h2, altText }
@ -130,3 +133,14 @@ export function postProcessorUpdate(app: App) {
app.workspace.updateOptions();
}
export function findPatternForFile(fileName: string, settings: LinkRangeSettings) : Pattern {
const file = app.vault.getFiles().find((file) => file.basename === fileName);
let pattern = [...settings.patterns].reverse().find((pattern: Pattern) => file?.path.startsWith(pattern.path))
if (!pattern) {
pattern = settings.getDefaultPattern();
}
return pattern;
}