@preventive/compare 0.0.0-stage → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-APACHE +202 -0
- package/LICENSE-MIT +21 -0
- package/compare.d.ts +185 -0
- package/compare.js +29 -0
- package/names.d.ts +42 -0
- package/names.js +9 -0
- package/package.json +131 -4
- package/pretty.d.ts +14 -0
- package/pretty.js +4 -0
- package/src/blocks.js +17 -0
- package/src/changes.js +90 -0
- package/src/css.js +70 -0
- package/src/error.js +12 -0
- package/src/layout.js +94 -0
- package/src/marked.js +20 -0
- package/src/names/analyze.js +235 -0
- package/src/names/binds.js +20 -0
- package/src/names/check.js +68 -0
- package/src/names/diff.js +33 -0
- package/src/names/misread.js +38 -0
- package/src/names/pairing.js +111 -0
- package/src/names/rename.js +69 -0
- package/src/names/syntax.js +63 -0
- package/src/oxc.js +6 -0
- package/src/oxc.node.js +16 -0
- package/src/pairs.js +100 -0
- package/src/patch.js +78 -0
- package/src/paths.js +22 -0
- package/src/peers.js +18 -0
- package/src/pretty.js +106 -0
- package/src/quote.js +16 -0
- package/src/renames.js +206 -0
- package/src/same-bytes.js +18 -0
- package/src/same-bytes.node.js +7 -0
- package/src/script.js +57 -0
- package/src/similarity.js +53 -0
- package/src/text.js +41 -0
- package/src/tree.js +98 -0
- package/src/whitespace.js +44 -0
- package/src/words.js +57 -0
- package/README.md +0 -3
package/src/words.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Changed lines read word by word: what changed within a line and the one it
|
|
2
|
+
// pairs with, as one shortest edit script between their words finds it.
|
|
3
|
+
|
|
4
|
+
import { script } from './script.js'
|
|
5
|
+
|
|
6
|
+
// Words as a word diff compares them, so `foo(a)` -> `foo(b)` marks `a` alone.
|
|
7
|
+
const TOKEN = /[\p{L}\p{N}_$]+|\s+|[^\p{L}\p{N}_$\s]/gu
|
|
8
|
+
// Past these a word diff is noise (minified lines) or too slow to be worth it.
|
|
9
|
+
const MAX_CHARS = 2000
|
|
10
|
+
const MAX_TOKENS = 800
|
|
11
|
+
// Lines whose words are mostly replaced are not one line edited.
|
|
12
|
+
export const MAX_CHANGED = 0.6
|
|
13
|
+
|
|
14
|
+
// A line as a word diff reads it, each distinct word numbered in `ids` as the
|
|
15
|
+
// script compares them; null past the size limits.
|
|
16
|
+
export function tokenized(line, ids) {
|
|
17
|
+
if (line.length > MAX_CHARS) return null
|
|
18
|
+
const tokens = line.match(TOKEN) ?? []
|
|
19
|
+
if (tokens.length > MAX_TOKENS) return null
|
|
20
|
+
return { line, tokens, words: tokens.map((token) => {
|
|
21
|
+
if (!ids.has(token)) ids.set(token, ids.size)
|
|
22
|
+
return ids.get(token)
|
|
23
|
+
}) }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// A line's words the script changed as ranges `[from, to)`, those split by blanks alone merged.
|
|
27
|
+
function changedRanges({ tokens, line }, kept) {
|
|
28
|
+
const ranges = []
|
|
29
|
+
for (let at = 0, i = 0; i < tokens.length; at += tokens[i++].length) {
|
|
30
|
+
if (kept[i]) continue
|
|
31
|
+
const last = ranges.at(-1)
|
|
32
|
+
if (last && /^\s*$/u.test(line.slice(last[1], at))) last[1] = at + tokens[i].length
|
|
33
|
+
else ranges.push([at, at + tokens[i].length])
|
|
34
|
+
}
|
|
35
|
+
return ranges
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const part = (ranges, line) => ranges.reduce((sum, [from, to]) => sum + to - from, 0) / Math.max(1, line.length)
|
|
39
|
+
|
|
40
|
+
// What a word view marks in two lines read: the ranges, the smaller share of
|
|
41
|
+
// either they cover (all of it where the script keeps no word), and the work.
|
|
42
|
+
export function wordView(a, b) {
|
|
43
|
+
const kept = { a: a.words.map(() => 0), b: b.words.map(() => 0) }
|
|
44
|
+
const steps = script(a.words, b.words, kept)
|
|
45
|
+
const ranges = { a: changedRanges(a, kept.a), b: changedRanges(b, kept.b) }
|
|
46
|
+
return { ranges, share: kept.a.includes(1) ? Math.min(part(ranges.a, a.line), part(ranges.b, b.line)) : 1, steps }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Lines the same have no ranges, not null: null is a line with no word view.
|
|
50
|
+
export function wordRanges(left, right) {
|
|
51
|
+
if (left === right) return { a: [], b: [] }
|
|
52
|
+
const ids = new Map()
|
|
53
|
+
const a = tokenized(left, ids), b = tokenized(right, ids)
|
|
54
|
+
if (a === null || b === null) return null
|
|
55
|
+
const { ranges, share } = wordView(a, b)
|
|
56
|
+
return share <= MAX_CHANGED ? ranges : null
|
|
57
|
+
}
|
package/README.md
DELETED