From 74e7312f1703addca2b421d2e5ea34e4f82febc1 Mon Sep 17 00:00:00 2001 From: Ruben Nitsche Date: Sat, 7 Jan 2023 14:56:59 +0100 Subject: [PATCH 1/2] added automatic re-indexing --- main.ts | 67 ++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/main.ts b/main.ts index 8c931af..3694e5c 100644 --- a/main.ts +++ b/main.ts @@ -3,6 +3,7 @@ import { MarkdownView, Plugin } from 'obsidian'; export default class MyPlugin extends Plugin { private detailLineRegex = /\[\^(\d+)\]\:/; + private reOnlyDetails = /\[\^(\d+)\]\:/gi; private reOnlyMarkers = /\[\^(\d+)\]/gi; private numericalRe = /(\d+)/ @@ -27,12 +28,11 @@ export default class MyPlugin extends Plugin { const doc = mdView.sourceMode.cmEditor; const cursorPosition = doc.getCursor(); const lineText = doc.getLine(cursorPosition.line); - const markdownText = mdView.data; if (this.shouldJumpFromDetailToMarker(lineText, cursorPosition, doc)) return; if (this.shouldJumpFromMarkerToDetail(lineText, cursorPosition, doc)) return; - return this.shouldCreateNewFootnote(lineText, cursorPosition, doc, markdownText); + return this.shouldCreateNewFootnote(lineText, cursorPosition, doc); } private shouldJumpFromDetailToMarker(lineText: string, cursorPosition: CodeMirror.Position, doc: CodeMirror.Editor) { @@ -116,43 +116,56 @@ export default class MyPlugin extends Plugin { return false; } - private shouldCreateNewFootnote(lineText: string, cursorPosition: CodeMirror.Position, doc: CodeMirror.Editor, markdownText: string) { + private shouldCreateNewFootnote(lineText: string, cursorPosition: CodeMirror.Position, doc: CodeMirror.Editor) { // create new footnote with the next numerical index - let matches = markdownText.match(this.reOnlyMarkers); let numbers: Array = []; let currentMax = 1; + let match - if (matches != null) { - for (let i = 0; i <= matches.length - 1; i++) { - let match = matches[i]; - match = match.replace("[^", ""); - match = match.replace("]", ""); - let matchNumber = Number(match); - numbers[i] = matchNumber; - if (matchNumber + 1 > currentMax) { - currentMax = matchNumber + 1; - } + // search highest footnote ID of footnotes before the cursor position + let beforeCursor = doc.getRange({line: 0, ch: 0}, doc.getCursor()); + + while ((match = this.reOnlyMarkers.exec(beforeCursor)) !== null) { + if (Number(match[1]) + 1 > currentMax) { + currentMax = Number(match[1]) + 1; } } - let footNoteId = currentMax; - let footnoteMarker = `[^${footNoteId}]`; - let linePart1 = lineText.substr(0, cursorPosition.ch) - let linePart2 = lineText.substr(cursorPosition.ch); - let newLine = linePart1 + footnoteMarker + linePart2 - doc.replaceRange(newLine, {line: cursorPosition.line, ch: 0}, {line: cursorPosition.line, ch: lineText.length}) + // increment footnote IDs after the cursor position + // only IDs bigger or equal to the new ID will be incremented in case an earlier one is used more than once + // this also includes the footnote details which is fine since we need to increment those anyways + let afterCursor = doc.getRange(doc.getCursor(), {line: doc.lastLine(), ch: doc.getLine(doc.lastLine()).length}); - let lastLine = doc.getLine(doc.lineCount() - 1); + while ((match = this.reOnlyMarkers.exec(afterCursor)) !== null) { + if (Number(match[1]) >= footNoteId) { + const p = doc.offsetToPos(beforeCursor.length+match.index); + doc.replaceRange(String(Number(match[1])+1), {line: p.line, ch: p.ch + 2}, {line: p.line, ch: p.ch + 3}); + } + } + // add new footnote marker + let footnoteMarker = `[^${footNoteId}]`; + doc.replaceRange(footnoteMarker, doc.getCursor()) + + // add new footnote detail + // search for the correct place first let footnoteDetail = `[^${footNoteId}]: `; - if (lastLine.length > 0) { - doc.replaceRange("\n" + footnoteDetail, {line: doc.lineCount(), ch: 0}) - } else { - doc.replaceRange(footnoteDetail, {line: doc.lineCount(), ch: 0}) + while ((match = this.reOnlyDetails.exec(afterCursor)) !== null) { + if (Number(match[1]) == footNoteId) { + const p = doc.offsetToPos(beforeCursor.length + match.index + footnoteMarker.length); + doc.replaceRange(footnoteDetail + '\n', {line: p.line, ch: 0}); + doc.setCursor({line: p.line, ch: footnoteDetail.length}); + return; + } } - - doc.setCursor({line: doc.lineCount(), ch: footnoteDetail.length}); + // if no footnote details where found just add a line to the bottom of the document + if (footNoteId == 1) { + // this is just for aesthetics + doc.replaceRange('\n', {line: doc.lastLine(), ch: doc.getLine(doc.lastLine()).length}); + } + doc.replaceRange('\n' + footnoteDetail, {line: doc.lastLine(), ch: doc.getLine(doc.lastLine()).length}); + doc.setCursor({line: doc.lastLine(), ch: footnoteDetail.length}); } } \ No newline at end of file From a6246260501b376779fc4c32a6934c9987114edd Mon Sep 17 00:00:00 2001 From: Ruben Nitsche Date: Sat, 7 Jan 2023 15:04:44 +0100 Subject: [PATCH 2/2] adjusted README --- README.md | 93 ++++--------------------------------------------------- 1 file changed, 6 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index 1a39523..26cc16c 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ This hotkey lets you: - Places your cursor so you can fill in the details quickly - Jump from your footnote TO the footnote detail - Jump from your footnote detail BACK to the footnote +- New in January 2022: Automatic re-indexing of footnotes ![Overview](https://github.com/akaalias/obsidian-footnotes/blob/master/basic.gif?raw=true) @@ -36,6 +37,9 @@ I personally use Command+Shift+6 because "6" on - And a new footnote details marker (e.g. `[^2]: `) is inserted on the last line of the document - And my cursor is now placed at the end of the detail marker (e.g. `[^2]: ▊`) +### Scenario: Footnote is inserted in between existing footnotes +- All existing footnotes get updated and the new one inserted as in the previous Scenarios + ### Scenario: Jumping TO a footnote detail - Given I'm on a footnote detail line (e.g. `[^1]: ▊`) - When I hit `my footnote hotkey` @@ -47,93 +51,8 @@ I personally use Command+Shift+6 because "6" on - Then my cursor is placed to the right of the footnote (e.g. `[^1]: ▊`) ### Known Limitations or Untested Scenarios -#### Indices are not updated -Inserting new footnote in-between two existing footnotes will insert the next numeric index (e.g. `1, 3, 2`). - -It will NOT update the indices according to their natural order (e.g. `1, 2, 3`). - -```markdown -Example sentence[^1] with two▊ footnotes[^2] already. - -[^1]: Foo -[^2]: Bar -``` - -After insertion: - -```markdown -Example sentence[^1] with two[^3] footnotes[^2] already. - -[^1]: Foo -[^2]: Bar -[^3]: Baz -``` - -See "Automatically Re-Index Footnotes" below for a proposed feature - -## Future Possible Feature Ideas -### Automatically Re-Index Footnotes -Re-index and re-sort all footnotes when you insert a new one in-between one or more existing numbered footnotes: - -```markdown -Example sentence[^1] with two▊ footnotes[^2] already. - -[^1]: Foo -[^2]: Bar -``` -#### Base Scenario -- Given there are two footnotes already -- When I enter a new footnote in-between those two -- Then the NEW footnote gets the index "2" -- And the previously second footnote gets the index "3" -- And the NEW footnote detail is inserted as the second entry at the bottom -- And the previously second footnote detail at the bottom is updated to be "3" -- And the previously second footnote detail at the bottom is updated to be in third position - -```markdown -Example sentence[^1] with two[^2] footnotes[^3] already. - -[^1]: Foo -[^2]: Baz -[^3]: Bar▊ -``` - -#### Edge Cases to consider ("What if...?") -##### What if... new footnote is inserted before the first footnote? - ```markdown - Some sentence▊ with existing note[^1] - - [^1]: Details - ``` -##### What if... text has the same footnote at several places? - ```markdown - Some sentence with existing note[^1] and the same▊ footnote re-appears later[^1]. - - - [^1]: Details - ``` -##### What if...Footnote details are spread across the text? - ```markdown - Some sentence with existing note[^1] some more text▊ - - [^1]: Inline footnote details - - Another text part▊ - ``` -##### What if... the footnote details are multi-line on the bottom? - ```markdown - Some sentence with existing note[^1] some more text▊ - - [^1]: The details that - Span across - Multiple lines - ``` -##### What if... there are non-numeric footnotes in the text? - ```markdown - Some sentence with existing note[^✝] some more text▊ - - [^✝]: Details - ``` +#### Manually Deleting Footnotes or Copy-Pasting Text Chunks +Because the plugin only gets triggered when it is manually invoked for example by pressing the hotkey, this is an unsolved issue. ## Background This plugin is based on the great idea by [jacob.4ristotle](https://forum.obsidian.md/u/jacob.4ristotle/summary) posted in the ["Footnote Shortcut"](https://forum.obsidian.md/t/footnote-shortcut/8872) thread.