94 lines
2.7 KiB
TypeScript
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(' ')
|
|
} |