Files
spencermountain-compromise/src/1-one/change/api/replace.js
T

135 lines
3.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const dollarStub = /\$[0-9a-z]+/g
const fns = {}
// case logic
const isTitleCase = (str) => /^\p{Lu}[\p{Ll}'’]/u.test(str) || /^\p{Lu}$/u.test(str)
const toTitleCase = (str) => str.replace(/^\p{Ll}/u, x => x.toUpperCase())
const toLowerCase = (str) => str.replace(/^\p{Lu}/u, x => x.toLowerCase())
// doc.replace('foo', (m)=>{})
const replaceByFn = function (main, fn, keep) {
main.forEach(m => {
const out = fn(m)
m.replaceWith(out, keep)
})
return main
}
// support 'foo $0' replacements
const subDollarSign = function (input, main) {
if (typeof input !== 'string') {
return input
}
const groups = main.groups()
input = input.replace(dollarStub, a => {
const num = a.replace(/\$/, '')
if (Object.hasOwn(groups, num)) {
return groups[num].text()
}
return a
})
return input
}
fns.replaceWith = function (input, keep = {}) {
let ptrs = this.fullPointer
// support keep-all option
if (keep === true) {
keep = {
tags: true,
case: true,
possessives: true,
}
}
const main = this
this.uncache()
if (typeof input === 'function') {
return replaceByFn(main, input, keep)
}
const terms = main.docs[0]
if (!terms) return main
const isOriginalPossessive = keep.possessives && terms.at(-1).tags.has('Possessive')
const isOriginalTitleCase = keep.case && isTitleCase(terms[0].text)
// support 'foo $0' replacements
input = subDollarSign(input, main)
const original = this.update(ptrs)
// soften-up pointer
ptrs = ptrs.map(ptr => ptr.slice(0, 3))
// original.freeze()
let oldTags = (original.docs[0] || []).map(term => Array.from(term.tags))
const originalPre = original.docs[0][0].pre
const originalPost = original.docs[0].at(-1).post
// slide this in
if (typeof input === 'string') {
input = this.fromText(input).compute('id')
}
main.insertAfter(input)
// are we replacing part of a contraction?
if (original.has('@hasContraction') && main.contractions) {
const more = main.grow('@hasContraction+')
more.contractions().expand()
}
// delete the original terms
main.delete(original) //science.
// keep "John's"
if (isOriginalPossessive) {
const tmp = main.docs[0]
const term = tmp.at(-1)
if (!term.tags.has('Possessive')) {
term.text += "'s"
term.normal += "'s"
term.tags.add('Possessive')
}
}
// try to keep some pre-punctuation
if (originalPre && main.docs[0]) {
main.docs[0][0].pre = originalPre
}
// try to keep any post-punctuation
if (originalPost && main.docs[0]) {
const lastOne = main.docs[0].at(-1)
if (!lastOne.post.trim()) {
lastOne.post = originalPost
}
}
// what should we return?
const m = main.toView(ptrs).compute(['index', 'freeze', 'lexicon'])
if (m.world.compute.preTagger) {
m.compute('preTagger')
}
m.compute('unfreeze')
// replace any old tags
if (keep.tags) {
// truncate old tags to only touch new terms
oldTags = oldTags.slice(0, input.wordCount())
m.terms().forEach((term, i) => {
term.tagSafe(oldTags[i])
})
}
if (!m.docs[0] || !m.docs[0][0]) return m
// try to co-erce case, too
if (keep.case) {
const transformCase = isOriginalTitleCase ? toTitleCase : toLowerCase
m.docs[0][0].text = transformCase(m.docs[0][0].text)
}
return m
}
fns.replace = function (match, input, keep) {
if (match && !input) {
return this.replaceWith(match, keep)
}
const m = this.match(match)
if (!m.found) {
return this
}
this.soften()
return m.replaceWith(input, keep)
}
export default fns