@nerd-bible/wordgard 0.5.3-beta7 → 0.6.2-beta2
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/dist/command.js +11 -10
- package/dist/doc.d.ts +45 -48
- package/dist/doc.js +131 -128
- package/dist/editor.d.ts +190 -104
- package/dist/editor.js +1982 -1624
- package/dist/highlight-code.d.ts +249 -0
- package/dist/highlight-code.js +264 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/phrases.d.ts +3 -2
- package/dist/phrases.js +20 -1
- package/dist/schema.d.ts +37 -1
- package/dist/schema.js +154 -127
- package/dist/set.d.ts +3 -2
- package/dist/set.js +8 -6
- package/dist/state.d.ts +68 -22
- package/dist/state.js +137 -107
- package/dist/table.d.ts +3 -2
- package/dist/table.js +32 -14
- package/package.json +3 -2
package/dist/state.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ChangeSet, Node, Pos,
|
|
1
|
+
import { Plot, Slice, ChangeSet, Node, Pos, Mark, Schema } from 'wordgard/doc';
|
|
2
2
|
|
|
3
3
|
type wgNode = Node;
|
|
4
4
|
/**
|
|
@@ -87,6 +87,12 @@ declare abstract class GardSelection {
|
|
|
87
87
|
anchorSide: -1 | 1;
|
|
88
88
|
};
|
|
89
89
|
/**
|
|
90
|
+
Create a content slice and context from this selection. The
|
|
91
|
+
default implementation concatenates the slices for the
|
|
92
|
+
selection's `ranges`.
|
|
93
|
+
*/
|
|
94
|
+
toSlice(doc: Plot.Doc): Slice;
|
|
95
|
+
/**
|
|
90
96
|
The side that the selection head is associated with. -1 means it
|
|
91
97
|
is after the element before its position, 1 means it is before
|
|
92
98
|
the element after its position. This influences where the
|
|
@@ -144,6 +150,10 @@ declare abstract class GardSelection {
|
|
|
144
150
|
*/
|
|
145
151
|
static range(anchor: number, head?: number, headSide?: -1 | 1, goalColumn?: number): GardSelection.Text;
|
|
146
152
|
/**
|
|
153
|
+
Create a text selection.
|
|
154
|
+
*/
|
|
155
|
+
static undirectionalRange(from: number, to: number): GardSelection.Text;
|
|
156
|
+
/**
|
|
147
157
|
Create a node selection.
|
|
148
158
|
*/
|
|
149
159
|
static node(pos: number, node: wgNode, goalColumn?: number): GardSelection.Node;
|
|
@@ -196,6 +206,13 @@ declare namespace GardSelection {
|
|
|
196
206
|
class Text extends GardSelection {
|
|
197
207
|
private _headSide;
|
|
198
208
|
/**
|
|
209
|
+
When `false`, indicates that this selection may swap `head`
|
|
210
|
+
and `anchor` when extended. Used for things like selections
|
|
211
|
+
created by double-clicking, which don't have a clearly
|
|
212
|
+
defined anchor.
|
|
213
|
+
*/
|
|
214
|
+
readonly directional: boolean;
|
|
215
|
+
/**
|
|
199
216
|
A set of active marks that should be applied to content
|
|
200
217
|
inserted at this selection (replacing the contextual marks).
|
|
201
218
|
Used mostly for making the effect of toggling inline styles
|
|
@@ -248,6 +265,11 @@ declare namespace GardSelection {
|
|
|
248
265
|
used for things like toggling emphasis on a cursor selection.
|
|
249
266
|
*/
|
|
250
267
|
marks?: Mark.Set;
|
|
268
|
+
/**
|
|
269
|
+
Set to `false` to indicate that this selection isn't {@link
|
|
270
|
+
GardSelection.Text.directional directional}.
|
|
271
|
+
*/
|
|
272
|
+
directional?: boolean;
|
|
251
273
|
};
|
|
252
274
|
/**
|
|
253
275
|
The representation of a text selection when serialized to JSON.
|
|
@@ -256,6 +278,7 @@ declare namespace GardSelection {
|
|
|
256
278
|
anchor: number;
|
|
257
279
|
head?: number;
|
|
258
280
|
side?: -1 | 1;
|
|
281
|
+
directional?: boolean;
|
|
259
282
|
marks?: Record<string, any>;
|
|
260
283
|
};
|
|
261
284
|
}
|
|
@@ -771,15 +794,6 @@ declare class TextblockMap {
|
|
|
771
794
|
Get the document position for a given string index.
|
|
772
795
|
*/
|
|
773
796
|
fromIndex(index: number): number;
|
|
774
|
-
/**
|
|
775
|
-
Get the position at the start or end of the textblock. Note that
|
|
776
|
-
in bidirectional text this may not be the actual start or end
|
|
777
|
-
position of the node.
|
|
778
|
-
*/
|
|
779
|
-
visualSide(start: boolean): {
|
|
780
|
-
pos: number;
|
|
781
|
-
side: -1 | 1;
|
|
782
|
-
};
|
|
783
797
|
}
|
|
784
798
|
|
|
785
799
|
type DocSource = Plot.Doc | HTMLElement | DocumentFragment | string | Node.JSON | ((schema: Schema) => Plot.Doc);
|
|
@@ -834,12 +848,16 @@ declare class GardState {
|
|
|
834
848
|
*/
|
|
835
849
|
get sel(): GardSelection.Resolved;
|
|
836
850
|
/**
|
|
837
|
-
Retrieve the value of a {@link GardState.Field state field}
|
|
838
|
-
|
|
839
|
-
you pass `false`
|
|
851
|
+
Retrieve the value of a {@link GardState.Field state field} or
|
|
852
|
+
{@link GardState.Field.Derived derived field}. Throws an error
|
|
853
|
+
when the state doesn't have that field, unless you pass `false`
|
|
854
|
+
as second parameter, in which case you'll get `undefined` for a
|
|
855
|
+
regular field, or the value will be eagerly computed for a
|
|
856
|
+
derived field.
|
|
840
857
|
*/
|
|
841
|
-
field<T>(field: GardState.Field<T>): T;
|
|
858
|
+
field<T>(field: GardState.Field<T> | GardState.Field.Derived<T>): T;
|
|
842
859
|
field<T>(field: GardState.Field<T>, require: false): T | undefined;
|
|
860
|
+
field<T>(field: GardState.Field.Derived<T>, require: false): T;
|
|
843
861
|
/**
|
|
844
862
|
Get the value of a state {@link GardState.Facet facet}.
|
|
845
863
|
*/
|
|
@@ -1016,6 +1034,34 @@ declare namespace GardState {
|
|
|
1016
1034
|
*/
|
|
1017
1035
|
fromJSON?: (json: any, state: GardState) => Value;
|
|
1018
1036
|
};
|
|
1037
|
+
/**
|
|
1038
|
+
Define a field-like state slot that is computed from other
|
|
1039
|
+
slots in the state. Automatically tracks dependencies and only
|
|
1040
|
+
recomputes when one of the inputs accessed by the given
|
|
1041
|
+
function changes. Use {@link GardState.field} to read the
|
|
1042
|
+
value of such a field.
|
|
1043
|
+
*/
|
|
1044
|
+
class Derived<Value> {
|
|
1045
|
+
private compareF;
|
|
1046
|
+
private constructor();
|
|
1047
|
+
/**
|
|
1048
|
+
Define a derived field.
|
|
1049
|
+
*/
|
|
1050
|
+
static define<Value>(compute: (state: GardState) => Value, spec?: {
|
|
1051
|
+
/**
|
|
1052
|
+
Optional function used to compare the value of the field,
|
|
1053
|
+
to prevent propagating a change when a recomputed result
|
|
1054
|
+
is the same as the old value. Should return `true` when
|
|
1055
|
+
the inputs can be considered the same. Defaults to `===`.
|
|
1056
|
+
*/
|
|
1057
|
+
compare?: (a: Value, b: Value) => boolean;
|
|
1058
|
+
}): Derived<Value>;
|
|
1059
|
+
/**
|
|
1060
|
+
Derived fields must be included in your state configuration
|
|
1061
|
+
for them to be used.
|
|
1062
|
+
*/
|
|
1063
|
+
get extension(): GardState.Extension;
|
|
1064
|
+
}
|
|
1019
1065
|
}
|
|
1020
1066
|
/**
|
|
1021
1067
|
A facet is a labeled value that is associated with an editor
|
|
@@ -1061,7 +1107,7 @@ declare namespace GardState {
|
|
|
1061
1107
|
*/
|
|
1062
1108
|
of(value: Input): GardState.Extension;
|
|
1063
1109
|
/**
|
|
1064
|
-
Create an extension that
|
|
1110
|
+
Create an extension that derives a value for the facet from a
|
|
1065
1111
|
state. The given function should only depend on the state, not
|
|
1066
1112
|
any external non-constant inputs. Its return value will be kept
|
|
1067
1113
|
on state update, unless any of the fields or facets (including
|
|
@@ -1072,20 +1118,20 @@ declare namespace GardState {
|
|
|
1072
1118
|
can use the {@link GardState.Facet.from `from`} method
|
|
1073
1119
|
instead.
|
|
1074
1120
|
*/
|
|
1075
|
-
|
|
1121
|
+
derive(get: (state: GardState) => Input): GardState.Extension;
|
|
1076
1122
|
/**
|
|
1077
|
-
Create an extension that
|
|
1123
|
+
Create an extension that derives zero or more values for this
|
|
1078
1124
|
facet from a state.
|
|
1079
1125
|
*/
|
|
1080
|
-
|
|
1126
|
+
deriveN(get: (state: GardState) => readonly Input[]): GardState.Extension;
|
|
1081
1127
|
/**
|
|
1082
1128
|
Shorthand method for registering a facet source with a state
|
|
1083
1129
|
field as input. If the field's type corresponds to this facet's
|
|
1084
1130
|
input type, the getter function can be omitted. If given, it
|
|
1085
1131
|
will be used to produce the input from the field value.
|
|
1086
1132
|
*/
|
|
1087
|
-
from<T extends Input>(field: GardState.Field<T>): GardState.Extension;
|
|
1088
|
-
from<T>(field: GardState.Field<T>, get: (value: T) => Input): GardState.Extension;
|
|
1133
|
+
from<T extends Input>(field: GardState.Field<T> | GardState.Field.Derived<T>): GardState.Extension;
|
|
1134
|
+
from<T>(field: GardState.Field<T> | GardState.Field.Derived<T>, get: (value: T) => Input): GardState.Extension;
|
|
1089
1135
|
tag: Output;
|
|
1090
1136
|
}
|
|
1091
1137
|
namespace Facet {
|
|
@@ -1136,7 +1182,7 @@ declare namespace GardState {
|
|
|
1136
1182
|
/**
|
|
1137
1183
|
A facet reader can be used to fetch the value of a facet,
|
|
1138
1184
|
through {@link GardState.facet} or as a dependency in {@link
|
|
1139
|
-
GardState.Facet.
|
|
1185
|
+
GardState.Facet.derive `Facet.derive`}, but not to define
|
|
1140
1186
|
new values for the facet.
|
|
1141
1187
|
*/
|
|
1142
1188
|
type Reader<Output> = {
|
|
@@ -1314,7 +1360,7 @@ declare namespace GardState {
|
|
|
1314
1360
|
*/
|
|
1315
1361
|
const visualCursorMotion: Facet<boolean, boolean>;
|
|
1316
1362
|
/**
|
|
1317
|
-
@hidden
|
|
1363
|
+
@hidden
|
|
1318
1364
|
*/
|
|
1319
1365
|
const isAtom: Facet<[Plot.Type<unknown>, boolean], Map<Plot.Type<unknown>, boolean>>;
|
|
1320
1366
|
}
|
package/dist/state.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Leaf, ValidationError, Pos, ChangeSet, Plot, SchemaError, parse, Schema } from 'wordgard/doc';
|
|
1
|
+
import { Leaf, ValidationError, Slice, Pos, ChangeSet, Plot, SchemaError, parse, Schema } from 'wordgard/doc';
|
|
2
2
|
import { findClusterBreak } from '@marijn/find-cluster-break';
|
|
3
3
|
|
|
4
4
|
function dec(str) {
|
|
@@ -444,8 +444,20 @@ class TextblockMap {
|
|
|
444
444
|
return off;
|
|
445
445
|
}
|
|
446
446
|
moveVisually(start, side, forward, skipped) {
|
|
447
|
-
|
|
448
|
-
|
|
447
|
+
if (!this.text.length)
|
|
448
|
+
return null;
|
|
449
|
+
let startIndex = this.toIndex(start), { order, ltr } = this, spanI;
|
|
450
|
+
if (startIndex == 0 && side < 0) { if (!forward)
|
|
451
|
+
return null;
|
|
452
|
+
startIndex = order[spanI = 0].side(false, this.ltr);
|
|
453
|
+
}
|
|
454
|
+
else if (startIndex == this.text.length && side > 0) { if (forward)
|
|
455
|
+
return null;
|
|
456
|
+
startIndex = order[spanI = order.length - 1].side(true, this.ltr);
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
spanI = BidiSpan.find(order, startIndex, side);
|
|
460
|
+
}
|
|
449
461
|
let span = order[spanI], spanEnd = span.side(forward, ltr);
|
|
450
462
|
if (startIndex == spanEnd) {
|
|
451
463
|
let nextI = spanI += forward ? 1 : -1;
|
|
@@ -463,8 +475,14 @@ class TextblockMap {
|
|
|
463
475
|
if (skipped)
|
|
464
476
|
skipped[0] = this.text.slice(Math.min(startIndex, nextIndex), Math.max(startIndex, nextIndex));
|
|
465
477
|
let nextSpan = spanI == (forward ? order.length - 1 : 0) ? null : order[spanI + (forward ? 1 : -1)];
|
|
466
|
-
if (
|
|
467
|
-
|
|
478
|
+
if (nextIndex == spanEnd) {
|
|
479
|
+
if (!nextSpan)
|
|
480
|
+
return forward ? { pos: this.start + this.node.contentLength, side: 1 }
|
|
481
|
+
: { pos: this.start, side: -1 };
|
|
482
|
+
if (nextSpan.level + (forward ? 0 : 1) < span.level)
|
|
483
|
+
return { pos: this.fromIndex(nextSpan.side(!forward, this.ltr)),
|
|
484
|
+
side: nextSpan.forward(forward, this.ltr) ? 1 : -1 };
|
|
485
|
+
}
|
|
468
486
|
return { pos: this.fromIndex(nextIndex), side: nextIndex != spanEnd ? 1 : span.forward(forward, ltr) ? -1 : 1 };
|
|
469
487
|
}
|
|
470
488
|
skipWord(start, side, forward, visually) {
|
|
@@ -500,18 +518,6 @@ class TextblockMap {
|
|
|
500
518
|
return cur; let segments = [...new Intl.Segmenter(undefined, { granularity: "word" }).segment(word)];
|
|
501
519
|
return history.get(segments[forward ? 0 : segments.length - 1].segment.length) || cur;
|
|
502
520
|
}
|
|
503
|
-
visualSide(start) {
|
|
504
|
-
let pos, side;
|
|
505
|
-
if (start) {
|
|
506
|
-
let span = this.order[0];
|
|
507
|
-
[pos, side] = span.ltr == this.ltr ? [span.from, 1] : [span.to, -1];
|
|
508
|
-
}
|
|
509
|
-
else {
|
|
510
|
-
let span = this.order[this.order.length - 1];
|
|
511
|
-
[pos, side] = span.ltr == this.ltr ? [span.to, -1] : [span.from, 1];
|
|
512
|
-
}
|
|
513
|
-
return { pos: this.fromIndex(pos), side };
|
|
514
|
-
}
|
|
515
521
|
moveLogically(start, forward) {
|
|
516
522
|
let index = this.toIndex(start);
|
|
517
523
|
let next = findClusterBreak(this.text, index, forward);
|
|
@@ -550,6 +556,14 @@ class GardSelection {
|
|
|
550
556
|
get ranges() { return [this]; }
|
|
551
557
|
get replacementRange() { return this; }
|
|
552
558
|
get domSelection() { return this; }
|
|
559
|
+
toSlice(doc) {
|
|
560
|
+
let slice;
|
|
561
|
+
for (let range of this.ranges) {
|
|
562
|
+
let cur = Slice.get(doc, range.from, range.to);
|
|
563
|
+
slice = slice ? slice.concat(cur) : cur;
|
|
564
|
+
}
|
|
565
|
+
return slice;
|
|
566
|
+
}
|
|
553
567
|
get headSide() { return this.head > this.anchor ? -1 : 1; }
|
|
554
568
|
get anchorSide() { return this.anchor > this.head ? -1 : 1; }
|
|
555
569
|
eqPos(other) {
|
|
@@ -580,14 +594,17 @@ class GardSelection {
|
|
|
580
594
|
return type.fromJSON(doc, json);
|
|
581
595
|
}
|
|
582
596
|
static cursor(pos, side, goalColumn) {
|
|
583
|
-
return GardSelection.Text.createInner(pos, pos, side, goalColumn);
|
|
597
|
+
return GardSelection.Text.createInner(pos, pos, side, true, goalColumn);
|
|
584
598
|
}
|
|
585
599
|
static near(cx, pos, side = 1, goalColumn) {
|
|
586
600
|
let norm = findNormalAt(cx, pos, side);
|
|
587
601
|
return GardSelection.cursor(norm.pos, norm.side, goalColumn);
|
|
588
602
|
}
|
|
589
603
|
static range(anchor, head, headSide, goalColumn) {
|
|
590
|
-
return GardSelection.Text.createInner(anchor, head ?? anchor, headSide, goalColumn);
|
|
604
|
+
return GardSelection.Text.createInner(anchor, head ?? anchor, headSide, true, goalColumn);
|
|
605
|
+
}
|
|
606
|
+
static undirectionalRange(from, to) {
|
|
607
|
+
return GardSelection.Text.createInner(from, to, 1, false);
|
|
591
608
|
}
|
|
592
609
|
static node(pos, node, goalColumn) {
|
|
593
610
|
return GardSelection.Node.create(pos, node, goalColumn);
|
|
@@ -604,9 +621,8 @@ class GardSelection {
|
|
|
604
621
|
return cursorAtStart(cx, block);
|
|
605
622
|
}
|
|
606
623
|
static atEnd(cx, block) {
|
|
607
|
-
let found = block
|
|
608
|
-
?
|
|
609
|
-
: cx.doc.inlineContent ? TextblockMap.get(cx, 0, cx.doc).visualSide(false)
|
|
624
|
+
let found = block ? { pos: block.end, side: 1 }
|
|
625
|
+
: cx.doc.inlineContent ? { pos: cx.doc.length, side: 1 }
|
|
610
626
|
: findNormalAt(cx, cx.doc.length, -1);
|
|
611
627
|
return GardSelection.cursor(found.pos, found.side);
|
|
612
628
|
}
|
|
@@ -618,15 +634,18 @@ class GardSelection {
|
|
|
618
634
|
GardSelection.define = define;
|
|
619
635
|
class Text extends GardSelection {
|
|
620
636
|
_headSide;
|
|
637
|
+
directional;
|
|
621
638
|
marks;
|
|
622
|
-
constructor(anchor, head, _headSide,
|
|
639
|
+
constructor(anchor, head, _headSide,
|
|
640
|
+
directional, goalColumn,
|
|
623
641
|
marks) {
|
|
624
642
|
super(anchor, head, goalColumn);
|
|
625
643
|
this._headSide = _headSide;
|
|
644
|
+
this.directional = directional;
|
|
626
645
|
this.marks = marks;
|
|
627
646
|
}
|
|
628
|
-
static createInner(anchor, head, side, goalColumn, marks) {
|
|
629
|
-
return new Text(anchor, head, side ?? (head > anchor ? -1 : 1), goalColumn, marks);
|
|
647
|
+
static createInner(anchor, head, side, directional, goalColumn, marks) {
|
|
648
|
+
return new Text(anchor, head, side ?? (head > anchor ? -1 : 1), directional, goalColumn, marks);
|
|
630
649
|
}
|
|
631
650
|
get headSide() {
|
|
632
651
|
return this._headSide;
|
|
@@ -636,7 +655,7 @@ class GardSelection {
|
|
|
636
655
|
}
|
|
637
656
|
static create(spec) {
|
|
638
657
|
let { anchor, head = anchor } = spec;
|
|
639
|
-
return Text.createInner(anchor, head, spec.headSide, spec.goalColumn, spec.marks);
|
|
658
|
+
return Text.createInner(anchor, head, spec.headSide, spec.directional !== false, spec.goalColumn, spec.marks);
|
|
640
659
|
}
|
|
641
660
|
map(change, cx, assoc = -1) {
|
|
642
661
|
let from, to;
|
|
@@ -647,7 +666,7 @@ class GardSelection {
|
|
|
647
666
|
from = change.mapPos(this.from, 1);
|
|
648
667
|
to = Math.max(from, change.mapPos(this.to, -1));
|
|
649
668
|
}
|
|
650
|
-
return Text.createInner(from, to, this.headSide, this.goalColumn, this.marks);
|
|
669
|
+
return Text.createInner(from, to, this.headSide, this.directional, this.goalColumn, this.marks);
|
|
651
670
|
}
|
|
652
671
|
eq(other) {
|
|
653
672
|
return other instanceof Text && this.eqPos(other) && this.headSide == other.headSide &&
|
|
@@ -674,7 +693,7 @@ class GardSelection {
|
|
|
674
693
|
throw new ValidationError("Invalid JSON representation for GardSelection.Text");
|
|
675
694
|
let anchor = json.anchor, head = typeof json.head == "number" ? json.head : anchor;
|
|
676
695
|
let marks = json.marks ? doc.schema.marksFromJSON(json.marks) : undefined;
|
|
677
|
-
return Text.createInner(anchor, head, json.side == 1 || json.side == -1 ? json.side : undefined, undefined, marks);
|
|
696
|
+
return Text.createInner(anchor, head, json.side == 1 || json.side == -1 ? json.side : undefined, json.directional !== false, undefined, marks);
|
|
678
697
|
}));
|
|
679
698
|
})(Text = GardSelection.Text || (GardSelection.Text = {}));
|
|
680
699
|
class Node extends GardSelection {
|
|
@@ -744,8 +763,8 @@ class GardSelection {
|
|
|
744
763
|
;return GardSelection})(GardSelection);
|
|
745
764
|
function cursorAtStart(cx, block) {
|
|
746
765
|
let found = block
|
|
747
|
-
?
|
|
748
|
-
: cx.doc.inlineContent ?
|
|
766
|
+
? { pos: block.start, side: -1 }
|
|
767
|
+
: cx.doc.inlineContent ? { pos: 0, side: -1 }
|
|
749
768
|
: findNormalAt(cx, 0, 1);
|
|
750
769
|
return GardSelection.cursor(found.pos, found.side);
|
|
751
770
|
}
|
|
@@ -790,11 +809,8 @@ function scanNormalFrom(cx, from, side, forward) {
|
|
|
790
809
|
let bottom = pos.pos, step = forward ? 1 : -1;
|
|
791
810
|
for (let { parent, index } = pos, p = pos.pos;;) {
|
|
792
811
|
let { node, parent: next } = parent;
|
|
793
|
-
if (node.inlineContent)
|
|
794
|
-
|
|
795
|
-
return TextblockMap.get(cx, parent.start, parent.node).visualSide(forward);
|
|
796
|
-
return { pos: p, side: forward ? 1 : -1 };
|
|
797
|
-
}
|
|
812
|
+
if (node.inlineContent)
|
|
813
|
+
return { pos: p, side: forward ? -1 : 1 };
|
|
798
814
|
if (index == (forward ? node.content.length : 0)) {
|
|
799
815
|
let barrier = !next || isBarrier(cx, node);
|
|
800
816
|
if ((bottom != from) && pastBarrier && barrier)
|
|
@@ -857,10 +873,8 @@ function findNormalAt(cx, pos, bias) {
|
|
|
857
873
|
let nextIndex = index + (dir > 0 ? 0 : -1), next = parent.node.content[nextIndex];
|
|
858
874
|
if (next.isLeaf || isBarrier(cx, next))
|
|
859
875
|
break;
|
|
860
|
-
if (!parent.node.inlineContent && next.inlineContent
|
|
861
|
-
|
|
862
|
-
return TextblockMap.get(cx, startPos, next).visualSide(dir > 0);
|
|
863
|
-
}
|
|
876
|
+
if (!parent.node.inlineContent && next.inlineContent)
|
|
877
|
+
return { pos: curPos + dir, side: -dir };
|
|
864
878
|
parent = Pos.Plot.create(parent, next, curPos, nextIndex);
|
|
865
879
|
index = dir > 0 ? 0 : next.content.length;
|
|
866
880
|
curPos += dir;
|
|
@@ -886,8 +900,7 @@ function skipWord(cx, start, side, forward) {
|
|
|
886
900
|
return next;
|
|
887
901
|
if (!block.parent)
|
|
888
902
|
return last;
|
|
889
|
-
let end =
|
|
890
|
-
: forward ? { pos: block.end, side: -1 } : { pos: block.start, side: 1 };
|
|
903
|
+
let end = forward ? { pos: block.end, side: 1 } : { pos: block.start, side: -1 };
|
|
891
904
|
if (end.pos != start)
|
|
892
905
|
last = end;
|
|
893
906
|
pos = forward ? block.after : block.before;
|
|
@@ -932,13 +945,14 @@ function wordAt(state, pos, bias) {
|
|
|
932
945
|
cur = res.parent.node.content[i++];
|
|
933
946
|
}
|
|
934
947
|
if (!Intl.Segmenter)
|
|
935
|
-
return GardSelection.
|
|
948
|
+
return GardSelection.undirectionalRange(start, end);
|
|
936
949
|
let best = null, local = pos - start;
|
|
937
950
|
for (let segment of new Intl.Segmenter(undefined, { granularity: "word" }).segment(text)) {
|
|
938
951
|
if (segment.isWordLike && segment.index <= local && segment.index + segment.segment.length >= local && (!best || bias > 0))
|
|
939
952
|
best = segment;
|
|
940
953
|
}
|
|
941
|
-
return best ? GardSelection.
|
|
954
|
+
return best ? GardSelection.undirectionalRange(start + best.index, start + best.index + best.segment.length)
|
|
955
|
+
: GardSelection.cursor(pos, bias);
|
|
942
956
|
}
|
|
943
957
|
|
|
944
958
|
class Transaction {
|
|
@@ -1240,7 +1254,7 @@ class GardState {
|
|
|
1240
1254
|
if (addr == null) {
|
|
1241
1255
|
if (require)
|
|
1242
1256
|
throw new RangeError("Field is not present in this state");
|
|
1243
|
-
return undefined;
|
|
1257
|
+
return field instanceof GardState.Field ? undefined : field.value(this);
|
|
1244
1258
|
}
|
|
1245
1259
|
let track = this.trackAccess;
|
|
1246
1260
|
if (track) {
|
|
@@ -1413,6 +1427,65 @@ class GardState {
|
|
|
1413
1427
|
}
|
|
1414
1428
|
}
|
|
1415
1429
|
GardState.Field = Field;
|
|
1430
|
+
(function (Field) {
|
|
1431
|
+
class Derived {
|
|
1432
|
+
value;
|
|
1433
|
+
compareF;
|
|
1434
|
+
id = nextID++;
|
|
1435
|
+
dependencies = [];
|
|
1436
|
+
constructor(
|
|
1437
|
+
value, compareF) {
|
|
1438
|
+
this.value = value;
|
|
1439
|
+
this.compareF = compareF;
|
|
1440
|
+
}
|
|
1441
|
+
static define(compute, spec = {}) {
|
|
1442
|
+
return new Derived(compute, spec.compare || ((a, b) => a === b));
|
|
1443
|
+
}
|
|
1444
|
+
get extension() { return this; }
|
|
1445
|
+
slot(addresses) {
|
|
1446
|
+
let idx = addresses[this.id] >> 1;
|
|
1447
|
+
let depSet = new DependencySet;
|
|
1448
|
+
return {
|
|
1449
|
+
create: state => {
|
|
1450
|
+
state.values[idx] = state.recordAccess(this.dependencies, this.value);
|
|
1451
|
+
return 1;
|
|
1452
|
+
},
|
|
1453
|
+
update: (state, tr) => {
|
|
1454
|
+
depSet.update(this.dependencies, addresses);
|
|
1455
|
+
if ((depSet.doc && tr.docChanged) || (depSet.sel && (tr.docChanged || tr.selection)) ||
|
|
1456
|
+
(depSet.schema && tr.startState.schema != state.schema) || ensureAll(state, depSet.addrs)) {
|
|
1457
|
+
let newVal = state.recordAccess(this.dependencies, this.value);
|
|
1458
|
+
if (!this.compareF(newVal, state.values[idx])) {
|
|
1459
|
+
state.values[idx] = newVal;
|
|
1460
|
+
return 1;
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
return 0;
|
|
1464
|
+
},
|
|
1465
|
+
reconfigure: (state, oldState) => {
|
|
1466
|
+
let newVal, oldAddr = oldState.config.address[this.id];
|
|
1467
|
+
if (oldAddr != null) {
|
|
1468
|
+
let oldVal = getAddr(oldState, oldAddr);
|
|
1469
|
+
if (this.dependencies.every(dep => {
|
|
1470
|
+
return dep instanceof GardState.Facet ? oldState.facet(dep) === state.facet(dep)
|
|
1471
|
+
: dep instanceof GardState.Field ? oldState.field(dep, false) == state.field(dep, false)
|
|
1472
|
+
: true;
|
|
1473
|
+
}) || this.compareF(newVal = state.recordAccess(this.dependencies, this.value), oldVal)) {
|
|
1474
|
+
state.values[idx] = oldVal;
|
|
1475
|
+
return 0;
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
else {
|
|
1479
|
+
newVal = state.recordAccess(this.dependencies, this.value);
|
|
1480
|
+
}
|
|
1481
|
+
state.values[idx] = newVal;
|
|
1482
|
+
return 1;
|
|
1483
|
+
}
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
Field.Derived = Derived;
|
|
1488
|
+
})(Field = GardState.Field || (GardState.Field = {}));
|
|
1416
1489
|
class Facet {
|
|
1417
1490
|
combine;
|
|
1418
1491
|
compareInput;
|
|
@@ -1438,24 +1511,26 @@ class GardState {
|
|
|
1438
1511
|
return new GardState.Facet(config.combine || ((a) => a), config.compareInput || ((a, b) => a === b), config.compare || (!config.combine ? sameArray : (a, b) => a === b), !!config.static, config.enables);
|
|
1439
1512
|
}
|
|
1440
1513
|
of(value) {
|
|
1441
|
-
return new FacetProvider(
|
|
1514
|
+
return new FacetProvider(this, nextID++, 1, value);
|
|
1442
1515
|
}
|
|
1443
|
-
|
|
1516
|
+
derive(get) {
|
|
1444
1517
|
if (this.isStatic)
|
|
1445
|
-
throw new Error("Can't
|
|
1446
|
-
|
|
1518
|
+
throw new Error("Can't derive a static facet");
|
|
1519
|
+
let field = Field.Derived.define(get, { compare: this.compareInput });
|
|
1520
|
+
return [field, new FacetProvider(this, field.id, 0, null)];
|
|
1447
1521
|
}
|
|
1448
|
-
|
|
1522
|
+
deriveN(get) {
|
|
1449
1523
|
if (this.isStatic)
|
|
1450
|
-
throw new Error("Can't
|
|
1451
|
-
|
|
1524
|
+
throw new Error("Can't derive a static facet");
|
|
1525
|
+
let field = Field.Derived.define(get, { compare: (a, b) => compareArray(a, b, this.compareInput) });
|
|
1526
|
+
return [field, new FacetProvider(this, field.id, 2, null)];
|
|
1452
1527
|
}
|
|
1453
1528
|
from(field, get) {
|
|
1454
1529
|
if (this.isStatic)
|
|
1455
|
-
throw new Error("Can't
|
|
1530
|
+
throw new Error("Can't derive a static facet");
|
|
1456
1531
|
if (!get)
|
|
1457
|
-
|
|
1458
|
-
return
|
|
1532
|
+
return new FacetProvider(this, field.id, 0, null);
|
|
1533
|
+
return this.derive(s => get(s.field(field)));
|
|
1459
1534
|
}
|
|
1460
1535
|
tag;
|
|
1461
1536
|
}
|
|
@@ -1547,10 +1622,6 @@ class GardState {
|
|
|
1547
1622
|
address[p.id] = (staticValues.length << 1) | 1;
|
|
1548
1623
|
staticValues.push(p.value);
|
|
1549
1624
|
}
|
|
1550
|
-
else {
|
|
1551
|
-
address[p.id] = dynamicSlots.length << 1;
|
|
1552
|
-
dynamicSlots.push(a => p.dynamicSlot(a));
|
|
1553
|
-
}
|
|
1554
1625
|
}
|
|
1555
1626
|
address[facet.id] = dynamicSlots.length << 1;
|
|
1556
1627
|
dynamicSlots.push(a => dynamicFacetSlot(a, facet, providers));
|
|
@@ -1617,6 +1688,9 @@ class GardState {
|
|
|
1617
1688
|
if (ext.provides)
|
|
1618
1689
|
inner(ext.provides, prec);
|
|
1619
1690
|
}
|
|
1691
|
+
else if (ext instanceof GardState.Field.Derived) {
|
|
1692
|
+
result[prec].push(ext);
|
|
1693
|
+
}
|
|
1620
1694
|
else if (ext instanceof FacetProvider) {
|
|
1621
1695
|
result[prec].push(ext);
|
|
1622
1696
|
if (ext.facet.extensions)
|
|
@@ -1722,60 +1796,16 @@ class DependencySet {
|
|
|
1722
1796
|
}
|
|
1723
1797
|
class FacetProvider {
|
|
1724
1798
|
facet;
|
|
1799
|
+
id;
|
|
1725
1800
|
flags;
|
|
1726
1801
|
value;
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1802
|
+
extension; constructor(facet,
|
|
1803
|
+
id, flags,
|
|
1804
|
+
value) {
|
|
1730
1805
|
this.facet = facet;
|
|
1806
|
+
this.id = id;
|
|
1731
1807
|
this.flags = flags;
|
|
1732
1808
|
this.value = value;
|
|
1733
|
-
this.dependencies = dependencies; }
|
|
1734
|
-
dynamicSlot(addresses) {
|
|
1735
|
-
let getter = this.value;
|
|
1736
|
-
let compare = this.facet.compareInput;
|
|
1737
|
-
let id = this.id, idx = addresses[id] >> 1;
|
|
1738
|
-
let multi = this.flags & 2;
|
|
1739
|
-
let dependencies = this.dependencies;
|
|
1740
|
-
let auto = this.flags & 4 ? dependencies : null;
|
|
1741
|
-
let depSet = new DependencySet;
|
|
1742
|
-
return {
|
|
1743
|
-
create(state) {
|
|
1744
|
-
state.values[idx] = state.recordAccess(auto, getter);
|
|
1745
|
-
return 1;
|
|
1746
|
-
},
|
|
1747
|
-
update(state, tr) {
|
|
1748
|
-
depSet.update(dependencies, addresses);
|
|
1749
|
-
if ((depSet.doc && tr.docChanged) || (depSet.sel && (tr.docChanged || tr.selection)) ||
|
|
1750
|
-
(depSet.schema && tr.startState.schema != state.schema) || ensureAll(state, depSet.addrs)) {
|
|
1751
|
-
let newVal = state.recordAccess(auto, getter);
|
|
1752
|
-
if (multi ? !compareArray(newVal, state.values[idx], compare) : !compare(newVal, state.values[idx])) {
|
|
1753
|
-
state.values[idx] = newVal;
|
|
1754
|
-
return 1;
|
|
1755
|
-
}
|
|
1756
|
-
}
|
|
1757
|
-
return 0;
|
|
1758
|
-
},
|
|
1759
|
-
reconfigure(state, oldState) {
|
|
1760
|
-
let newVal, oldAddr = oldState.config.address[id];
|
|
1761
|
-
if (oldAddr != null) {
|
|
1762
|
-
let oldVal = getAddr(oldState, oldAddr);
|
|
1763
|
-
if (dependencies.every(dep => {
|
|
1764
|
-
return dep instanceof GardState.Facet ? oldState.facet(dep) === state.facet(dep)
|
|
1765
|
-
: dep instanceof GardState.Field ? oldState.field(dep, false) == state.field(dep, false)
|
|
1766
|
-
: true;
|
|
1767
|
-
}) || (multi ? compareArray(newVal = getter(state), oldVal, compare) : compare(newVal = getter(state), oldVal))) {
|
|
1768
|
-
state.values[idx] = oldVal;
|
|
1769
|
-
return 0;
|
|
1770
|
-
}
|
|
1771
|
-
}
|
|
1772
|
-
else {
|
|
1773
|
-
newVal = state.recordAccess(auto, getter);
|
|
1774
|
-
}
|
|
1775
|
-
state.values[idx] = newVal;
|
|
1776
|
-
return 1;
|
|
1777
|
-
}
|
|
1778
|
-
};
|
|
1779
1809
|
}
|
|
1780
1810
|
}
|
|
1781
1811
|
function compareArray(a, b, compare) {
|
package/dist/table.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as wordgard_state from 'wordgard/state';
|
|
2
2
|
import { GardState, GardSelection, Transaction } from 'wordgard/state';
|
|
3
3
|
import { Menu, Command } from 'wordgard/command';
|
|
4
|
-
import {
|
|
4
|
+
import { Plot, Slice, ChangeSet } from 'wordgard/doc';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
Enable table support with the given configuration. The returned
|
|
@@ -144,6 +144,7 @@ declare class CellSelection extends GardSelection {
|
|
|
144
144
|
readonly head: number;
|
|
145
145
|
readonly headSide: -1;
|
|
146
146
|
};
|
|
147
|
+
toSlice(doc: Plot.Doc): Slice;
|
|
147
148
|
eq(other: GardSelection): boolean;
|
|
148
149
|
map(changes: ChangeSet, cx: GardSelection.Context, assoc?: -1 | 1): GardSelection.Text | CellSelection;
|
|
149
150
|
/**
|
|
@@ -210,6 +211,6 @@ declare const splitCell: Command.Pure;
|
|
|
210
211
|
/**
|
|
211
212
|
@hidden exported for testing
|
|
212
213
|
*/
|
|
213
|
-
declare function handleTablePaste(state: GardState, slice: Slice,
|
|
214
|
+
declare function handleTablePaste(state: GardState, slice: Slice, drop?: number): false | Transaction.Spec;
|
|
214
215
|
|
|
215
216
|
export { CellSelection, addColumn, addRow, deleteColumn, deleteRow, handleTablePaste, mergeCells, splitCell, tableMenu, tables, toggleHeaderCell };
|