Skip to content

Commit

Permalink
Fix issues with replaceWith
Browse files Browse the repository at this point in the history
1. The `keep` options are now respected when using the function-param
   variant of `replaceWith`.
2. `keep: case` now respects the case of the original target, instead of
   basing the decision on the first term of a sentence. I believe this is
   the expected behavior.
3. Unmatched replacements no longer crash when used with keep options.
   Demonstrated by: `replace-with-no-match`.
4. Add type definitions for `keep`, there's some outdated documentation
   around that refers to old names for these params. Also, `possessives`
   was undocumented. This makes it easier to use correctly.
5. Add a test for `keep: possessives`.
  • Loading branch information
tony-scio committed Sep 21, 2024
1 parent 8565e74 commit 03caf3e
Show file tree
Hide file tree
Showing 10 changed files with 58 additions and 18 deletions.
2 changes: 1 addition & 1 deletion builds/compromise.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion builds/one/compromise-one.cjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion builds/one/compromise-one.mjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion builds/three/compromise-three.cjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion builds/three/compromise-three.mjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion builds/two/compromise-two.cjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion builds/two/compromise-two.mjs

Large diffs are not rendered by default.

24 changes: 14 additions & 10 deletions src/1-one/change/api/replace.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
const dollarStub = /\$[0-9a-z]+/g
const fns = {}

const titleCase = function (str) {
return str.replace(/\w\S*/g, txt => txt.charAt(0).toUpperCase() + txt.substring(1).toLowerCase())
}
// 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) {
const replaceByFn = function (main, fn, keep) {
main.forEach(m => {
let out = fn(m)
m.replaceWith(out)
m.replaceWith(out, keep)
})
return main
}
Expand All @@ -35,10 +36,12 @@ fns.replaceWith = function (input, keep = {}) {
let main = this
this.uncache()
if (typeof input === 'function') {
return replaceByFn(main, input)
return replaceByFn(main, input, keep)
}
let terms = main.docs[0]
let isPossessive = keep.possessives && terms[terms.length - 1].tags.has('Possessive')
if (!terms) return main
let isOriginalPossessive = keep.possessives && terms[terms.length - 1].tags.has('Possessive')
let isOriginalTitleCase = keep.case && isTitleCase(terms[0].text)
// support 'foo $0' replacements
input = subDollarSign(input, main)

Expand All @@ -61,7 +64,7 @@ fns.replaceWith = function (input, keep = {}) {
main.delete(original) //science.

// keep "John's"
if (isPossessive) {
if (isOriginalPossessive) {
let tmp = main.docs[0]
let term = tmp[tmp.length - 1]
if (!term.tags.has('Possessive')) {
Expand All @@ -83,8 +86,9 @@ fns.replaceWith = function (input, keep = {}) {
})
}
// try to co-erce case, too
if (keep.case && m.docs[0] && m.docs[0][0] && m.docs[0][0].index[1] === 0) {
m.docs[0][0].text = titleCase(m.docs[0][0].text)
if (keep.case && m.docs[0] && m.docs[0][0]) {
let transformCase = isOriginalTitleCase ? toTitleCase : toLowerCase
m.docs[0][0].text = transformCase(m.docs[0][0].text)
}

// try to keep some pre-post punctuation
Expand Down
27 changes: 27 additions & 0 deletions tests/two/transform/replace.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ test('replace-with-function', function (t) {
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 }) + '!'
Expand All @@ -110,6 +114,19 @@ test('replace-with-function', function (t) {
t.end()
})

test('replace-with-possessives', function (t) {
const fn = p => {
if (p.has('john')) {
return 'johnny'
}
return 'nancy'
}
let 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')
Expand Down Expand Up @@ -180,3 +197,13 @@ test('replace is cloned', function (t) {

t.end()
})

test('replace-with-no-match', function (t) {
let 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()
})
11 changes: 10 additions & 1 deletion types/view/one.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ import type { Document, Pointer, Groups, JsonProps, outMethods, matchOptions, Te

export type Matchable = string | View | Net | ParsedMatch

export interface ReplaceWithProps {
/** preserve the case of the original, ignoring the case of the replacement */
case?: boolean
/** preserve whether the original was a possessive */
possessives?: boolean
/** preserve all of the tags of the original, regardless of the tags of the replacement */
tags?: boolean
}

declare class View {
// Utils
/** is this document empty? */
Expand Down Expand Up @@ -158,7 +167,7 @@ declare class View {
/** search and replace match with new content */
replace: (from: string | View, to?: string | Function, keep?: object) => View
/** substitute-in new content */
replaceWith: (to: string | Function, keep?: object) => View
replaceWith: (to: string | Function, keep?: ReplaceWithProps) => View

/** remove any duplicate matches */
unique: () => View
Expand Down

0 comments on commit 03caf3e

Please sign in to comment.