create better structure inspired by ObsidianTweaks'
This commit is contained in:
94
src/Utils.ts
Normal file
94
src/Utils.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
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(' ')
|
||||
}
|
||||
Reference in New Issue
Block a user