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.
@@ -1,14 +1,66 @@
1
1
  import { Assumption } from "./Assumption.js";
2
2
  import { AnySymbol } from "./Symbol.js";
3
- import { WithId } from "../shared/utils.js";
3
+ import { WithId, WithNote, WithType } from "../shared/utils.js";
4
4
  /**
5
- * The types of editorial change an edit may state. Each is read off the
6
- * shape of the change, as `proposeEdits` reads it. Whether a change
7
- * corrects an error is not: it is a reason for the change, and stated as
8
- * its motivation (`correctedError`).
5
+ * A reason for a musical change, in the editor's words. The edition
6
+ * states its motivations once (`Edition.motivations`), and an edit of
7
+ * any version names the one it was made for. Every change of a version
8
+ * that derives from another states a motivation: a program states
9
+ * whether it is musical or technical (`musicalChange`,
10
+ * `technicalChange`), and the editor puts a reason of their own in
11
+ * place of a musical one. Speculating about why a change was made is
12
+ * kept to the musical changes.
13
+ * @see crm:E33 Linguistic Object
14
+ * @category Format types
15
+ */
16
+ export type Motivation = WithType<'motivation'> & WithId & WithNote;
17
+ /**
18
+ * The motivation of a musical change whose reason is not given yet: a
19
+ * change of the performance, such as an added accent or a perforation
20
+ * put elsewhere. A program states it for a change whose shape a musical
21
+ * decision makes, under the rule that later versions are the more
22
+ * refined; the editor puts a reason of their own in its place.
23
+ * @category Versions and stemma
24
+ */
25
+ export declare const musicalChange = "https://w3id.org/reo/type/motivation/musical";
26
+ /**
27
+ * The motivation of a technical change: one that changes only how the
28
+ * performance is carried, a redundancy cleaned up or added, or a
29
+ * command recoded for another reproducing system. A program states it
30
+ * for a change of such a shape, and the procedure of the version's
31
+ * creation explains it, so that no reason of its own is given.
32
+ * @category Versions and stemma
33
+ */
34
+ export declare const technicalChange = "https://w3id.org/reo/type/motivation/technical";
35
+ /**
36
+ * The kinds of change an edit can be: one that changes the
37
+ * performance, and one that changes only how it is carried.
9
38
  * @category Vocabulary
10
39
  */
11
- export declare const editTypes: readonly ['additional-accent', 'add-redundancy', 'remove-redundancy',
40
+ export declare const changeKinds: readonly ['musical', 'technical'];
41
+ /**
42
+ * The kind of change an edit is (`kindOfChange`).
43
+ * @category Format types
44
+ */
45
+ export type ChangeKind = typeof changeKinds[number];
46
+ /**
47
+ * What kind of change the edit is, as its motivation says: technical
48
+ * if it is `technicalChange`, musical if it is `musicalChange` or a
49
+ * reason of the editor's.
50
+ *
51
+ * An edit without a motivation is of no kind: the edits of a version
52
+ * that derives from none state its text rather than a change.
53
+ * @category Versions and stemma
54
+ */
55
+ export declare const kindOfChange: (edit: Readonly<Pick<Edit, 'motivation'>>) => ChangeKind | undefined;
56
+ /**
57
+ * The shapes a change can take, read off what it inserts and deletes
58
+ * and the systems of the two versions (`shapeOf`, `proposeEdits`). A
59
+ * shape is not stated: it is what a program reads to propose a
60
+ * motivation, and what a view can call a change.
61
+ * @category Vocabulary
62
+ */
63
+ export declare const editShapes: readonly ['additional-accent', 'add-redundancy', 'remove-redundancy',
12
64
  /**
13
65
  * Carries the text into another reproducing system's coding: a
14
66
  * latched pair of one system giving way to the other's held command,
@@ -17,18 +69,24 @@ export declare const editTypes: readonly ['additional-accent', 'add-redundancy',
17
69
  */
18
70
  'recoding', 'shift', 'shorten', 'prolong'];
19
71
  /**
20
- * The type of editorial change applied to a symbol or set of symbols.
21
- * Classifies the shape of the edit, e.g. whether it adds an accent,
22
- * shifts a note, or shortens/prolongs a perforation. Why it was made is
23
- * its motivation.
72
+ * The shape of a change (`editShapes`).
24
73
  * @category Format types
25
74
  */
26
- export type EditType = typeof editTypes[number];
75
+ export type EditShape = typeof editShapes[number];
76
+ /**
77
+ * The motivation a program states for a change of this shape. A
78
+ * recoding and a redundancy removed or added are technical changes
79
+ * (`technicalChange`). Every other change of a later version is taken
80
+ * for a refinement, and so for a musical change whose reason is not
81
+ * given yet (`musicalChange`).
82
+ * @category Versions and stemma
83
+ */
84
+ export declare const motivationProposedFor: (shape: EditShape | undefined) => string;
27
85
  /**
28
86
  * A set of edits transforms a version of a roll into another version.
29
87
  * Edits insert or delete symbols, or both (= replace).
30
- * Edits may be motivated by a given set of reasons, e.g.
31
- * to strengthen an accent or to correct an error.
88
+ * What kind of change an edit is follows from its motivation
89
+ * (`kindOfChange`).
32
90
  * If an edit is the interpretation of a metamark,
33
91
  * such as a pencil mark, this should be made explicit
34
92
  * using a meaning comprehension on the `@annotation` field.
@@ -42,13 +100,10 @@ export type EditType = typeof editTypes[number];
42
100
  export interface Edit extends WithId, Assumption {
43
101
  type: 'edit';
44
102
  /**
45
- * The type of editorial change, as its shape shows it (e.g. 'shift', 'additional-accent').
46
- * @see crm:P2 has type
47
- */
48
- editType?: EditType;
49
- /**
50
- * A textual description of the motivation for this edit,
51
- * referencing a motivation defined in the version's motivations list.
103
+ * Why the edit was made: one of the edition's motivations, by id,
104
+ * or one the vocabulary publishes, by IRI (`musicalChange`,
105
+ * `technicalChange`). An edit of a version that derives from none
106
+ * states none.
52
107
  * @see crm:P17 was motivated by
53
108
  */
54
109
  motivation?: string;
package/lib/model/Edit.js CHANGED
@@ -1,11 +1,47 @@
1
1
  /**
2
- * The types of editorial change an edit may state. Each is read off the
3
- * shape of the change, as `proposeEdits` reads it. Whether a change
4
- * corrects an error is not: it is a reason for the change, and stated as
5
- * its motivation (`correctedError`).
2
+ * The motivation of a musical change whose reason is not given yet: a
3
+ * change of the performance, such as an added accent or a perforation
4
+ * put elsewhere. A program states it for a change whose shape a musical
5
+ * decision makes, under the rule that later versions are the more
6
+ * refined; the editor puts a reason of their own in its place.
7
+ * @category Versions and stemma
8
+ */
9
+ export const musicalChange = 'https://w3id.org/reo/type/motivation/musical';
10
+ /**
11
+ * The motivation of a technical change: one that changes only how the
12
+ * performance is carried, a redundancy cleaned up or added, or a
13
+ * command recoded for another reproducing system. A program states it
14
+ * for a change of such a shape, and the procedure of the version's
15
+ * creation explains it, so that no reason of its own is given.
16
+ * @category Versions and stemma
17
+ */
18
+ export const technicalChange = 'https://w3id.org/reo/type/motivation/technical';
19
+ /**
20
+ * The kinds of change an edit can be: one that changes the
21
+ * performance, and one that changes only how it is carried.
6
22
  * @category Vocabulary
7
23
  */
8
- export const editTypes = [
24
+ export const changeKinds = ['musical', 'technical'];
25
+ /**
26
+ * What kind of change the edit is, as its motivation says: technical
27
+ * if it is `technicalChange`, musical if it is `musicalChange` or a
28
+ * reason of the editor's.
29
+ *
30
+ * An edit without a motivation is of no kind: the edits of a version
31
+ * that derives from none state its text rather than a change.
32
+ * @category Versions and stemma
33
+ */
34
+ export const kindOfChange = (edit) => edit.motivation === undefined
35
+ ? undefined
36
+ : edit.motivation === technicalChange ? 'technical' : 'musical';
37
+ /**
38
+ * The shapes a change can take, read off what it inserts and deletes
39
+ * and the systems of the two versions (`shapeOf`, `proposeEdits`). A
40
+ * shape is not stated: it is what a program reads to propose a
41
+ * motivation, and what a view can call a change.
42
+ * @category Vocabulary
43
+ */
44
+ export const editShapes = [
9
45
  'additional-accent',
10
46
  'add-redundancy',
11
47
  'remove-redundancy',
@@ -20,6 +56,17 @@ export const editTypes = [
20
56
  'shorten',
21
57
  'prolong',
22
58
  ];
59
+ /**
60
+ * The motivation a program states for a change of this shape. A
61
+ * recoding and a redundancy removed or added are technical changes
62
+ * (`technicalChange`). Every other change of a later version is taken
63
+ * for a refinement, and so for a musical change whose reason is not
64
+ * given yet (`musicalChange`).
65
+ * @category Versions and stemma
66
+ */
67
+ export const motivationProposedFor = (shape) => shape === 'recoding' || shape === 'remove-redundancy' || shape === 'add-redundancy'
68
+ ? technicalChange
69
+ : musicalChange;
23
70
  /**
24
71
  * Whether the object is an edit.
25
72
  * @category Lookups
@@ -1,10 +1,12 @@
1
1
  import { RollCopy } from "./RollCopy.js";
2
2
  import { Version } from "./Version.js";
3
- import { DateAssignment, PropositionSet } from "./Assumption.js";
4
- import { Editor, Person, Place } from "./Agent.js";
3
+ import { Motivation } from "./Edit.js";
4
+ import { DateAssignment } from "./Assumption.js";
5
+ import { Editor, Person, Place, Software } from "./Agent.js";
5
6
  /**
6
- * This type describes the creation of an edition,
7
- * i.e. the editor, publisher, and publication date.
7
+ * This type describes the creation of an edition: the editors,
8
+ * publisher and publication date, the editorial principles the edition
9
+ * follows, and the programs it was made with.
8
10
  * @see lrmoo:F28 Expression Creation
9
11
  * @category Format types
10
12
  */
@@ -28,6 +30,25 @@ export interface EditionCreation {
28
30
  * @see dcterms:date
29
31
  */
30
32
  publicationDate: Date;
33
+ /**
34
+ * The editorial principles the edition follows, by IRI: the
35
+ * published rules its stemma and its edits rest on, whether the
36
+ * editor applied them or a program did, such as that the same
37
+ * addition at the same place is not made twice independently
38
+ * (`stemmaRules`, `editRules`). Adopting a proposal names those it
39
+ * follows. An edition that names none leaves it out.
40
+ * @see crm:P33 used specific technique
41
+ */
42
+ principles?: string[];
43
+ /**
44
+ * The programs the edition was made with, each with its release:
45
+ * the release of this library that found its stemma and grouped its
46
+ * edits, which finds them again with the defaults it has. Adopting a
47
+ * proposal names it, and saving the edition does not, so that the
48
+ * release named is the one that found what the edition states.
49
+ * @see crm:P16 used specific object
50
+ */
51
+ software?: Software[];
31
52
  }
32
53
  /**
33
54
  * Describes the event of recording and documents
@@ -160,14 +181,13 @@ export interface Edition {
160
181
  */
161
182
  versions: Version[];
162
183
  /**
163
- * The statements the edition holds together under one belief, each
164
- * set with the reasoning that concluded it: a stemma a program
165
- * proposed and the editor adopted, the edits it grouped. A statement
166
- * belongs to a set by naming it in its annotation. An edition that
167
- * holds no such set leaves it out.
168
- * @see crminf:I4 Proposition Set
184
+ * The editor's motivations, each a reason for musical changes in
185
+ * words, stated once and named by the edits of any version
186
+ * (`Edit.motivation`). The motivations the vocabulary publishes are
187
+ * named by IRI and not stated here (`musicalChange`,
188
+ * `technicalChange`).
169
189
  */
170
- propositionSets?: PropositionSet[];
190
+ motivations?: Motivation[];
171
191
  }
172
192
  /**
173
193
  * What an edition states about itself rather than about the copies and
@@ -1,36 +1,9 @@
1
1
  import { Edit } from "./Edit.js";
2
2
  import { Concept } from "./Agent.js";
3
- import { ActorAssignment, Belief, Certainty, DateAssignment, ObjectAssumption, PropositionSet, ReferenceAssumption } from "./Assumption.js";
3
+ import { ActorAssignment, Belief, Certainty, DateAssignment, ObjectAssumption, ReferenceAssumption } from "./Assumption.js";
4
4
  import { CollationTolerance } from "../collation/Collation.js";
5
5
  import { AnySymbol } from "./Symbol.js";
6
- import { WithId, WithNote, WithType } from "../shared/utils.js";
7
- /**
8
- * A motivation provides a reason or rationale for an editorial change.
9
- * Motivations are defined at the version level and referenced by edits.
10
- * @see crm:E33 Linguistic Object
11
- * @category Format types
12
- */
13
- export type Motivation = WithType<'motivation'> & WithId & WithNote;
14
- /**
15
- * The motivation of an edit that puts right what the version it derives
16
- * from got wrong: a perforation put in the wrong place, struck or left
17
- * out. Whether a change corrects an error is not read off its shape, as
18
- * its type is, but is a reason for it, and so the editor's to give.
19
- * The edit types held it once, and a document written then is read with
20
- * this motivation in place of that type.
21
- * @category Versions and stemma
22
- */
23
- export declare const correctedError = "corrected-error";
24
- /**
25
- * The version's statement of what the motivation of a corrected error
26
- * means, to stand in its `motivations`.
27
- * @category Versions and stemma
28
- */
29
- export declare const correctedErrorMotivation: {
30
- readonly type: 'motivation';
31
- readonly id: "corrected-error";
32
- readonly note: 'Puts right an error of the version it derives from.';
33
- };
6
+ import { WithId } from "../shared/utils.js";
34
7
  /**
35
8
  * A derivation names the version another one was derived from, together
36
9
  * with the tolerance the two were collated at. How precisely the copies
@@ -168,10 +141,6 @@ export interface Version extends WithId {
168
141
  * @see reo:involvedEdit
169
142
  */
170
143
  edits?: Edit[];
171
- /**
172
- * A collection of motivations used in this version's edits.
173
- */
174
- motivations: Motivation[];
175
144
  }
176
145
  /**
177
146
  * The edits the version states, none where it leaves its text unstated.
@@ -190,19 +159,14 @@ export declare const insertedBy: (version: Readonly<Version>) => AnySymbol[];
190
159
  export declare const deletedBy: (version: Readonly<Version>) => string[];
191
160
  /**
192
161
  * The parents the version names, in the order of `basedOn`, each with the
193
- * certainty its derivation is held with and the belief it rests on. Given
194
- * the edition, a derivation that belongs to a proposition set, as an
195
- * adopted stemma's do, is read with the belief of the set, and names it
196
- * (`propositionSet`); without it, only a belief of its own is read.
162
+ * certainty its derivation is held with and the belief it rests on, where
163
+ * it carries one.
197
164
  * @category Versions and stemma
198
165
  */
199
- export declare const derivationsOf: (version: Readonly<Version>, edition?: {
200
- propositionSets?: readonly PropositionSet[];
201
- }) => {
166
+ export declare const derivationsOf: (version: Readonly<Version>) => {
202
167
  parent: string;
203
168
  certainty: Certainty;
204
169
  belief?: Belief;
205
- propositionSet?: string;
206
170
  }[];
207
171
  /**
208
172
  * The derivation the version's text is read against: the first of those
@@ -1,25 +1,5 @@
1
- import { beliefIn, certainties, certaintyOf, idOf } from "./Assumption.js";
1
+ import { certainties, certaintyOf, idOf } from "./Assumption.js";
2
2
  import { defaultCollationTolerance } from "../collation/Collation.js";
3
- /**
4
- * The motivation of an edit that puts right what the version it derives
5
- * from got wrong: a perforation put in the wrong place, struck or left
6
- * out. Whether a change corrects an error is not read off its shape, as
7
- * its type is, but is a reason for it, and so the editor's to give.
8
- * The edit types held it once, and a document written then is read with
9
- * this motivation in place of that type.
10
- * @category Versions and stemma
11
- */
12
- export const correctedError = 'corrected-error';
13
- /**
14
- * The version's statement of what the motivation of a corrected error
15
- * means, to stand in its `motivations`.
16
- * @category Versions and stemma
17
- */
18
- export const correctedErrorMotivation = {
19
- type: 'motivation',
20
- id: correctedError,
21
- note: 'Puts right an error of the version it derives from.'
22
- };
23
3
  /**
24
4
  * The tolerance the derivation was collated at, or the default where it states none.
25
5
  * @category Collation and alignment
@@ -42,20 +22,16 @@ export const insertedBy = (version) => editsOf(version).flatMap(edit => edit.ins
42
22
  export const deletedBy = (version) => editsOf(version).flatMap(edit => edit.delete ?? []);
43
23
  /**
44
24
  * The parents the version names, in the order of `basedOn`, each with the
45
- * certainty its derivation is held with and the belief it rests on. Given
46
- * the edition, a derivation that belongs to a proposition set, as an
47
- * adopted stemma's do, is read with the belief of the set, and names it
48
- * (`propositionSet`); without it, only a belief of its own is read.
25
+ * certainty its derivation is held with and the belief it rests on, where
26
+ * it carries one.
49
27
  * @category Versions and stemma
50
28
  */
51
- export const derivationsOf = (version, edition = {}) => (version.basedOn ?? []).map(derivation => {
52
- const belief = beliefIn(edition, derivation);
53
- const set = derivation['@annotation']?.belief === undefined ? derivation['@annotation']?.id : undefined;
29
+ export const derivationsOf = (version) => (version.basedOn ?? []).map(derivation => {
30
+ const belief = derivation['@annotation']?.belief;
54
31
  return {
55
32
  parent: idOf(derivation),
56
33
  certainty: belief?.certainty ?? 'true',
57
- ...(belief && { belief }),
58
- ...(set !== undefined && { propositionSet: set })
34
+ ...(belief && { belief })
59
35
  };
60
36
  });
61
37
  const rankOf = (derivation) => certainties.indexOf(certaintyOf(derivation));
@@ -9,15 +9,18 @@ import { EditionOp } from "./draft.js";
9
9
  * tolerance it was collated at and becomes the principal one; the
10
10
  * hypotheses the child stated beside its former one stay.
11
11
  *
12
- * The edits the child already stated are rewritten only as far as a
13
- * collation wrote them (`isCollationsOwn`). An editor's reading of the
12
+ * The edits the child already stated are rewritten only as far as they
13
+ * are the program's (`isCollationsOwn`). An editor's reading of the
14
14
  * difference between the two texts stays, and the symbols it speaks for
15
15
  * are left out of the collation, so that connecting the two again
16
16
  * neither doubles them nor silently drops what somebody established by
17
17
  * hand. An equivalence is the collation's own, since it is drawn from
18
18
  * the two systems' vocabularies rather than read off the paper, but one
19
19
  * drawn again over the very same symbols is kept as it stands, with its
20
- * identifier and whatever was written on it.
20
+ * identifier and whatever was written on it. An insertion or deletion
21
+ * drawn anew has the motivation its shape calls for
22
+ * (`motivationProposedFor`): the generic musical one, or the technical
23
+ * one, which an equivalence drawn anew always has.
21
24
  *
22
25
  * The child's own text is what it shows less what the parent hands
23
26
  * down, so that connecting two versions already connected collates the
@@ -40,7 +43,8 @@ export declare const collateSymbols: (versionId: string, symbolIds: readonly str
40
43
  * Takes the named copies' reading of a symbol back out of the symbol it
41
44
  * was collated into: their carriers pass to a new symbol of the
42
45
  * version's own, the remaining copies keep the symbol they had, and the
43
- * version states the exchange.
46
+ * version states the exchange, with the motivation its shape calls for
47
+ * (`motivationProposedFor`) where the version derives from another.
44
48
  *
45
49
  * This is the inverse of the hand-over a collation makes, and it is
46
50
  * what lets a derivation be collated a second time. A collated symbol
@@ -1,14 +1,18 @@
1
1
  import { v4 } from "uuid";
2
2
  import { getAt, pathIn } from "../lookup/paths.js";
3
- import { collationsOf, defaultCollationTolerance, isCollationsOwn, unchecked } from "../collation/Collation.js";
3
+ import { collationsOf, defaultCollationTolerance, isCollationsOwn } from "../collation/Collation.js";
4
+ import { motivationProposedFor, technicalChange } from "../model/Edit.js";
4
5
  import { collationToleranceOf, derivesFrom, editsOf, insertedBy, principalDerivationOf } from "../model/Version.js";
5
6
  import { substitutionsBetween } from "../collation/substitution.js";
6
7
  import { trackerBarOf } from "../systems/index.js";
7
8
  import { assignReference, idOf } from "../model/Assumption.js";
8
- import { noChange, onVersion, insertion, deletion, asUnchecked, declaring, dropInsertions, reading } from "./draft.js";
9
+ import { noChange, onVersion, insertion, deletion, dropInsertions, reading } from "./draft.js";
9
10
  import { hypothesesBeside } from "./versions.js";
11
+ import { shapeOf } from "./edits.js";
12
+ import { predecessorOf } from "../analysis/stemma.js";
10
13
  import { placeOf, snapshotOf } from "../analysis/text.js";
11
14
  import { copyOfFeature, versionIn } from "../lookup/lookup.js";
15
+ import { motivationOfChanges, proposedMotivations } from "../filiation/edits.js";
12
16
  /** The carriers of each collated symbol pass to its counterpart. */
13
17
  const handOverCarriers = (edition, draft, collations) => collations.forEach(({ symbol, counterpart }) => {
14
18
  const path = pathIn(edition, counterpart.id);
@@ -43,15 +47,18 @@ const byExchange = (edits) => new Map(edits.map(edit => [exchangeKey((edit.inser
43
47
  * tolerance it was collated at and becomes the principal one; the
44
48
  * hypotheses the child stated beside its former one stay.
45
49
  *
46
- * The edits the child already stated are rewritten only as far as a
47
- * collation wrote them (`isCollationsOwn`). An editor's reading of the
50
+ * The edits the child already stated are rewritten only as far as they
51
+ * are the program's (`isCollationsOwn`). An editor's reading of the
48
52
  * difference between the two texts stays, and the symbols it speaks for
49
53
  * are left out of the collation, so that connecting the two again
50
54
  * neither doubles them nor silently drops what somebody established by
51
55
  * hand. An equivalence is the collation's own, since it is drawn from
52
56
  * the two systems' vocabularies rather than read off the paper, but one
53
57
  * drawn again over the very same symbols is kept as it stands, with its
54
- * identifier and whatever was written on it.
58
+ * identifier and whatever was written on it. An insertion or deletion
59
+ * drawn anew has the motivation its shape calls for
60
+ * (`motivationProposedFor`): the generic musical one, or the technical
61
+ * one, which an equivalence drawn anew always has.
55
62
  *
56
63
  * The child's own text is what it shows less what the parent hands
57
64
  * down, so that connecting two versions already connected collates the
@@ -101,31 +108,36 @@ export const connectVersions = (childId, parentId, tolerance = defaultCollationT
101
108
  return alreadyStated.get(exchangeKey(inserted, deleted)) ?? {
102
109
  type: 'edit',
103
110
  id: v4(),
104
- editType: 'recoding',
105
- motivation: unchecked,
111
+ motivation: technicalChange,
106
112
  insert: [...by],
107
113
  delete: deleted
108
114
  };
109
115
  };
110
- const edits = [
111
- ...established,
112
- ...substituted.map(equivalence),
113
- ...own.filter(symbol => !collated.has(symbol.id) && !paired.has(symbol.id)).map(insertion).map(asUnchecked),
116
+ const bare = [
117
+ ...own.filter(symbol => !collated.has(symbol.id) && !paired.has(symbol.id)).map(insertion),
114
118
  ...inherited
115
119
  .filter(symbol => !matched.has(symbol.id) && !paired.has(symbol.id))
116
- .map(symbol => asUnchecked(deletion(symbol.id)))
120
+ .map(symbol => deletion(symbol.id))
117
121
  ];
122
+ const drawn = [...established, ...substituted.map(equivalence), ...bare];
118
123
  // Read off the child as it stands, since once its edits are written a
119
124
  // reading of it would copy them all.
120
- const motivations = declaring(child?.motivations ?? [], edits);
121
125
  const basedOn = [
122
126
  { ...assignReference(parentId), collationTolerance: tolerance },
123
127
  ...(child ? hypothesesBeside(child, parentId) : [])
124
128
  ];
129
+ // What kind of change an insertion or a deletion is depends on what it stands among.
130
+ const connected = { ...edition, versions: edition.versions.map(version => version.id === childId ? { ...version, edits: drawn, basedOn } : version) };
131
+ const motivations = proposedMotivations(connected, childId);
132
+ const anew = new Set(bare.map(edit => edit.id));
133
+ const edits = drawn.map(edit => {
134
+ if (!anew.has(edit.id))
135
+ return edit;
136
+ return { ...edit, motivation: motivationOfChanges(motivations, [...(edit.insert ?? []).map(symbol => symbol.id), ...(edit.delete ?? [])]) };
137
+ });
125
138
  return onVersion(childId, (version, draft) => {
126
139
  handOverCarriers(edition, draft, collations);
127
140
  version.edits = edits;
128
- version.motivations = motivations;
129
141
  version.basedOn = basedOn;
130
142
  });
131
143
  });
@@ -184,7 +196,8 @@ const sharedWith = (edition, symbol, copies) => {
184
196
  * Takes the named copies' reading of a symbol back out of the symbol it
185
197
  * was collated into: their carriers pass to a new symbol of the
186
198
  * version's own, the remaining copies keep the symbol they had, and the
187
- * version states the exchange.
199
+ * version states the exchange, with the motivation its shape calls for
200
+ * (`motivationProposedFor`) where the version derives from another.
188
201
  *
189
202
  * This is the inverse of the hand-over a collation makes, and it is
190
203
  * what lets a derivation be collated a second time. A collated symbol
@@ -217,14 +230,18 @@ export const separateReadings = (versionId, copies, symbolIds) => reading(editio
217
230
  return noChange;
218
231
  const inserted = new Set(insertedBy(version).map(symbol => symbol.id));
219
232
  const stated = editsOf(version);
220
- const separations = shared.map(({ symbol, mine }) => ({
221
- type: 'edit',
222
- id: v4(),
223
- insert: [readingOf(symbol, [...mine])],
224
- // A symbol the version inserts itself stays where it is, minus
225
- // the carriers that leave it. Only one it inherits is exchanged.
226
- ...(inserted.has(symbol.id) ? {} : { delete: [symbol.id] })
227
- }));
233
+ const derived = predecessorOf(edition, versionId) !== undefined;
234
+ const separations = shared.map(({ symbol, mine }) => {
235
+ const separation = {
236
+ type: 'edit',
237
+ id: v4(),
238
+ insert: [readingOf(symbol, [...mine])],
239
+ // A symbol the version inserts itself stays where it is, minus
240
+ // the carriers that leave it. Only one it inherits is exchanged.
241
+ ...(inserted.has(symbol.id) ? {} : { delete: [symbol.id] })
242
+ };
243
+ return derived ? { ...separation, motivation: motivationProposedFor(shapeOf(edition, versionId, separation)) } : separation;
244
+ });
228
245
  return onVersion(versionId, (version, draft) => {
229
246
  shared.forEach(({ symbol, theirs }) => {
230
247
  const path = pathIn(edition, symbol.id);
package/lib/ops/copies.js CHANGED
@@ -21,8 +21,7 @@ export const createVersion = (copy) => draft => {
21
21
  draft.versions.push({
22
22
  id: v4(),
23
23
  system: systemOf(bar),
24
- edits: asSymbols(featuresOf(copy), bar).map(insertion),
25
- motivations: []
24
+ edits: asSymbols(featuresOf(copy), bar).map(insertion)
26
25
  });
27
26
  };
28
27
  /**
@@ -3,7 +3,7 @@ import { Draft } from "immer";
3
3
  import { Edition } from "../model/Edition.js";
4
4
  import { AnySymbol } from "../model/Symbol.js";
5
5
  import { Edit } from "../model/Edit.js";
6
- import { Motivation, Version } from "../model/Version.js";
6
+ import { Version } from "../model/Version.js";
7
7
  import { RollCopy } from "../model/RollCopy.js";
8
8
  import { Belief, ReferenceAssumption } from "../model/Assumption.js";
9
9
  import { FeatureOrPatch } from "../model/Feature.js";
@@ -42,14 +42,6 @@ export declare const droppedIds: (before: readonly WithId[], after: readonly Wit
42
42
  export type Ids = ReadonlySet<string>;
43
43
  export declare const insertion: (symbol: AnySymbol) => Edit;
44
44
  export declare const deletion: (symbolId: string) => Edit;
45
- /** The edit as a collation leaves it: made by rule, and not yet read by anybody. */
46
- export declare const asUnchecked: (edit: Edit) => Edit;
47
- /**
48
- * The motivations with the unchecked one declared, where any edit
49
- * names it and the version does not state it yet. An edit naming a
50
- * motivation the version leaves undeclared points at nothing.
51
- */
52
- export declare const declaring: (motivations: Motivation[], edits: readonly Edit[]) => Motivation[];
53
45
  export declare const insertedIn: (versions: readonly Version[]) => AnySymbol[];
54
46
  /** The edits with the change applied, less those it emptied; the very same array where it changed none. */
55
47
  export declare const edited: (edits: Edit[], change: (edit: Edit) => Edit) => Edit[];
package/lib/ops/draft.js CHANGED
@@ -1,7 +1,6 @@
1
1
  /** What the operations are built from: the type of an operation, how one finds what it writes to on the draft, and the edits it writes. */
2
2
  import { isDraft } from "immer";
3
3
  import { v4 } from "uuid";
4
- import { unchecked, uncheckedMotivation } from "../collation/Collation.js";
5
4
  import { insertedBy } from "../model/Version.js";
6
5
  import { featuresOf } from "../model/RollCopy.js";
7
6
  import { assignReference } from "../model/Assumption.js";
@@ -45,17 +44,6 @@ export const droppedIds = (before, after) => {
45
44
  };
46
45
  export const insertion = (symbol) => ({ type: 'edit', id: v4(), insert: [symbol] });
47
46
  export const deletion = (symbolId) => ({ type: 'edit', id: v4(), delete: [symbolId] });
48
- /** The edit as a collation leaves it: made by rule, and not yet read by anybody. */
49
- export const asUnchecked = (edit) => ({ ...edit, motivation: unchecked });
50
- /**
51
- * The motivations with the unchecked one declared, where any edit
52
- * names it and the version does not state it yet. An edit naming a
53
- * motivation the version leaves undeclared points at nothing.
54
- */
55
- export const declaring = (motivations, edits) => edits.some(edit => edit.motivation === unchecked)
56
- && !motivations.some(motivation => motivation.id === unchecked)
57
- ? [...motivations, uncheckedMotivation]
58
- : motivations;
59
47
  const isEmpty = (edit) => !edit.insert?.length && !edit.delete?.length;
60
48
  export const insertedIn = (versions) => versions.flatMap(insertedBy);
61
49
  /** The edits with the change applied, less those it emptied; the very same array where it changed none. */
@@ -1,15 +1,30 @@
1
- import { Edit } from "../model/Edit.js";
1
+ import { Edit, EditShape } from "../model/Edit.js";
2
2
  import { EditionOp } from "./draft.js";
3
+ import { Edition } from "../model/Edition.js";
4
+ /**
5
+ * A guess at the shape of an edit, from the symbols it exchanges and
6
+ * from the systems the version and the one it is based on are coded
7
+ * for, or nothing where it takes no shape the guess knows. It looks at
8
+ * the edit alone: a single expression deleted is taken for a redundancy
9
+ * removed, which `proposeEdits` checks against the text.
10
+ * @category Lookups
11
+ */
12
+ export declare const shapeOf: (edition: Edition, versionId: string, edit: Readonly<Edit>) => EditShape | undefined;
3
13
  /**
4
14
  * Replaces the edits with a single one carrying all their insertions
5
- * and deletions, classified by a guess at what the exchange does, where
6
- * its shape says. The edit is the editor's: it belongs to no proposition
7
- * set a program's run concluded, as one of those merged may have.
15
+ * and deletions. It keeps the first motivation of the editor's that any
16
+ * of them states, and where none does, it has the one its shape calls
17
+ * for (`motivationProposedFor`), so that a merge says it is a musical
18
+ * or a technical change, unless the version derives from none. A
19
+ * grouping that says no more is the program's by rule
20
+ * (`isCollationsOwn`), until the editor says why it was made.
8
21
  * @category Operations
9
22
  */
10
23
  export declare const mergeEdits: (versionId: string, toMerge: readonly Edit[]) => EditionOp;
11
24
  /**
12
- * Replaces the edit with one edit per inserted and one per deleted symbol.
25
+ * Replaces the edit with one edit per inserted and one per deleted
26
+ * symbol. Each part keeps the motivation of the edit, since splitting
27
+ * an edit does not change why it was made.
13
28
  * @category Operations
14
29
  */
15
30
  export declare const splitEdit: (versionId: string, toSplit: Edit) => EditionOp;