linked-rolls 0.75.0 → 0.76.0

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/lib/ops/edits.js CHANGED
@@ -1,5 +1,6 @@
1
1
  /** Merging and splitting the edits of a version. */
2
2
  import { v4 } from "uuid";
3
+ import { motivationProposedFor, musicalChange, technicalChange } from "../model/Edit.js";
3
4
  import { editsOf } from "../model/Version.js";
4
5
  import { keyOf } from "../systems/TrackerBar.js";
5
6
  import { trackerBarOf } from "../systems/index.js";
@@ -16,7 +17,7 @@ const lengthOf = (span) => subtract(span.to, span.from);
16
17
  /** How far apart two onsets may lie for the one symbol to count as a replacement of the other. */
17
18
  const REPLACEMENT_TOLERANCE = mm(5);
18
19
  /** Shorten or prolong, where the inserted symbol starts about where the deleted one did. */
19
- const replacementType = (edition, inserted, deleted) => {
20
+ const replacementShape = (edition, inserted, deleted) => {
20
21
  const after = placeOf(edition, inserted);
21
22
  const before = placeOf(edition, deleted);
22
23
  if (!after || !before || distance(after.from, before.from) >= REPLACEMENT_TOLERANCE)
@@ -26,12 +27,14 @@ const replacementType = (edition, inserted, deleted) => {
26
27
  /** Whether the two symbols say the same thing, as a perforation put elsewhere does. */
27
28
  const sameKind = (a, b) => a.type !== 'text' && b.type !== 'text' && keyOf(a) === keyOf(b);
28
29
  /**
29
- * A guess at what an edit does, from the symbols it exchanges and from
30
- * the systems the version and the one it is based on are coded for, or
31
- * nothing where its shape does not say. Whether it corrects an error is
32
- * not guessed: that is a motivation, the editor's to give.
30
+ * A guess at the shape of an edit, from the symbols it exchanges and
31
+ * from the systems the version and the one it is based on are coded
32
+ * for, or nothing where it takes no shape the guess knows. It looks at
33
+ * the edit alone: a single expression deleted is taken for a redundancy
34
+ * removed, which `proposeEdits` checks against the text.
35
+ * @category Lookups
33
36
  */
34
- const guessEditType = (edition, versionId, edit) => {
37
+ export const shapeOf = (edition, versionId, edit) => {
35
38
  const inserts = edit.insert ?? [];
36
39
  const deletes = symbolsIn(edition, edit.delete ?? []);
37
40
  const inserted = expressionTypesOf(inserts);
@@ -45,8 +48,7 @@ const guessEditType = (edition, versionId, edit) => {
45
48
  * SforzandoForte says the same thing in the other system's words, and
46
49
  * a red command the green bar cannot read struck, or a green hold put
47
50
  * in where the red had no word for it, belongs to the same act. That
48
- * is a recoding. Calling it a corrected error would say the editor
49
- * made a mistake. Only an insertion spelling the bar's own accent
51
+ * is a recoding. Only an insertion spelling the bar's own accent
50
52
  * reads more narrowly, as the accent it spells.
51
53
  */
52
54
  const transferred = bar && parentBar && bar.id !== parentBar.id;
@@ -62,45 +64,51 @@ const guessEditType = (edition, versionId, edit) => {
62
64
  if (inserted.length === 0 && deleted.length === 1)
63
65
  return 'remove-redundancy';
64
66
  if (inserts.length === 1 && deletes.length === 1) {
65
- return replacementType(edition, inserts[0], deletes[0]) ?? (sameKind(inserts[0], deletes[0]) ? 'shift' : undefined);
67
+ return replacementShape(edition, inserts[0], deletes[0]) ?? (sameKind(inserts[0], deletes[0]) ? 'shift' : undefined);
66
68
  }
67
69
  return undefined;
68
70
  };
69
71
  /**
70
72
  * Replaces the edits with a single one carrying all their insertions
71
- * and deletions, classified by a guess at what the exchange does, where
72
- * its shape says. The edit is the editor's: it belongs to no proposition
73
- * set a program's run concluded, as one of those merged may have.
73
+ * and deletions. It keeps the first motivation of the editor's that any
74
+ * of them states, and where none does, it has the one its shape calls
75
+ * for (`motivationProposedFor`), so that a merge says it is a musical
76
+ * or a technical change, unless the version derives from none. A
77
+ * grouping that says no more is the program's by rule
78
+ * (`isCollationsOwn`), until the editor says why it was made.
74
79
  * @category Operations
75
80
  */
76
81
  export const mergeEdits = (versionId, toMerge) => reading(edition => {
77
82
  if (toMerge.length === 0)
78
83
  return noChange;
79
- // The edit is the editor's grouping, whatever set of a program's the first one belonged to.
80
- const { editType: _guessed, '@annotation': annotation, ...first } = toMerge[0];
84
+ const { motivation: _first, ...first } = toMerge[0];
81
85
  const merged = {
82
86
  ...first,
83
- ...(annotation?.belief && { '@annotation': annotation }),
84
87
  id: v4(),
85
88
  insert: toMerge.flatMap(edit => edit.insert ?? []),
86
89
  delete: toMerge.flatMap(edit => edit.delete ?? [])
87
90
  };
88
- const editType = guessEditType(edition, versionId, merged);
89
- if (editType)
90
- merged.editType = editType;
91
+ const editors = toMerge.find(edit => edit.motivation !== undefined && edit.motivation !== musicalChange && edit.motivation !== technicalChange);
92
+ if (editors)
93
+ merged.motivation = editors.motivation;
94
+ else if (predecessorOf(edition, versionId))
95
+ merged.motivation = motivationProposedFor(shapeOf(edition, versionId, merged));
91
96
  const mergedIds = new Set(toMerge.map(edit => edit.id));
92
97
  return onVersion(versionId, version => {
93
98
  version.edits = [...editsOf(version).filter(edit => !mergedIds.has(edit.id)), merged];
94
99
  });
95
100
  });
96
101
  /**
97
- * Replaces the edit with one edit per inserted and one per deleted symbol.
102
+ * Replaces the edit with one edit per inserted and one per deleted
103
+ * symbol. Each part keeps the motivation of the edit, since splitting
104
+ * an edit does not change why it was made.
98
105
  * @category Operations
99
106
  */
100
107
  export const splitEdit = (versionId, toSplit) => {
108
+ const kept = toSplit.motivation === undefined ? {} : { motivation: toSplit.motivation };
101
109
  const parts = [
102
- ...(toSplit.insert ?? []).map(insertion),
103
- ...(toSplit.delete ?? []).map(deletion)
110
+ ...(toSplit.insert ?? []).map(symbol => ({ ...insertion(symbol), ...kept })),
111
+ ...(toSplit.delete ?? []).map(id => ({ ...deletion(id), ...kept }))
104
112
  ];
105
113
  return onVersion(versionId, version => {
106
114
  version.edits = [...editsOf(version).filter(edit => edit.id !== toSplit.id), ...parts];
@@ -8,6 +8,6 @@ export { createVersion, addCopy, alignmentFor, alignCopy, alignCopies, chooseRef
8
8
  export { stateFeatureCondition, addFeature, addBorneFeature, removeFeatures, mergeObstacle, mergeObstacleIn, mergeFeatures, type FeatureAct, type MergeObstacle } from "./features.js";
9
9
  export { detachVersion, removeVersion, removeSymbols, deriveVersion, stateDerivation, clearDerivation } from "./versions.js";
10
10
  export { connectVersions, collateSymbols, separateReadings } from "./collation.js";
11
- export { mergeEdits, splitEdit } from "./edits.js";
11
+ export { mergeEdits, splitEdit, shapeOf } from "./edits.js";
12
12
  export { placeCommand, unplaceCommand, pairCommands, unpairCommand } from "./commands.js";
13
13
  export { createBelief, clearBelief, setCertainty, addReason, removeReason } from "./beliefs.js";
package/lib/ops/index.js CHANGED
@@ -8,6 +8,6 @@ export { createVersion, addCopy, alignmentFor, alignCopy, alignCopies, chooseRef
8
8
  export { stateFeatureCondition, addFeature, addBorneFeature, removeFeatures, mergeObstacle, mergeObstacleIn, mergeFeatures } from "./features.js";
9
9
  export { detachVersion, removeVersion, removeSymbols, deriveVersion, stateDerivation, clearDerivation } from "./versions.js";
10
10
  export { connectVersions, collateSymbols, separateReadings } from "./collation.js";
11
- export { mergeEdits, splitEdit } from "./edits.js";
11
+ export { mergeEdits, splitEdit, shapeOf } from "./edits.js";
12
12
  export { placeCommand, unplaceCommand, pairCommands, unpairCommand } from "./commands.js";
13
13
  export { createBelief, clearBelief, setCertainty, addReason, removeReason } from "./beliefs.js";
@@ -5,8 +5,9 @@ import { EditionOp } from "./draft.js";
5
5
  export declare const hypothesesBeside: (version: Readonly<Version>, parentId: string) => Derivation[];
6
6
  /**
7
7
  * Makes the version stand on its own: what it inherited becomes its
8
- * own insertions, and its derivations go, the hypotheses among them,
9
- * with the motivations that belonged to them.
8
+ * own insertions, and its derivations go, the hypotheses among them.
9
+ * Its edits now state its text rather than a change, and say nothing of
10
+ * why they were made.
10
11
  * @category Operations
11
12
  */
12
13
  export declare const detachVersion: (versionId: string) => EditionOp;
@@ -28,8 +28,9 @@ const dropDerivations = (version, matches) => {
28
28
  };
29
29
  /**
30
30
  * Makes the version stand on its own: what it inherited becomes its
31
- * own insertions, and its derivations go, the hypotheses among them,
32
- * with the motivations that belonged to them.
31
+ * own insertions, and its derivations go, the hypotheses among them.
32
+ * Its edits now state its text rather than a change, and say nothing of
33
+ * why they were made.
33
34
  * @category Operations
34
35
  */
35
36
  export const detachVersion = (versionId) => reading(edition => {
@@ -37,7 +38,6 @@ export const detachVersion = (versionId) => reading(edition => {
37
38
  return onVersion(versionId, version => {
38
39
  version.edits = edits;
39
40
  delete version.basedOn;
40
- version.motivations = [];
41
41
  });
42
42
  });
43
43
  /**
@@ -76,8 +76,7 @@ export const deriveVersion = (versionId, editIds) => onVersion(versionId, (versi
76
76
  id: v4(),
77
77
  system: stateOf(version).system,
78
78
  basedOn: [assignReference(versionId)],
79
- edits: moved,
80
- motivations: []
79
+ edits: moved
81
80
  });
82
81
  });
83
82
  /**