Files
BindThem/src/Utils.ts
olivier 7022dadd83
Some checks failed
Node.js build / build (20.x) (push) Has been cancelled
Node.js build / build (22.x) (push) Has been cancelled
create better structure inspired by ObsidianTweaks'
2026-02-26 15:19:52 -05:00

94 lines
2.7 KiB
TypeScript

import { Editor, EditorRange, EditorSelection } from 'obsidian'
// ============================================================
// Selection Utilities
// ============================================================
/**
* Get the main (primary) selection from the editor
*/
export function getMainSelection(editor: Editor): EditorSelection {
return {
anchor: editor.getCursor('anchor'),
head: editor.getCursor('head'),
}
}
/**
* Convert a selection to a range (from/to with sorted positions)
*/
export function selectionToRange(selection: EditorSelection): EditorRange {
const sortedPositions = [selection.anchor, selection.head].sort((a, b) => {
if (a.line !== b.line) return a.line - b.line
return a.ch - b.ch
})
return {
from: sortedPositions[0],
to: sortedPositions[1],
}
}
/**
* Convert a selection to encompass full lines
*/
export function selectionToLine(editor: Editor, selection: EditorSelection): EditorSelection {
const range = selectionToRange(selection)
return {
anchor: { line: range.from.line, ch: 0 },
head: { line: range.to.line, ch: editor.getLine(range.to.line).length },
}
}
// ============================================================
// Text Utilities
// ============================================================
/**
* Get leading whitespace (indentation) from a line
*/
export function getLeadingWhitespace(lineContent: string): string {
const indentation = lineContent.match(/^\s+/)
return indentation ? indentation[0] : ''
}
/**
* Check if a character is a letter or digit
*/
export function isLetterOrDigit(char: string): boolean {
return /\p{L}\p{M}*/u.test(char) || /\d/.test(char)
}
/**
* Get the word range at a given position
*/
export function wordRangeAtPos(
pos: { line: number; ch: number },
lineContent: string
): { anchor: { line: number; ch: number }; head: { line: number; ch: number } } {
let start = pos.ch
let end = pos.ch
while (start > 0 && isLetterOrDigit(lineContent.charAt(start - 1))) start--
while (end < lineContent.length && isLetterOrDigit(lineContent.charAt(end))) end++
return { anchor: { line: pos.line, ch: start }, head: { line: pos.line, ch: end } }
}
// ============================================================
// Case Transformation Utilities
// ============================================================
const LOWERCASE_ARTICLES = ['the', 'a', 'an']
/**
* Convert text to title case
*/
export function toTitleCase(text: string): string {
return text
.split(/(\s+)/)
.map((w, i, all) => {
if (i > 0 && i < all.length - 1 && LOWERCASE_ARTICLES.includes(w.toLowerCase())) {
return w.toLowerCase()
}
return w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()
})
.join(' ')
}