create better structure inspired by ObsidianTweaks'
This commit is contained in:
139
src/ToggleHeading.ts
Normal file
139
src/ToggleHeading.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import {
|
||||
App,
|
||||
Editor,
|
||||
EditorChange,
|
||||
EditorPosition,
|
||||
EditorTransaction,
|
||||
MarkdownView,
|
||||
} from 'obsidian'
|
||||
import BindThemPlugin from './main'
|
||||
import { Heading } from './Entities'
|
||||
import { getMainSelection, selectionToRange } from './Utils'
|
||||
import { HEADING_REGEX } from './Constants'
|
||||
|
||||
/**
|
||||
* ToggleHeading provides commands to toggle markdown heading levels
|
||||
*/
|
||||
export class ToggleHeading {
|
||||
public app: App
|
||||
private plugin: BindThemPlugin
|
||||
|
||||
constructor(app: App, plugin: BindThemPlugin) {
|
||||
this.app = app
|
||||
this.plugin = plugin
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a change to set the heading level for a specific line
|
||||
*/
|
||||
private setHeading(
|
||||
editor: Editor,
|
||||
heading: Heading,
|
||||
line: number
|
||||
): EditorChange {
|
||||
const headingStr = '#'.repeat(heading)
|
||||
const text = editor.getLine(line)
|
||||
const matches = HEADING_REGEX.exec(text)!
|
||||
|
||||
const from: EditorPosition = {
|
||||
line: line,
|
||||
ch: 0,
|
||||
}
|
||||
const to: EditorPosition = {
|
||||
line: line,
|
||||
ch: matches[1].length + matches[2].length,
|
||||
}
|
||||
|
||||
const replacementStr = heading === Heading.NORMAL ? '' : headingStr + ' '
|
||||
|
||||
return {
|
||||
from: from,
|
||||
to: to,
|
||||
text: replacementStr,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the heading level of a specific line
|
||||
*/
|
||||
private getHeadingOfLine(editor: Editor, line: number): Heading {
|
||||
const text = editor.getLine(line)
|
||||
const matches = HEADING_REGEX.exec(text)!
|
||||
|
||||
return matches[1].length as Heading
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle heading for the current selection(s)
|
||||
* If the line already has the specified heading, remove it (set to NORMAL)
|
||||
*/
|
||||
public toggleHeading(
|
||||
editor: Editor,
|
||||
view: MarkdownView,
|
||||
heading: Heading
|
||||
): void {
|
||||
const selections = editor.listSelections()
|
||||
const mainSelection = getMainSelection(editor)
|
||||
const mainRange = selectionToRange(mainSelection)
|
||||
|
||||
const mainHeading = this.getHeadingOfLine(editor, mainRange.from.line)
|
||||
const targetHeading = heading === mainHeading ? Heading.NORMAL : heading
|
||||
|
||||
const changes: Array<EditorChange> = []
|
||||
|
||||
// Collect unique lines from all selections
|
||||
const linesToSet: Set<number> = new Set()
|
||||
for (const selection of selections) {
|
||||
const range = selectionToRange(selection)
|
||||
for (let line = range.from.line; line <= range.to.line; line++) {
|
||||
linesToSet.add(line)
|
||||
}
|
||||
}
|
||||
|
||||
// Create changes for each line
|
||||
for (const line of linesToSet) {
|
||||
if (editor.getLine(line) === '') {
|
||||
continue
|
||||
}
|
||||
changes.push(this.setHeading(editor, targetHeading, line))
|
||||
}
|
||||
|
||||
const transaction: EditorTransaction = {
|
||||
changes: changes,
|
||||
}
|
||||
|
||||
editor.transaction(transaction, 'ToggleHeading' + heading.toString())
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle heading with stripping of inline formatting
|
||||
* This removes bold/italic formatting when toggling headings
|
||||
*/
|
||||
public toggleHeadingWithStrip(
|
||||
editor: Editor,
|
||||
view: MarkdownView,
|
||||
level: number
|
||||
): void {
|
||||
const cursor = editor.getCursor()
|
||||
const line = editor.getLine(cursor.line)
|
||||
const match = line.match(/^(#+)\s(.*)/)
|
||||
|
||||
let content = line
|
||||
let currentLevel = 0
|
||||
|
||||
if (match) {
|
||||
currentLevel = match[1].length
|
||||
content = match[2]
|
||||
}
|
||||
|
||||
// Strip bold and italic formatting
|
||||
content = content
|
||||
.replace(/(\*\*|__)(.*?)\1/g, '$2')
|
||||
.replace(/(\*|_)(.*?)\1/g, '$2')
|
||||
|
||||
editor.setLine(
|
||||
cursor.line,
|
||||
currentLevel === level ? content : '#'.repeat(level) + ' ' + content
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user