@davmash/bygone 0.8.12 → 0.8.14
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/CHANGELOG.md +6 -0
- package/README.md +1 -1
- package/docs/explore.md +16 -10
- package/docs/product-surface.md +10 -10
- package/docs/releasing.md +5 -0
- package/media/webview.css +1 -1
- package/media/webview.js +152 -152
- package/out/changeTour.js +1319 -33
- package/out/standalone-main.js +1628 -191
- package/out/tourCoverage.js +1319 -33
- package/package.json +1 -1
- package/web/presenter.css +12 -0
- package/web/web-host.js +5 -5
package/out/standalone-main.js
CHANGED
|
@@ -3158,7 +3158,31 @@ var require_menuUtils = __commonJS({
|
|
|
3158
3158
|
canRemovePanel: isMultiDiff && (session2?.multi?.files?.length || 0) > 1
|
|
3159
3159
|
};
|
|
3160
3160
|
}
|
|
3161
|
-
|
|
3161
|
+
async function collectComparisonSelection2(choosePaths, confirmSelection) {
|
|
3162
|
+
const paths = [];
|
|
3163
|
+
while (true) {
|
|
3164
|
+
const selectedPaths = await choosePaths(paths.length);
|
|
3165
|
+
if (!Array.isArray(selectedPaths) || selectedPaths.length === 0) {
|
|
3166
|
+
return [];
|
|
3167
|
+
}
|
|
3168
|
+
for (const selectedPath of selectedPaths) {
|
|
3169
|
+
if (typeof selectedPath === "string" && !paths.includes(selectedPath)) {
|
|
3170
|
+
paths.push(selectedPath);
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3173
|
+
if (paths.length < 2) {
|
|
3174
|
+
continue;
|
|
3175
|
+
}
|
|
3176
|
+
const decision = await confirmSelection([...paths]);
|
|
3177
|
+
if (decision === "compare") {
|
|
3178
|
+
return paths;
|
|
3179
|
+
}
|
|
3180
|
+
if (decision !== "add") {
|
|
3181
|
+
return [];
|
|
3182
|
+
}
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3185
|
+
module2.exports = { collectComparisonSelection: collectComparisonSelection2, getMenuCapabilities: getMenuCapabilities2 };
|
|
3162
3186
|
}
|
|
3163
3187
|
});
|
|
3164
3188
|
|
|
@@ -4077,16 +4101,41 @@ var require_changeTour = __commonJS({
|
|
|
4077
4101
|
}
|
|
4078
4102
|
validateMultiPanelFiles(value.files, panels.length, index);
|
|
4079
4103
|
validateMultiPanelSteps(value.steps, panels.length, index, true);
|
|
4080
|
-
if (Array.isArray(value.steps) && value.steps.length !== panels.length - 1) {
|
|
4081
|
-
throw new Error(`scenes[${index}].steps must match the number of explanation stages.`);
|
|
4082
|
-
}
|
|
4083
4104
|
if (Array.isArray(value.steps)) {
|
|
4105
|
+
let previousPairIndex = -1;
|
|
4084
4106
|
value.steps.forEach((step, stepIndex) => {
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4107
|
+
if (!isRecord2(step)) return;
|
|
4108
|
+
const pairIndex = Number(step.pairIndex);
|
|
4109
|
+
if (pairIndex < previousPairIndex) {
|
|
4110
|
+
throw new Error(`scenes[${index}].steps must remain grouped in explanation stage order.`);
|
|
4111
|
+
}
|
|
4112
|
+
previousPairIndex = pairIndex;
|
|
4113
|
+
if (step.stageId !== void 0) requireString(step.stageId, `scenes[${index}].steps[${stepIndex}].stageId`);
|
|
4114
|
+
if (step.stageIndex !== void 0) requireNonNegativeInteger(step.stageIndex, `scenes[${index}].steps[${stepIndex}].stageIndex`);
|
|
4115
|
+
if (step.focusIndex !== void 0) requireNonNegativeInteger(step.focusIndex, `scenes[${index}].steps[${stepIndex}].focusIndex`);
|
|
4116
|
+
if (step.focusCount !== void 0) requirePositiveInteger(step.focusCount, `scenes[${index}].steps[${stepIndex}].focusCount`);
|
|
4117
|
+
if (step.stageIndex !== void 0 && Number(step.stageIndex) !== pairIndex) {
|
|
4118
|
+
throw new Error(`scenes[${index}].steps[${stepIndex}].stageIndex must match pairIndex.`);
|
|
4119
|
+
}
|
|
4120
|
+
if (step.focusIndex !== void 0 && step.focusCount !== void 0 && Number(step.focusIndex) >= Number(step.focusCount)) {
|
|
4121
|
+
throw new Error(`scenes[${index}].steps[${stepIndex}].focusIndex must be less than focusCount.`);
|
|
4088
4122
|
}
|
|
4089
4123
|
});
|
|
4124
|
+
for (let stageIndex = 0; stageIndex < panels.length - 1; stageIndex += 1) {
|
|
4125
|
+
const panel = panels[stageIndex + 1];
|
|
4126
|
+
const stageSteps = value.steps.filter((step) => isRecord2(step) && Number(step.pairIndex) === stageIndex);
|
|
4127
|
+
if (!isRecord2(panel) || stageSteps.length === 0) {
|
|
4128
|
+
throw new Error(`scenes[${index}].steps must cover every explanation stage.`);
|
|
4129
|
+
}
|
|
4130
|
+
const firstStep = stageSteps[0];
|
|
4131
|
+
const fallbackStageId = isRecord2(firstStep) ? firstStep.id : void 0;
|
|
4132
|
+
if (stageSteps.some((step) => {
|
|
4133
|
+
if (!isRecord2(step)) return true;
|
|
4134
|
+
return (step.stageId ?? fallbackStageId) !== panel.stageId;
|
|
4135
|
+
})) {
|
|
4136
|
+
throw new Error(`scenes[${index}].panels must align with explanation stage ids.`);
|
|
4137
|
+
}
|
|
4138
|
+
}
|
|
4090
4139
|
}
|
|
4091
4140
|
}
|
|
4092
4141
|
function validateMultiPanelFiles(value, panelCount, index) {
|
|
@@ -4115,7 +4164,6 @@ var require_changeTour = __commonJS({
|
|
|
4115
4164
|
if (deconstructed) {
|
|
4116
4165
|
requireStringArray(step.introducedHunks, `scenes[${index}].steps[${stepIndex}].introducedHunks`);
|
|
4117
4166
|
if (step.introducedHunks.length === 0) throw new Error(`scenes[${index}].steps[${stepIndex}].introducedHunks must be non-empty.`);
|
|
4118
|
-
if (Number(step.pairIndex) !== stepIndex) throw new Error(`scenes[${index}].steps[${stepIndex}].pairIndex must match its explanation stage.`);
|
|
4119
4167
|
}
|
|
4120
4168
|
if (step.side !== "left" && step.side !== "right") throw new Error(`scenes[${index}].steps[${stepIndex}].side must be left or right.`);
|
|
4121
4169
|
if (step.startLine !== void 0) requirePositiveInteger(step.startLine, `scenes[${index}].steps[${stepIndex}].startLine`);
|
|
@@ -4755,85 +4803,1282 @@ var require_changeTour = __commonJS({
|
|
|
4755
4803
|
for (const { stage, claims } of stageClaims) {
|
|
4756
4804
|
const introducedHunks = [];
|
|
4757
4805
|
const introducedFiles = [];
|
|
4806
|
+
const introducedChanges = [];
|
|
4758
4807
|
for (const claim of claims) {
|
|
4759
4808
|
const cumulative = cumulativeByFile.get(claim.file) || /* @__PURE__ */ new Set();
|
|
4760
4809
|
claim.ids.forEach((id) => {
|
|
4761
4810
|
cumulative.add(id);
|
|
4762
4811
|
introducedHunks.push(id);
|
|
4763
4812
|
});
|
|
4764
|
-
cumulativeByFile.set(claim.file, cumulative);
|
|
4765
|
-
if (!introducedFiles.includes(claim.file.path)) introducedFiles.push(claim.file.path);
|
|
4813
|
+
cumulativeByFile.set(claim.file, cumulative);
|
|
4814
|
+
if (!introducedFiles.includes(claim.file.path)) introducedFiles.push(claim.file.path);
|
|
4815
|
+
const introducedChange = introducedChanges.find((change) => change.file === claim.file.path);
|
|
4816
|
+
if (introducedChange) {
|
|
4817
|
+
introducedChange.hunks.push(...claim.ids);
|
|
4818
|
+
} else {
|
|
4819
|
+
introducedChanges.push({ file: claim.file.path, hunks: [...claim.ids] });
|
|
4820
|
+
}
|
|
4821
|
+
}
|
|
4822
|
+
stages.push({
|
|
4823
|
+
id: stage.id,
|
|
4824
|
+
title: stage.title,
|
|
4825
|
+
narration: stage.narration,
|
|
4826
|
+
introducedHunks,
|
|
4827
|
+
introducedFiles,
|
|
4828
|
+
introducedChanges,
|
|
4829
|
+
cumulativeHunks: [...cumulativeByFile.values()].flatMap((ids) => [...ids]),
|
|
4830
|
+
files: materializableFiles.map((file) => buildFileState(
|
|
4831
|
+
file,
|
|
4832
|
+
cumulativeByFile.get(file) || /* @__PURE__ */ new Set(),
|
|
4833
|
+
wholeFileExclusions.has(file)
|
|
4834
|
+
))
|
|
4835
|
+
});
|
|
4836
|
+
}
|
|
4837
|
+
for (const file of materializableFiles) {
|
|
4838
|
+
const finalState = buildFileState(
|
|
4839
|
+
file,
|
|
4840
|
+
cumulativeByFile.get(file) || /* @__PURE__ */ new Set(),
|
|
4841
|
+
wholeFileExclusions.has(file)
|
|
4842
|
+
);
|
|
4843
|
+
const expectedExists = file.changeKind !== "deleted";
|
|
4844
|
+
if (finalState.exists !== expectedExists || finalState.content !== file.headContent) {
|
|
4845
|
+
throw new Error(`Final deconstructed state does not match the target for ${file.path}.`);
|
|
4846
|
+
}
|
|
4847
|
+
}
|
|
4848
|
+
return {
|
|
4849
|
+
id: scene.id,
|
|
4850
|
+
title: scene.title,
|
|
4851
|
+
baseOid: inventory.range.baseOid,
|
|
4852
|
+
targetOid: inventory.range.headOid,
|
|
4853
|
+
baselineFiles,
|
|
4854
|
+
excludedHunks: [...excludedByFile.values()].flatMap((ids) => [...ids]),
|
|
4855
|
+
excludedFiles,
|
|
4856
|
+
stages
|
|
4857
|
+
};
|
|
4858
|
+
}
|
|
4859
|
+
function requireInventoryFile(inventory, path9, owner) {
|
|
4860
|
+
const matches = inventory.files.filter((file) => file.path === path9 || file.previousPath === path9);
|
|
4861
|
+
if (matches.length === 0) throw new Error(`${owner} references a file outside the change inventory: ${path9}`);
|
|
4862
|
+
if (matches.length > 1) throw new Error(`${owner} references an ambiguous changed path: ${path9}`);
|
|
4863
|
+
return matches[0];
|
|
4864
|
+
}
|
|
4865
|
+
function claimHunks(file, ids, owner, ownership) {
|
|
4866
|
+
const known = new Set(file.units.map((unit) => unit.id));
|
|
4867
|
+
for (const id of ids) {
|
|
4868
|
+
if (!known.has(id)) throw new Error(`${owner} references an unknown hunk in ${file.path}: ${id}`);
|
|
4869
|
+
const key = unitKey(file, id);
|
|
4870
|
+
const previousOwner = ownership.get(key);
|
|
4871
|
+
if (previousOwner) throw new Error(`Change unit ${id} in ${file.path} is assigned to both ${previousOwner} and ${owner}.`);
|
|
4872
|
+
ownership.set(key, owner);
|
|
4873
|
+
}
|
|
4874
|
+
}
|
|
4875
|
+
function unitKey(file, id) {
|
|
4876
|
+
return `${file.previousPath || ""}\0${file.path}\0${id}`;
|
|
4877
|
+
}
|
|
4878
|
+
function buildFileState(file, selected, wholeFileExcluded) {
|
|
4879
|
+
if (file.baseContent === void 0) throw new Error(`Missing base content for ${file.path}.`);
|
|
4880
|
+
const content = materializeChangeUnits(file.baseContent, file.units, selected);
|
|
4881
|
+
let exists = true;
|
|
4882
|
+
if (file.changeKind === "added") exists = wholeFileExcluded || selected.size > 0;
|
|
4883
|
+
if (file.changeKind === "deleted") exists = !(wholeFileExcluded || selected.size === file.units.length);
|
|
4884
|
+
return {
|
|
4885
|
+
path: file.path,
|
|
4886
|
+
previousPath: file.previousPath,
|
|
4887
|
+
changeKind: file.changeKind,
|
|
4888
|
+
exists,
|
|
4889
|
+
content: exists ? content : "",
|
|
4890
|
+
appliedHunks: [...selected]
|
|
4891
|
+
};
|
|
4892
|
+
}
|
|
4893
|
+
function Diff2() {
|
|
4894
|
+
}
|
|
4895
|
+
Diff2.prototype = {
|
|
4896
|
+
diff: function diff2(oldString, newString) {
|
|
4897
|
+
var _options$timeout;
|
|
4898
|
+
var options = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : {};
|
|
4899
|
+
var callback = options.callback;
|
|
4900
|
+
if (typeof options === "function") {
|
|
4901
|
+
callback = options;
|
|
4902
|
+
options = {};
|
|
4903
|
+
}
|
|
4904
|
+
this.options = options;
|
|
4905
|
+
var self2 = this;
|
|
4906
|
+
function done(value) {
|
|
4907
|
+
if (callback) {
|
|
4908
|
+
setTimeout(function() {
|
|
4909
|
+
callback(void 0, value);
|
|
4910
|
+
}, 0);
|
|
4911
|
+
return true;
|
|
4912
|
+
} else {
|
|
4913
|
+
return value;
|
|
4914
|
+
}
|
|
4915
|
+
}
|
|
4916
|
+
oldString = this.castInput(oldString);
|
|
4917
|
+
newString = this.castInput(newString);
|
|
4918
|
+
oldString = this.removeEmpty(this.tokenize(oldString));
|
|
4919
|
+
newString = this.removeEmpty(this.tokenize(newString));
|
|
4920
|
+
var newLen = newString.length, oldLen = oldString.length;
|
|
4921
|
+
var editLength = 1;
|
|
4922
|
+
var maxEditLength = newLen + oldLen;
|
|
4923
|
+
if (options.maxEditLength) {
|
|
4924
|
+
maxEditLength = Math.min(maxEditLength, options.maxEditLength);
|
|
4925
|
+
}
|
|
4926
|
+
var maxExecutionTime = (_options$timeout = options.timeout) !== null && _options$timeout !== void 0 ? _options$timeout : Infinity;
|
|
4927
|
+
var abortAfterTimestamp = Date.now() + maxExecutionTime;
|
|
4928
|
+
var bestPath = [{
|
|
4929
|
+
oldPos: -1,
|
|
4930
|
+
lastComponent: void 0
|
|
4931
|
+
}];
|
|
4932
|
+
var newPos = this.extractCommon(bestPath[0], newString, oldString, 0);
|
|
4933
|
+
if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
|
|
4934
|
+
return done([{
|
|
4935
|
+
value: this.join(newString),
|
|
4936
|
+
count: newString.length
|
|
4937
|
+
}]);
|
|
4938
|
+
}
|
|
4939
|
+
var minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
|
|
4940
|
+
function execEditLength() {
|
|
4941
|
+
for (var diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
|
|
4942
|
+
var basePath = void 0;
|
|
4943
|
+
var removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
|
|
4944
|
+
if (removePath) {
|
|
4945
|
+
bestPath[diagonalPath - 1] = void 0;
|
|
4946
|
+
}
|
|
4947
|
+
var canAdd = false;
|
|
4948
|
+
if (addPath) {
|
|
4949
|
+
var addPathNewPos = addPath.oldPos - diagonalPath;
|
|
4950
|
+
canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
|
|
4951
|
+
}
|
|
4952
|
+
var canRemove = removePath && removePath.oldPos + 1 < oldLen;
|
|
4953
|
+
if (!canAdd && !canRemove) {
|
|
4954
|
+
bestPath[diagonalPath] = void 0;
|
|
4955
|
+
continue;
|
|
4956
|
+
}
|
|
4957
|
+
if (!canRemove || canAdd && removePath.oldPos + 1 < addPath.oldPos) {
|
|
4958
|
+
basePath = self2.addToPath(addPath, true, void 0, 0);
|
|
4959
|
+
} else {
|
|
4960
|
+
basePath = self2.addToPath(removePath, void 0, true, 1);
|
|
4961
|
+
}
|
|
4962
|
+
newPos = self2.extractCommon(basePath, newString, oldString, diagonalPath);
|
|
4963
|
+
if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
|
|
4964
|
+
return done(buildValues2(self2, basePath.lastComponent, newString, oldString, self2.useLongestToken));
|
|
4965
|
+
} else {
|
|
4966
|
+
bestPath[diagonalPath] = basePath;
|
|
4967
|
+
if (basePath.oldPos + 1 >= oldLen) {
|
|
4968
|
+
maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
|
|
4969
|
+
}
|
|
4970
|
+
if (newPos + 1 >= newLen) {
|
|
4971
|
+
minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
|
|
4972
|
+
}
|
|
4973
|
+
}
|
|
4974
|
+
}
|
|
4975
|
+
editLength++;
|
|
4976
|
+
}
|
|
4977
|
+
if (callback) {
|
|
4978
|
+
(function exec() {
|
|
4979
|
+
setTimeout(function() {
|
|
4980
|
+
if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
|
|
4981
|
+
return callback();
|
|
4982
|
+
}
|
|
4983
|
+
if (!execEditLength()) {
|
|
4984
|
+
exec();
|
|
4985
|
+
}
|
|
4986
|
+
}, 0);
|
|
4987
|
+
})();
|
|
4988
|
+
} else {
|
|
4989
|
+
while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
|
|
4990
|
+
var ret = execEditLength();
|
|
4991
|
+
if (ret) {
|
|
4992
|
+
return ret;
|
|
4993
|
+
}
|
|
4994
|
+
}
|
|
4995
|
+
}
|
|
4996
|
+
},
|
|
4997
|
+
addToPath: function addToPath2(path9, added, removed, oldPosInc) {
|
|
4998
|
+
var last = path9.lastComponent;
|
|
4999
|
+
if (last && last.added === added && last.removed === removed) {
|
|
5000
|
+
return {
|
|
5001
|
+
oldPos: path9.oldPos + oldPosInc,
|
|
5002
|
+
lastComponent: {
|
|
5003
|
+
count: last.count + 1,
|
|
5004
|
+
added,
|
|
5005
|
+
removed,
|
|
5006
|
+
previousComponent: last.previousComponent
|
|
5007
|
+
}
|
|
5008
|
+
};
|
|
5009
|
+
} else {
|
|
5010
|
+
return {
|
|
5011
|
+
oldPos: path9.oldPos + oldPosInc,
|
|
5012
|
+
lastComponent: {
|
|
5013
|
+
count: 1,
|
|
5014
|
+
added,
|
|
5015
|
+
removed,
|
|
5016
|
+
previousComponent: last
|
|
5017
|
+
}
|
|
5018
|
+
};
|
|
5019
|
+
}
|
|
5020
|
+
},
|
|
5021
|
+
extractCommon: function extractCommon2(basePath, newString, oldString, diagonalPath) {
|
|
5022
|
+
var newLen = newString.length, oldLen = oldString.length, oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
|
|
5023
|
+
while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) {
|
|
5024
|
+
newPos++;
|
|
5025
|
+
oldPos++;
|
|
5026
|
+
commonCount++;
|
|
5027
|
+
}
|
|
5028
|
+
if (commonCount) {
|
|
5029
|
+
basePath.lastComponent = {
|
|
5030
|
+
count: commonCount,
|
|
5031
|
+
previousComponent: basePath.lastComponent
|
|
5032
|
+
};
|
|
5033
|
+
}
|
|
5034
|
+
basePath.oldPos = oldPos;
|
|
5035
|
+
return newPos;
|
|
5036
|
+
},
|
|
5037
|
+
equals: function equals2(left, right) {
|
|
5038
|
+
if (this.options.comparator) {
|
|
5039
|
+
return this.options.comparator(left, right);
|
|
5040
|
+
} else {
|
|
5041
|
+
return left === right || this.options.ignoreCase && left.toLowerCase() === right.toLowerCase();
|
|
5042
|
+
}
|
|
5043
|
+
},
|
|
5044
|
+
removeEmpty: function removeEmpty2(array) {
|
|
5045
|
+
var ret = [];
|
|
5046
|
+
for (var i = 0; i < array.length; i++) {
|
|
5047
|
+
if (array[i]) {
|
|
5048
|
+
ret.push(array[i]);
|
|
5049
|
+
}
|
|
5050
|
+
}
|
|
5051
|
+
return ret;
|
|
5052
|
+
},
|
|
5053
|
+
castInput: function castInput2(value) {
|
|
5054
|
+
return value;
|
|
5055
|
+
},
|
|
5056
|
+
tokenize: function tokenize2(value) {
|
|
5057
|
+
return value.split("");
|
|
5058
|
+
},
|
|
5059
|
+
join: function join4(chars) {
|
|
5060
|
+
return chars.join("");
|
|
5061
|
+
}
|
|
5062
|
+
};
|
|
5063
|
+
function buildValues2(diff2, lastComponent, newString, oldString, useLongestToken) {
|
|
5064
|
+
var components = [];
|
|
5065
|
+
var nextComponent;
|
|
5066
|
+
while (lastComponent) {
|
|
5067
|
+
components.push(lastComponent);
|
|
5068
|
+
nextComponent = lastComponent.previousComponent;
|
|
5069
|
+
delete lastComponent.previousComponent;
|
|
5070
|
+
lastComponent = nextComponent;
|
|
5071
|
+
}
|
|
5072
|
+
components.reverse();
|
|
5073
|
+
var componentPos = 0, componentLen = components.length, newPos = 0, oldPos = 0;
|
|
5074
|
+
for (; componentPos < componentLen; componentPos++) {
|
|
5075
|
+
var component = components[componentPos];
|
|
5076
|
+
if (!component.removed) {
|
|
5077
|
+
if (!component.added && useLongestToken) {
|
|
5078
|
+
var value = newString.slice(newPos, newPos + component.count);
|
|
5079
|
+
value = value.map(function(value2, i) {
|
|
5080
|
+
var oldValue = oldString[oldPos + i];
|
|
5081
|
+
return oldValue.length > value2.length ? oldValue : value2;
|
|
5082
|
+
});
|
|
5083
|
+
component.value = diff2.join(value);
|
|
5084
|
+
} else {
|
|
5085
|
+
component.value = diff2.join(newString.slice(newPos, newPos + component.count));
|
|
5086
|
+
}
|
|
5087
|
+
newPos += component.count;
|
|
5088
|
+
if (!component.added) {
|
|
5089
|
+
oldPos += component.count;
|
|
5090
|
+
}
|
|
5091
|
+
} else {
|
|
5092
|
+
component.value = diff2.join(oldString.slice(oldPos, oldPos + component.count));
|
|
5093
|
+
oldPos += component.count;
|
|
5094
|
+
if (componentPos && components[componentPos - 1].added) {
|
|
5095
|
+
var tmp = components[componentPos - 1];
|
|
5096
|
+
components[componentPos - 1] = components[componentPos];
|
|
5097
|
+
components[componentPos] = tmp;
|
|
5098
|
+
}
|
|
5099
|
+
}
|
|
5100
|
+
}
|
|
5101
|
+
var finalComponent = components[componentLen - 1];
|
|
5102
|
+
if (componentLen > 1 && typeof finalComponent.value === "string" && (finalComponent.added || finalComponent.removed) && diff2.equals("", finalComponent.value)) {
|
|
5103
|
+
components[componentLen - 2].value += finalComponent.value;
|
|
5104
|
+
components.pop();
|
|
5105
|
+
}
|
|
5106
|
+
return components;
|
|
5107
|
+
}
|
|
5108
|
+
var characterDiff2 = new Diff2();
|
|
5109
|
+
function diffChars2(oldStr, newStr, options) {
|
|
5110
|
+
return characterDiff2.diff(oldStr, newStr, options);
|
|
5111
|
+
}
|
|
5112
|
+
var extendedWordChars2 = /^[A-Za-z\xC0-\u02C6\u02C8-\u02D7\u02DE-\u02FF\u1E00-\u1EFF]+$/;
|
|
5113
|
+
var reWhitespace2 = /\S/;
|
|
5114
|
+
var wordDiff2 = new Diff2();
|
|
5115
|
+
wordDiff2.equals = function(left, right) {
|
|
5116
|
+
if (this.options.ignoreCase) {
|
|
5117
|
+
left = left.toLowerCase();
|
|
5118
|
+
right = right.toLowerCase();
|
|
5119
|
+
}
|
|
5120
|
+
return left === right || this.options.ignoreWhitespace && !reWhitespace2.test(left) && !reWhitespace2.test(right);
|
|
5121
|
+
};
|
|
5122
|
+
wordDiff2.tokenize = function(value) {
|
|
5123
|
+
var tokens = value.split(/([^\S\r\n]+|[()[\]{}'"\r\n]|\b)/);
|
|
5124
|
+
for (var i = 0; i < tokens.length - 1; i++) {
|
|
5125
|
+
if (!tokens[i + 1] && tokens[i + 2] && extendedWordChars2.test(tokens[i]) && extendedWordChars2.test(tokens[i + 2])) {
|
|
5126
|
+
tokens[i] += tokens[i + 2];
|
|
5127
|
+
tokens.splice(i + 1, 2);
|
|
5128
|
+
i--;
|
|
5129
|
+
}
|
|
5130
|
+
}
|
|
5131
|
+
return tokens;
|
|
5132
|
+
};
|
|
5133
|
+
function diffWordsWithSpace2(oldStr, newStr, options) {
|
|
5134
|
+
return wordDiff2.diff(oldStr, newStr, options);
|
|
5135
|
+
}
|
|
5136
|
+
var lineDiff2 = new Diff2();
|
|
5137
|
+
lineDiff2.tokenize = function(value) {
|
|
5138
|
+
if (this.options.stripTrailingCr) {
|
|
5139
|
+
value = value.replace(/\r\n/g, "\n");
|
|
5140
|
+
}
|
|
5141
|
+
var retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
|
|
5142
|
+
if (!linesAndNewlines[linesAndNewlines.length - 1]) {
|
|
5143
|
+
linesAndNewlines.pop();
|
|
5144
|
+
}
|
|
5145
|
+
for (var i = 0; i < linesAndNewlines.length; i++) {
|
|
5146
|
+
var line = linesAndNewlines[i];
|
|
5147
|
+
if (i % 2 && !this.options.newlineIsToken) {
|
|
5148
|
+
retLines[retLines.length - 1] += line;
|
|
5149
|
+
} else {
|
|
5150
|
+
if (this.options.ignoreWhitespace) {
|
|
5151
|
+
line = line.trim();
|
|
5152
|
+
}
|
|
5153
|
+
retLines.push(line);
|
|
5154
|
+
}
|
|
5155
|
+
}
|
|
5156
|
+
return retLines;
|
|
5157
|
+
};
|
|
5158
|
+
var sentenceDiff2 = new Diff2();
|
|
5159
|
+
sentenceDiff2.tokenize = function(value) {
|
|
5160
|
+
return value.split(/(\S.+?[.!?])(?=\s+|$)/);
|
|
5161
|
+
};
|
|
5162
|
+
var cssDiff2 = new Diff2();
|
|
5163
|
+
cssDiff2.tokenize = function(value) {
|
|
5164
|
+
return value.split(/([{}:;,]|\s+)/);
|
|
5165
|
+
};
|
|
5166
|
+
function _typeof2(obj) {
|
|
5167
|
+
"@babel/helpers - typeof";
|
|
5168
|
+
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
|
|
5169
|
+
_typeof2 = function(obj2) {
|
|
5170
|
+
return typeof obj2;
|
|
5171
|
+
};
|
|
5172
|
+
} else {
|
|
5173
|
+
_typeof2 = function(obj2) {
|
|
5174
|
+
return obj2 && typeof Symbol === "function" && obj2.constructor === Symbol && obj2 !== Symbol.prototype ? "symbol" : typeof obj2;
|
|
5175
|
+
};
|
|
5176
|
+
}
|
|
5177
|
+
return _typeof2(obj);
|
|
5178
|
+
}
|
|
5179
|
+
var objectPrototypeToString2 = Object.prototype.toString;
|
|
5180
|
+
var jsonDiff2 = new Diff2();
|
|
5181
|
+
jsonDiff2.useLongestToken = true;
|
|
5182
|
+
jsonDiff2.tokenize = lineDiff2.tokenize;
|
|
5183
|
+
jsonDiff2.castInput = function(value) {
|
|
5184
|
+
var _this$options = this.options, undefinedReplacement = _this$options.undefinedReplacement, _this$options$stringi = _this$options.stringifyReplacer, stringifyReplacer = _this$options$stringi === void 0 ? function(k, v) {
|
|
5185
|
+
return typeof v === "undefined" ? undefinedReplacement : v;
|
|
5186
|
+
} : _this$options$stringi;
|
|
5187
|
+
return typeof value === "string" ? value : JSON.stringify(canonicalize2(value, null, null, stringifyReplacer), stringifyReplacer, " ");
|
|
5188
|
+
};
|
|
5189
|
+
jsonDiff2.equals = function(left, right) {
|
|
5190
|
+
return Diff2.prototype.equals.call(jsonDiff2, left.replace(/,([\r\n])/g, "$1"), right.replace(/,([\r\n])/g, "$1"));
|
|
5191
|
+
};
|
|
5192
|
+
function canonicalize2(obj, stack, replacementStack, replacer, key) {
|
|
5193
|
+
stack = stack || [];
|
|
5194
|
+
replacementStack = replacementStack || [];
|
|
5195
|
+
if (replacer) {
|
|
5196
|
+
obj = replacer(key, obj);
|
|
5197
|
+
}
|
|
5198
|
+
var i;
|
|
5199
|
+
for (i = 0; i < stack.length; i += 1) {
|
|
5200
|
+
if (stack[i] === obj) {
|
|
5201
|
+
return replacementStack[i];
|
|
5202
|
+
}
|
|
5203
|
+
}
|
|
5204
|
+
var canonicalizedObj;
|
|
5205
|
+
if ("[object Array]" === objectPrototypeToString2.call(obj)) {
|
|
5206
|
+
stack.push(obj);
|
|
5207
|
+
canonicalizedObj = new Array(obj.length);
|
|
5208
|
+
replacementStack.push(canonicalizedObj);
|
|
5209
|
+
for (i = 0; i < obj.length; i += 1) {
|
|
5210
|
+
canonicalizedObj[i] = canonicalize2(obj[i], stack, replacementStack, replacer, key);
|
|
5211
|
+
}
|
|
5212
|
+
stack.pop();
|
|
5213
|
+
replacementStack.pop();
|
|
5214
|
+
return canonicalizedObj;
|
|
5215
|
+
}
|
|
5216
|
+
if (obj && obj.toJSON) {
|
|
5217
|
+
obj = obj.toJSON();
|
|
5218
|
+
}
|
|
5219
|
+
if (_typeof2(obj) === "object" && obj !== null) {
|
|
5220
|
+
stack.push(obj);
|
|
5221
|
+
canonicalizedObj = {};
|
|
5222
|
+
replacementStack.push(canonicalizedObj);
|
|
5223
|
+
var sortedKeys = [], _key;
|
|
5224
|
+
for (_key in obj) {
|
|
5225
|
+
if (obj.hasOwnProperty(_key)) {
|
|
5226
|
+
sortedKeys.push(_key);
|
|
5227
|
+
}
|
|
5228
|
+
}
|
|
5229
|
+
sortedKeys.sort();
|
|
5230
|
+
for (i = 0; i < sortedKeys.length; i += 1) {
|
|
5231
|
+
_key = sortedKeys[i];
|
|
5232
|
+
canonicalizedObj[_key] = canonicalize2(obj[_key], stack, replacementStack, replacer, _key);
|
|
5233
|
+
}
|
|
5234
|
+
stack.pop();
|
|
5235
|
+
replacementStack.pop();
|
|
5236
|
+
} else {
|
|
5237
|
+
canonicalizedObj = obj;
|
|
5238
|
+
}
|
|
5239
|
+
return canonicalizedObj;
|
|
5240
|
+
}
|
|
5241
|
+
var arrayDiff2 = new Diff2();
|
|
5242
|
+
arrayDiff2.tokenize = function(value) {
|
|
5243
|
+
return value.slice();
|
|
5244
|
+
};
|
|
5245
|
+
arrayDiff2.join = arrayDiff2.removeEmpty = function(value) {
|
|
5246
|
+
return value;
|
|
5247
|
+
};
|
|
5248
|
+
function diffArrays2(oldArr, newArr, callback) {
|
|
5249
|
+
return arrayDiff2.diff(oldArr, newArr, callback);
|
|
5250
|
+
}
|
|
5251
|
+
var DEFAULT_MAX_INLINE_HIGHLIGHT_LINE_LENGTH2 = 500;
|
|
5252
|
+
var DEFAULT_DIFF_TIMEOUT_MS2 = 100;
|
|
5253
|
+
var MAX_INLINE_HIGHLIGHT_LINE_LENGTH2 = readPositiveIntegerEnv4(
|
|
5254
|
+
"BYGONE_MAX_INLINE_HIGHLIGHT_LINE_LENGTH",
|
|
5255
|
+
DEFAULT_MAX_INLINE_HIGHLIGHT_LINE_LENGTH2
|
|
5256
|
+
);
|
|
5257
|
+
var DIFF_TIMEOUT_MS2 = readPositiveIntegerEnv4("BYGONE_DIFF_TIMEOUT_MS", DEFAULT_DIFF_TIMEOUT_MS2);
|
|
5258
|
+
function buildTwoWayDiffModel3(leftContent, rightContent, options = {}) {
|
|
5259
|
+
const leftLines = normalizeLines2(leftContent);
|
|
5260
|
+
const rightLines = normalizeLines2(rightContent);
|
|
5261
|
+
const changes = diffArrays2(leftLines, rightLines, {
|
|
5262
|
+
timeout: options.timeoutMs ?? DIFF_TIMEOUT_MS2
|
|
5263
|
+
});
|
|
5264
|
+
if (!changes) {
|
|
5265
|
+
return buildConservativeTwoWayDiffModel2(leftLines, rightLines);
|
|
5266
|
+
}
|
|
5267
|
+
const rows = [];
|
|
5268
|
+
const renderedLeftLines = [];
|
|
5269
|
+
const renderedRightLines = [];
|
|
5270
|
+
const blocks = [];
|
|
5271
|
+
let leftLineNumber = 1;
|
|
5272
|
+
let rightLineNumber = 1;
|
|
5273
|
+
for (let index = 0; index < changes.length; index++) {
|
|
5274
|
+
const change = changes[index];
|
|
5275
|
+
const removedLines = change.removed ? change.value : [];
|
|
5276
|
+
const addedLines = change.added ? change.value : [];
|
|
5277
|
+
if (!change.added && !change.removed) {
|
|
5278
|
+
for (const line of change.value) {
|
|
5279
|
+
renderedLeftLines.push(makeDiffLine2("context", line, leftLineNumber));
|
|
5280
|
+
renderedRightLines.push(makeDiffLine2("context", line, rightLineNumber));
|
|
5281
|
+
rows.push(makeDiffRow2(
|
|
5282
|
+
makeDiffCell2("context", line, leftLineNumber++),
|
|
5283
|
+
makeDiffCell2("context", line, rightLineNumber++)
|
|
5284
|
+
));
|
|
5285
|
+
}
|
|
5286
|
+
continue;
|
|
5287
|
+
}
|
|
5288
|
+
if (change.removed && index + 1 < changes.length && changes[index + 1].added) {
|
|
5289
|
+
const nextChange = changes[index + 1];
|
|
5290
|
+
const leftStart = renderedLeftLines.length;
|
|
5291
|
+
const rightStart = renderedRightLines.length;
|
|
5292
|
+
const alignedLines = alignReplacementLines2(removedLines, nextChange.value);
|
|
5293
|
+
for (const { left: removedLine, right: addedLine } of alignedLines) {
|
|
5294
|
+
let renderedLeftLine;
|
|
5295
|
+
let renderedRightLine;
|
|
5296
|
+
if (removedLine !== void 0) {
|
|
5297
|
+
renderedLeftLine = makeDiffLine2("removed", removedLine, leftLineNumber);
|
|
5298
|
+
renderedLeftLines.push(renderedLeftLine);
|
|
5299
|
+
}
|
|
5300
|
+
if (addedLine !== void 0) {
|
|
5301
|
+
renderedRightLine = makeDiffLine2("added", addedLine, rightLineNumber);
|
|
5302
|
+
renderedRightLines.push(renderedRightLine);
|
|
5303
|
+
}
|
|
5304
|
+
if (renderedLeftLine && renderedRightLine) {
|
|
5305
|
+
applyInlineHighlightPair2(renderedLeftLine, renderedRightLine);
|
|
5306
|
+
}
|
|
5307
|
+
rows.push(makeDiffRow2(
|
|
5308
|
+
removedLine === void 0 ? makePlaceholder2() : makeDiffCell2("removed", removedLine, leftLineNumber++),
|
|
5309
|
+
addedLine === void 0 ? makePlaceholder2() : makeDiffCell2("added", addedLine, rightLineNumber++)
|
|
5310
|
+
));
|
|
5311
|
+
}
|
|
5312
|
+
blocks.push(makeDiffBlock2("replace", leftStart, renderedLeftLines.length, rightStart, renderedRightLines.length));
|
|
5313
|
+
index++;
|
|
5314
|
+
continue;
|
|
5315
|
+
}
|
|
5316
|
+
if (change.removed) {
|
|
5317
|
+
const leftStart = renderedLeftLines.length;
|
|
5318
|
+
const rightStart = renderedRightLines.length;
|
|
5319
|
+
for (const line of removedLines) {
|
|
5320
|
+
renderedLeftLines.push(makeDiffLine2("removed", line, leftLineNumber));
|
|
5321
|
+
rows.push(makeDiffRow2(
|
|
5322
|
+
makeDiffCell2("removed", line, leftLineNumber++),
|
|
5323
|
+
makePlaceholder2()
|
|
5324
|
+
));
|
|
5325
|
+
}
|
|
5326
|
+
blocks.push(makeDiffBlock2("delete", leftStart, renderedLeftLines.length, rightStart, renderedRightLines.length));
|
|
5327
|
+
continue;
|
|
5328
|
+
}
|
|
5329
|
+
if (change.added) {
|
|
5330
|
+
const leftStart = renderedLeftLines.length;
|
|
5331
|
+
const rightStart = renderedRightLines.length;
|
|
5332
|
+
for (const line of addedLines) {
|
|
5333
|
+
renderedRightLines.push(makeDiffLine2("added", line, rightLineNumber));
|
|
5334
|
+
rows.push(makeDiffRow2(
|
|
5335
|
+
makePlaceholder2(),
|
|
5336
|
+
makeDiffCell2("added", line, rightLineNumber++)
|
|
5337
|
+
));
|
|
5338
|
+
}
|
|
5339
|
+
blocks.push(makeDiffBlock2("insert", leftStart, renderedLeftLines.length, rightStart, renderedRightLines.length));
|
|
5340
|
+
}
|
|
5341
|
+
}
|
|
5342
|
+
const hasChanges = rows.some((row) => row.left.kind !== "context" || row.right.kind !== "context");
|
|
5343
|
+
return {
|
|
5344
|
+
rows,
|
|
5345
|
+
leftLines: renderedLeftLines,
|
|
5346
|
+
rightLines: renderedRightLines,
|
|
5347
|
+
blocks,
|
|
5348
|
+
hasChanges
|
|
5349
|
+
};
|
|
5350
|
+
}
|
|
5351
|
+
function buildConservativeTwoWayDiffModel2(leftLines, rightLines) {
|
|
5352
|
+
const renderedLeftLines = leftLines.map((line, index) => makeDiffLine2("removed", line, index + 1));
|
|
5353
|
+
const renderedRightLines = rightLines.map((line, index) => makeDiffLine2("added", line, index + 1));
|
|
5354
|
+
const rows = [
|
|
5355
|
+
...leftLines.map((line, index) => makeDiffRow2(
|
|
5356
|
+
makeDiffCell2("removed", line, index + 1),
|
|
5357
|
+
makePlaceholder2()
|
|
5358
|
+
)),
|
|
5359
|
+
...rightLines.map((line, index) => makeDiffRow2(
|
|
5360
|
+
makePlaceholder2(),
|
|
5361
|
+
makeDiffCell2("added", line, index + 1)
|
|
5362
|
+
))
|
|
5363
|
+
];
|
|
5364
|
+
let blockKind = "replace";
|
|
5365
|
+
if (leftLines.length === 0) {
|
|
5366
|
+
blockKind = "insert";
|
|
5367
|
+
} else if (rightLines.length === 0) {
|
|
5368
|
+
blockKind = "delete";
|
|
5369
|
+
}
|
|
5370
|
+
return {
|
|
5371
|
+
rows,
|
|
5372
|
+
leftLines: renderedLeftLines,
|
|
5373
|
+
rightLines: renderedRightLines,
|
|
5374
|
+
blocks: rows.length === 0 ? [] : [makeDiffBlock2(blockKind, 0, leftLines.length, 0, rightLines.length)],
|
|
5375
|
+
hasChanges: rows.length > 0
|
|
5376
|
+
};
|
|
5377
|
+
}
|
|
5378
|
+
function normalizeLines2(content) {
|
|
5379
|
+
if (content.length === 0) {
|
|
5380
|
+
return [];
|
|
5381
|
+
}
|
|
5382
|
+
const lines = content.replace(/\r\n/g, "\n").split("\n");
|
|
5383
|
+
if (lines[lines.length - 1] === "") {
|
|
5384
|
+
lines.pop();
|
|
5385
|
+
}
|
|
5386
|
+
return lines;
|
|
5387
|
+
}
|
|
5388
|
+
var MAX_ALIGNMENT_CELLS2 = 1e4;
|
|
5389
|
+
var MAX_SCORING_LINE_LENGTH2 = 2e3;
|
|
5390
|
+
var MINIMUM_MATCH_SCORE2 = 0.52;
|
|
5391
|
+
var MINIMUM_SINGLE_PAIR_SCORE2 = 0.35;
|
|
5392
|
+
var MINIMUM_CONTEXTUAL_MATCH_SCORE2 = 0.38;
|
|
5393
|
+
var MINIMUM_CONTEXTUAL_RUN_SCORE2 = 0.33;
|
|
5394
|
+
var HIGH_CONFIDENCE_MATCH_SCORE2 = 0.86;
|
|
5395
|
+
var AMBIGUITY_MARGIN2 = 0.08;
|
|
5396
|
+
var CONTEXTUAL_AMBIGUITY_MARGIN2 = 0.05;
|
|
5397
|
+
var GAP_PENALTY2 = -0.12;
|
|
5398
|
+
var MAX_BOUNDED_CANDIDATES2 = 5e3;
|
|
5399
|
+
var MAX_LARGE_HUNK_ANCHORS2 = 2e3;
|
|
5400
|
+
var MAX_RARE_TOKEN_OCCURRENCES2 = 4;
|
|
5401
|
+
var MAX_CONTEXTUAL_HUNK_LINES2 = 8;
|
|
5402
|
+
var MAX_CONTEXTUAL_LENGTH_DELTA2 = 4;
|
|
5403
|
+
function alignReplacementLines2(leftLines, rightLines) {
|
|
5404
|
+
if (leftLines.length === 0) {
|
|
5405
|
+
return rightLines.map((right) => ({ right }));
|
|
5406
|
+
}
|
|
5407
|
+
if (rightLines.length === 0) {
|
|
5408
|
+
return leftLines.map((left) => ({ left }));
|
|
5409
|
+
}
|
|
5410
|
+
if (leftLines.length === 1 && rightLines.length === 1) {
|
|
5411
|
+
const singletonScore = scoreReplacementLinePair2(leftLines[0], rightLines[0]);
|
|
5412
|
+
if ((singletonScore.eligible || singletonScore.score >= MINIMUM_SINGLE_PAIR_SCORE2) && haveCompatibleLineRoles2(leftLines[0], rightLines[0]) && isInformativeLine2(normalizeMatchingContent2(leftLines[0])) && isInformativeLine2(normalizeMatchingContent2(rightLines[0]))) {
|
|
5413
|
+
return [{ left: leftLines[0], right: rightLines[0] }];
|
|
5414
|
+
}
|
|
5415
|
+
return [{ left: leftLines[0] }, { right: rightLines[0] }];
|
|
5416
|
+
}
|
|
5417
|
+
if (leftLines.length * rightLines.length > MAX_ALIGNMENT_CELLS2) {
|
|
5418
|
+
return alignLargeReplacementLines2(leftLines, rightLines);
|
|
5419
|
+
}
|
|
5420
|
+
const pairScores = buildPairScores2(leftLines, rightLines);
|
|
5421
|
+
const scores = Array.from(
|
|
5422
|
+
{ length: leftLines.length + 1 },
|
|
5423
|
+
() => new Array(rightLines.length + 1).fill(Number.NEGATIVE_INFINITY)
|
|
5424
|
+
);
|
|
5425
|
+
const moves = Array.from(
|
|
5426
|
+
{ length: leftLines.length + 1 },
|
|
5427
|
+
() => new Array(rightLines.length + 1).fill(null)
|
|
5428
|
+
);
|
|
5429
|
+
scores[0][0] = 0;
|
|
5430
|
+
for (let leftIndex2 = 1; leftIndex2 <= leftLines.length; leftIndex2++) {
|
|
5431
|
+
scores[leftIndex2][0] = scores[leftIndex2 - 1][0] + GAP_PENALTY2;
|
|
5432
|
+
moves[leftIndex2][0] = "left";
|
|
5433
|
+
}
|
|
5434
|
+
for (let rightIndex2 = 1; rightIndex2 <= rightLines.length; rightIndex2++) {
|
|
5435
|
+
scores[0][rightIndex2] = scores[0][rightIndex2 - 1] + GAP_PENALTY2;
|
|
5436
|
+
moves[0][rightIndex2] = "right";
|
|
5437
|
+
}
|
|
5438
|
+
for (let leftIndex2 = 1; leftIndex2 <= leftLines.length; leftIndex2++) {
|
|
5439
|
+
for (let rightIndex2 = 1; rightIndex2 <= rightLines.length; rightIndex2++) {
|
|
5440
|
+
const pairScore = pairScores[leftIndex2 - 1][rightIndex2 - 1];
|
|
5441
|
+
const candidates = [
|
|
5442
|
+
{ move: "left", score: scores[leftIndex2 - 1][rightIndex2] + GAP_PENALTY2 },
|
|
5443
|
+
{ move: "right", score: scores[leftIndex2][rightIndex2 - 1] + GAP_PENALTY2 },
|
|
5444
|
+
{
|
|
5445
|
+
move: "match",
|
|
5446
|
+
score: pairScore === null ? Number.NEGATIVE_INFINITY : scores[leftIndex2 - 1][rightIndex2 - 1] + pairScore
|
|
5447
|
+
}
|
|
5448
|
+
];
|
|
5449
|
+
const best = candidates.reduce((current, candidate) => candidate.score > current.score ? candidate : current);
|
|
5450
|
+
scores[leftIndex2][rightIndex2] = best.score;
|
|
5451
|
+
moves[leftIndex2][rightIndex2] = best.move;
|
|
5452
|
+
}
|
|
5453
|
+
}
|
|
5454
|
+
const aligned = [];
|
|
5455
|
+
let leftIndex = leftLines.length;
|
|
5456
|
+
let rightIndex = rightLines.length;
|
|
5457
|
+
while (leftIndex > 0 || rightIndex > 0) {
|
|
5458
|
+
const move = moves[leftIndex][rightIndex];
|
|
5459
|
+
if (move === "match") {
|
|
5460
|
+
aligned.push({ left: leftLines[--leftIndex], right: rightLines[--rightIndex] });
|
|
5461
|
+
} else if (move === "left") {
|
|
5462
|
+
aligned.push({ left: leftLines[--leftIndex] });
|
|
5463
|
+
} else {
|
|
5464
|
+
aligned.push({ right: rightLines[--rightIndex] });
|
|
5465
|
+
}
|
|
5466
|
+
}
|
|
5467
|
+
return aligned.reverse();
|
|
5468
|
+
}
|
|
5469
|
+
function scoreReplacementLinePair2(left, right) {
|
|
5470
|
+
const normalizedLeft = normalizeMatchingContent2(left);
|
|
5471
|
+
const normalizedRight = normalizeMatchingContent2(right);
|
|
5472
|
+
const leftInformative = isInformativeLine2(normalizedLeft);
|
|
5473
|
+
const rightInformative = isInformativeLine2(normalizedRight);
|
|
5474
|
+
if (!leftInformative || !rightInformative || normalizedLeft.length > MAX_SCORING_LINE_LENGTH2 || normalizedRight.length > MAX_SCORING_LINE_LENGTH2) {
|
|
5475
|
+
return {
|
|
5476
|
+
score: 0,
|
|
5477
|
+
eligible: false,
|
|
5478
|
+
characterSimilarity: 0,
|
|
5479
|
+
tokenSimilarity: 0,
|
|
5480
|
+
lengthSimilarity: 0
|
|
5481
|
+
};
|
|
5482
|
+
}
|
|
5483
|
+
if (normalizedLeft === normalizedRight) {
|
|
5484
|
+
return {
|
|
5485
|
+
score: 1,
|
|
5486
|
+
eligible: true,
|
|
5487
|
+
characterSimilarity: 1,
|
|
5488
|
+
tokenSimilarity: 1,
|
|
5489
|
+
lengthSimilarity: 1
|
|
5490
|
+
};
|
|
5491
|
+
}
|
|
5492
|
+
const leftTokens = tokenizeMatchingContent2(normalizedLeft);
|
|
5493
|
+
const rightTokens = tokenizeMatchingContent2(normalizedRight);
|
|
5494
|
+
const characterSimilarity = lineSimilarity2(normalizedLeft, normalizedRight);
|
|
5495
|
+
const tokenSimilarity = multisetDiceSimilarity2(leftTokens, rightTokens);
|
|
5496
|
+
const lengthSimilarity = Math.min(normalizedLeft.length, normalizedRight.length) / Math.max(normalizedLeft.length, normalizedRight.length);
|
|
5497
|
+
const boundarySimilarity = tokenBoundarySimilarity2(leftTokens, rightTokens);
|
|
5498
|
+
const score = characterSimilarity * 0.52 + tokenSimilarity * 0.32 + lengthSimilarity * 0.1 + boundarySimilarity * 0.06;
|
|
5499
|
+
const hasContentEvidence = characterSimilarity >= 0.52 || tokenSimilarity >= 0.45 || characterSimilarity >= 0.48 && tokenSimilarity >= 0.28;
|
|
5500
|
+
return {
|
|
5501
|
+
score,
|
|
5502
|
+
eligible: hasContentEvidence && score >= MINIMUM_MATCH_SCORE2,
|
|
5503
|
+
characterSimilarity,
|
|
5504
|
+
tokenSimilarity,
|
|
5505
|
+
lengthSimilarity
|
|
5506
|
+
};
|
|
5507
|
+
}
|
|
5508
|
+
function buildPairScores2(leftLines, rightLines) {
|
|
5509
|
+
const scored = leftLines.map((left) => rightLines.map((right) => scoreReplacementLinePair2(left, right)));
|
|
5510
|
+
const structuralPairs = collectUniqueStructuralPairs2(leftLines, rightLines);
|
|
5511
|
+
const leftRankings = scored.map((row) => rankEligibleScores2(row));
|
|
5512
|
+
const rightRankings = rightLines.map((_right, rightIndex) => rankEligibleScores2(scored.map((row) => row[rightIndex])));
|
|
5513
|
+
const accepted = scored.map((row, leftIndex) => row.map((candidate, rightIndex) => {
|
|
5514
|
+
if (structuralPairs.has(`${leftIndex}:${rightIndex}`)) {
|
|
5515
|
+
return Math.max(candidate.score, 1);
|
|
5516
|
+
}
|
|
5517
|
+
if (!candidate.eligible) {
|
|
5518
|
+
return null;
|
|
5519
|
+
}
|
|
5520
|
+
const leftRanking = leftRankings[leftIndex];
|
|
5521
|
+
const rightRanking = rightRankings[rightIndex];
|
|
5522
|
+
const isBestOnBothSides = candidate.score === leftRanking.best && candidate.score === rightRanking.best;
|
|
5523
|
+
const requiredMargin = candidate.score >= HIGH_CONFIDENCE_MATCH_SCORE2 ? AMBIGUITY_MARGIN2 / 2 : AMBIGUITY_MARGIN2;
|
|
5524
|
+
const hasLeftMargin = leftRanking.best - leftRanking.second >= requiredMargin;
|
|
5525
|
+
const hasRightMargin = rightRanking.best - rightRanking.second >= requiredMargin;
|
|
5526
|
+
return isBestOnBothSides && hasLeftMargin && hasRightMargin ? candidate.score : null;
|
|
5527
|
+
}));
|
|
5528
|
+
if (Math.max(leftLines.length, rightLines.length) > MAX_CONTEXTUAL_HUNK_LINES2 || Math.abs(leftLines.length - rightLines.length) > MAX_CONTEXTUAL_LENGTH_DELTA2) {
|
|
5529
|
+
return accepted;
|
|
5530
|
+
}
|
|
5531
|
+
const isContextualCandidate = (candidate, leftIndex, rightIndex) => candidate.score >= MINIMUM_CONTEXTUAL_MATCH_SCORE2 && haveCompatibleLineRoles2(leftLines[leftIndex], rightLines[rightIndex]) && Math.abs(leftIndex - rightIndex) <= MAX_CONTEXTUAL_LENGTH_DELTA2;
|
|
5532
|
+
const leftContextRankings = scored.map((row, leftIndex) => rankScores2(row, (candidate, rightIndex) => isContextualCandidate(candidate, leftIndex, rightIndex)));
|
|
5533
|
+
const rightContextRankings = rightLines.map((_right, rightIndex) => rankScores2(
|
|
5534
|
+
scored.map((row) => row[rightIndex]),
|
|
5535
|
+
(candidate, leftIndex) => isContextualCandidate(candidate, leftIndex, rightIndex)
|
|
5536
|
+
));
|
|
5537
|
+
const isWeakRunCandidate = (candidate, leftIndex, rightIndex) => candidate.score >= MINIMUM_CONTEXTUAL_RUN_SCORE2 && haveCompatibleLineRoles2(leftLines[leftIndex], rightLines[rightIndex]) && Math.abs(leftIndex - rightIndex) <= MAX_CONTEXTUAL_LENGTH_DELTA2;
|
|
5538
|
+
const leftWeakRankings = scored.map((row, leftIndex) => rankScores2(row, (candidate, rightIndex) => isWeakRunCandidate(candidate, leftIndex, rightIndex)));
|
|
5539
|
+
const rightWeakRankings = rightLines.map((_right, rightIndex) => rankScores2(
|
|
5540
|
+
scored.map((row) => row[rightIndex]),
|
|
5541
|
+
(candidate, leftIndex) => isWeakRunCandidate(candidate, leftIndex, rightIndex)
|
|
5542
|
+
));
|
|
5543
|
+
const isUnambiguousContextualCandidate = (leftIndex, rightIndex) => {
|
|
5544
|
+
const candidate = scored[leftIndex][rightIndex];
|
|
5545
|
+
if (!isContextualCandidate(candidate, leftIndex, rightIndex)) {
|
|
5546
|
+
return false;
|
|
5547
|
+
}
|
|
5548
|
+
const leftRanking = leftContextRankings[leftIndex];
|
|
5549
|
+
const rightRanking = rightContextRankings[rightIndex];
|
|
5550
|
+
return candidate.score === leftRanking.best && candidate.score === rightRanking.best && leftRanking.best - leftRanking.second >= CONTEXTUAL_AMBIGUITY_MARGIN2 && rightRanking.best - rightRanking.second >= CONTEXTUAL_AMBIGUITY_MARGIN2;
|
|
5551
|
+
};
|
|
5552
|
+
const isUnambiguousWeakRunAnchor = (leftIndex, rightIndex) => {
|
|
5553
|
+
const candidate = scored[leftIndex][rightIndex];
|
|
5554
|
+
if (!isWeakRunCandidate(candidate, leftIndex, rightIndex)) {
|
|
5555
|
+
return false;
|
|
5556
|
+
}
|
|
5557
|
+
const leftRanking = leftWeakRankings[leftIndex];
|
|
5558
|
+
const rightRanking = rightWeakRankings[rightIndex];
|
|
5559
|
+
return candidate.score === leftRanking.best && candidate.score === rightRanking.best && leftRanking.best - leftRanking.second >= CONTEXTUAL_AMBIGUITY_MARGIN2 && rightRanking.best - rightRanking.second >= CONTEXTUAL_AMBIGUITY_MARGIN2;
|
|
5560
|
+
};
|
|
5561
|
+
return accepted.map((row, leftIndex) => row.map((acceptedScore, rightIndex) => {
|
|
5562
|
+
if (acceptedScore !== null) {
|
|
5563
|
+
return acceptedScore;
|
|
5564
|
+
}
|
|
5565
|
+
const hasLocalSupport = isUnambiguousContextualCandidate(leftIndex, rightIndex) && (hasAcceptedDiagonalNeighbor2(accepted, leftIndex, rightIndex) || hasContextualDiagonalNeighbor2(
|
|
5566
|
+
scored,
|
|
5567
|
+
leftIndex,
|
|
5568
|
+
rightIndex,
|
|
5569
|
+
isUnambiguousContextualCandidate
|
|
5570
|
+
));
|
|
5571
|
+
const hasRunSupport = hasAnchoredWeakDiagonalRun2(
|
|
5572
|
+
scored,
|
|
5573
|
+
leftIndex,
|
|
5574
|
+
rightIndex,
|
|
5575
|
+
(candidateLeftIndex, candidateRightIndex) => isWeakRunCandidate(
|
|
5576
|
+
scored[candidateLeftIndex][candidateRightIndex],
|
|
5577
|
+
candidateLeftIndex,
|
|
5578
|
+
candidateRightIndex
|
|
5579
|
+
),
|
|
5580
|
+
isUnambiguousWeakRunAnchor
|
|
5581
|
+
);
|
|
5582
|
+
if (!hasLocalSupport && !hasRunSupport) {
|
|
5583
|
+
return null;
|
|
5584
|
+
}
|
|
5585
|
+
return scored[leftIndex][rightIndex].score;
|
|
5586
|
+
}));
|
|
5587
|
+
}
|
|
5588
|
+
function hasAcceptedDiagonalNeighbor2(accepted, leftIndex, rightIndex) {
|
|
5589
|
+
return leftIndex > 0 && rightIndex > 0 && accepted[leftIndex - 1][rightIndex - 1] !== null || leftIndex + 1 < accepted.length && rightIndex + 1 < accepted[leftIndex + 1].length && accepted[leftIndex + 1][rightIndex + 1] !== null;
|
|
5590
|
+
}
|
|
5591
|
+
function hasContextualDiagonalNeighbor2(scored, leftIndex, rightIndex, isCandidate) {
|
|
5592
|
+
return leftIndex > 0 && rightIndex > 0 && isCandidate(leftIndex - 1, rightIndex - 1) || leftIndex + 1 < scored.length && rightIndex + 1 < scored[leftIndex + 1].length && isCandidate(leftIndex + 1, rightIndex + 1);
|
|
5593
|
+
}
|
|
5594
|
+
function hasAnchoredWeakDiagonalRun2(scored, leftIndex, rightIndex, isCandidate, isAnchor) {
|
|
5595
|
+
let startLeft = leftIndex;
|
|
5596
|
+
let startRight = rightIndex;
|
|
5597
|
+
while (startLeft > 0 && startRight > 0 && isCandidate(startLeft - 1, startRight - 1)) {
|
|
5598
|
+
startLeft--;
|
|
5599
|
+
startRight--;
|
|
5600
|
+
}
|
|
5601
|
+
let endLeft = leftIndex;
|
|
5602
|
+
let endRight = rightIndex;
|
|
5603
|
+
let scoreTotal = scored[leftIndex][rightIndex].score;
|
|
5604
|
+
let length = 1;
|
|
5605
|
+
while (endLeft + 1 < scored.length && endRight + 1 < scored[endLeft + 1].length && isCandidate(endLeft + 1, endRight + 1)) {
|
|
5606
|
+
endLeft++;
|
|
5607
|
+
endRight++;
|
|
5608
|
+
scoreTotal += scored[endLeft][endRight].score;
|
|
5609
|
+
length++;
|
|
5610
|
+
}
|
|
5611
|
+
for (let scanLeft = leftIndex - 1, scanRight = rightIndex - 1; scanLeft >= startLeft && scanRight >= startRight; scanLeft--, scanRight--) {
|
|
5612
|
+
scoreTotal += scored[scanLeft][scanRight].score;
|
|
5613
|
+
length++;
|
|
5614
|
+
}
|
|
5615
|
+
return length >= 3 && scoreTotal / length >= MINIMUM_CONTEXTUAL_MATCH_SCORE2 && isAnchor(startLeft, startRight) && isAnchor(endLeft, endRight);
|
|
5616
|
+
}
|
|
5617
|
+
function rankScores2(scores, include) {
|
|
5618
|
+
let best = Number.NEGATIVE_INFINITY;
|
|
5619
|
+
let second = Number.NEGATIVE_INFINITY;
|
|
5620
|
+
scores.forEach((candidate, index) => {
|
|
5621
|
+
if (!include(candidate, index)) {
|
|
5622
|
+
return;
|
|
5623
|
+
}
|
|
5624
|
+
if (candidate.score > best) {
|
|
5625
|
+
second = best;
|
|
5626
|
+
best = candidate.score;
|
|
5627
|
+
} else if (candidate.score > second) {
|
|
5628
|
+
second = candidate.score;
|
|
5629
|
+
}
|
|
5630
|
+
});
|
|
5631
|
+
return { best, second };
|
|
5632
|
+
}
|
|
5633
|
+
function rankEligibleScores2(scores) {
|
|
5634
|
+
let best = Number.NEGATIVE_INFINITY;
|
|
5635
|
+
let second = Number.NEGATIVE_INFINITY;
|
|
5636
|
+
for (const candidate of scores) {
|
|
5637
|
+
if (!candidate.eligible) {
|
|
5638
|
+
continue;
|
|
5639
|
+
}
|
|
5640
|
+
if (candidate.score > best) {
|
|
5641
|
+
second = best;
|
|
5642
|
+
best = candidate.score;
|
|
5643
|
+
} else if (candidate.score > second) {
|
|
5644
|
+
second = candidate.score;
|
|
5645
|
+
}
|
|
5646
|
+
}
|
|
5647
|
+
return { best, second };
|
|
5648
|
+
}
|
|
5649
|
+
function alignLargeReplacementLines2(leftLines, rightLines) {
|
|
5650
|
+
const anchors = collectLargeHunkAnchors2(leftLines, rightLines);
|
|
5651
|
+
const aligned = [];
|
|
5652
|
+
let leftStart = 0;
|
|
5653
|
+
let rightStart = 0;
|
|
5654
|
+
for (const anchor of anchors) {
|
|
5655
|
+
appendBoundedSegment2(
|
|
5656
|
+
aligned,
|
|
5657
|
+
leftLines.slice(leftStart, anchor.leftIndex),
|
|
5658
|
+
rightLines.slice(rightStart, anchor.rightIndex)
|
|
5659
|
+
);
|
|
5660
|
+
aligned.push({ left: leftLines[anchor.leftIndex], right: rightLines[anchor.rightIndex] });
|
|
5661
|
+
leftStart = anchor.leftIndex + 1;
|
|
5662
|
+
rightStart = anchor.rightIndex + 1;
|
|
5663
|
+
}
|
|
5664
|
+
appendBoundedSegment2(aligned, leftLines.slice(leftStart), rightLines.slice(rightStart));
|
|
5665
|
+
return aligned;
|
|
5666
|
+
}
|
|
5667
|
+
function collectLargeHunkAnchors2(leftLines, rightLines) {
|
|
5668
|
+
const candidatePairs = collectRareTokenCandidatePairs2(leftLines, rightLines);
|
|
5669
|
+
if (candidatePairs === null) {
|
|
5670
|
+
return [];
|
|
5671
|
+
}
|
|
5672
|
+
const structuralPairs = collectUniqueStructuralPairs2(leftLines, rightLines);
|
|
5673
|
+
const scored = candidatePairs.map(({ leftIndex, rightIndex, exact }) => ({
|
|
5674
|
+
leftIndex,
|
|
5675
|
+
rightIndex,
|
|
5676
|
+
exact,
|
|
5677
|
+
structural: structuralPairs.has(`${leftIndex}:${rightIndex}`),
|
|
5678
|
+
match: scoreReplacementLinePair2(leftLines[leftIndex], rightLines[rightIndex])
|
|
5679
|
+
})).filter((candidate) => candidate.exact || candidate.structural || candidate.match.eligible);
|
|
5680
|
+
const leftRankings = rankAnchorCandidates2(scored, "leftIndex");
|
|
5681
|
+
const rightRankings = rankAnchorCandidates2(scored, "rightIndex");
|
|
5682
|
+
const confident = scored.filter((candidate) => {
|
|
5683
|
+
if (candidate.exact || candidate.structural) {
|
|
5684
|
+
return true;
|
|
5685
|
+
}
|
|
5686
|
+
const leftRanking = leftRankings.get(candidate.leftIndex);
|
|
5687
|
+
const rightRanking = rightRankings.get(candidate.rightIndex);
|
|
5688
|
+
if (!leftRanking || !rightRanking || candidate.match.score !== leftRanking.best || candidate.match.score !== rightRanking.best) {
|
|
5689
|
+
return false;
|
|
5690
|
+
}
|
|
5691
|
+
const requiredMargin = candidate.match.score >= HIGH_CONFIDENCE_MATCH_SCORE2 ? AMBIGUITY_MARGIN2 / 2 : AMBIGUITY_MARGIN2;
|
|
5692
|
+
return leftRanking.best - leftRanking.second >= requiredMargin && rightRanking.best - rightRanking.second >= requiredMargin;
|
|
5693
|
+
});
|
|
5694
|
+
const anchors = selectMaximumWeightMonotonicAnchors2(confident, rightLines.length);
|
|
5695
|
+
return anchors.length <= MAX_LARGE_HUNK_ANCHORS2 ? anchors : [];
|
|
5696
|
+
}
|
|
5697
|
+
function collectRareTokenCandidatePairs2(leftLines, rightLines) {
|
|
5698
|
+
const leftExact = buildLinePostings2(leftLines, normalizeMatchingContent2);
|
|
5699
|
+
const rightExact = buildLinePostings2(rightLines, normalizeMatchingContent2);
|
|
5700
|
+
const pairs = /* @__PURE__ */ new Map();
|
|
5701
|
+
for (const [line, leftIndices] of leftExact) {
|
|
5702
|
+
const rightIndices = rightExact.get(line);
|
|
5703
|
+
if (!isInformativeLine2(line) || leftIndices.length !== 1 || (rightIndices == null ? void 0 : rightIndices.length) !== 1) {
|
|
5704
|
+
continue;
|
|
5705
|
+
}
|
|
5706
|
+
const leftIndex = leftIndices[0];
|
|
5707
|
+
const rightIndex = rightIndices[0];
|
|
5708
|
+
pairs.set(`${leftIndex}:${rightIndex}`, { leftIndex, rightIndex, exact: true });
|
|
5709
|
+
if (pairs.size > MAX_BOUNDED_CANDIDATES2) {
|
|
5710
|
+
return null;
|
|
5711
|
+
}
|
|
5712
|
+
}
|
|
5713
|
+
const leftTokens = buildTokenPostings2(leftLines);
|
|
5714
|
+
const rightTokens = buildTokenPostings2(rightLines);
|
|
5715
|
+
for (const [token, leftIndices] of leftTokens) {
|
|
5716
|
+
const rightIndices = rightTokens.get(token);
|
|
5717
|
+
if (!rightIndices || leftIndices.length > MAX_RARE_TOKEN_OCCURRENCES2 || rightIndices.length > MAX_RARE_TOKEN_OCCURRENCES2) {
|
|
5718
|
+
continue;
|
|
5719
|
+
}
|
|
5720
|
+
for (const leftIndex of leftIndices) {
|
|
5721
|
+
for (const rightIndex of rightIndices) {
|
|
5722
|
+
const key = `${leftIndex}:${rightIndex}`;
|
|
5723
|
+
if (!pairs.has(key)) {
|
|
5724
|
+
pairs.set(key, { leftIndex, rightIndex, exact: false });
|
|
5725
|
+
if (pairs.size > MAX_BOUNDED_CANDIDATES2) {
|
|
5726
|
+
return null;
|
|
5727
|
+
}
|
|
5728
|
+
}
|
|
5729
|
+
}
|
|
5730
|
+
}
|
|
5731
|
+
}
|
|
5732
|
+
return [...pairs.values()];
|
|
5733
|
+
}
|
|
5734
|
+
function buildLinePostings2(lines, keyForLine) {
|
|
5735
|
+
const postings = /* @__PURE__ */ new Map();
|
|
5736
|
+
lines.forEach((line, index) => {
|
|
5737
|
+
const key = keyForLine(line);
|
|
5738
|
+
const indices = postings.get(key) ?? [];
|
|
5739
|
+
indices.push(index);
|
|
5740
|
+
postings.set(key, indices);
|
|
5741
|
+
});
|
|
5742
|
+
return postings;
|
|
5743
|
+
}
|
|
5744
|
+
function buildTokenPostings2(lines) {
|
|
5745
|
+
const postings = /* @__PURE__ */ new Map();
|
|
5746
|
+
lines.forEach((line, index) => {
|
|
5747
|
+
const tokens = new Set(tokenizeMatchingContent2(normalizeMatchingContent2(line)).filter(isMeaningfulAnchorToken2));
|
|
5748
|
+
for (const token of tokens) {
|
|
5749
|
+
const indices = postings.get(token) ?? [];
|
|
5750
|
+
indices.push(index);
|
|
5751
|
+
postings.set(token, indices);
|
|
5752
|
+
}
|
|
5753
|
+
});
|
|
5754
|
+
return postings;
|
|
5755
|
+
}
|
|
5756
|
+
function isMeaningfulAnchorToken2(token) {
|
|
5757
|
+
return token.length >= 2 && /[\p{L}\p{N}_$]/u.test(token);
|
|
5758
|
+
}
|
|
5759
|
+
function rankAnchorCandidates2(candidates, indexKey) {
|
|
5760
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
5761
|
+
for (const candidate of candidates) {
|
|
5762
|
+
const scores = grouped.get(candidate[indexKey]) ?? [];
|
|
5763
|
+
scores.push(candidate.match);
|
|
5764
|
+
grouped.set(candidate[indexKey], scores);
|
|
5765
|
+
}
|
|
5766
|
+
return new Map([...grouped].map(([index, scores]) => [index, rankEligibleScores2(scores)]));
|
|
5767
|
+
}
|
|
5768
|
+
function selectMaximumWeightMonotonicAnchors2(candidates, rightLength) {
|
|
5769
|
+
const ordered = [...candidates].sort((left, right) => left.leftIndex - right.leftIndex || left.rightIndex - right.rightIndex);
|
|
5770
|
+
const treeScores = new Array(rightLength + 2).fill(0);
|
|
5771
|
+
const treeCandidates = new Array(rightLength + 2).fill(-1);
|
|
5772
|
+
const bestScores = new Array(ordered.length).fill(0);
|
|
5773
|
+
const previous = new Array(ordered.length).fill(-1);
|
|
5774
|
+
for (let start = 0; start < ordered.length; ) {
|
|
5775
|
+
let end = start + 1;
|
|
5776
|
+
while (end < ordered.length && ordered[end].leftIndex === ordered[start].leftIndex) {
|
|
5777
|
+
end++;
|
|
5778
|
+
}
|
|
5779
|
+
for (let index = start; index < end; index++) {
|
|
5780
|
+
const prior = queryAnchorTree2(treeScores, treeCandidates, ordered[index].rightIndex);
|
|
5781
|
+
const anchorBonus = ordered[index].exact || ordered[index].structural ? 0.5 : 0;
|
|
5782
|
+
bestScores[index] = prior.score + ordered[index].match.score + anchorBonus;
|
|
5783
|
+
previous[index] = prior.candidateIndex;
|
|
5784
|
+
}
|
|
5785
|
+
for (let index = start; index < end; index++) {
|
|
5786
|
+
updateAnchorTree2(
|
|
5787
|
+
treeScores,
|
|
5788
|
+
treeCandidates,
|
|
5789
|
+
ordered[index].rightIndex + 1,
|
|
5790
|
+
bestScores[index],
|
|
5791
|
+
index
|
|
5792
|
+
);
|
|
5793
|
+
}
|
|
5794
|
+
start = end;
|
|
5795
|
+
}
|
|
5796
|
+
const best = queryAnchorTree2(treeScores, treeCandidates, rightLength + 1);
|
|
5797
|
+
const anchors = [];
|
|
5798
|
+
for (let index = best.candidateIndex; index >= 0; index = previous[index]) {
|
|
5799
|
+
anchors.push({ leftIndex: ordered[index].leftIndex, rightIndex: ordered[index].rightIndex });
|
|
5800
|
+
}
|
|
5801
|
+
return anchors.reverse();
|
|
5802
|
+
}
|
|
5803
|
+
function queryAnchorTree2(scores, candidates, position) {
|
|
5804
|
+
let score = 0;
|
|
5805
|
+
let candidateIndex = -1;
|
|
5806
|
+
for (let index = position; index > 0; index -= index & -index) {
|
|
5807
|
+
if (scores[index] > score) {
|
|
5808
|
+
score = scores[index];
|
|
5809
|
+
candidateIndex = candidates[index];
|
|
5810
|
+
}
|
|
5811
|
+
}
|
|
5812
|
+
return { score, candidateIndex };
|
|
5813
|
+
}
|
|
5814
|
+
function updateAnchorTree2(scores, candidates, position, score, candidateIndex) {
|
|
5815
|
+
for (let index = position; index < scores.length; index += index & -index) {
|
|
5816
|
+
if (score > scores[index]) {
|
|
5817
|
+
scores[index] = score;
|
|
5818
|
+
candidates[index] = candidateIndex;
|
|
5819
|
+
}
|
|
5820
|
+
}
|
|
5821
|
+
}
|
|
5822
|
+
function appendBoundedSegment2(aligned, leftLines, rightLines) {
|
|
5823
|
+
if (leftLines.length * rightLines.length <= MAX_ALIGNMENT_CELLS2) {
|
|
5824
|
+
aligned.push(...alignReplacementLines2(leftLines, rightLines));
|
|
5825
|
+
return;
|
|
5826
|
+
}
|
|
5827
|
+
aligned.push(...leftLines.map((left) => ({ left })));
|
|
5828
|
+
aligned.push(...rightLines.map((right) => ({ right })));
|
|
5829
|
+
}
|
|
5830
|
+
function normalizeMatchingContent2(line) {
|
|
5831
|
+
return line.trim().replace(/\s+/g, " ");
|
|
5832
|
+
}
|
|
5833
|
+
function collectUniqueStructuralPairs2(leftLines, rightLines) {
|
|
5834
|
+
const leftPostings = buildStructuralPostings2(leftLines);
|
|
5835
|
+
const rightPostings = buildStructuralPostings2(rightLines);
|
|
5836
|
+
const pairs = /* @__PURE__ */ new Set();
|
|
5837
|
+
for (const [key, leftIndices] of leftPostings) {
|
|
5838
|
+
const rightIndices = rightPostings.get(key);
|
|
5839
|
+
if (leftIndices.length === 1 && (rightIndices == null ? void 0 : rightIndices.length) === 1) {
|
|
5840
|
+
pairs.add(`${leftIndices[0]}:${rightIndices[0]}`);
|
|
5841
|
+
}
|
|
5842
|
+
}
|
|
5843
|
+
return pairs;
|
|
5844
|
+
}
|
|
5845
|
+
function buildStructuralPostings2(lines) {
|
|
5846
|
+
const postings = /* @__PURE__ */ new Map();
|
|
5847
|
+
lines.forEach((line, index) => {
|
|
5848
|
+
const key = declarationAnchorKey2(line);
|
|
5849
|
+
if (!key) {
|
|
5850
|
+
return;
|
|
5851
|
+
}
|
|
5852
|
+
const indices = postings.get(key) ?? [];
|
|
5853
|
+
indices.push(index);
|
|
5854
|
+
postings.set(key, indices);
|
|
5855
|
+
});
|
|
5856
|
+
return postings;
|
|
5857
|
+
}
|
|
5858
|
+
function haveCompatibleLineRoles2(left, right) {
|
|
5859
|
+
const leftKey = declarationAnchorKey2(left);
|
|
5860
|
+
const rightKey = declarationAnchorKey2(right);
|
|
5861
|
+
if (!leftKey || !rightKey) {
|
|
5862
|
+
return leftKey === rightKey;
|
|
5863
|
+
}
|
|
5864
|
+
return leftKey.split(":", 1)[0] === rightKey.split(":", 1)[0];
|
|
5865
|
+
}
|
|
5866
|
+
function declarationAnchorKey2(line) {
|
|
5867
|
+
const normalized = normalizeMatchingContent2(line);
|
|
5868
|
+
const patterns = [
|
|
5869
|
+
["function", /^(?:(?:export|default)\s+)*(?:async\s+)?function\s*\*?\s*([\p{L}_$][\p{L}\p{N}_$]*)\s*\(/u],
|
|
5870
|
+
["function", /^(?:async\s+)?def\s+([\p{L}_][\p{L}\p{N}_]*)\s*\(/u],
|
|
5871
|
+
["function", /^(?:pub(?:\([^)]*\))?\s+)?(?:async\s+)?fn\s+([\p{L}_][\p{L}\p{N}_]*)\s*(?:<[^>]*>)?\s*\(/u],
|
|
5872
|
+
["function", /^func\s+(?:\([^)]*\)\s*)?([\p{L}_][\p{L}\p{N}_]*)\s*\(/u],
|
|
5873
|
+
["class", /^(?:(?:export|default|abstract|public)\s+)*class\s+([\p{L}_$][\p{L}\p{N}_$]*)\b/u]
|
|
5874
|
+
];
|
|
5875
|
+
for (const [kind, pattern] of patterns) {
|
|
5876
|
+
const match = normalized.match(pattern);
|
|
5877
|
+
if (match) {
|
|
5878
|
+
return `${kind}:${match[1]}`;
|
|
5879
|
+
}
|
|
5880
|
+
}
|
|
5881
|
+
return null;
|
|
5882
|
+
}
|
|
5883
|
+
function isInformativeLine2(line) {
|
|
5884
|
+
if (line.length < 3) {
|
|
5885
|
+
return false;
|
|
5886
|
+
}
|
|
5887
|
+
return /[\p{L}\p{N}_$]/u.test(line) && !/^[\s()[\]{};,.:+\-*/=>|&!?'"`]+$/.test(line);
|
|
5888
|
+
}
|
|
5889
|
+
function tokenizeMatchingContent2(line) {
|
|
5890
|
+
return line.toLowerCase().match(new RegExp("[\\p{L}_$][\\p{L}\\p{N}_$]*|\\p{N}+(?:\\.\\p{N}+)?|[^\\s\\p{L}\\p{N}_$]", "gu")) ?? [];
|
|
5891
|
+
}
|
|
5892
|
+
function multisetDiceSimilarity2(leftTokens, rightTokens) {
|
|
5893
|
+
if (leftTokens.length + rightTokens.length === 0) {
|
|
5894
|
+
return 1;
|
|
5895
|
+
}
|
|
5896
|
+
const remaining = /* @__PURE__ */ new Map();
|
|
5897
|
+
leftTokens.forEach((token) => remaining.set(token, (remaining.get(token) ?? 0) + 1));
|
|
5898
|
+
let common = 0;
|
|
5899
|
+
for (const token of rightTokens) {
|
|
5900
|
+
const count = remaining.get(token) ?? 0;
|
|
5901
|
+
if (count > 0) {
|
|
5902
|
+
common++;
|
|
5903
|
+
remaining.set(token, count - 1);
|
|
5904
|
+
}
|
|
5905
|
+
}
|
|
5906
|
+
return 2 * common / (leftTokens.length + rightTokens.length);
|
|
5907
|
+
}
|
|
5908
|
+
function tokenBoundarySimilarity2(leftTokens, rightTokens) {
|
|
5909
|
+
if (leftTokens.length === 0 || rightTokens.length === 0) {
|
|
5910
|
+
return 0;
|
|
5911
|
+
}
|
|
5912
|
+
const sameStart = leftTokens[0] === rightTokens[0] ? 0.5 : 0;
|
|
5913
|
+
const sameEnd = leftTokens[leftTokens.length - 1] === rightTokens[rightTokens.length - 1] ? 0.5 : 0;
|
|
5914
|
+
return sameStart + sameEnd;
|
|
5915
|
+
}
|
|
5916
|
+
function lineSimilarity2(left, right) {
|
|
5917
|
+
const maxLength = left.length + right.length;
|
|
5918
|
+
if (maxLength === 0) {
|
|
5919
|
+
return 1;
|
|
5920
|
+
}
|
|
5921
|
+
const commonLength = diffChars2(left, right).filter((change) => !change.added && !change.removed).reduce((total, change) => total + change.value.length, 0);
|
|
5922
|
+
return 2 * commonLength / maxLength;
|
|
5923
|
+
}
|
|
5924
|
+
function applyInlineHighlightPair2(leftLine, rightLine) {
|
|
5925
|
+
if (leftLine.content.length > MAX_INLINE_HIGHLIGHT_LINE_LENGTH2 || rightLine.content.length > MAX_INLINE_HIGHLIGHT_LINE_LENGTH2) {
|
|
5926
|
+
return;
|
|
5927
|
+
}
|
|
5928
|
+
const { leftSegments, rightSegments, hasInlineChanges } = buildInlineSegments2(leftLine.content, rightLine.content);
|
|
5929
|
+
if (hasInlineChanges) {
|
|
5930
|
+
leftLine.segments = leftSegments;
|
|
5931
|
+
rightLine.segments = rightSegments;
|
|
5932
|
+
}
|
|
5933
|
+
}
|
|
5934
|
+
function buildInlineSegments2(leftContent, rightContent) {
|
|
5935
|
+
const changes = diffWordsWithSpace2(leftContent, rightContent);
|
|
5936
|
+
const leftSegments = [];
|
|
5937
|
+
const rightSegments = [];
|
|
5938
|
+
let hasInlineChanges = false;
|
|
5939
|
+
for (const change of changes) {
|
|
5940
|
+
const value = change.value;
|
|
5941
|
+
if (!change.added && !change.removed) {
|
|
5942
|
+
const contextSegment = {
|
|
5943
|
+
kind: "context",
|
|
5944
|
+
text: value,
|
|
5945
|
+
emphasis: false
|
|
5946
|
+
};
|
|
5947
|
+
leftSegments.push(contextSegment);
|
|
5948
|
+
rightSegments.push(contextSegment);
|
|
5949
|
+
continue;
|
|
5950
|
+
}
|
|
5951
|
+
const emphasis = /[^\s]/.test(value);
|
|
5952
|
+
hasInlineChanges = hasInlineChanges || emphasis;
|
|
5953
|
+
if (change.removed) {
|
|
5954
|
+
leftSegments.push({
|
|
5955
|
+
kind: "removed",
|
|
5956
|
+
text: value,
|
|
5957
|
+
emphasis
|
|
5958
|
+
});
|
|
4766
5959
|
}
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
cumulativeHunks: [...cumulativeByFile.values()].flatMap((ids) => [...ids]),
|
|
4774
|
-
files: materializableFiles.map((file) => buildFileState(
|
|
4775
|
-
file,
|
|
4776
|
-
cumulativeByFile.get(file) || /* @__PURE__ */ new Set(),
|
|
4777
|
-
wholeFileExclusions.has(file)
|
|
4778
|
-
))
|
|
4779
|
-
});
|
|
4780
|
-
}
|
|
4781
|
-
for (const file of materializableFiles) {
|
|
4782
|
-
const finalState = buildFileState(
|
|
4783
|
-
file,
|
|
4784
|
-
cumulativeByFile.get(file) || /* @__PURE__ */ new Set(),
|
|
4785
|
-
wholeFileExclusions.has(file)
|
|
4786
|
-
);
|
|
4787
|
-
const expectedExists = file.changeKind !== "deleted";
|
|
4788
|
-
if (finalState.exists !== expectedExists || finalState.content !== file.headContent) {
|
|
4789
|
-
throw new Error(`Final deconstructed state does not match the target for ${file.path}.`);
|
|
5960
|
+
if (change.added) {
|
|
5961
|
+
rightSegments.push({
|
|
5962
|
+
kind: "added",
|
|
5963
|
+
text: value,
|
|
5964
|
+
emphasis
|
|
5965
|
+
});
|
|
4790
5966
|
}
|
|
4791
5967
|
}
|
|
4792
5968
|
return {
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
targetOid: inventory.range.headOid,
|
|
4797
|
-
baselineFiles,
|
|
4798
|
-
excludedHunks: [...excludedByFile.values()].flatMap((ids) => [...ids]),
|
|
4799
|
-
excludedFiles,
|
|
4800
|
-
stages
|
|
5969
|
+
leftSegments,
|
|
5970
|
+
rightSegments,
|
|
5971
|
+
hasInlineChanges
|
|
4801
5972
|
};
|
|
4802
5973
|
}
|
|
4803
|
-
function
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
5974
|
+
function makePlaceholder2() {
|
|
5975
|
+
return {
|
|
5976
|
+
kind: "placeholder",
|
|
5977
|
+
content: "",
|
|
5978
|
+
lineNumber: null
|
|
5979
|
+
};
|
|
4808
5980
|
}
|
|
4809
|
-
function
|
|
4810
|
-
|
|
4811
|
-
for (const id of ids) {
|
|
4812
|
-
if (!known.has(id)) throw new Error(`${owner} references an unknown hunk in ${file.path}: ${id}`);
|
|
4813
|
-
const key = unitKey(file, id);
|
|
4814
|
-
const previousOwner = ownership.get(key);
|
|
4815
|
-
if (previousOwner) throw new Error(`Change unit ${id} in ${file.path} is assigned to both ${previousOwner} and ${owner}.`);
|
|
4816
|
-
ownership.set(key, owner);
|
|
4817
|
-
}
|
|
5981
|
+
function makeDiffCell2(kind, content, lineNumber) {
|
|
5982
|
+
return { kind, content, lineNumber };
|
|
4818
5983
|
}
|
|
4819
|
-
function
|
|
4820
|
-
return
|
|
5984
|
+
function makeDiffLine2(kind, content, lineNumber) {
|
|
5985
|
+
return { kind, content, lineNumber };
|
|
4821
5986
|
}
|
|
4822
|
-
function
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
5987
|
+
function makeDiffRow2(left, right) {
|
|
5988
|
+
return { left, right };
|
|
5989
|
+
}
|
|
5990
|
+
function makeDiffBlock2(kind, leftStart, leftEnd, rightStart, rightEnd) {
|
|
5991
|
+
return { kind, leftStart, leftEnd, rightStart, rightEnd };
|
|
5992
|
+
}
|
|
5993
|
+
function readPositiveIntegerEnv4(name, fallback) {
|
|
5994
|
+
var _a;
|
|
5995
|
+
const value = typeof process !== "undefined" ? (_a = process.env) == null ? void 0 : _a[name] : void 0;
|
|
5996
|
+
const parsed = Number.parseInt(value ?? "", 10);
|
|
5997
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
5998
|
+
}
|
|
5999
|
+
var MAX_AUTOMATIC_TOUR_FOCUS_RANGES = 4;
|
|
6000
|
+
var NEARBY_TOUR_FOCUS_LINE_GAP = 8;
|
|
6001
|
+
var LARGE_TOUR_FOCUS_RANGE_LINES = 120;
|
|
6002
|
+
var TOUR_FOCUS_BOUNDARY_SEARCH_LINES = 12;
|
|
6003
|
+
function getChangeSourceRange(diffModel, blockIndex, side) {
|
|
6004
|
+
var _a, _b, _c;
|
|
6005
|
+
const block = diffModel == null ? void 0 : diffModel.blocks[blockIndex];
|
|
6006
|
+
const lines = side === "left" ? diffModel == null ? void 0 : diffModel.leftLines : diffModel == null ? void 0 : diffModel.rightLines;
|
|
6007
|
+
if (!block || !(lines == null ? void 0 : lines.length)) {
|
|
6008
|
+
return void 0;
|
|
6009
|
+
}
|
|
6010
|
+
const start = side === "left" ? block.leftStart : block.rightStart;
|
|
6011
|
+
const end = side === "left" ? block.leftEnd : block.rightEnd;
|
|
6012
|
+
const changedLines = lines.slice(start, end);
|
|
6013
|
+
const startLine = ((_a = changedLines[0]) == null ? void 0 : _a.lineNumber) ?? ((_b = lines[Math.min(start, lines.length - 1)]) == null ? void 0 : _b.lineNumber);
|
|
6014
|
+
if (!Number.isInteger(startLine)) {
|
|
6015
|
+
return void 0;
|
|
6016
|
+
}
|
|
4828
6017
|
return {
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
changeKind: file.changeKind,
|
|
4832
|
-
exists,
|
|
4833
|
-
content: exists ? content : "",
|
|
4834
|
-
appliedHunks: [...selected]
|
|
6018
|
+
startLine,
|
|
6019
|
+
endLine: ((_c = changedLines.at(-1)) == null ? void 0 : _c.lineNumber) ?? startLine
|
|
4835
6020
|
};
|
|
4836
6021
|
}
|
|
6022
|
+
function buildTourFocusRanges(diffModel, side) {
|
|
6023
|
+
const ranges = diffModel.blocks.map((_, blockIndex) => getChangeSourceRange(diffModel, blockIndex, side)).filter((range) => Boolean(range));
|
|
6024
|
+
if (ranges.length === 0) return [];
|
|
6025
|
+
const nearby = ranges.reduce((grouped, range) => {
|
|
6026
|
+
const previous = grouped.at(-1);
|
|
6027
|
+
if (previous && range.startLine - previous.endLine <= NEARBY_TOUR_FOCUS_LINE_GAP) {
|
|
6028
|
+
previous.endLine = Math.max(previous.endLine, range.endLine);
|
|
6029
|
+
} else {
|
|
6030
|
+
grouped.push({ ...range });
|
|
6031
|
+
}
|
|
6032
|
+
return grouped;
|
|
6033
|
+
}, []);
|
|
6034
|
+
const capped = capFocusRanges(nearby, MAX_AUTOMATIC_TOUR_FOCUS_RANGES);
|
|
6035
|
+
if (capped.length !== 1) return capped;
|
|
6036
|
+
const [only] = capped;
|
|
6037
|
+
const lineCount = only.endLine - only.startLine + 1;
|
|
6038
|
+
if (lineCount < LARGE_TOUR_FOCUS_RANGE_LINES) return capped;
|
|
6039
|
+
const lines = side === "left" ? diffModel.leftLines : diffModel.rightLines;
|
|
6040
|
+
const partCount = Math.min(
|
|
6041
|
+
MAX_AUTOMATIC_TOUR_FOCUS_RANGES,
|
|
6042
|
+
Math.ceil(lineCount / LARGE_TOUR_FOCUS_RANGE_LINES)
|
|
6043
|
+
);
|
|
6044
|
+
const boundaries = [];
|
|
6045
|
+
for (let partIndex = 1; partIndex < partCount; partIndex += 1) {
|
|
6046
|
+
const target = only.startLine + Math.round(lineCount * partIndex / partCount) - 1;
|
|
6047
|
+
boundaries.push(findBlankLineBoundary(lines, target, only.startLine, only.endLine));
|
|
6048
|
+
}
|
|
6049
|
+
const uniqueBoundaries = [...new Set(boundaries)].filter((line) => line >= only.startLine && line < only.endLine).sort((left, right) => left - right);
|
|
6050
|
+
const split = [];
|
|
6051
|
+
let startLine = only.startLine;
|
|
6052
|
+
for (const endLine of uniqueBoundaries) {
|
|
6053
|
+
split.push({ startLine, endLine });
|
|
6054
|
+
startLine = endLine + 1;
|
|
6055
|
+
}
|
|
6056
|
+
split.push({ startLine, endLine: only.endLine });
|
|
6057
|
+
return split;
|
|
6058
|
+
}
|
|
6059
|
+
function capFocusRanges(ranges, maximum) {
|
|
6060
|
+
if (ranges.length <= maximum) return ranges.map((range) => ({ ...range }));
|
|
6061
|
+
const capped = [];
|
|
6062
|
+
for (let index = 0; index < maximum; index += 1) {
|
|
6063
|
+
const startIndex = Math.floor(index * ranges.length / maximum);
|
|
6064
|
+
const endIndex = Math.floor((index + 1) * ranges.length / maximum) - 1;
|
|
6065
|
+
capped.push({
|
|
6066
|
+
startLine: ranges[startIndex].startLine,
|
|
6067
|
+
endLine: ranges[endIndex].endLine
|
|
6068
|
+
});
|
|
6069
|
+
}
|
|
6070
|
+
return capped;
|
|
6071
|
+
}
|
|
6072
|
+
function findBlankLineBoundary(lines, target, minimum, maximum) {
|
|
6073
|
+
for (let distance = 0; distance <= TOUR_FOCUS_BOUNDARY_SEARCH_LINES; distance += 1) {
|
|
6074
|
+
for (const candidate of distance === 0 ? [target] : [target + distance, target - distance]) {
|
|
6075
|
+
if (candidate < minimum || candidate >= maximum) continue;
|
|
6076
|
+
const line = lines.find((entry) => entry.lineNumber === candidate);
|
|
6077
|
+
if ((line == null ? void 0 : line.content.trim()) === "") return candidate;
|
|
6078
|
+
}
|
|
6079
|
+
}
|
|
6080
|
+
return Math.min(Math.max(target, minimum), maximum - 1);
|
|
6081
|
+
}
|
|
4837
6082
|
var import_child_process32 = require("child_process");
|
|
4838
6083
|
var CHANGE_TOUR_CONTEXT_VERSION = 1;
|
|
4839
6084
|
var DEFAULT_MAX_PATCH_BYTES = 128 * 1024;
|
|
@@ -5162,7 +6407,8 @@ var require_changeTour = __commonJS({
|
|
|
5162
6407
|
repoRoot,
|
|
5163
6408
|
authoredScene,
|
|
5164
6409
|
authoredScene.base || ((_a = source.range) == null ? void 0 : _a.base),
|
|
5165
|
-
authoredScene.target || ((_b = source.range) == null ? void 0 : _b.head)
|
|
6410
|
+
authoredScene.target || ((_b = source.range) == null ? void 0 : _b.head),
|
|
6411
|
+
[...available.keys()]
|
|
5166
6412
|
);
|
|
5167
6413
|
scenes.push(deconstructedScene);
|
|
5168
6414
|
sceneIds.push(deconstructedScene.id);
|
|
@@ -5208,7 +6454,7 @@ var require_changeTour = __commonJS({
|
|
|
5208
6454
|
}
|
|
5209
6455
|
return { scenes, chapters };
|
|
5210
6456
|
}
|
|
5211
|
-
function buildDeconstructedManifestScene(repoRoot, source, baseRef, targetRef) {
|
|
6457
|
+
function buildDeconstructedManifestScene(repoRoot, source, baseRef, targetRef, availableFilePaths = []) {
|
|
5212
6458
|
const compiled = buildDeconstructedScene(repoRoot, source, { baseRef, headRef: targetRef });
|
|
5213
6459
|
const panels = [{
|
|
5214
6460
|
id: "explanation-baseline",
|
|
@@ -5220,9 +6466,15 @@ var require_changeTour = __commonJS({
|
|
|
5220
6466
|
role: "stage",
|
|
5221
6467
|
stageId: stage.id
|
|
5222
6468
|
}))];
|
|
5223
|
-
const
|
|
6469
|
+
const materializablePaths = new Set(compiled.baselineFiles.map((file) => file.path));
|
|
6470
|
+
const includedPaths = availableFilePaths.length > 0 ? availableFilePaths.filter((filePath) => materializablePaths.has(filePath)).sort() : [...materializablePaths].sort();
|
|
6471
|
+
const missingIntroducedPath = compiled.stages.flatMap((stage) => stage.introducedFiles).find((filePath) => !includedPaths.includes(filePath));
|
|
6472
|
+
if (missingIntroducedPath) {
|
|
6473
|
+
throw new Error(`Deconstructed tour file is not renderable: ${missingIntroducedPath}`);
|
|
6474
|
+
}
|
|
6475
|
+
const introducedPaths = new Set(compiled.stages.flatMap((stage) => stage.introducedFiles));
|
|
5224
6476
|
let totalContentBytes = 0;
|
|
5225
|
-
const files = includedPaths.
|
|
6477
|
+
const files = includedPaths.flatMap((filePath) => {
|
|
5226
6478
|
const states = [
|
|
5227
6479
|
compiled.baselineFiles.find((file) => file.path === filePath),
|
|
5228
6480
|
...compiled.stages.map((stage) => stage.files.find((file) => file.path === filePath))
|
|
@@ -5230,18 +6482,23 @@ var require_changeTour = __commonJS({
|
|
|
5230
6482
|
if (states.some((state) => !state)) {
|
|
5231
6483
|
throw new Error(`Deconstructed scene ${source.id} is missing virtual state for ${filePath}.`);
|
|
5232
6484
|
}
|
|
5233
|
-
|
|
6485
|
+
const fileContentBytes = states.reduce((total, state) => total + Buffer.byteLength((state == null ? void 0 : state.content) || "", "utf8"), 0);
|
|
6486
|
+
const exceedsFileLimit = states.some((state) => {
|
|
6487
|
+
const content = Buffer.from((state == null ? void 0 : state.content) || "", "utf8");
|
|
6488
|
+
return content.length > DEFAULT_MAX_TOUR_FILE_BYTES || hasLineLongerThan(content, DEFAULT_MAX_TOUR_LINE_BYTES);
|
|
6489
|
+
});
|
|
6490
|
+
const exceedsSceneLimit = totalContentBytes + fileContentBytes > DEFAULT_MAX_STACK_CONTENT_BYTES;
|
|
6491
|
+
if (exceedsFileLimit || exceedsSceneLimit) {
|
|
6492
|
+
if (introducedPaths.has(filePath)) {
|
|
6493
|
+
throw new Error(exceedsFileLimit ? `Deconstructed tour file is too large to present: ${filePath}` : `Deconstructed tour content exceeds ${DEFAULT_MAX_STACK_CONTENT_BYTES} bytes.`);
|
|
6494
|
+
}
|
|
6495
|
+
return [];
|
|
6496
|
+
}
|
|
6497
|
+
totalContentBytes += fileContentBytes;
|
|
6498
|
+
return [{
|
|
5234
6499
|
path: filePath,
|
|
5235
6500
|
panels: states.map((state, index) => {
|
|
5236
6501
|
if (!state) throw new Error(`Missing virtual state for ${filePath}.`);
|
|
5237
|
-
const content = Buffer.from(state.content, "utf8");
|
|
5238
|
-
if (content.length > DEFAULT_MAX_TOUR_FILE_BYTES || hasLineLongerThan(content, DEFAULT_MAX_TOUR_LINE_BYTES)) {
|
|
5239
|
-
throw new Error(`Deconstructed tour file is too large to present: ${filePath}`);
|
|
5240
|
-
}
|
|
5241
|
-
totalContentBytes += content.length;
|
|
5242
|
-
if (totalContentBytes > DEFAULT_MAX_STACK_CONTENT_BYTES) {
|
|
5243
|
-
throw new Error(`Deconstructed tour content exceeds ${DEFAULT_MAX_STACK_CONTENT_BYTES} bytes.`);
|
|
5244
|
-
}
|
|
5245
6502
|
return {
|
|
5246
6503
|
id: panels[index].id,
|
|
5247
6504
|
label: `${panels[index].label} / ${filePath}${state.exists ? "" : " (absent)"}`,
|
|
@@ -5250,9 +6507,10 @@ var require_changeTour = __commonJS({
|
|
|
5250
6507
|
exists: state.exists
|
|
5251
6508
|
};
|
|
5252
6509
|
})
|
|
5253
|
-
};
|
|
6510
|
+
}];
|
|
5254
6511
|
});
|
|
5255
6512
|
if (files.length === 0) throw new Error(`Deconstructed scene ${source.id} has no staged files.`);
|
|
6513
|
+
const steps = buildDeconstructedFocusSteps(source.id, compiled.stages, files);
|
|
5256
6514
|
return {
|
|
5257
6515
|
id: source.id,
|
|
5258
6516
|
kind: "deconstructed-diff",
|
|
@@ -5270,17 +6528,63 @@ var require_changeTour = __commonJS({
|
|
|
5270
6528
|
},
|
|
5271
6529
|
panels,
|
|
5272
6530
|
files,
|
|
5273
|
-
steps
|
|
5274
|
-
id: stage.id,
|
|
5275
|
-
title: stage.title,
|
|
5276
|
-
body: stage.narration,
|
|
5277
|
-
file: stage.introducedFiles[0],
|
|
5278
|
-
pairIndex: index,
|
|
5279
|
-
side: "right",
|
|
5280
|
-
introducedHunks: stage.introducedHunks
|
|
5281
|
-
}))
|
|
6531
|
+
steps
|
|
5282
6532
|
};
|
|
5283
6533
|
}
|
|
6534
|
+
function buildDeconstructedFocusSteps(sceneId, stages, files) {
|
|
6535
|
+
const reservedIds = new Set(stages.map((stage) => stage.id));
|
|
6536
|
+
const usedIds = /* @__PURE__ */ new Set();
|
|
6537
|
+
return stages.flatMap((stage, stageIndex) => {
|
|
6538
|
+
const focuses = stage.introducedChanges.flatMap((change) => {
|
|
6539
|
+
const file = files.find((candidate) => candidate.path === change.file);
|
|
6540
|
+
const left = file == null ? void 0 : file.panels[stageIndex];
|
|
6541
|
+
const right = file == null ? void 0 : file.panels[stageIndex + 1];
|
|
6542
|
+
if (!file || !left || !right) {
|
|
6543
|
+
throw new Error(`Deconstructed scene ${sceneId} is missing comparison state for ${change.file}.`);
|
|
6544
|
+
}
|
|
6545
|
+
const diffModel = buildTwoWayDiffModel3(left.content, right.content);
|
|
6546
|
+
const side = left.exists && !right.exists ? "left" : "right";
|
|
6547
|
+
const ranges = buildTourFocusRanges(diffModel, side);
|
|
6548
|
+
if (ranges.length === 0) {
|
|
6549
|
+
throw new Error(`Deconstructed stage ${stage.id} has no visible change for ${change.file}.`);
|
|
6550
|
+
}
|
|
6551
|
+
return ranges.map((range) => ({
|
|
6552
|
+
file: change.file,
|
|
6553
|
+
hunks: change.hunks,
|
|
6554
|
+
side,
|
|
6555
|
+
...range
|
|
6556
|
+
}));
|
|
6557
|
+
});
|
|
6558
|
+
return focuses.map((focus, focusIndex) => ({
|
|
6559
|
+
id: focusIndex === 0 ? claimDeconstructedStageId(stage.id, usedIds) : claimDeconstructedFocusId(`${stage.id}-focus-${focusIndex + 1}`, reservedIds, usedIds),
|
|
6560
|
+
title: focuses.length === 1 ? stage.title : `${stage.title} \xB7 ${focus.file} \xB7 Focus ${focusIndex + 1}/${focuses.length}`,
|
|
6561
|
+
body: stage.narration,
|
|
6562
|
+
file: focus.file,
|
|
6563
|
+
pairIndex: stageIndex,
|
|
6564
|
+
side: focus.side,
|
|
6565
|
+
startLine: focus.startLine,
|
|
6566
|
+
endLine: focus.endLine,
|
|
6567
|
+
introducedHunks: [...focus.hunks],
|
|
6568
|
+
stageId: stage.id,
|
|
6569
|
+
stageIndex,
|
|
6570
|
+
focusIndex,
|
|
6571
|
+
focusCount: focuses.length
|
|
6572
|
+
}));
|
|
6573
|
+
});
|
|
6574
|
+
}
|
|
6575
|
+
function claimDeconstructedStageId(stageId, usedIds) {
|
|
6576
|
+
usedIds.add(stageId);
|
|
6577
|
+
return stageId;
|
|
6578
|
+
}
|
|
6579
|
+
function claimDeconstructedFocusId(preferredId, reservedIds, usedIds) {
|
|
6580
|
+
let candidate = preferredId;
|
|
6581
|
+
let suffix = 2;
|
|
6582
|
+
while (usedIds.has(candidate) || reservedIds.has(candidate)) {
|
|
6583
|
+
candidate = `${preferredId}-${suffix++}`;
|
|
6584
|
+
}
|
|
6585
|
+
usedIds.add(candidate);
|
|
6586
|
+
return candidate;
|
|
6587
|
+
}
|
|
5284
6588
|
function buildStackedScene(repoRoot, source, availableFilePaths) {
|
|
5285
6589
|
const stack = source.stack.map((entry) => ({
|
|
5286
6590
|
id: entry.id,
|
|
@@ -9297,7 +10601,7 @@ var require_package = __commonJS({
|
|
|
9297
10601
|
displayName: "Bygone",
|
|
9298
10602
|
desktopName: "com.davidmashburn.bygone.desktop",
|
|
9299
10603
|
description: "See how change happened. Visual diff and file history for VS Code.",
|
|
9300
|
-
version: "0.8.
|
|
10604
|
+
version: "0.8.14",
|
|
9301
10605
|
author: "David Mashburn",
|
|
9302
10606
|
license: "MIT",
|
|
9303
10607
|
private: false,
|
|
@@ -22531,7 +23835,7 @@ var { searchFileHistory: searchFileHistory2 } = (init_gitHistorySearch(), __toCo
|
|
|
22531
23835
|
var { classifyAuthoredTourPaths: classifyAuthoredTourPaths2, discoverAuthoredTourDocument: discoverAuthoredTourDocument2 } = (init_tourDocument(), __toCommonJS(tourDocument_exports));
|
|
22532
23836
|
var { materializeBranchReviewTrees: materializeBranchReviewTrees2, materializeGitTree: materializeGitTree2, resolveBranchReviewRange: resolveBranchReviewRange2, resolveReviewPathPair: resolveReviewPathPair2 } = (init_gitComparison(), __toCommonJS(gitComparison_exports));
|
|
22533
23837
|
var { buildDirectoryNavigationState } = require_navigationUtils();
|
|
22534
|
-
var { getMenuCapabilities } = require_menuUtils();
|
|
23838
|
+
var { collectComparisonSelection, getMenuCapabilities } = require_menuUtils();
|
|
22535
23839
|
var {
|
|
22536
23840
|
cloneSessionSource,
|
|
22537
23841
|
createBranchReviewSource,
|
|
@@ -22611,6 +23915,8 @@ var wordWrapEnabled = false;
|
|
|
22611
23915
|
var wordWrapAvailable = false;
|
|
22612
23916
|
var repositorySearch = null;
|
|
22613
23917
|
var lastRepositoryReplacement = null;
|
|
23918
|
+
var pendingLaunchPrompt = null;
|
|
23919
|
+
var nextLaunchPromptId = 1;
|
|
22614
23920
|
var appIsQuitting = false;
|
|
22615
23921
|
var restoringWindowState = false;
|
|
22616
23922
|
if (!singleInstanceLock) {
|
|
@@ -23041,40 +24347,52 @@ ${HELP_URL}`,
|
|
|
23041
24347
|
{
|
|
23042
24348
|
label: "File",
|
|
23043
24349
|
submenu: [
|
|
24350
|
+
{
|
|
24351
|
+
label: "New Blank Comparison",
|
|
24352
|
+
accelerator: "CmdOrCtrl+N",
|
|
24353
|
+
click: () => runMenuAction("open a blank comparison", openBlankDiff)
|
|
24354
|
+
},
|
|
24355
|
+
{ type: "separator" },
|
|
23044
24356
|
{
|
|
23045
24357
|
label: "Compare Files\u2026",
|
|
23046
24358
|
accelerator: "CmdOrCtrl+O",
|
|
23047
|
-
click: () =>
|
|
23048
|
-
void openCompareFilesDialog();
|
|
23049
|
-
}
|
|
24359
|
+
click: () => runMenuAction("compare files", openCompareFilesDialog)
|
|
23050
24360
|
},
|
|
23051
24361
|
{
|
|
23052
24362
|
label: "Compare Directories\u2026",
|
|
23053
24363
|
accelerator: "CmdOrCtrl+Shift+O",
|
|
23054
|
-
click: () =>
|
|
23055
|
-
void openCompareDirectoriesDialog();
|
|
23056
|
-
}
|
|
24364
|
+
click: () => runMenuAction("compare directories", openCompareDirectoriesDialog)
|
|
23057
24365
|
},
|
|
24366
|
+
{ type: "separator" },
|
|
23058
24367
|
{
|
|
23059
|
-
label: "
|
|
23060
|
-
|
|
23061
|
-
click: () =>
|
|
23062
|
-
void addPanelFromMenu("left");
|
|
23063
|
-
}
|
|
24368
|
+
label: "View File or Directory History\u2026",
|
|
24369
|
+
accelerator: "CmdOrCtrl+Shift+H",
|
|
24370
|
+
click: () => runMenuAction("open Git history", openHistoryDialog)
|
|
23064
24371
|
},
|
|
23065
24372
|
{
|
|
23066
|
-
label: "
|
|
23067
|
-
|
|
23068
|
-
click: () => {
|
|
23069
|
-
void addPanelFromMenu("right");
|
|
23070
|
-
}
|
|
24373
|
+
label: "Open Authored Tour\u2026",
|
|
24374
|
+
click: () => runMenuAction("open an authored tour", openAuthoredTourDialog)
|
|
23071
24375
|
},
|
|
24376
|
+
{ type: "separator" },
|
|
23072
24377
|
{
|
|
23073
|
-
label: "
|
|
23074
|
-
|
|
23075
|
-
|
|
23076
|
-
|
|
23077
|
-
|
|
24378
|
+
label: "Comparison Panels",
|
|
24379
|
+
submenu: [
|
|
24380
|
+
{
|
|
24381
|
+
label: "Add Panel to Left\u2026",
|
|
24382
|
+
enabled: canAddPanel,
|
|
24383
|
+
click: () => runMenuAction("add a panel", () => addPanelFromMenu("left"))
|
|
24384
|
+
},
|
|
24385
|
+
{
|
|
24386
|
+
label: "Add Panel to Right\u2026",
|
|
24387
|
+
enabled: canAddPanel,
|
|
24388
|
+
click: () => runMenuAction("add a panel", () => addPanelFromMenu("right"))
|
|
24389
|
+
},
|
|
24390
|
+
{
|
|
24391
|
+
label: "Remove Active Panel",
|
|
24392
|
+
enabled: canRemovePanel,
|
|
24393
|
+
click: () => runMenuAction("remove the active panel", removeActivePanelFromMenu)
|
|
24394
|
+
}
|
|
24395
|
+
]
|
|
23078
24396
|
},
|
|
23079
24397
|
{ type: "separator" },
|
|
23080
24398
|
...fileActionItems,
|
|
@@ -23136,9 +24454,7 @@ ${HELP_URL}`,
|
|
|
23136
24454
|
{
|
|
23137
24455
|
label: "Search in Files\u2026",
|
|
23138
24456
|
accelerator: "CmdOrCtrl+Alt+F",
|
|
23139
|
-
click: () =>
|
|
23140
|
-
void openRepositorySearchDialog();
|
|
23141
|
-
}
|
|
24457
|
+
click: () => runMenuAction("search in files", openRepositorySearchDialog)
|
|
23142
24458
|
},
|
|
23143
24459
|
{ type: "separator" },
|
|
23144
24460
|
{
|
|
@@ -23182,35 +24498,13 @@ ${HELP_URL}`,
|
|
|
23182
24498
|
label: "Git",
|
|
23183
24499
|
submenu: [
|
|
23184
24500
|
{
|
|
23185
|
-
label: "
|
|
23186
|
-
|
|
23187
|
-
click: () => {
|
|
23188
|
-
void openHistoryDialog();
|
|
23189
|
-
}
|
|
24501
|
+
label: "Compare Revisions\u2026",
|
|
24502
|
+
click: () => runMenuAction("compare Git revisions", openGitRefsDialog)
|
|
23190
24503
|
},
|
|
23191
24504
|
{
|
|
23192
|
-
label: "
|
|
24505
|
+
label: "Review Branch Change\u2026",
|
|
23193
24506
|
accelerator: "CmdOrCtrl+Shift+R",
|
|
23194
|
-
click: () =>
|
|
23195
|
-
void openBranchReviewDialog();
|
|
23196
|
-
}
|
|
23197
|
-
},
|
|
23198
|
-
{ type: "separator" },
|
|
23199
|
-
{
|
|
23200
|
-
label: "Older Commit",
|
|
23201
|
-
accelerator: "Alt+Left",
|
|
23202
|
-
enabled: isHistory,
|
|
23203
|
-
click: () => {
|
|
23204
|
-
void navigateHistory("back");
|
|
23205
|
-
}
|
|
23206
|
-
},
|
|
23207
|
-
{
|
|
23208
|
-
label: "Newer Commit",
|
|
23209
|
-
accelerator: "Alt+Right",
|
|
23210
|
-
enabled: isHistory,
|
|
23211
|
-
click: () => {
|
|
23212
|
-
void navigateHistory("forward");
|
|
23213
|
-
}
|
|
24507
|
+
click: () => runMenuAction("review a branch change", openBranchReviewDialog)
|
|
23214
24508
|
}
|
|
23215
24509
|
]
|
|
23216
24510
|
},
|
|
@@ -23219,15 +24513,11 @@ ${HELP_URL}`,
|
|
|
23219
24513
|
submenu: [
|
|
23220
24514
|
{
|
|
23221
24515
|
label: "Present Current Branch",
|
|
23222
|
-
click: () =>
|
|
23223
|
-
void presentCurrentBranch();
|
|
23224
|
-
}
|
|
24516
|
+
click: () => runMenuAction("present the current branch", presentCurrentBranch)
|
|
23225
24517
|
},
|
|
23226
24518
|
{
|
|
23227
|
-
label: "
|
|
23228
|
-
click: () =>
|
|
23229
|
-
void openAuthoredTourDialog().catch((error) => showError(`Could not open Bygone presentation: ${getErrorMessage(error)}`));
|
|
23230
|
-
}
|
|
24519
|
+
label: "Present Branch or Ref\u2026",
|
|
24520
|
+
click: () => runMenuAction("present a branch or ref", openPresentBranchDialog)
|
|
23231
24521
|
},
|
|
23232
24522
|
{ type: "separator" },
|
|
23233
24523
|
{
|
|
@@ -23260,6 +24550,23 @@ ${HELP_URL}`,
|
|
|
23260
24550
|
click: () => {
|
|
23261
24551
|
void returnToDirectoryView();
|
|
23262
24552
|
}
|
|
24553
|
+
},
|
|
24554
|
+
{ type: "separator" },
|
|
24555
|
+
{
|
|
24556
|
+
label: "Older Commit",
|
|
24557
|
+
accelerator: "Alt+Left",
|
|
24558
|
+
enabled: isHistory,
|
|
24559
|
+
click: () => {
|
|
24560
|
+
void navigateHistory("back");
|
|
24561
|
+
}
|
|
24562
|
+
},
|
|
24563
|
+
{
|
|
24564
|
+
label: "Newer Commit",
|
|
24565
|
+
accelerator: "Alt+Right",
|
|
24566
|
+
enabled: isHistory,
|
|
24567
|
+
click: () => {
|
|
24568
|
+
void navigateHistory("forward");
|
|
24569
|
+
}
|
|
23263
24570
|
}
|
|
23264
24571
|
]
|
|
23265
24572
|
},
|
|
@@ -23362,9 +24669,38 @@ ${HELP_URL}`,
|
|
|
23362
24669
|
];
|
|
23363
24670
|
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
|
|
23364
24671
|
}
|
|
24672
|
+
function runMenuAction(description, action) {
|
|
24673
|
+
void Promise.resolve().then(action).catch((error) => showError(`Could not ${description}: ${getErrorMessage(error)}`));
|
|
24674
|
+
}
|
|
23365
24675
|
async function presentCurrentBranch() {
|
|
23366
|
-
const
|
|
23367
|
-
|
|
24676
|
+
const repoRoot = await resolveOrChooseGitRepository("Select a Git repository to present");
|
|
24677
|
+
if (repoRoot) {
|
|
24678
|
+
await openTourPresentation([], repoRoot);
|
|
24679
|
+
}
|
|
24680
|
+
}
|
|
24681
|
+
async function openPresentBranchDialog() {
|
|
24682
|
+
const repoRoot = await resolveOrChooseGitRepository("Select a Git repository to present");
|
|
24683
|
+
if (!repoRoot) {
|
|
24684
|
+
return;
|
|
24685
|
+
}
|
|
24686
|
+
openLaunchPrompt({
|
|
24687
|
+
kind: "present-branch",
|
|
24688
|
+
title: "Present Branch or Ref",
|
|
24689
|
+
description: `Repository: ${repoRoot}`,
|
|
24690
|
+
submitLabel: "Present",
|
|
24691
|
+
fields: [
|
|
24692
|
+
{ name: "head", label: "Head branch or ref", value: "HEAD", required: true },
|
|
24693
|
+
{ name: "base", label: "Base branch or ref (optional)", value: "" }
|
|
24694
|
+
]
|
|
24695
|
+
}, async (values) => {
|
|
24696
|
+
const head = values.head.trim() || "HEAD";
|
|
24697
|
+
const base = values.base.trim();
|
|
24698
|
+
const args = [head];
|
|
24699
|
+
if (base) {
|
|
24700
|
+
args.push(tokensFor("base")[0], base);
|
|
24701
|
+
}
|
|
24702
|
+
await openTourPresentation(args, repoRoot);
|
|
24703
|
+
});
|
|
23368
24704
|
}
|
|
23369
24705
|
async function showRepositorySearchStatus() {
|
|
23370
24706
|
const capability = detectRipgrepCapability2();
|
|
@@ -23973,6 +25309,24 @@ async function handleRendererMessage(message) {
|
|
|
23973
25309
|
await refreshSession();
|
|
23974
25310
|
return;
|
|
23975
25311
|
}
|
|
25312
|
+
if (message.type === "submitLaunchPrompt" && Number.isInteger(message.requestId) && message.requestId === pendingLaunchPrompt?.requestId && message.values && typeof message.values === "object" && !Array.isArray(message.values)) {
|
|
25313
|
+
const prompt = pendingLaunchPrompt;
|
|
25314
|
+
pendingLaunchPrompt = null;
|
|
25315
|
+
const values = Object.fromEntries(prompt.fieldNames.map((fieldName) => [
|
|
25316
|
+
fieldName,
|
|
25317
|
+
typeof message.values[fieldName] === "string" ? message.values[fieldName] : ""
|
|
25318
|
+
]));
|
|
25319
|
+
try {
|
|
25320
|
+
await prompt.submit(values);
|
|
25321
|
+
} catch (error) {
|
|
25322
|
+
await showError(`Could not ${prompt.description}: ${getErrorMessage(error)}`);
|
|
25323
|
+
}
|
|
25324
|
+
return;
|
|
25325
|
+
}
|
|
25326
|
+
if (message.type === "cancelLaunchPrompt" && Number.isInteger(message.requestId) && message.requestId === pendingLaunchPrompt?.requestId) {
|
|
25327
|
+
pendingLaunchPrompt = null;
|
|
25328
|
+
return;
|
|
25329
|
+
}
|
|
23976
25330
|
if (message.type === "searchChangeSet" && Number.isInteger(message.requestId)) {
|
|
23977
25331
|
searchCurrentChangeSet(message);
|
|
23978
25332
|
return;
|
|
@@ -24126,24 +25480,115 @@ async function handleRendererMessage(message) {
|
|
|
24126
25480
|
}
|
|
24127
25481
|
}
|
|
24128
25482
|
async function openCompareFilesDialog() {
|
|
24129
|
-
|
|
25483
|
+
const filePaths = await collectComparisonPaths("file");
|
|
25484
|
+
if (filePaths.length < 2) {
|
|
24130
25485
|
return;
|
|
24131
25486
|
}
|
|
24132
|
-
|
|
24133
|
-
|
|
24134
|
-
|
|
25487
|
+
if (filePaths.length === 2) {
|
|
25488
|
+
await openDiff(filePaths[0], filePaths[1]);
|
|
25489
|
+
} else {
|
|
25490
|
+
await openMultiDiff(filePaths);
|
|
25491
|
+
}
|
|
25492
|
+
}
|
|
25493
|
+
async function collectComparisonPaths(kind) {
|
|
25494
|
+
ensureExploreWindow();
|
|
25495
|
+
const isDirectory = kind === "directory";
|
|
25496
|
+
const noun = isDirectory ? "directory" : "file";
|
|
25497
|
+
return collectComparisonSelection(async (selectedCount) => {
|
|
25498
|
+
const result = await dialog.showOpenDialog(mainWindow, {
|
|
25499
|
+
title: selectedCount === 0 ? `Select ${noun}s to compare` : `Add ${noun}${selectedCount === 1 ? "" : "s"} to the comparison`,
|
|
25500
|
+
properties: [isDirectory ? "openDirectory" : "openFile", "multiSelections"]
|
|
25501
|
+
});
|
|
25502
|
+
if (result.canceled) {
|
|
25503
|
+
return null;
|
|
25504
|
+
}
|
|
25505
|
+
return result.filePaths.map((selectedPath) => path8.resolve(selectedPath));
|
|
25506
|
+
}, async (paths) => {
|
|
25507
|
+
const choice = await dialog.showMessageBox(mainWindow, {
|
|
25508
|
+
type: "question",
|
|
25509
|
+
buttons: [`Compare ${paths.length}`, "Add More\u2026", "Cancel"],
|
|
25510
|
+
defaultId: 0,
|
|
25511
|
+
cancelId: 2,
|
|
25512
|
+
message: `Compare ${paths.length} selected ${noun}${paths.length === 1 ? "" : "s"}?`,
|
|
25513
|
+
detail: paths.join("\n")
|
|
25514
|
+
});
|
|
25515
|
+
if (choice.response === 0) {
|
|
25516
|
+
return "compare";
|
|
25517
|
+
}
|
|
25518
|
+
return choice.response === 1 ? "add" : "cancel";
|
|
25519
|
+
});
|
|
25520
|
+
}
|
|
25521
|
+
function ensureExploreWindow() {
|
|
25522
|
+
ensureMainWindow();
|
|
25523
|
+
if (!mainWindow.isVisible()) {
|
|
25524
|
+
mainWindow.show();
|
|
25525
|
+
}
|
|
25526
|
+
mainWindow.focus();
|
|
25527
|
+
}
|
|
25528
|
+
function openLaunchPrompt(config, submit) {
|
|
25529
|
+
ensureExploreWindow();
|
|
25530
|
+
const requestId = nextLaunchPromptId++;
|
|
25531
|
+
pendingLaunchPrompt = {
|
|
25532
|
+
requestId,
|
|
25533
|
+
fieldNames: config.fields.map((field) => field.name),
|
|
25534
|
+
description: config.failureDescription || config.title.toLowerCase(),
|
|
25535
|
+
submit
|
|
25536
|
+
};
|
|
25537
|
+
postToRenderer({
|
|
25538
|
+
type: "openLaunchPrompt",
|
|
25539
|
+
requestId,
|
|
25540
|
+
...config
|
|
24135
25541
|
});
|
|
24136
|
-
|
|
24137
|
-
|
|
24138
|
-
|
|
25542
|
+
}
|
|
25543
|
+
async function resolveOrChooseGitRepository(title) {
|
|
25544
|
+
const sourcePath = session.source?.repoRoot || session.source?.path || session.source?.paths?.[0];
|
|
25545
|
+
const sourceCandidate = sourcePath && getPathKind(sourcePath) === "file" ? path8.dirname(sourcePath) : sourcePath;
|
|
25546
|
+
const candidates = [sourceCandidate, launchArguments.cwd, process.cwd()].filter((candidate, index, all) => candidate && all.indexOf(candidate) === index);
|
|
25547
|
+
for (const candidate of candidates) {
|
|
25548
|
+
try {
|
|
25549
|
+
return fs7.realpathSync(runGit(["rev-parse", "--show-toplevel"], candidate));
|
|
25550
|
+
} catch {
|
|
24139
25551
|
}
|
|
24140
|
-
return;
|
|
24141
25552
|
}
|
|
24142
|
-
|
|
24143
|
-
|
|
24144
|
-
|
|
24145
|
-
|
|
25553
|
+
ensureExploreWindow();
|
|
25554
|
+
const result = await dialog.showOpenDialog(mainWindow, {
|
|
25555
|
+
title,
|
|
25556
|
+
properties: ["openDirectory"]
|
|
25557
|
+
});
|
|
25558
|
+
if (result.canceled || result.filePaths.length === 0) {
|
|
25559
|
+
return null;
|
|
25560
|
+
}
|
|
25561
|
+
try {
|
|
25562
|
+
return fs7.realpathSync(runGit(["rev-parse", "--show-toplevel"], result.filePaths[0]));
|
|
25563
|
+
} catch (error) {
|
|
25564
|
+
await showError(`The selected directory is not inside a Git repository: ${getErrorMessage(error)}`);
|
|
25565
|
+
return null;
|
|
25566
|
+
}
|
|
25567
|
+
}
|
|
25568
|
+
async function openGitRefsDialog() {
|
|
25569
|
+
const repoRoot = await resolveOrChooseGitRepository("Select a Git repository to compare");
|
|
25570
|
+
if (!repoRoot) {
|
|
25571
|
+
return;
|
|
24146
25572
|
}
|
|
25573
|
+
openLaunchPrompt({
|
|
25574
|
+
kind: "git-refs",
|
|
25575
|
+
title: "Compare Git Revisions",
|
|
25576
|
+
description: `Repository: ${repoRoot}`,
|
|
25577
|
+
submitLabel: "Compare",
|
|
25578
|
+
fields: [{
|
|
25579
|
+
name: "refs",
|
|
25580
|
+
label: "Revisions (two or more, separated by spaces)",
|
|
25581
|
+
value: "HEAD~1 HEAD",
|
|
25582
|
+
required: true
|
|
25583
|
+
}]
|
|
25584
|
+
}, async (values) => {
|
|
25585
|
+
const refs = values.refs.trim().split(/\s+/).filter(Boolean);
|
|
25586
|
+
if (refs.length < 2) {
|
|
25587
|
+
await showInfo("Enter at least two Git revisions to compare.");
|
|
25588
|
+
return;
|
|
25589
|
+
}
|
|
25590
|
+
await openGitRefs(repoRoot, refs);
|
|
25591
|
+
});
|
|
24147
25592
|
}
|
|
24148
25593
|
async function openDroppedFiles(paths) {
|
|
24149
25594
|
const normalizedPaths = paths.filter((candidate) => typeof candidate === "string" && candidate.length > 0).map((candidate) => path8.resolve(candidate)).filter((candidate, index, all) => all.indexOf(candidate) === index);
|
|
@@ -24208,37 +25653,29 @@ async function openHistoryDialog() {
|
|
|
24208
25653
|
await openHistory(targetPath, historyIncludeStagedPreference);
|
|
24209
25654
|
}
|
|
24210
25655
|
async function openBranchReviewDialog() {
|
|
24211
|
-
|
|
24212
|
-
|
|
24213
|
-
runGit(["rev-parse", "--show-toplevel"], reviewRoot);
|
|
24214
|
-
} catch {
|
|
24215
|
-
if (!mainWindow) {
|
|
24216
|
-
return;
|
|
24217
|
-
}
|
|
24218
|
-
const result = await dialog.showOpenDialog(mainWindow, {
|
|
24219
|
-
title: "Select a Git repository to review",
|
|
24220
|
-
properties: ["openDirectory"]
|
|
24221
|
-
});
|
|
24222
|
-
if (result.canceled || result.filePaths.length === 0) {
|
|
24223
|
-
return;
|
|
24224
|
-
}
|
|
24225
|
-
reviewRoot = result.filePaths[0];
|
|
24226
|
-
}
|
|
24227
|
-
await openGitBranchReview(reviewRoot, "HEAD");
|
|
24228
|
-
}
|
|
24229
|
-
async function openCompareDirectoriesDialog() {
|
|
24230
|
-
if (!mainWindow) {
|
|
25656
|
+
const repoRoot = await resolveOrChooseGitRepository("Select a Git repository to review");
|
|
25657
|
+
if (!repoRoot) {
|
|
24231
25658
|
return;
|
|
24232
25659
|
}
|
|
24233
|
-
|
|
24234
|
-
|
|
24235
|
-
|
|
25660
|
+
openLaunchPrompt({
|
|
25661
|
+
kind: "review-branch",
|
|
25662
|
+
title: "Review Branch Change",
|
|
25663
|
+
description: `Repository: ${repoRoot}`,
|
|
25664
|
+
submitLabel: "Review",
|
|
25665
|
+
fields: [
|
|
25666
|
+
{ name: "head", label: "Head branch or ref", value: "HEAD", required: true },
|
|
25667
|
+
{ name: "base", label: "Base branch or ref (optional)", value: "" }
|
|
25668
|
+
]
|
|
25669
|
+
}, async (values) => {
|
|
25670
|
+
await openGitBranchReview(repoRoot, values.head.trim() || "HEAD", values.base.trim() || void 0);
|
|
24236
25671
|
});
|
|
24237
|
-
|
|
24238
|
-
|
|
25672
|
+
}
|
|
25673
|
+
async function openCompareDirectoriesDialog() {
|
|
25674
|
+
const directoryPaths = await collectComparisonPaths("directory");
|
|
25675
|
+
if (directoryPaths.length < 2) {
|
|
24239
25676
|
return;
|
|
24240
25677
|
}
|
|
24241
|
-
await openDirectories(
|
|
25678
|
+
await openDirectories(directoryPaths);
|
|
24242
25679
|
}
|
|
24243
25680
|
async function openDirectories(dirs, options = {}) {
|
|
24244
25681
|
const resolvedDirs = dirs.map((dir) => path8.resolve(dir)).filter((dir, index, all) => all.indexOf(dir) === index);
|