@adecore/editor 0.39.0 → 0.40.1

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.
Files changed (53) hide show
  1. package/dist/controller.d.ts +1 -0
  2. package/dist/controller.js +17 -28
  3. package/dist/engine.js +2 -9
  4. package/dist/find.js +12 -13
  5. package/dist/layout.js +7 -2
  6. package/dist/paint.d.ts +2 -0
  7. package/dist/paint.js +23 -20
  8. package/dist/view.d.ts +3 -2
  9. package/dist/view.js +46 -59
  10. package/package.json +6 -6
  11. package/src/attribution.ts +14 -0
  12. package/src/code-block.ts +25 -0
  13. package/src/column-selection.ts +10 -0
  14. package/src/controller.ts +189 -28
  15. package/src/engine.ts +87 -9
  16. package/src/fake.ts +79 -0
  17. package/src/find-search.ts +20 -0
  18. package/src/find.ts +135 -14
  19. package/src/geometry-stops.ts +10 -0
  20. package/src/grammar-preparation.ts +23 -0
  21. package/src/index.ts +1 -0
  22. package/src/input.ts +13 -0
  23. package/src/keymap-table.ts +9 -0
  24. package/src/keymap.ts +25 -0
  25. package/src/layout-changes.ts +7 -0
  26. package/src/layout.ts +219 -3
  27. package/src/lens.ts +3 -0
  28. package/src/listeners.ts +1 -0
  29. package/src/metrics.ts +14 -0
  30. package/src/modifier-gesture.ts +17 -0
  31. package/src/native-input.ts +27 -0
  32. package/src/offsets.ts +10 -0
  33. package/src/outline.ts +38 -0
  34. package/src/overview.ts +13 -0
  35. package/src/paint.ts +140 -20
  36. package/src/php-html.ts +1 -0
  37. package/src/pointer.ts +50 -0
  38. package/src/scroll-animation.ts +10 -0
  39. package/src/scroll.ts +7 -0
  40. package/src/search-client.ts +32 -0
  41. package/src/search-worker.ts +38 -0
  42. package/src/semantic.ts +12 -0
  43. package/src/shiki-document.ts +19 -0
  44. package/src/shiki-worker.ts +68 -0
  45. package/src/shiki.ts +37 -0
  46. package/src/testing.ts +17 -0
  47. package/src/theme-scopes.ts +14 -0
  48. package/src/tokens.ts +31 -0
  49. package/src/touch.ts +16 -0
  50. package/src/tracked-range.ts +5 -0
  51. package/src/uniform-geometry.ts +10 -0
  52. package/src/view.ts +481 -75
  53. package/src/worker-client.ts +54 -0
@@ -50,21 +50,27 @@ export class AttributionRuns {
50
50
  /* Marks name lines one-based and inclusive; `lineBounds` answers for the document as it is now. */
51
51
  set(marks: readonly (EditorLineHighlight & { id: string })[], lineCount: number, lineBounds: (line: number) => { start: number; end: number }): void {
52
52
  const clamp = (line: number): number => Math.min(lineCount, Math.max(1, Math.trunc(line) || 1)) - 1;
53
+
53
54
  this.runs = marks.map((mark) => {
54
55
  const start = clamp(mark.startLine);
55
56
  const end = Math.max(start, clamp(mark.endLine));
57
+
56
58
  const emphasis = (mark.emphasis ?? []).slice(0, end - start + 1).flatMap((ranges, index) => {
57
59
  const bounds = lineBounds(start + index);
58
60
  const length = bounds.end - bounds.start;
61
+
59
62
  return ranges.flatMap(([from, to]) => {
60
63
  if (!Number.isFinite(from) || !Number.isFinite(to)) {
61
64
  return [];
62
65
  }
66
+
63
67
  const first = Math.min(length, Math.max(0, Math.trunc(from)));
64
68
  const last = Math.min(length, Math.max(0, Math.trunc(to)));
69
+
65
70
  return last > first ? [{ from: bounds.start + first, to: bounds.start + last }] : [];
66
71
  });
67
72
  });
73
+
68
74
  return {
69
75
  id: mark.id,
70
76
  color: mark.color,
@@ -93,29 +99,37 @@ export class AttributionRuns {
93
99
  /* The run on each line from `first` to `last`, zero-based, where `from` and `to` are the offsets they span; where runs overlap the one set last wins. */
94
100
  linesIn(first: number, last: number, from: number, to: number, lineOf: (offset: number) => number): Map<number, AttributedLines> {
95
101
  const lines = new Map<number, AttributedLines>();
102
+
96
103
  for (const run of this.runs) {
97
104
  if (run.to < from || run.from > to) {
98
105
  continue;
99
106
  }
107
+
100
108
  const value = { id: run.id, color: run.color, sign: run.sign, fill: run.fill, bar: run.bar };
101
109
  const emphasis = new Map<number, { from: number; to: number }[]>();
110
+
102
111
  for (const range of run.emphasis) {
103
112
  if (range.to <= from || range.from > to) {
104
113
  continue;
105
114
  }
115
+
106
116
  const endLine = Math.min(lineOf(range.to - 1), last);
117
+
107
118
  for (let line = Math.max(lineOf(range.from), first); line <= endLine; line++) {
108
119
  const ranges = emphasis.get(line) ?? [];
109
120
  ranges.push(range);
110
121
  emphasis.set(line, ranges);
111
122
  }
112
123
  }
124
+
113
125
  const end = Math.min(Math.max(lineOf(run.from), lineOf(run.to)), last);
126
+
114
127
  for (let line = Math.max(lineOf(run.from), first); line <= end; line++) {
115
128
  const ranges = emphasis.get(line);
116
129
  lines.set(line, ranges === undefined ? value : { ...value, emphasis: ranges });
117
130
  }
118
131
  }
132
+
119
133
  return lines;
120
134
  }
121
135
  }
package/src/code-block.ts CHANGED
@@ -19,86 +19,111 @@ export function renderCodeBlock(
19
19
  block.className = 'se-code-block';
20
20
  block.style.tabSize = String(tabSize);
21
21
  const lines = text.split(/\r?\n/);
22
+
22
23
  lines.forEach((line, index) => {
23
24
  const row = document.createElement('div');
24
25
  row.className = 'se-code-row';
26
+
25
27
  if (options.color !== undefined) {
26
28
  row.style.background = tintValue(options.color);
27
29
  }
30
+
28
31
  const gutter = codeGutter(document, options, index);
29
32
  const code = document.createElement('div');
30
33
  code.className = 'se-code-text';
34
+
31
35
  if (options.faded) {
32
36
  code.dataset.faded = 'true';
33
37
  }
38
+
34
39
  const emphasis = options.emphasis?.[index] ?? [];
40
+
35
41
  if (emphasis.length > 0 && options.color !== undefined) {
36
42
  row.style.setProperty('--se-emphasis', colorValue(options.color));
37
43
  }
44
+
38
45
  fill(code, line, tokens?.[index] ?? null, emphasis);
39
46
  row.append(gutter, code);
40
47
  block.append(row);
41
48
  });
49
+
42
50
  container.append(block);
43
51
  }
44
52
 
45
53
  function codeGutter(document: Document, options: EditorCodeBlockOptions, index: number): HTMLElement {
46
54
  const gutter = document.createElement('div');
47
55
  gutter.className = 'se-code-gutter';
56
+
48
57
  if (options.firstLine !== undefined) {
49
58
  const number = document.createElement('span');
50
59
  number.textContent = String(options.firstLine + index);
51
60
  gutter.append(number);
52
61
  }
62
+
53
63
  if (options.sign) {
54
64
  const sign = document.createElement('span');
55
65
  sign.className = 'se-line-sign';
56
66
  sign.textContent = options.sign;
67
+
57
68
  if (options.color !== undefined) {
58
69
  sign.style.color = colorValue(options.color);
59
70
  }
71
+
60
72
  gutter.append(sign);
61
73
  }
74
+
62
75
  if (options.color !== undefined) {
63
76
  const bar = document.createElement('span');
64
77
  bar.className = 'se-attribution';
65
78
  bar.style.background = colorValue(options.color);
66
79
  gutter.append(bar);
67
80
  }
81
+
68
82
  return gutter;
69
83
  }
70
84
 
71
85
  function fill(element: HTMLElement, line: string, tokens: readonly LineToken[] | null, emphasis: ReadonlyArray<readonly [number, number]>): void {
72
86
  if (tokens === null && emphasis.length === 0) {
73
87
  element.textContent = line;
88
+
74
89
  return;
75
90
  }
91
+
76
92
  const spans: Array<{ start: number; end: number; token: LineToken | null }> = [];
77
93
  let start = 0;
94
+
78
95
  for (const token of tokens ?? []) {
79
96
  const end = Math.min(line.length, start + token.length);
97
+
80
98
  if (end > start) {
81
99
  spans.push({ start, end, token });
82
100
  }
101
+
83
102
  start = end;
84
103
  }
104
+
85
105
  if (start < line.length) {
86
106
  spans.push({ start, end: line.length, token: null });
87
107
  }
108
+
88
109
  for (const { start: from, end: to, token } of spans) {
89
110
  // A token is cut at the edges of every emphasis that crosses it, so each piece is one color and either on or off.
90
111
  const cuts = [...new Set([from, to, ...emphasis.flatMap((range) => range).filter((cut) => cut > from && cut < to)])].sort(
91
112
  (left, right) => left - right
92
113
  );
114
+
93
115
  for (let i = 0; i < cuts.length - 1; i++) {
94
116
  const piece = element.ownerDocument.createElement('span');
95
117
  piece.textContent = line.slice(cuts[i]!, cuts[i + 1]!);
118
+
96
119
  if (token !== null) {
97
120
  styleSpan(piece, token);
98
121
  }
122
+
99
123
  if (emphasis.some(([emphasisStart, emphasisEnd]) => emphasisStart <= cuts[i]! && cuts[i + 1]! <= emphasisEnd)) {
100
124
  piece.className = 'se-code-emphasis';
101
125
  }
126
+
102
127
  element.append(piece);
103
128
  }
104
129
  }
@@ -28,35 +28,45 @@ export function columnSelections(layout: EditorLayout, from: ContentPoint, to: C
28
28
  const right = Math.max(fromCell, toCell);
29
29
  const reversed = toCell < fromCell;
30
30
  const result: Selection[] = [];
31
+
31
32
  for (let line = Math.min(fromLine, toLine); line <= Math.max(fromLine, toLine); line++) {
32
33
  const row = layout.rowForLine(line);
34
+
33
35
  if (row.kind !== 'text' || layout.isHidden(line)) {
34
36
  continue;
35
37
  }
38
+
36
39
  const geometry = layout.geometry(row);
37
40
  const endX = geometry.rowStarts.length > 1 ? geometry.rowEnds[0]! : xOfStop(geometry, stopCount(geometry) - 1);
41
+
38
42
  const offsetAt = (cell: number): number => layout.offsetAtX(geometry, 0, TEXT_PADDING + cell * charWidth).offset;
43
+
39
44
  if (cellAt(layout, endX + TEXT_PADDING) < left) {
40
45
  if (left === right) {
41
46
  const end = document.getLine(line).end;
42
47
  result.push({ anchor: end, head: end });
43
48
  }
49
+
44
50
  continue;
45
51
  }
52
+
46
53
  const start = offsetAt(left);
47
54
  const end = offsetAt(right);
48
55
  result.push(reversed ? { anchor: end, head: start } : { anchor: start, head: end });
49
56
  }
57
+
50
58
  if (result.length === 0) {
51
59
  const end = document.getLine(toLine).end;
52
60
  result.push({ anchor: end, head: end });
53
61
  }
62
+
54
63
  return result;
55
64
  }
56
65
 
57
66
  /* Whether two points are in the same cell of the same line, which makes a press with alt a click and not yet a box. */
58
67
  export function sameCell(layout: EditorLayout, from: ContentPoint, to: ContentPoint): boolean {
59
68
  const document = layout.document;
69
+
60
70
  return (
61
71
  cellAt(layout, from.x) === cellAt(layout, to.x) &&
62
72
  document.positionAt(layout.hitTest(from.x, from.y)).line === document.positionAt(layout.hitTest(to.x, to.y)).line