Upstream spencermountain/compromise state of the touched files before 33ab46246d

This commit is contained in:
upstream committed 2026-10-14 19:51:12 +02:00
1 parent 236b97ae91
commit b3dcee7617
2 files changed
+361

No files matched your search

+134
View File
@@ -0,0 +1,134 @@
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
+227
View File
@@ -0,0 +1,227 @@
import test from 'tape'
import nlp from '../../lib/two.js'
const here = '[two/change/replace] '
test('replace-basic :', function (t) {
let m = nlp('the dog played').match('dog').replace('cat').all()
t.equal(m.out('text'), 'the cat played', here + 'dog-cat')
m = nlp('the dog played').match('the dog').replace('a cat').all()
t.equal(m.out('text'), 'a cat played', here + 'a-cat')
m = nlp('the dog played').match('#Noun').replace('snake').all()
t.equal(m.out('text'), 'the snake played', here + 'snake')
m = nlp('the pit bull played').match('#Noun+').replace('snake').all()
t.equal(m.out('text'), 'the snake played', here + 'pit bull')
m = nlp('the pit bull dog played').match('#Noun+').replace('grey snake').all()
t.equal(m.out('text'), 'the grey snake played', here + 'pit bull dog')
t.end()
})
test('match-replace :', function (t) {
const arr = [
['the dog played', 'the dog', 'the cat', 'the cat played'],
['the dog played', 'the #Noun', 'the cat', 'the cat played'],
['the dog played', 'the (dog|hamster|pet-snake)', 'the cat', 'the cat played'],
['the boy and the girl', 'the #Noun', 'the house', 'the house and the house'],
['the boy and the girl', 'the cat', 'the house', 'the boy and the girl'],
['the boy and the girl', '(boy|girl)', 'cat', 'the cat and the cat'],
]
arr.forEach(function (a) {
const str = nlp(a[0]).replace(a[1], a[2]).all().out('text')
const msg = str + ' -- ' + a[3]
t.equal(str, a[3], here + msg)
})
t.end()
})
test('replace-with-punctuation', function (t) {
const doc = nlp('Simon, how is Pamela and Jason?')
const str = doc.match('#Person').replace('PERSON').all().out()
t.equal(str, 'PERSON, how is PERSON and PERSON?', here + 'replace-with-punctuation')
t.end()
})
test('structured-object-replace :', function (t) {
let r = nlp('fun times in cool town')
const term = r.match('times')
r.replace(term, 'day')
t.equal(r.out(), 'fun day in cool town', here + 'structured-replace')
r = nlp('fun times in cool town')
const terms = r.match('cool town')
r.replace(terms, 'shitsville')
t.equal(r.out(), 'fun times in shitsville', here + 'structured-replace-multi')
t.end()
})
test('replace-keep some punctuation', function (t) {
let doc = nlp('two weeks').tag('Cool')
doc.replace('two', '2', { tags: true })
t.equal(doc.match('#Cool+').text(), '2 weeks', here + 'replace-keep-tags')
doc = nlp('first sentence. I am trying it out.')
const m = doc.match('trying').tag('HashTag')
m.replaceWith('working', { tags: true })
t.equal(doc.match('#HashTag+').text(), 'working', here + 'replacewith-keep-tags')
t.end()
})
test('replace over implict', function (t) {
const doc = nlp("i'm good")
doc.match('am').replaceWith('was')
t.equal(doc.text(), 'i was good', here + 'replace over implicit')
t.end()
})
test('replace-with-Doc', function (t) {
const b = nlp('sneaks').tag('Cool')
const doc = nlp(`john walks quickly`)
doc.match('walks').replaceWith(b, { tags: true })
t.equal(doc.text(), 'john sneaks quickly', here + 'doc-replace')
t.equal(doc.has('#Cool'), true, here + 'doc-replace tags')
t.end()
})
test('replace-with-function', function (t) {
const fn = p => {
if (p.has('john')) {
return 'johnny'
}
return 'nancy'
}
let doc = nlp('spencer and John')
doc.replace('#Person', fn)
t.equal(doc.text(), 'nancy and johnny', here + 'replace function')
doc = nlp('spencer and John')
doc.match('#Person').replaceWith(fn, { case: true })
t.equal(doc.text(), 'nancy and Johnny')
doc = nlp('Thurs, Feb 2nd, 2016')
doc.match('feb').replaceWith(m => {
return m.text({ trim: true }) + '!'
})
t.equal(doc.text(), 'Thurs, Feb! 2nd, 2016', here + 'replaceWith function')
doc = nlp('Spencer is very cool.')
doc.match('spencer').replaceWith((m) => m.text() + 's')
doc.match('is').replaceWith(() => 'are')
t.equal(doc.text(), 'Spencers are very cool.', here + 'replaceWith twice')
t.end()
})
test('replace-with-possessives', function (t) {
const fn = p => {
if (p.has('john')) {
return 'johnny'
}
return 'nancy'
}
const doc = nlp('spencer\'s house is cooler than John\'s house.')
doc.replace('#Person', fn, { case: true, possessives: true })
t.equal(doc.text(), 'nancy\'s house is cooler than Johnny\'s house.')
t.end()
})
test('replace-tags-param', function (t) {
let doc = nlp('Spencer is very cool.')
doc.match('spencer').replaceWith('jogging')
const m = doc.match('jogging')
t.equal(m.has('#Person'), false, here + 'tags not-kept - default')
doc = nlp('Spencer is very cool.')
doc.match('spencer').replaceWith('jogging', { tags: true })
t.equal(doc.has('(jogging && #Person)'), true, here + 'tags kept - boolean')
t.equal(doc.has('(jogging && #Gerund)'), false, here + 'tags kept - boolean')
doc = nlp('Spencer is very cool.')
doc.match('spencer').replaceWith('jogging', { tags: true })
t.equal(doc.has('(jogging && #Person)'), true, here + 'tags kept - boolean')
t.equal(doc.has('(jogging && #Gerund)'), false, 'here+tags kept - boolean')
t.end()
})
test('replace-case-param', function (t) {
let doc = nlp('Spencer is very cool.')
doc.match('spencer').replaceWith('jogging', { case: true })
t.equal(doc.text(), 'Jogging is very cool.', here + here + 'case kept ')
doc = nlp('spencer is very cool.')
doc.match('spencer').replaceWith('jogging', { case: false }) // Jogging?
t.equal(doc.text(), 'jogging is very cool.', here + here + 'lowsercase kept - default')
doc = nlp('Spencer is very cool.')
doc.match('spencer').replaceWith('jogging', { case: false })
t.equal(doc.text(), 'jogging is very cool.', 'dont-keep')
t.end()
})
test('replace-repair', function (t) {
let doc = nlp('the cat and the dog')
doc.replace('#Noun', 'house')
t.equal(doc.text(), 'the house and the house', here + 'repair-one-one')
doc = nlp('the cat and the dog')
doc.replace('the #Noun', 'the house')
t.equal(doc.text(), 'the house and the house', here + 'repair-two-two')
doc = nlp('the cat and the dog')
doc.replace('#Noun', 'cool house')
t.equal(doc.text(), 'the cool house and the cool house', here + 'repair-one-two')
doc = nlp('the cat and the dog')
doc.replace('the #Noun', 'house')
t.equal(doc.text(), 'house and house', here + 'repair-two-one')
t.end()
})
test('replace is cloned', function (t) {
const doc = nlp('before match after. second sentence here.')
const m = doc.match('match')
doc.match('sentence').replace(m)
t.equal(doc.text(), 'before match after. second match here.', here + 'replaced-text')
const id = m.docs[0][0].id
const foundIds = doc.termList().filter(term => term.id === id)
t.equal(foundIds.length, 1, 'id-different')
t.equal(m.length, 1, 'match-unchanged')
m.tag('NewTag')
t.equal(m.match('#NewTag').length, 1, 'tags-are-cloned')
t.end()
})
test('replace-with-no-match', function (t) {
const doc = nlp('original')
doc.match('missing').replaceWith('unreached')
t.equal(doc.text(), 'original')
doc.match('missing').replaceWith('unreached', { tags: true, possessives: true, case: true })
t.equal(doc.text(), 'original')
t.end()
})
test('replace-with-pre-post', function (t) {
let doc = nlp('`Target`')
doc.match('Target').replaceWith('Replacement')
t.equal(doc.text(), '`Replacement`')
doc = nlp('`John\'s`')
doc.match('John').replaceWith('Spencer', { possessives: true })
t.equal(doc.text(), '`Spencer\'s`')
doc = nlp('the `pit bull` played').match('#Noun+').replace('snake').all()
t.equal(doc.text(), 'the `snake` played')
doc = nlp('the `snake` played').match('#Noun+').replace('pit bull').all()
t.equal(doc.text(), 'the `pit bull` played')
t.end()
})