@thxgg/steward 0.1.24 → 0.1.26
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/.output/nitro.json +1 -1
- package/.output/public/_nuxt/{Bc2V3wPK.js → B2ow85x_.js} +2 -2
- package/.output/public/_nuxt/{U78rMDmo.js → B6CbIr08.js} +1 -1
- package/.output/public/_nuxt/{BknRrWsw.js → BLQAF8wp.js} +1 -1
- package/.output/public/_nuxt/BXuwtOqb.js +1 -0
- package/.output/public/_nuxt/CAMiEhze.js +1 -0
- package/.output/public/_nuxt/{BRQ9Cxaw.js → CIBCqZF5.js} +1 -1
- package/.output/public/_nuxt/Ce0-nlm9.js +1 -0
- package/.output/public/_nuxt/{T11EuTtn.js → ConzneVY.js} +1 -1
- package/.output/public/_nuxt/D0qxz_Pn.js +1310 -0
- package/.output/public/_nuxt/D3PDtLSF.js +3 -0
- package/.output/public/_nuxt/{C73kduX-.js → DdKC0UAK.js} +1 -1
- package/.output/public/_nuxt/Detail.BGdvrJGh.css +1 -0
- package/.output/public/_nuxt/{C53_p0K1.js → Dkh9ic1y.js} +1 -1
- package/.output/public/_nuxt/LEjJTR7-.js +1 -0
- package/.output/public/_nuxt/{BTmXUZ_s.js → UqZfMfrZ.js} +1 -1
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/25438e34-19a2-421d-aede-53fd18f1ccd4.json +1 -0
- package/.output/public/_nuxt/dckrK0oj.js +1 -0
- package/.output/public/_nuxt/entry.DT4p6_uW.css +1 -0
- package/.output/public/_nuxt/pIWeVmPw.js +1 -0
- package/.output/public/_nuxt/xrHaPo1U.js +60 -0
- package/.output/server/chunks/_/prd-service.mjs.map +1 -1
- package/.output/server/chunks/build/{Detail-DMMUwTWr.mjs → Detail-rpcemNXe.mjs} +674 -481
- package/.output/server/chunks/build/Detail-rpcemNXe.mjs.map +1 -0
- package/.output/server/chunks/build/DiffViewer-styles.B1FB5NJj.mjs +8 -0
- package/.output/server/chunks/build/DiffViewer-styles.B1FB5NJj.mjs.map +1 -0
- package/.output/server/chunks/build/{_prd_-ByugK4Yi.mjs → _prd_-CeibvZOH.mjs} +67 -233
- package/.output/server/chunks/build/_prd_-CeibvZOH.mjs.map +1 -0
- package/.output/server/chunks/build/client.precomputed.mjs +1 -1
- package/.output/server/chunks/build/{default-BKKgG7HJ.mjs → default-iq8SaDDN.mjs} +3 -3
- package/.output/server/chunks/build/default-iq8SaDDN.mjs.map +1 -0
- package/.output/server/chunks/build/{error-404-Bf6kdO80.mjs → error-404-DFale9A5.mjs} +2 -2
- package/.output/server/chunks/build/error-404-DFale9A5.mjs.map +1 -0
- package/.output/server/chunks/build/{index-DE1tjHAd.mjs → index-Po00RvHm.mjs} +2 -2
- package/.output/server/chunks/build/index-Po00RvHm.mjs.map +1 -0
- package/.output/server/chunks/build/{nuxt-link-SvT1nf8Z.mjs → nuxt-link-B4oWFn7n.mjs} +2 -2
- package/.output/server/chunks/build/nuxt-link-B4oWFn7n.mjs.map +1 -0
- package/.output/server/chunks/build/{repo-graph-DzT45gSB.mjs → repo-graph-BQVFpA-w.mjs} +5 -4
- package/.output/server/chunks/build/repo-graph-BQVFpA-w.mjs.map +1 -0
- package/.output/server/chunks/build/server.mjs +7 -7
- package/.output/server/chunks/build/styles.mjs +4 -5
- package/.output/server/chunks/build/{usePrd-hXZOmvAv.mjs → usePrd-Bb6jlnNZ.mjs} +2 -2
- package/.output/server/chunks/build/usePrd-Bb6jlnNZ.mjs.map +1 -0
- package/.output/server/chunks/nitro/nitro.mjs +983 -678
- package/.output/server/chunks/nitro/nitro.mjs.map +1 -1
- package/.output/server/node_modules/@pierre/diffs/dist/components/File.js +324 -0
- package/.output/server/node_modules/@pierre/diffs/dist/components/FileDiff.js +395 -0
- package/.output/server/node_modules/@pierre/diffs/dist/components/FileStream.js +161 -0
- package/.output/server/node_modules/@pierre/diffs/dist/components/web-components.js +25 -0
- package/.output/server/node_modules/@pierre/diffs/dist/constants.js +23 -0
- package/.output/server/node_modules/@pierre/diffs/dist/highlighter/languages/areLanguagesAttached.js +11 -0
- package/.output/server/node_modules/@pierre/diffs/dist/highlighter/languages/attachResolvedLanguages.js +20 -0
- package/.output/server/node_modules/@pierre/diffs/dist/highlighter/languages/cleanUpResolvedLanguages.js +11 -0
- package/.output/server/node_modules/@pierre/diffs/dist/highlighter/languages/constants.js +8 -0
- package/.output/server/node_modules/@pierre/diffs/dist/highlighter/languages/getResolvedLanguages.js +16 -0
- package/.output/server/node_modules/@pierre/diffs/dist/highlighter/languages/getResolvedOrResolveLanguage.js +11 -0
- package/.output/server/node_modules/@pierre/diffs/dist/highlighter/languages/hasResolvedLanguages.js +11 -0
- package/.output/server/node_modules/@pierre/diffs/dist/highlighter/languages/resolveLanguage.js +30 -0
- package/.output/server/node_modules/@pierre/diffs/dist/highlighter/languages/resolveLanguages.js +25 -0
- package/.output/server/node_modules/@pierre/diffs/dist/highlighter/shared_highlighter.js +71 -0
- package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/areThemesAttached.js +12 -0
- package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/attachResolvedThemes.js +24 -0
- package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/cleanUpResolvedThemes.js +11 -0
- package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/constants.js +9 -0
- package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/getResolvedOrResolveTheme.js +11 -0
- package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/getResolvedThemes.js +16 -0
- package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/hasResolvedThemes.js +11 -0
- package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/registerCustomCSSVariableTheme.js +18 -0
- package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/registerCustomTheme.js +14 -0
- package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/resolveTheme.js +35 -0
- package/.output/server/node_modules/@pierre/diffs/dist/highlighter/themes/resolveThemes.js +21 -0
- package/.output/server/node_modules/@pierre/diffs/dist/index.js +84 -0
- package/.output/server/node_modules/@pierre/diffs/dist/managers/LineSelectionManager.js +282 -0
- package/.output/server/node_modules/@pierre/diffs/dist/managers/MouseEventManager.js +244 -0
- package/.output/server/node_modules/@pierre/diffs/dist/managers/ResizeManager.js +132 -0
- package/.output/server/node_modules/@pierre/diffs/dist/managers/ScrollSyncManager.js +62 -0
- package/.output/server/node_modules/@pierre/diffs/dist/managers/UniversalRenderingManager.js +32 -0
- package/.output/server/node_modules/@pierre/diffs/dist/renderers/DiffHunksRenderer.js +655 -0
- package/.output/server/node_modules/@pierre/diffs/dist/renderers/FileRenderer.js +243 -0
- package/.output/server/node_modules/@pierre/diffs/dist/shiki-stream/stream.js +32 -0
- package/.output/server/node_modules/@pierre/diffs/dist/shiki-stream/tokenizer.js +71 -0
- package/.output/server/node_modules/@pierre/diffs/dist/sprite.js +55 -0
- package/.output/server/node_modules/@pierre/diffs/dist/style.js +6 -0
- package/.output/server/node_modules/@pierre/diffs/dist/themes/pierre-dark.js +1328 -0
- package/.output/server/node_modules/@pierre/diffs/dist/themes/pierre-light.js +1328 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/areFilesEqual.js +8 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/areObjectsEqual.js +18 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/areOptionsEqual.js +12 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/areSelectionsEqual.js +8 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/areThemesEqual.js +9 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/cleanLastNewline.js +8 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/createAnnotationElement.js +21 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/createAnnotationWrapperNode.js +12 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/createCodeNode.js +12 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/createEmptyRowBuffer.js +16 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/createFileHeaderElement.js +84 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/createHoverContentNode.js +15 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/createNoNewlineElement.js +24 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/createPreElement.js +28 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/createRowNodes.js +20 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/createSeparator.js +69 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/createSpanNodeFromToken.js +13 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/createStyleElement.js +19 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/createTransformerWithState.js +56 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/createUnsafeCSSStyleNode.js +12 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/cssWrappers.js +21 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/diffAcceptRejectHunk.js +82 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/formatCSSVariablePrefix.js +8 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/getFiletypeFromFileName.js +343 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/getHighlighterOptions.js +13 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/getHighlighterThemeStyles.js +40 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/getHunkSeparatorSlotName.js +8 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/getIconForType.js +15 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/getLineAnnotationName.js +8 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/getLineEndingType.js +11 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/getLineNodes.js +15 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/getSingularPatch.js +20 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/getThemes.js +16 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/getTotalLineCountFromHunks.js +10 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/hast_utils.js +42 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/isWorkerContext.js +8 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/parseDiffDecorations.js +34 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/parseDiffFromFile.js +23 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/parseLineType.js +17 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/parsePatchFiles.js +211 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/prerenderHTMLIfNecessary.js +10 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/processLine.js +42 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/renderDiffWithHighlighter.js +339 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/renderFileWithHighlighter.js +52 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/setLanguageOverride.js +11 -0
- package/.output/server/node_modules/@pierre/diffs/dist/utils/setWrapperNodeProps.js +29 -0
- package/.output/server/node_modules/@pierre/diffs/package.json +89 -0
- package/.output/server/node_modules/@shikijs/transformers/dist/index.mjs +831 -0
- package/.output/server/node_modules/@shikijs/transformers/package.json +37 -0
- package/.output/server/node_modules/diff/libesm/convert/dmp.js +21 -0
- package/.output/server/node_modules/diff/libesm/convert/xml.js +31 -0
- package/.output/server/node_modules/diff/libesm/diff/array.js +16 -0
- package/.output/server/node_modules/diff/libesm/diff/base.js +253 -0
- package/.output/server/node_modules/diff/libesm/diff/character.js +7 -0
- package/.output/server/node_modules/diff/libesm/diff/css.js +10 -0
- package/.output/server/node_modules/diff/libesm/diff/json.js +78 -0
- package/.output/server/node_modules/diff/libesm/diff/line.js +65 -0
- package/.output/server/node_modules/diff/libesm/diff/sentence.js +43 -0
- package/.output/server/node_modules/diff/libesm/diff/word.js +296 -0
- package/.output/server/node_modules/diff/libesm/index.js +30 -0
- package/.output/server/node_modules/diff/libesm/package.json +1 -0
- package/.output/server/node_modules/diff/libesm/patch/apply.js +257 -0
- package/.output/server/node_modules/diff/libesm/patch/create.js +228 -0
- package/.output/server/node_modules/diff/libesm/patch/line-endings.js +44 -0
- package/.output/server/node_modules/diff/libesm/patch/parse.js +147 -0
- package/.output/server/node_modules/diff/libesm/patch/reverse.js +23 -0
- package/.output/server/node_modules/diff/libesm/util/distance-iterator.js +37 -0
- package/.output/server/node_modules/diff/libesm/util/params.js +14 -0
- package/.output/server/node_modules/diff/libesm/util/string.js +128 -0
- package/.output/server/node_modules/diff/package.json +132 -0
- package/.output/server/package.json +4 -1
- package/README.md +41 -0
- package/bin/prd +1 -1
- package/dist/host/src/index.js +10 -0
- package/dist/host/src/sync.js +201 -0
- package/dist/server/utils/db.js +64 -0
- package/dist/server/utils/git.js +8 -6
- package/dist/server/utils/prd-state.js +24 -2
- package/dist/server/utils/repos.js +12 -2
- package/dist/server/utils/state-migration.js +4 -3
- package/dist/server/utils/sync-apply.js +380 -0
- package/dist/server/utils/sync-export.js +183 -0
- package/dist/server/utils/sync-identity.js +231 -0
- package/dist/server/utils/sync-inspect.js +103 -0
- package/dist/server/utils/sync-merge.js +579 -0
- package/dist/server/utils/sync-schema.js +100 -0
- package/package.json +2 -1
- package/.output/public/_nuxt/6tINjQEd.js +0 -141
- package/.output/public/_nuxt/B2mIQf5X.js +0 -3
- package/.output/public/_nuxt/C0BBSDJ7.js +0 -1
- package/.output/public/_nuxt/CN46Bgts.js +0 -1
- package/.output/public/_nuxt/CTJgb0zb.js +0 -1
- package/.output/public/_nuxt/Cce168lk.js +0 -30
- package/.output/public/_nuxt/CyVSeLw5.js +0 -1
- package/.output/public/_nuxt/Detail.CYc96mGf.css +0 -1
- package/.output/public/_nuxt/ZNypZshD.js +0 -13
- package/.output/public/_nuxt/builds/meta/8c342d49-fe70-4f67-a987-821c16f86125.json +0 -1
- package/.output/public/_nuxt/entry.Bw0CE6Iz.css +0 -1
- package/.output/public/_nuxt/pYJYAY-W.js +0 -60
- package/.output/server/chunks/build/Detail-DMMUwTWr.mjs.map +0 -1
- package/.output/server/chunks/build/DiffViewer-styles-1.mjs-d2dQvARr.mjs +0 -4
- package/.output/server/chunks/build/DiffViewer-styles-1.mjs-d2dQvARr.mjs.map +0 -1
- package/.output/server/chunks/build/DiffViewer-styles-2.mjs-X6QKNjM0.mjs +0 -4
- package/.output/server/chunks/build/DiffViewer-styles-2.mjs-X6QKNjM0.mjs.map +0 -1
- package/.output/server/chunks/build/DiffViewer-styles.0AbHFl6N.mjs +0 -8
- package/.output/server/chunks/build/DiffViewer-styles.0AbHFl6N.mjs.map +0 -1
- package/.output/server/chunks/build/DiffViewer-styles.BDwAqkTk.mjs +0 -8
- package/.output/server/chunks/build/DiffViewer-styles.BDwAqkTk.mjs.map +0 -1
- package/.output/server/chunks/build/DiffViewer-styles.DRJh5Ui4.mjs +0 -10
- package/.output/server/chunks/build/DiffViewer-styles.DRJh5Ui4.mjs.map +0 -1
- package/.output/server/chunks/build/_prd_-ByugK4Yi.mjs.map +0 -1
- package/.output/server/chunks/build/default-BKKgG7HJ.mjs.map +0 -1
- package/.output/server/chunks/build/error-404-Bf6kdO80.mjs.map +0 -1
- package/.output/server/chunks/build/index-DE1tjHAd.mjs.map +0 -1
- package/.output/server/chunks/build/nuxt-link-SvT1nf8Z.mjs.map +0 -1
- package/.output/server/chunks/build/repo-graph-DzT45gSB.mjs.map +0 -1
- package/.output/server/chunks/build/usePrd-hXZOmvAv.mjs.map +0 -1
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { diffLines } from '../diff/line.js';
|
|
2
|
+
export const INCLUDE_HEADERS = {
|
|
3
|
+
includeIndex: true,
|
|
4
|
+
includeUnderline: true,
|
|
5
|
+
includeFileHeaders: true
|
|
6
|
+
};
|
|
7
|
+
export const FILE_HEADERS_ONLY = {
|
|
8
|
+
includeIndex: false,
|
|
9
|
+
includeUnderline: false,
|
|
10
|
+
includeFileHeaders: true
|
|
11
|
+
};
|
|
12
|
+
export const OMIT_HEADERS = {
|
|
13
|
+
includeIndex: false,
|
|
14
|
+
includeUnderline: false,
|
|
15
|
+
includeFileHeaders: false
|
|
16
|
+
};
|
|
17
|
+
export function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
|
|
18
|
+
let optionsObj;
|
|
19
|
+
if (!options) {
|
|
20
|
+
optionsObj = {};
|
|
21
|
+
}
|
|
22
|
+
else if (typeof options === 'function') {
|
|
23
|
+
optionsObj = { callback: options };
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
optionsObj = options;
|
|
27
|
+
}
|
|
28
|
+
if (typeof optionsObj.context === 'undefined') {
|
|
29
|
+
optionsObj.context = 4;
|
|
30
|
+
}
|
|
31
|
+
// We copy this into its own variable to placate TypeScript, which thinks
|
|
32
|
+
// optionsObj.context might be undefined in the callbacks below.
|
|
33
|
+
const context = optionsObj.context;
|
|
34
|
+
// @ts-expect-error (runtime check for something that is correctly a static type error)
|
|
35
|
+
if (optionsObj.newlineIsToken) {
|
|
36
|
+
throw new Error('newlineIsToken may not be used with patch-generation functions, only with diffing functions');
|
|
37
|
+
}
|
|
38
|
+
if (!optionsObj.callback) {
|
|
39
|
+
return diffLinesResultToPatch(diffLines(oldStr, newStr, optionsObj));
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
const { callback } = optionsObj;
|
|
43
|
+
diffLines(oldStr, newStr, Object.assign(Object.assign({}, optionsObj), { callback: (diff) => {
|
|
44
|
+
const patch = diffLinesResultToPatch(diff);
|
|
45
|
+
// TypeScript is unhappy without the cast because it does not understand that `patch` may
|
|
46
|
+
// be undefined here only if `callback` is StructuredPatchCallbackAbortable:
|
|
47
|
+
callback(patch);
|
|
48
|
+
} }));
|
|
49
|
+
}
|
|
50
|
+
function diffLinesResultToPatch(diff) {
|
|
51
|
+
// STEP 1: Build up the patch with no "" lines and with the arrays
|
|
52
|
+
// of lines containing trailing newline characters. We'll tidy up later...
|
|
53
|
+
if (!diff) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
diff.push({ value: '', lines: [] }); // Append an empty value to make cleanup easier
|
|
57
|
+
function contextLines(lines) {
|
|
58
|
+
return lines.map(function (entry) { return ' ' + entry; });
|
|
59
|
+
}
|
|
60
|
+
const hunks = [];
|
|
61
|
+
let oldRangeStart = 0, newRangeStart = 0, curRange = [], oldLine = 1, newLine = 1;
|
|
62
|
+
for (let i = 0; i < diff.length; i++) {
|
|
63
|
+
const current = diff[i], lines = current.lines || splitLines(current.value);
|
|
64
|
+
current.lines = lines;
|
|
65
|
+
if (current.added || current.removed) {
|
|
66
|
+
// If we have previous context, start with that
|
|
67
|
+
if (!oldRangeStart) {
|
|
68
|
+
const prev = diff[i - 1];
|
|
69
|
+
oldRangeStart = oldLine;
|
|
70
|
+
newRangeStart = newLine;
|
|
71
|
+
if (prev) {
|
|
72
|
+
curRange = context > 0 ? contextLines(prev.lines.slice(-context)) : [];
|
|
73
|
+
oldRangeStart -= curRange.length;
|
|
74
|
+
newRangeStart -= curRange.length;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Output our changes
|
|
78
|
+
for (const line of lines) {
|
|
79
|
+
curRange.push((current.added ? '+' : '-') + line);
|
|
80
|
+
}
|
|
81
|
+
// Track the updated file position
|
|
82
|
+
if (current.added) {
|
|
83
|
+
newLine += lines.length;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
oldLine += lines.length;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
// Identical context lines. Track line changes
|
|
91
|
+
if (oldRangeStart) {
|
|
92
|
+
// Close out any changes that have been output (or join overlapping)
|
|
93
|
+
if (lines.length <= context * 2 && i < diff.length - 2) {
|
|
94
|
+
// Overlapping
|
|
95
|
+
for (const line of contextLines(lines)) {
|
|
96
|
+
curRange.push(line);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// end the range and output
|
|
101
|
+
const contextSize = Math.min(lines.length, context);
|
|
102
|
+
for (const line of contextLines(lines.slice(0, contextSize))) {
|
|
103
|
+
curRange.push(line);
|
|
104
|
+
}
|
|
105
|
+
const hunk = {
|
|
106
|
+
oldStart: oldRangeStart,
|
|
107
|
+
oldLines: (oldLine - oldRangeStart + contextSize),
|
|
108
|
+
newStart: newRangeStart,
|
|
109
|
+
newLines: (newLine - newRangeStart + contextSize),
|
|
110
|
+
lines: curRange
|
|
111
|
+
};
|
|
112
|
+
hunks.push(hunk);
|
|
113
|
+
oldRangeStart = 0;
|
|
114
|
+
newRangeStart = 0;
|
|
115
|
+
curRange = [];
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
oldLine += lines.length;
|
|
119
|
+
newLine += lines.length;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Step 2: eliminate the trailing `\n` from each line of each hunk, and, where needed, add
|
|
123
|
+
// "".
|
|
124
|
+
for (const hunk of hunks) {
|
|
125
|
+
for (let i = 0; i < hunk.lines.length; i++) {
|
|
126
|
+
if (hunk.lines[i].endsWith('\n')) {
|
|
127
|
+
hunk.lines[i] = hunk.lines[i].slice(0, -1);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
hunk.lines.splice(i + 1, 0, '\');
|
|
131
|
+
i++; // Skip the line we just added, then continue iterating
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
oldFileName: oldFileName, newFileName: newFileName,
|
|
137
|
+
oldHeader: oldHeader, newHeader: newHeader,
|
|
138
|
+
hunks: hunks
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* creates a unified diff patch.
|
|
144
|
+
* @param patch either a single structured patch object (as returned by `structuredPatch`) or an array of them (as returned by `parsePatch`)
|
|
145
|
+
*/
|
|
146
|
+
export function formatPatch(patch, headerOptions) {
|
|
147
|
+
if (!headerOptions) {
|
|
148
|
+
headerOptions = INCLUDE_HEADERS;
|
|
149
|
+
}
|
|
150
|
+
if (Array.isArray(patch)) {
|
|
151
|
+
if (patch.length > 1 && !headerOptions.includeFileHeaders) {
|
|
152
|
+
throw new Error('Cannot omit file headers on a multi-file patch. '
|
|
153
|
+
+ '(The result would be unparseable; how would a tool trying to apply '
|
|
154
|
+
+ 'the patch know which changes are to which file?)');
|
|
155
|
+
}
|
|
156
|
+
return patch.map(p => formatPatch(p, headerOptions)).join('\n');
|
|
157
|
+
}
|
|
158
|
+
const ret = [];
|
|
159
|
+
if (headerOptions.includeIndex && patch.oldFileName == patch.newFileName) {
|
|
160
|
+
ret.push('Index: ' + patch.oldFileName);
|
|
161
|
+
}
|
|
162
|
+
if (headerOptions.includeUnderline) {
|
|
163
|
+
ret.push('===================================================================');
|
|
164
|
+
}
|
|
165
|
+
if (headerOptions.includeFileHeaders) {
|
|
166
|
+
ret.push('--- ' + patch.oldFileName + (typeof patch.oldHeader === 'undefined' ? '' : '\t' + patch.oldHeader));
|
|
167
|
+
ret.push('+++ ' + patch.newFileName + (typeof patch.newHeader === 'undefined' ? '' : '\t' + patch.newHeader));
|
|
168
|
+
}
|
|
169
|
+
for (let i = 0; i < patch.hunks.length; i++) {
|
|
170
|
+
const hunk = patch.hunks[i];
|
|
171
|
+
// Unified Diff Format quirk: If the chunk size is 0,
|
|
172
|
+
// the first number is one lower than one would expect.
|
|
173
|
+
// https://www.artima.com/weblogs/viewpost.jsp?thread=164293
|
|
174
|
+
if (hunk.oldLines === 0) {
|
|
175
|
+
hunk.oldStart -= 1;
|
|
176
|
+
}
|
|
177
|
+
if (hunk.newLines === 0) {
|
|
178
|
+
hunk.newStart -= 1;
|
|
179
|
+
}
|
|
180
|
+
ret.push('@@ -' + hunk.oldStart + ',' + hunk.oldLines
|
|
181
|
+
+ ' +' + hunk.newStart + ',' + hunk.newLines
|
|
182
|
+
+ ' @@');
|
|
183
|
+
for (const line of hunk.lines) {
|
|
184
|
+
ret.push(line);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return ret.join('\n') + '\n';
|
|
188
|
+
}
|
|
189
|
+
export function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
|
|
190
|
+
if (typeof options === 'function') {
|
|
191
|
+
options = { callback: options };
|
|
192
|
+
}
|
|
193
|
+
if (!(options === null || options === void 0 ? void 0 : options.callback)) {
|
|
194
|
+
const patchObj = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options);
|
|
195
|
+
if (!patchObj) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
return formatPatch(patchObj, options === null || options === void 0 ? void 0 : options.headerOptions);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
const { callback } = options;
|
|
202
|
+
structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, Object.assign(Object.assign({}, options), { callback: patchObj => {
|
|
203
|
+
if (!patchObj) {
|
|
204
|
+
callback(undefined);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
callback(formatPatch(patchObj, options.headerOptions));
|
|
208
|
+
}
|
|
209
|
+
} }));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
export function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) {
|
|
213
|
+
return createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Split `text` into an array of lines, including the trailing newline character (where present)
|
|
217
|
+
*/
|
|
218
|
+
function splitLines(text) {
|
|
219
|
+
const hasTrailingNl = text.endsWith('\n');
|
|
220
|
+
const result = text.split('\n').map(line => line + '\n');
|
|
221
|
+
if (hasTrailingNl) {
|
|
222
|
+
result.pop();
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
result.push(result.pop().slice(0, -1));
|
|
226
|
+
}
|
|
227
|
+
return result;
|
|
228
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export function unixToWin(patch) {
|
|
2
|
+
if (Array.isArray(patch)) {
|
|
3
|
+
// It would be cleaner if instead of the line below we could just write
|
|
4
|
+
// return patch.map(unixToWin)
|
|
5
|
+
// but mysteriously TypeScript (v5.7.3 at the time of writing) does not like this and it will
|
|
6
|
+
// refuse to compile, thinking that unixToWin could then return StructuredPatch[][] and the
|
|
7
|
+
// result would be incompatible with the overload signatures.
|
|
8
|
+
// See bug report at https://github.com/microsoft/TypeScript/issues/61398.
|
|
9
|
+
return patch.map(p => unixToWin(p));
|
|
10
|
+
}
|
|
11
|
+
return Object.assign(Object.assign({}, patch), { hunks: patch.hunks.map(hunk => (Object.assign(Object.assign({}, hunk), { lines: hunk.lines.map((line, i) => {
|
|
12
|
+
var _a;
|
|
13
|
+
return (line.startsWith('\\') || line.endsWith('\r') || ((_a = hunk.lines[i + 1]) === null || _a === void 0 ? void 0 : _a.startsWith('\\')))
|
|
14
|
+
? line
|
|
15
|
+
: line + '\r';
|
|
16
|
+
}) }))) });
|
|
17
|
+
}
|
|
18
|
+
export function winToUnix(patch) {
|
|
19
|
+
if (Array.isArray(patch)) {
|
|
20
|
+
// (See comment above equivalent line in unixToWin)
|
|
21
|
+
return patch.map(p => winToUnix(p));
|
|
22
|
+
}
|
|
23
|
+
return Object.assign(Object.assign({}, patch), { hunks: patch.hunks.map(hunk => (Object.assign(Object.assign({}, hunk), { lines: hunk.lines.map(line => line.endsWith('\r') ? line.substring(0, line.length - 1) : line) }))) });
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Returns true if the patch consistently uses Unix line endings (or only involves one line and has
|
|
27
|
+
* no line endings).
|
|
28
|
+
*/
|
|
29
|
+
export function isUnix(patch) {
|
|
30
|
+
if (!Array.isArray(patch)) {
|
|
31
|
+
patch = [patch];
|
|
32
|
+
}
|
|
33
|
+
return !patch.some(index => index.hunks.some(hunk => hunk.lines.some(line => !line.startsWith('\\') && line.endsWith('\r'))));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Returns true if the patch uses Windows line endings and only Windows line endings.
|
|
37
|
+
*/
|
|
38
|
+
export function isWin(patch) {
|
|
39
|
+
if (!Array.isArray(patch)) {
|
|
40
|
+
patch = [patch];
|
|
41
|
+
}
|
|
42
|
+
return patch.some(index => index.hunks.some(hunk => hunk.lines.some(line => line.endsWith('\r'))))
|
|
43
|
+
&& patch.every(index => index.hunks.every(hunk => hunk.lines.every((line, i) => { var _a; return line.startsWith('\\') || line.endsWith('\r') || ((_a = hunk.lines[i + 1]) === null || _a === void 0 ? void 0 : _a.startsWith('\\')); })));
|
|
44
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a patch into structured data, in the same structure returned by `structuredPatch`.
|
|
3
|
+
*
|
|
4
|
+
* @return a JSON object representation of the a patch, suitable for use with the `applyPatch` method.
|
|
5
|
+
*/
|
|
6
|
+
export function parsePatch(uniDiff) {
|
|
7
|
+
const diffstr = uniDiff.split(/\n/), list = [];
|
|
8
|
+
let i = 0;
|
|
9
|
+
function parseIndex() {
|
|
10
|
+
const index = {};
|
|
11
|
+
list.push(index);
|
|
12
|
+
// Parse diff metadata
|
|
13
|
+
while (i < diffstr.length) {
|
|
14
|
+
const line = diffstr[i];
|
|
15
|
+
// File header found, end parsing diff metadata
|
|
16
|
+
if ((/^(---|\+\+\+|@@)\s/).test(line)) {
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
// Try to parse the line as a diff header, like
|
|
20
|
+
// Index: README.md
|
|
21
|
+
// or
|
|
22
|
+
// diff -r 9117c6561b0b -r 273ce12ad8f1 .hgignore
|
|
23
|
+
// or
|
|
24
|
+
// Index: something with multiple words
|
|
25
|
+
// and extract the filename (or whatever else is used as an index name)
|
|
26
|
+
// from the end (i.e. 'README.md', '.hgignore', or
|
|
27
|
+
// 'something with multiple words' in the examples above).
|
|
28
|
+
//
|
|
29
|
+
// TODO: It seems awkward that we indiscriminately trim off trailing
|
|
30
|
+
// whitespace here. Theoretically, couldn't that be meaningful -
|
|
31
|
+
// e.g. if the patch represents a diff of a file whose name ends
|
|
32
|
+
// with a space? Seems wrong to nuke it.
|
|
33
|
+
// But this behaviour has been around since v2.2.1 in 2015, so if
|
|
34
|
+
// it's going to change, it should be done cautiously and in a new
|
|
35
|
+
// major release, for backwards-compat reasons.
|
|
36
|
+
// -- ExplodingCabbage
|
|
37
|
+
const headerMatch = (/^(?:Index:|diff(?: -r \w+)+)\s+/).exec(line);
|
|
38
|
+
if (headerMatch) {
|
|
39
|
+
index.index = line.substring(headerMatch[0].length).trim();
|
|
40
|
+
}
|
|
41
|
+
i++;
|
|
42
|
+
}
|
|
43
|
+
// Parse file headers if they are defined. Unified diff requires them, but
|
|
44
|
+
// there's no technical issues to have an isolated hunk without file header
|
|
45
|
+
parseFileHeader(index);
|
|
46
|
+
parseFileHeader(index);
|
|
47
|
+
// Parse hunks
|
|
48
|
+
index.hunks = [];
|
|
49
|
+
while (i < diffstr.length) {
|
|
50
|
+
const line = diffstr[i];
|
|
51
|
+
if ((/^(Index:\s|diff\s|---\s|\+\+\+\s|===================================================================)/).test(line)) {
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
else if ((/^@@/).test(line)) {
|
|
55
|
+
index.hunks.push(parseHunk());
|
|
56
|
+
}
|
|
57
|
+
else if (line) {
|
|
58
|
+
throw new Error('Unknown line ' + (i + 1) + ' ' + JSON.stringify(line));
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
i++;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Parses the --- and +++ headers, if none are found, no lines
|
|
66
|
+
// are consumed.
|
|
67
|
+
function parseFileHeader(index) {
|
|
68
|
+
const fileHeaderMatch = (/^(---|\+\+\+)\s+/).exec(diffstr[i]);
|
|
69
|
+
if (fileHeaderMatch) {
|
|
70
|
+
const prefix = fileHeaderMatch[1], data = diffstr[i].substring(3).trim().split('\t', 2), header = (data[1] || '').trim();
|
|
71
|
+
let fileName = data[0].replace(/\\\\/g, '\\');
|
|
72
|
+
if (fileName.startsWith('"') && fileName.endsWith('"')) {
|
|
73
|
+
fileName = fileName.substr(1, fileName.length - 2);
|
|
74
|
+
}
|
|
75
|
+
if (prefix === '---') {
|
|
76
|
+
index.oldFileName = fileName;
|
|
77
|
+
index.oldHeader = header;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
index.newFileName = fileName;
|
|
81
|
+
index.newHeader = header;
|
|
82
|
+
}
|
|
83
|
+
i++;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Parses a hunk
|
|
87
|
+
// This assumes that we are at the start of a hunk.
|
|
88
|
+
function parseHunk() {
|
|
89
|
+
var _a;
|
|
90
|
+
const chunkHeaderIndex = i, chunkHeaderLine = diffstr[i++], chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);
|
|
91
|
+
const hunk = {
|
|
92
|
+
oldStart: +chunkHeader[1],
|
|
93
|
+
oldLines: typeof chunkHeader[2] === 'undefined' ? 1 : +chunkHeader[2],
|
|
94
|
+
newStart: +chunkHeader[3],
|
|
95
|
+
newLines: typeof chunkHeader[4] === 'undefined' ? 1 : +chunkHeader[4],
|
|
96
|
+
lines: []
|
|
97
|
+
};
|
|
98
|
+
// Unified Diff Format quirk: If the chunk size is 0,
|
|
99
|
+
// the first number is one lower than one would expect.
|
|
100
|
+
// https://www.artima.com/weblogs/viewpost.jsp?thread=164293
|
|
101
|
+
if (hunk.oldLines === 0) {
|
|
102
|
+
hunk.oldStart += 1;
|
|
103
|
+
}
|
|
104
|
+
if (hunk.newLines === 0) {
|
|
105
|
+
hunk.newStart += 1;
|
|
106
|
+
}
|
|
107
|
+
let addCount = 0, removeCount = 0;
|
|
108
|
+
for (; i < diffstr.length && (removeCount < hunk.oldLines || addCount < hunk.newLines || ((_a = diffstr[i]) === null || _a === void 0 ? void 0 : _a.startsWith('\\'))); i++) {
|
|
109
|
+
const operation = (diffstr[i].length == 0 && i != (diffstr.length - 1)) ? ' ' : diffstr[i][0];
|
|
110
|
+
if (operation === '+' || operation === '-' || operation === ' ' || operation === '\\') {
|
|
111
|
+
hunk.lines.push(diffstr[i]);
|
|
112
|
+
if (operation === '+') {
|
|
113
|
+
addCount++;
|
|
114
|
+
}
|
|
115
|
+
else if (operation === '-') {
|
|
116
|
+
removeCount++;
|
|
117
|
+
}
|
|
118
|
+
else if (operation === ' ') {
|
|
119
|
+
addCount++;
|
|
120
|
+
removeCount++;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
throw new Error(`Hunk at line ${chunkHeaderIndex + 1} contained invalid line ${diffstr[i]}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// Handle the empty block count case
|
|
128
|
+
if (!addCount && hunk.newLines === 1) {
|
|
129
|
+
hunk.newLines = 0;
|
|
130
|
+
}
|
|
131
|
+
if (!removeCount && hunk.oldLines === 1) {
|
|
132
|
+
hunk.oldLines = 0;
|
|
133
|
+
}
|
|
134
|
+
// Perform sanity checking
|
|
135
|
+
if (addCount !== hunk.newLines) {
|
|
136
|
+
throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1));
|
|
137
|
+
}
|
|
138
|
+
if (removeCount !== hunk.oldLines) {
|
|
139
|
+
throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1));
|
|
140
|
+
}
|
|
141
|
+
return hunk;
|
|
142
|
+
}
|
|
143
|
+
while (i < diffstr.length) {
|
|
144
|
+
parseIndex();
|
|
145
|
+
}
|
|
146
|
+
return list;
|
|
147
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function reversePatch(structuredPatch) {
|
|
2
|
+
if (Array.isArray(structuredPatch)) {
|
|
3
|
+
// (See comment in unixToWin for why we need the pointless-looking anonymous function here)
|
|
4
|
+
return structuredPatch.map(patch => reversePatch(patch)).reverse();
|
|
5
|
+
}
|
|
6
|
+
return Object.assign(Object.assign({}, structuredPatch), { oldFileName: structuredPatch.newFileName, oldHeader: structuredPatch.newHeader, newFileName: structuredPatch.oldFileName, newHeader: structuredPatch.oldHeader, hunks: structuredPatch.hunks.map(hunk => {
|
|
7
|
+
return {
|
|
8
|
+
oldLines: hunk.newLines,
|
|
9
|
+
oldStart: hunk.newStart,
|
|
10
|
+
newLines: hunk.oldLines,
|
|
11
|
+
newStart: hunk.oldStart,
|
|
12
|
+
lines: hunk.lines.map(l => {
|
|
13
|
+
if (l.startsWith('-')) {
|
|
14
|
+
return `+${l.slice(1)}`;
|
|
15
|
+
}
|
|
16
|
+
if (l.startsWith('+')) {
|
|
17
|
+
return `-${l.slice(1)}`;
|
|
18
|
+
}
|
|
19
|
+
return l;
|
|
20
|
+
})
|
|
21
|
+
};
|
|
22
|
+
}) });
|
|
23
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Iterator that traverses in the range of [min, max], stepping
|
|
2
|
+
// by distance from a given start position. I.e. for [0, 4], with
|
|
3
|
+
// start of 2, this will iterate 2, 3, 1, 4, 0.
|
|
4
|
+
export default function (start, minLine, maxLine) {
|
|
5
|
+
let wantForward = true, backwardExhausted = false, forwardExhausted = false, localOffset = 1;
|
|
6
|
+
return function iterator() {
|
|
7
|
+
if (wantForward && !forwardExhausted) {
|
|
8
|
+
if (backwardExhausted) {
|
|
9
|
+
localOffset++;
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
wantForward = false;
|
|
13
|
+
}
|
|
14
|
+
// Check if trying to fit beyond text length, and if not, check it fits
|
|
15
|
+
// after offset location (or desired location on first iteration)
|
|
16
|
+
if (start + localOffset <= maxLine) {
|
|
17
|
+
return start + localOffset;
|
|
18
|
+
}
|
|
19
|
+
forwardExhausted = true;
|
|
20
|
+
}
|
|
21
|
+
if (!backwardExhausted) {
|
|
22
|
+
if (!forwardExhausted) {
|
|
23
|
+
wantForward = true;
|
|
24
|
+
}
|
|
25
|
+
// Check if trying to fit before text beginning, and if not, check it fits
|
|
26
|
+
// before offset location
|
|
27
|
+
if (minLine <= start - localOffset) {
|
|
28
|
+
return start - localOffset++;
|
|
29
|
+
}
|
|
30
|
+
backwardExhausted = true;
|
|
31
|
+
return iterator();
|
|
32
|
+
}
|
|
33
|
+
// We tried to fit hunk before text beginning and beyond text length, then
|
|
34
|
+
// hunk can't fit on the text. Return undefined
|
|
35
|
+
return undefined;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function generateOptions(options, defaults) {
|
|
2
|
+
if (typeof options === 'function') {
|
|
3
|
+
defaults.callback = options;
|
|
4
|
+
}
|
|
5
|
+
else if (options) {
|
|
6
|
+
for (const name in options) {
|
|
7
|
+
/* istanbul ignore else */
|
|
8
|
+
if (Object.prototype.hasOwnProperty.call(options, name)) {
|
|
9
|
+
defaults[name] = options[name];
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return defaults;
|
|
14
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
export function longestCommonPrefix(str1, str2) {
|
|
2
|
+
let i;
|
|
3
|
+
for (i = 0; i < str1.length && i < str2.length; i++) {
|
|
4
|
+
if (str1[i] != str2[i]) {
|
|
5
|
+
return str1.slice(0, i);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
return str1.slice(0, i);
|
|
9
|
+
}
|
|
10
|
+
export function longestCommonSuffix(str1, str2) {
|
|
11
|
+
let i;
|
|
12
|
+
// Unlike longestCommonPrefix, we need a special case to handle all scenarios
|
|
13
|
+
// where we return the empty string since str1.slice(-0) will return the
|
|
14
|
+
// entire string.
|
|
15
|
+
if (!str1 || !str2 || str1[str1.length - 1] != str2[str2.length - 1]) {
|
|
16
|
+
return '';
|
|
17
|
+
}
|
|
18
|
+
for (i = 0; i < str1.length && i < str2.length; i++) {
|
|
19
|
+
if (str1[str1.length - (i + 1)] != str2[str2.length - (i + 1)]) {
|
|
20
|
+
return str1.slice(-i);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return str1.slice(-i);
|
|
24
|
+
}
|
|
25
|
+
export function replacePrefix(string, oldPrefix, newPrefix) {
|
|
26
|
+
if (string.slice(0, oldPrefix.length) != oldPrefix) {
|
|
27
|
+
throw Error(`string ${JSON.stringify(string)} doesn't start with prefix ${JSON.stringify(oldPrefix)}; this is a bug`);
|
|
28
|
+
}
|
|
29
|
+
return newPrefix + string.slice(oldPrefix.length);
|
|
30
|
+
}
|
|
31
|
+
export function replaceSuffix(string, oldSuffix, newSuffix) {
|
|
32
|
+
if (!oldSuffix) {
|
|
33
|
+
return string + newSuffix;
|
|
34
|
+
}
|
|
35
|
+
if (string.slice(-oldSuffix.length) != oldSuffix) {
|
|
36
|
+
throw Error(`string ${JSON.stringify(string)} doesn't end with suffix ${JSON.stringify(oldSuffix)}; this is a bug`);
|
|
37
|
+
}
|
|
38
|
+
return string.slice(0, -oldSuffix.length) + newSuffix;
|
|
39
|
+
}
|
|
40
|
+
export function removePrefix(string, oldPrefix) {
|
|
41
|
+
return replacePrefix(string, oldPrefix, '');
|
|
42
|
+
}
|
|
43
|
+
export function removeSuffix(string, oldSuffix) {
|
|
44
|
+
return replaceSuffix(string, oldSuffix, '');
|
|
45
|
+
}
|
|
46
|
+
export function maximumOverlap(string1, string2) {
|
|
47
|
+
return string2.slice(0, overlapCount(string1, string2));
|
|
48
|
+
}
|
|
49
|
+
// Nicked from https://stackoverflow.com/a/60422853/1709587
|
|
50
|
+
function overlapCount(a, b) {
|
|
51
|
+
// Deal with cases where the strings differ in length
|
|
52
|
+
let startA = 0;
|
|
53
|
+
if (a.length > b.length) {
|
|
54
|
+
startA = a.length - b.length;
|
|
55
|
+
}
|
|
56
|
+
let endB = b.length;
|
|
57
|
+
if (a.length < b.length) {
|
|
58
|
+
endB = a.length;
|
|
59
|
+
}
|
|
60
|
+
// Create a back-reference for each index
|
|
61
|
+
// that should be followed in case of a mismatch.
|
|
62
|
+
// We only need B to make these references:
|
|
63
|
+
const map = Array(endB);
|
|
64
|
+
let k = 0; // Index that lags behind j
|
|
65
|
+
map[0] = 0;
|
|
66
|
+
for (let j = 1; j < endB; j++) {
|
|
67
|
+
if (b[j] == b[k]) {
|
|
68
|
+
map[j] = map[k]; // skip over the same character (optional optimisation)
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
map[j] = k;
|
|
72
|
+
}
|
|
73
|
+
while (k > 0 && b[j] != b[k]) {
|
|
74
|
+
k = map[k];
|
|
75
|
+
}
|
|
76
|
+
if (b[j] == b[k]) {
|
|
77
|
+
k++;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Phase 2: use these references while iterating over A
|
|
81
|
+
k = 0;
|
|
82
|
+
for (let i = startA; i < a.length; i++) {
|
|
83
|
+
while (k > 0 && a[i] != b[k]) {
|
|
84
|
+
k = map[k];
|
|
85
|
+
}
|
|
86
|
+
if (a[i] == b[k]) {
|
|
87
|
+
k++;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return k;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Returns true if the string consistently uses Windows line endings.
|
|
94
|
+
*/
|
|
95
|
+
export function hasOnlyWinLineEndings(string) {
|
|
96
|
+
return string.includes('\r\n') && !string.startsWith('\n') && !string.match(/[^\r]\n/);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Returns true if the string consistently uses Unix line endings.
|
|
100
|
+
*/
|
|
101
|
+
export function hasOnlyUnixLineEndings(string) {
|
|
102
|
+
return !string.includes('\r\n') && string.includes('\n');
|
|
103
|
+
}
|
|
104
|
+
export function trailingWs(string) {
|
|
105
|
+
// Yes, this looks overcomplicated and dumb - why not replace the whole function with
|
|
106
|
+
// return string.match(/\s*$/)[0]
|
|
107
|
+
// you ask? Because:
|
|
108
|
+
// 1. the trap described at https://markamery.com/blog/quadratic-time-regexes/ would mean doing
|
|
109
|
+
// this would cause this function to take O(n²) time in the worst case (specifically when
|
|
110
|
+
// there is a massive run of NON-TRAILING whitespace in `string`), and
|
|
111
|
+
// 2. the fix proposed in the same blog post, of using a negative lookbehind, is incompatible
|
|
112
|
+
// with old Safari versions that we'd like to not break if possible (see
|
|
113
|
+
// https://github.com/kpdecker/jsdiff/pull/550)
|
|
114
|
+
// It feels absurd to do this with an explicit loop instead of a regex, but I really can't see a
|
|
115
|
+
// better way that doesn't result in broken behaviour.
|
|
116
|
+
let i;
|
|
117
|
+
for (i = string.length - 1; i >= 0; i--) {
|
|
118
|
+
if (!string[i].match(/\s/)) {
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return string.substring(i + 1);
|
|
123
|
+
}
|
|
124
|
+
export function leadingWs(string) {
|
|
125
|
+
// Thankfully the annoying considerations described in trailingWs don't apply here:
|
|
126
|
+
const match = string.match(/^\s*/);
|
|
127
|
+
return match ? match[0] : '';
|
|
128
|
+
}
|