@adecore/editor-react 0.42.0 → 0.44.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.
Files changed (150) hide show
  1. package/dist/CallHierarchyPanel.js +177 -181
  2. package/dist/EditorContextMenu.js +118 -105
  3. package/dist/LanguagePopups.js +7 -0
  4. package/dist/PickPopup.js +245 -104
  5. package/dist/WrapCard.d.ts +8 -0
  6. package/dist/WrapCard.js +172 -0
  7. package/dist/call-hierarchy-model.d.ts +4 -2
  8. package/dist/call-hierarchy-model.js +20 -0
  9. package/dist/call-hierarchy.d.ts +3 -0
  10. package/dist/call-hierarchy.js +54 -22
  11. package/dist/clipboard-history.d.ts +31 -0
  12. package/dist/clipboard-history.js +70 -0
  13. package/dist/code-actions.d.ts +3 -1
  14. package/dist/code-actions.js +50 -10
  15. package/dist/code-vision.js +2 -3
  16. package/dist/completion.d.ts +28 -0
  17. package/dist/completion.js +83 -27
  18. package/dist/context-menu-rows.d.ts +1 -1
  19. package/dist/context-menu-rows.js +4 -0
  20. package/dist/definition-link.js +15 -5
  21. package/dist/document-colors.d.ts +24 -0
  22. package/dist/document-colors.js +194 -0
  23. package/dist/document-links.d.ts +22 -0
  24. package/dist/document-links.js +133 -0
  25. package/dist/document-query.d.ts +15 -0
  26. package/dist/document-query.js +54 -0
  27. package/dist/edit-locations.d.ts +20 -0
  28. package/dist/edit-locations.js +136 -0
  29. package/dist/editor-language.d.ts +23 -1
  30. package/dist/editor-language.js +83 -2
  31. package/dist/emmet.d.ts +13 -0
  32. package/dist/emmet.js +62 -0
  33. package/dist/folding.js +2 -3
  34. package/dist/format-on-type.js +3 -5
  35. package/dist/highlights.js +2 -3
  36. package/dist/history.d.ts +7 -0
  37. package/dist/history.js +79 -1
  38. package/dist/host-types.d.ts +10 -0
  39. package/dist/index.d.ts +13 -2
  40. package/dist/index.js +10 -0
  41. package/dist/inlay-hints.js +2 -2
  42. package/dist/inline-completion.d.ts +39 -0
  43. package/dist/inline-completion.js +221 -0
  44. package/dist/key-priority.d.ts +9 -5
  45. package/dist/key-priority.js +11 -6
  46. package/dist/linked-editing.d.ts +26 -0
  47. package/dist/linked-editing.js +215 -0
  48. package/dist/live-templates.d.ts +16 -0
  49. package/dist/live-templates.js +289 -0
  50. package/dist/locales/en.json +61 -2
  51. package/dist/locales/nl.json +61 -2
  52. package/dist/markup-context.d.ts +55 -0
  53. package/dist/markup-context.js +408 -0
  54. package/dist/markup.d.ts +39 -0
  55. package/dist/markup.js +465 -0
  56. package/dist/navigation.js +4 -6
  57. package/dist/paste-history.d.ts +7 -0
  58. package/dist/paste-history.js +104 -0
  59. package/dist/peek.js +3 -4
  60. package/dist/pick.d.ts +21 -2
  61. package/dist/pick.js +152 -19
  62. package/dist/popups.d.ts +10 -0
  63. package/dist/popups.js +2 -1
  64. package/dist/postfix.d.ts +21 -0
  65. package/dist/postfix.js +212 -0
  66. package/dist/project-language.d.ts +20 -1
  67. package/dist/project-language.js +132 -18
  68. package/dist/rename.js +8 -11
  69. package/dist/rendered-docs.js +1 -2
  70. package/dist/selection-ranges.js +2 -3
  71. package/dist/shortcut-keys.d.ts +24 -0
  72. package/dist/signature.js +3 -6
  73. package/dist/snippet-insert.d.ts +10 -0
  74. package/dist/snippet-insert.js +217 -0
  75. package/dist/snippet-session.d.ts +2 -2
  76. package/dist/snippet-session.js +3 -3
  77. package/dist/snippet.d.ts +7 -1
  78. package/dist/snippet.js +65 -8
  79. package/dist/surround-constructs.d.ts +10 -0
  80. package/dist/surround-constructs.js +100 -0
  81. package/dist/surround.d.ts +18 -0
  82. package/dist/surround.js +399 -0
  83. package/dist/symbol-picker.js +4 -6
  84. package/dist/symbols.js +22 -3
  85. package/dist/template-builtins.d.ts +3 -0
  86. package/dist/template-builtins.js +46 -0
  87. package/dist/template-context.d.ts +12 -0
  88. package/dist/template-context.js +56 -0
  89. package/dist/templates.d.ts +59 -0
  90. package/dist/templates.js +103 -0
  91. package/dist/testing.d.ts +23 -1
  92. package/dist/testing.js +114 -0
  93. package/package.json +7 -6
  94. package/src/CallHierarchyPanel.tsx +26 -11
  95. package/src/EditorContextMenu.tsx +7 -0
  96. package/src/LanguagePopups.tsx +4 -0
  97. package/src/PickPopup.tsx +47 -5
  98. package/src/WrapCard.tsx +64 -0
  99. package/src/call-hierarchy-model.ts +25 -3
  100. package/src/call-hierarchy.ts +62 -20
  101. package/src/clipboard-history.ts +102 -0
  102. package/src/code-actions.ts +48 -11
  103. package/src/code-vision.ts +3 -3
  104. package/src/completion.ts +123 -25
  105. package/src/context-menu-rows.ts +3 -2
  106. package/src/definition-link.ts +17 -5
  107. package/src/document-colors.ts +189 -0
  108. package/src/document-links.ts +129 -0
  109. package/src/document-query.ts +71 -0
  110. package/src/edit-locations.ts +162 -0
  111. package/src/editor-language.ts +86 -3
  112. package/src/emmet-module.d.ts +44 -0
  113. package/src/emmet.ts +73 -0
  114. package/src/folding.ts +3 -3
  115. package/src/format-on-type.ts +5 -5
  116. package/src/highlights.ts +3 -3
  117. package/src/history.ts +81 -1
  118. package/src/host-types.ts +17 -0
  119. package/src/index.ts +28 -3
  120. package/src/inlay-hints.ts +2 -2
  121. package/src/inline-completion.ts +221 -0
  122. package/src/key-priority.ts +11 -6
  123. package/src/linked-editing.ts +199 -0
  124. package/src/live-templates.ts +315 -0
  125. package/src/locales/en.json +61 -2
  126. package/src/locales/nl.json +61 -2
  127. package/src/markup-context.ts +387 -0
  128. package/src/markup.ts +377 -0
  129. package/src/navigation.ts +6 -6
  130. package/src/paste-history.ts +111 -0
  131. package/src/peek.ts +4 -4
  132. package/src/pick.ts +218 -20
  133. package/src/popups.ts +21 -1
  134. package/src/postfix.ts +224 -0
  135. package/src/project-language.ts +139 -19
  136. package/src/rename.ts +11 -11
  137. package/src/rendered-docs.ts +2 -2
  138. package/src/selection-ranges.ts +3 -3
  139. package/src/signature.ts +6 -6
  140. package/src/snippet-insert.ts +258 -0
  141. package/src/snippet-session.ts +9 -4
  142. package/src/snippet.ts +82 -7
  143. package/src/surround-constructs.ts +85 -0
  144. package/src/surround.ts +430 -0
  145. package/src/symbol-picker.ts +6 -6
  146. package/src/symbols.ts +30 -4
  147. package/src/template-builtins.ts +94 -0
  148. package/src/template-context.ts +65 -0
  149. package/src/templates.ts +197 -0
  150. package/src/testing.ts +126 -1
@@ -0,0 +1,54 @@
1
+ /*
2
+ * Asks the servers for something of the whole document: once it is open, a moment after the last edit, and
3
+ * whenever what the servers offer changes. An edit clears the answer at once, since its ranges point into a
4
+ * text that is gone; an answer for an older text is dropped.
5
+ */
6
+ export class DocumentQuery {
7
+ language;
8
+ timers;
9
+ delay;
10
+ ask;
11
+ apply;
12
+ timer = null;
13
+ controller = null;
14
+ generation = 0;
15
+ constructor(language, timers, delay, ask, apply) {
16
+ this.language = language;
17
+ this.timers = timers;
18
+ this.delay = delay;
19
+ this.ask = ask;
20
+ this.apply = apply;
21
+ const offs = [language.onProvidersChanged(() => this.run()), language.editor.onTextChange(() => {
22
+ this.cancel();
23
+ this.apply(null);
24
+ this.timer = this.timers.set(() => this.run(), this.delay);
25
+ })];
26
+ language.onDispose(() => {
27
+ this.cancel();
28
+ for (const off of offs) {
29
+ off();
30
+ }
31
+ });
32
+ }
33
+ /* Asks again now, as a refresh of the host does. */
34
+ run() {
35
+ this.cancel();
36
+ const generation = this.generation;
37
+ const controller = new AbortController();
38
+ this.controller = controller;
39
+ this.ask(controller.signal).then(value => {
40
+ if (generation === this.generation && !this.language.isDisposed) {
41
+ this.apply(value);
42
+ }
43
+ },
44
+ // A server that fails or refuses answers nothing; the next edit asks again.
45
+ () => undefined);
46
+ }
47
+ cancel() {
48
+ this.generation++;
49
+ this.timers.clear(this.timer);
50
+ this.timer = null;
51
+ this.controller?.abort();
52
+ this.controller = null;
53
+ }
54
+ }
@@ -0,0 +1,20 @@
1
+ import type { EditorContentChange, EditorPosition, EditorRange } from '@adecore/editor';
2
+ export interface EditPlace {
3
+ readonly uri: string;
4
+ readonly selections: readonly EditorRange[];
5
+ }
6
+ export declare class EditLocations {
7
+ private readonly limit;
8
+ private entries;
9
+ private index;
10
+ private arriving;
11
+ constructor(limit?: number);
12
+ get places(): readonly EditPlace[];
13
+ record(place: EditPlace): void;
14
+ moved(uri: string, position: EditorPosition): void;
15
+ previous(current: EditPlace | null): EditPlace | null;
16
+ next(current: EditPlace | null): EditPlace | null;
17
+ map(uri: string, changes: readonly EditorContentChange[]): void;
18
+ forget(matches: (uri: string) => boolean): void;
19
+ private arrive;
20
+ }
@@ -0,0 +1,136 @@
1
+ const LIMIT = 150;
2
+ /* Two places are one when they are in one file with as many carets, and every caret is within three lines of its twin. */
3
+ function isSame(left, right) {
4
+ return right !== null && left.uri === right.uri && left.selections.length === right.selections.length && left.selections.every((selection, index) => Math.abs(selection.end.line - right.selections[index].end.line) < 4);
5
+ }
6
+ function before(left, right) {
7
+ return left.line < right.line || left.line === right.line && left.character < right.character;
8
+ }
9
+ /* Where the text of a change ends, in the text after the change. */
10
+ function endOf(change) {
11
+ const lines = change.text.split('\n');
12
+ const {
13
+ start
14
+ } = change.range;
15
+ return lines.length === 1 ? {
16
+ line: start.line,
17
+ character: start.character + change.text.length
18
+ } : {
19
+ line: start.line + lines.length - 1,
20
+ character: lines[lines.length - 1].length
21
+ };
22
+ }
23
+ /* A position after a change: untouched in front of it, at the start when the change replaced the text around it, moved along behind it. */
24
+ function follow(position, change) {
25
+ const {
26
+ start,
27
+ end
28
+ } = change.range;
29
+ if (!before(start, position)) {
30
+ return position;
31
+ }
32
+ if (before(position, end)) {
33
+ return start;
34
+ }
35
+ const landed = endOf(change);
36
+ return position.line === end.line ? {
37
+ line: landed.line,
38
+ character: landed.character + position.character - end.character
39
+ } : {
40
+ line: position.line + landed.line - end.line,
41
+ character: position.character
42
+ };
43
+ }
44
+ /*
45
+ * Where the project was edited, for Last Edit Location and Next Edit Location. An edit within three lines of the
46
+ * last one is the same place, so typing a paragraph is one stop. A caret that moves without an edit puts the walk
47
+ * back at the end, and so does an edit.
48
+ */
49
+ export class EditLocations {
50
+ limit;
51
+ entries = [];
52
+ index = 0;
53
+ arriving = null;
54
+ constructor(limit = LIMIT) {
55
+ this.limit = limit;
56
+ }
57
+ get places() {
58
+ return this.entries;
59
+ }
60
+ /* An edit left the carets here. */
61
+ record(place) {
62
+ const last = this.entries.at(-1);
63
+ if (last !== undefined && isSame(last, place)) {
64
+ this.entries[this.entries.length - 1] = place;
65
+ } else {
66
+ this.entries.push(place);
67
+ if (this.entries.length > this.limit) {
68
+ this.entries.shift();
69
+ }
70
+ }
71
+ this.index = this.entries.length;
72
+ this.arriving = null;
73
+ }
74
+ /* The caret moved without an edit. The move that lands on the place a walk went to does not count. */
75
+ moved(uri, position) {
76
+ if (this.arriving?.uri === uri && this.arriving.line === position.line) {
77
+ return;
78
+ }
79
+ this.arriving = null;
80
+ this.index = this.entries.length;
81
+ }
82
+ /* The place of an earlier edit that is not the one the carets are on; null when there is none. */
83
+ previous(current) {
84
+ for (let i = this.index - 1; i >= 0; i--) {
85
+ if (!isSame(this.entries[i], current)) {
86
+ this.index = i;
87
+ return this.arrive(this.entries[i]);
88
+ }
89
+ }
90
+ return null;
91
+ }
92
+ /* The place of a later edit; it moves one past the place it returns, so Last Edit Location and Next Edit Location are not exact opposites. */
93
+ next(current) {
94
+ for (let i = this.index; i < this.entries.length; i++) {
95
+ if (!isSame(this.entries[i], current)) {
96
+ this.index = i + 1;
97
+ return this.arrive(this.entries[i]);
98
+ }
99
+ }
100
+ return null;
101
+ }
102
+ /* The places of a file follow its edits, in the order the editor made them. */
103
+ map(uri, changes) {
104
+ if (!this.entries.some(entry => entry.uri === uri)) {
105
+ return;
106
+ }
107
+ this.entries = this.entries.map(entry => {
108
+ if (entry.uri !== uri) {
109
+ return entry;
110
+ }
111
+ const selections = changes.reduce((current, change) => current.map(selection => ({
112
+ start: follow(selection.start, change),
113
+ end: follow(selection.end, change)
114
+ })), entry.selections);
115
+ return {
116
+ uri,
117
+ selections
118
+ };
119
+ });
120
+ }
121
+ /* Drops the places of files that are gone. */
122
+ forget(matches) {
123
+ this.entries = this.entries.filter(entry => !matches(entry.uri));
124
+ this.index = this.entries.length;
125
+ if (this.arriving !== null && matches(this.arriving.uri)) {
126
+ this.arriving = null;
127
+ }
128
+ }
129
+ arrive(place) {
130
+ this.arriving = {
131
+ uri: place.uri,
132
+ line: place.selections.at(-1).end.line
133
+ };
134
+ return place;
135
+ }
136
+ }
@@ -1,6 +1,10 @@
1
1
  import { type Keymap } from '@adecore/editor/keymap';
2
2
  import type { Editor, EditorKeyHandler, EditorPosition } from '@adecore/editor';
3
- import type { Location } from '@adecore/lsp';
3
+ import type { LanguageService, Location, TextDocumentSaveReason } from '@adecore/lsp';
4
+ import { DocumentColorsFeature } from './document-colors.ts';
5
+ import { DocumentLinksFeature } from './document-links.ts';
6
+ import { InlineCompletionFeature } from './inline-completion.ts';
7
+ import { LinkedEditingFeature } from './linked-editing.ts';
4
8
  import { CallHierarchyFeature } from './call-hierarchy.ts';
5
9
  import { CodeActionsFeature } from './code-actions.ts';
6
10
  import { CodeVisionFeature } from './code-vision.ts';
@@ -12,7 +16,9 @@ import { FormatOnTypeFeature } from './format-on-type.ts';
12
16
  import { HighlightsFeature } from './highlights.ts';
13
17
  import { HistoryFeature } from './history.ts';
14
18
  import { HoverFeature } from './hover.ts';
19
+ import { MarkupFeature } from './markup.ts';
15
20
  import { NavigationFeature } from './navigation.ts';
21
+ import { PasteHistoryFeature } from './paste-history.ts';
16
22
  import { PeekFeature } from './peek.ts';
17
23
  import { PickFeature } from './pick.ts';
18
24
  import { SymbolPickerFeature } from './symbol-picker.ts';
@@ -24,11 +30,14 @@ import { InlayHintsFeature } from './inlay-hints.ts';
24
30
  import { type SemanticTokensOptions } from './semantic-tokens.ts';
25
31
  import { SignatureFeature } from './signature.ts';
26
32
  import { SnippetFeature } from './snippet-session.ts';
33
+ import { SurroundFeature } from './surround.ts';
27
34
  import { SymbolsFeature } from './symbols.ts';
35
+ import { TemplatesFeature } from './live-templates.ts';
28
36
  import type { Place } from './navigation-history.ts';
29
37
  import type { LanguageDocumentHandle, ProjectLanguage } from './project-language.ts';
30
38
  export interface EditorLanguageOptions {
31
39
  semanticTokens?: SemanticTokensOptions;
40
+ serve?: boolean;
32
41
  }
33
42
  export declare class EditorLanguage {
34
43
  readonly editor: Editor;
@@ -38,6 +47,7 @@ export declare class EditorLanguage {
38
47
  readonly project: ProjectLanguage;
39
48
  readonly document: LanguageDocumentHandle;
40
49
  readonly languageId: string;
50
+ readonly served: boolean;
41
51
  readonly popups: import("./popups.ts").PopupStore;
42
52
  readonly diagnostics: DiagnosticsFeature;
43
53
  readonly hover: HoverFeature;
@@ -45,6 +55,9 @@ export declare class EditorLanguage {
45
55
  readonly completion: CompletionFeature;
46
56
  readonly signature: SignatureFeature;
47
57
  readonly snippets: SnippetFeature;
58
+ readonly markup: MarkupFeature;
59
+ readonly templates: TemplatesFeature;
60
+ readonly surround: SurroundFeature;
48
61
  readonly highlights: HighlightsFeature;
49
62
  readonly pick: PickFeature;
50
63
  readonly codeActions: CodeActionsFeature;
@@ -60,11 +73,18 @@ export declare class EditorLanguage {
60
73
  readonly codeVision: CodeVisionFeature;
61
74
  readonly formatOnType: FormatOnTypeFeature;
62
75
  readonly renderedDocs: RenderedDocsFeature;
76
+ readonly pasteHistory: PasteHistoryFeature;
77
+ readonly links: DocumentLinksFeature;
78
+ readonly colors: DocumentColorsFeature;
79
+ readonly linkedEditing: LinkedEditingFeature;
80
+ readonly inlineCompletion: InlineCompletionFeature;
63
81
  private readonly disposers;
64
82
  private readonly keys;
83
+ private readonly timers;
65
84
  private disposed;
66
85
  constructor(project: ProjectLanguage, editor: Editor, uri: string, languageId: string, timers?: Timers, options?: EditorLanguageOptions);
67
86
  get uri(): string;
87
+ get service(): LanguageService;
68
88
  get isDisposed(): boolean;
69
89
  get place(): Place;
70
90
  goTo(location: Location): void;
@@ -73,7 +93,9 @@ export declare class EditorLanguage {
73
93
  locations(places: readonly Location[], anchor: EditorPosition, title: string): void;
74
94
  onProvidersChanged(listener: () => void): () => void;
75
95
  onKey(handler: EditorKeyHandler, priority?: number): () => void;
96
+ prepareSave(reason?: TextDocumentSaveReason): Promise<void>;
76
97
  onDispose(dispose: () => void): void;
77
98
  dispose(): void;
99
+ private otherTexts;
78
100
  private placeOpenedCaret;
79
101
  }
@@ -1,4 +1,8 @@
1
1
  import { KEYMAP } from '@adecore/editor/keymap';
2
+ import { DocumentColorsFeature } from './document-colors.js';
3
+ import { DocumentLinksFeature } from './document-links.js';
4
+ import { InlineCompletionFeature } from './inline-completion.js';
5
+ import { LinkedEditingFeature } from './linked-editing.js';
2
6
  import { CallHierarchyFeature } from './call-hierarchy.js';
3
7
  import { CodeActionsFeature } from './code-actions.js';
4
8
  import { CodeVisionFeature } from './code-vision.js';
@@ -12,7 +16,9 @@ import { HighlightsFeature } from './highlights.js';
12
16
  import { HistoryFeature } from './history.js';
13
17
  import { HoverFeature } from './hover.js';
14
18
  import { KeyDispatcher } from './key-priority.js';
19
+ import { MarkupFeature } from './markup.js';
15
20
  import { NavigationFeature, locationRow } from './navigation.js';
21
+ import { PasteHistoryFeature } from './paste-history.js';
16
22
  import { PeekFeature } from './peek.js';
17
23
  import { PickFeature } from './pick.js';
18
24
  import { SymbolPickerFeature } from './symbol-picker.js';
@@ -26,7 +32,11 @@ import { SelectionRangesFeature } from './selection-ranges.js';
26
32
  import { SemanticTokensFeature } from './semantic-tokens.js';
27
33
  import { SignatureFeature } from './signature.js';
28
34
  import { SnippetFeature } from './snippet-session.js';
35
+ import { SurroundFeature } from './surround.js';
29
36
  import { SymbolsFeature } from './symbols.js';
37
+ import { TemplatesFeature } from './live-templates.js';
38
+ /* How long the edits servers want before a save are waited for. */
39
+ const SAVE_PARTICIPANT_MS = 1_000;
30
40
  /*
31
41
  * The one place that connects an editor to the language servers: it opens the file's document for
32
42
  * the editor and lets go of it again. Every language feature of the editor hangs off this object.
@@ -39,6 +49,8 @@ export class EditorLanguage {
39
49
  project;
40
50
  document;
41
51
  languageId;
52
+ /* Whether a server serves this document; read it where a host meant "a language server is behind this editor". */
53
+ served;
42
54
  /* What the features of this editor have open over it: the hover card, the suggestions, the signature. */
43
55
  popups = createPopupStore();
44
56
  diagnostics;
@@ -47,6 +59,9 @@ export class EditorLanguage {
47
59
  completion;
48
60
  signature;
49
61
  snippets;
62
+ markup;
63
+ templates;
64
+ surround;
50
65
  highlights;
51
66
  pick;
52
67
  codeActions;
@@ -62,8 +77,14 @@ export class EditorLanguage {
62
77
  codeVision;
63
78
  formatOnType;
64
79
  renderedDocs;
80
+ pasteHistory;
81
+ links;
82
+ colors;
83
+ linkedEditing;
84
+ inlineCompletion;
65
85
  disposers = [];
66
86
  keys = new KeyDispatcher();
87
+ timers;
67
88
  disposed = false;
68
89
  constructor(project, editor, uri, languageId, timers = realTimers, options = {}) {
69
90
  this.editor = editor;
@@ -72,13 +93,20 @@ export class EditorLanguage {
72
93
  this.shortcuts = shortcutsOf(this.keymap, this.apple);
73
94
  this.project = project;
74
95
  this.languageId = languageId;
75
- this.document = project.acquire(uri, languageId, editor);
96
+ this.served = options.serve ?? true;
97
+ this.document = project.acquire(uri, languageId, editor, {
98
+ serve: this.served
99
+ });
100
+ this.timers = timers;
76
101
  this.onDispose(editor.onKeyDown(event => this.keys.dispatch(event)));
77
102
  this.pick = new PickFeature(this, timers);
78
103
  this.diagnostics = new DiagnosticsFeature(this);
79
104
  this.hover = new HoverFeature(this, timers);
80
105
  this.completion = new CompletionFeature(this, timers);
81
106
  this.snippets = new SnippetFeature(this, timers);
107
+ this.markup = new MarkupFeature(this);
108
+ this.templates = new TemplatesFeature(this);
109
+ this.surround = new SurroundFeature(this);
82
110
  this.signature = new SignatureFeature(this, timers);
83
111
  this.highlights = new HighlightsFeature(this, timers);
84
112
  this.symbols = new SymbolsFeature(this, timers);
@@ -87,6 +115,11 @@ export class EditorLanguage {
87
115
  this.inlayHints = new InlayHintsFeature(this, timers);
88
116
  this.codeActions = new CodeActionsFeature(this, timers);
89
117
  this.rename = new RenameFeature(this);
118
+ // Before navigation, whose Mod+click goes to a definition: a link under the pointer goes first.
119
+ this.links = new DocumentLinksFeature(this, timers);
120
+ this.colors = new DocumentColorsFeature(this, timers);
121
+ this.linkedEditing = new LinkedEditingFeature(this, timers);
122
+ this.inlineCompletion = new InlineCompletionFeature(this, timers);
90
123
  this.navigation = new NavigationFeature(this);
91
124
  this.peek = new PeekFeature(this, timers);
92
125
  this.callHierarchy = new CallHierarchyFeature(this, timers);
@@ -97,13 +130,26 @@ export class EditorLanguage {
97
130
  this.codeVision = new CodeVisionFeature(this, listener => this.symbols.onResult(listener), timers);
98
131
  this.formatOnType = new FormatOnTypeFeature(this);
99
132
  this.renderedDocs = new RenderedDocsFeature(this, timers);
133
+ this.pasteHistory = new PasteHistoryFeature(this);
100
134
  new FoldingFeature(this, listener => this.symbols.onResult(listener), timers);
135
+ const copies = editor.onCopy?.(copy => project.clipboard.add(copy.text, {
136
+ wholeLines: copy.wholeLines
137
+ }));
138
+ if (copies !== undefined) {
139
+ this.onDispose(copies);
140
+ }
141
+ editor.setWordSources?.(() => this.otherTexts());
142
+ this.onDispose(() => editor.setWordSources?.(null));
101
143
  this.placeOpenedCaret(editor.getCaret());
102
144
  this.onDispose(editor.onCaret(position => this.placeOpenedCaret(position)));
103
145
  }
104
146
  get uri() {
105
147
  return this.document.uri;
106
148
  }
149
+ /* The service this document is asked through: the project's, or one that answers nothing when no server serves it. */
150
+ get service() {
151
+ return this.document.service;
152
+ }
107
153
  get isDisposed() {
108
154
  return this.disposed;
109
155
  }
@@ -169,7 +215,7 @@ export class EditorLanguage {
169
215
  listener();
170
216
  }
171
217
  }, () => undefined);
172
- const providers = this.project.service.onProvidersChanged(uri => {
218
+ const providers = this.service.onProvidersChanged(uri => {
173
219
  if (uri === this.uri) {
174
220
  listener();
175
221
  }
@@ -186,6 +232,37 @@ export class EditorLanguage {
186
232
  onKey(handler, priority = 0) {
187
233
  return this.keys.add(handler, priority);
188
234
  }
235
+ /*
236
+ * Lets the servers in before the host writes the file: tells them a save is coming and applies the edits they
237
+ * want made first, such as organized imports. A server that has not answered within a second is passed over,
238
+ * as the specification allows. Call it, then save, then let the service send `didSave`.
239
+ */
240
+ async prepareSave(reason = 1) {
241
+ const {
242
+ service
243
+ } = this.project;
244
+ await service.willSave?.(this.uri, reason).catch(() => undefined);
245
+ if (service.willSaveWaitUntil === undefined) {
246
+ return;
247
+ }
248
+ const controller = new AbortController();
249
+ const timer = this.timers.set(() => controller.abort(), SAVE_PARTICIPANT_MS);
250
+ try {
251
+ const edits = await service.willSaveWaitUntil(this.uri, reason, {
252
+ signal: controller.signal
253
+ });
254
+ if (!controller.signal.aborted && edits !== null && edits.length > 0) {
255
+ this.editor.applyEdits(edits.map(edit => ({
256
+ range: edit.range,
257
+ text: edit.newText
258
+ })));
259
+ }
260
+ } catch {
261
+ // A server that fails or is too slow does not keep the file from being saved.
262
+ } finally {
263
+ this.timers.clear(timer);
264
+ }
265
+ }
189
266
  /* Runs `dispose` when the editor lets go of its document. */
190
267
  onDispose(dispose) {
191
268
  this.disposers.push(dispose);
@@ -200,6 +277,10 @@ export class EditorLanguage {
200
277
  }
201
278
  this.document.release();
202
279
  }
280
+ /* The texts of the other open editors of the project, which Hippie completion takes words from. */
281
+ otherTexts() {
282
+ return this.project.openEditors().filter(open => open.uri !== this.uri).map(open => open.editor.getText());
283
+ }
203
284
  /* A file opened from another one lands on the line; this puts the caret on the column the jump meant. */
204
285
  placeOpenedCaret(position) {
205
286
  const column = this.project.takeCaret(this.uri, position.line);
@@ -0,0 +1,13 @@
1
+ import type { MarkupSyntax } from './markup-context.ts';
2
+ export interface AbbreviationOptions {
3
+ readonly syntax: MarkupSyntax;
4
+ readonly indent: string;
5
+ readonly wrap?: readonly string[];
6
+ }
7
+ export interface FoundAbbreviation {
8
+ readonly abbreviation: string;
9
+ readonly start: number;
10
+ readonly end: number;
11
+ }
12
+ export declare function abbreviationBefore(line: string, character: number): FoundAbbreviation | null;
13
+ export declare function expandAbbreviation(abbreviation: string, options: AbbreviationOptions): string | null;
package/dist/emmet.js ADDED
@@ -0,0 +1,62 @@
1
+ // A reference and no import, so an app that type-checks this source reads the declaration of the package too.
2
+ // oxlint-disable-next-line typescript/triple-slash-reference
3
+ /// <reference path="./emmet-module.d.ts" />
4
+ import { extract, markup, markupAbbreviation, resolveConfig } from 'emmet';
5
+ /* A text of an expansion is snippet syntax, where these mean a tab stop or end one. */
6
+ function escapeText(text) {
7
+ return text.replace(/[$}\\]/g, '\\$&');
8
+ }
9
+ function field(index, placeholder) {
10
+ return placeholder === '' ? `\${${index}}` : `\${${index}:${escapeText(placeholder)}}`;
11
+ }
12
+ /* `lipsum` is the other name of `lorem` that IntelliJ and the Emmet documentation accept. */
13
+ function renameLipsum(node) {
14
+ if (node.name !== undefined && /^lipsum(?=\d|$)/i.test(node.name)) {
15
+ node.name = 'lorem' + node.name.slice(6);
16
+ }
17
+ for (const child of node.children) {
18
+ renameLipsum(child);
19
+ }
20
+ }
21
+ /* The abbreviation that ends at `character` of a line, or null where nothing before the caret reads as one. */
22
+ export function abbreviationBefore(line, character) {
23
+ const found = extract(line, character, {
24
+ type: 'markup',
25
+ lookAhead: true
26
+ });
27
+ if (found === undefined || !/^[A-Za-z!.#([{]/.test(found.abbreviation)) {
28
+ return null;
29
+ }
30
+ return {
31
+ abbreviation: found.abbreviation,
32
+ start: found.start,
33
+ end: found.end
34
+ };
35
+ }
36
+ /*
37
+ * The expansion of an abbreviation as snippet text, its places to fill as tab stops; null when it does not
38
+ * read as one. Lorem ipsum words after the first sentence differ per expansion.
39
+ */
40
+ export function expandAbbreviation(abbreviation, options) {
41
+ const text = options.wrap === undefined ? undefined : [...options.wrap];
42
+ try {
43
+ const tree = markupAbbreviation(abbreviation, {
44
+ text
45
+ });
46
+ for (const child of tree.children) {
47
+ renameLipsum(child);
48
+ }
49
+ const expanded = markup(tree, resolveConfig({
50
+ syntax: options.syntax === 'xml' ? 'xml' : 'html',
51
+ text,
52
+ options: {
53
+ 'output.field': field,
54
+ 'output.text': escapeText,
55
+ 'output.indent': options.indent
56
+ }
57
+ }));
58
+ return expanded === '' ? null : expanded;
59
+ } catch {
60
+ return null;
61
+ }
62
+ }
package/dist/folding.js CHANGED
@@ -60,14 +60,13 @@ export class FoldingFeature {
60
60
  this.language = language;
61
61
  const {
62
62
  editor,
63
- project,
64
63
  uri
65
64
  } = language;
66
65
  this.refresher = new Refresher(async signal => {
67
- if (!project.service.supports(METHOD, uri)) {
66
+ if (!language.service.supports(METHOD, uri)) {
68
67
  return;
69
68
  }
70
- const ranges = await project.service.foldingRanges(uri, {
69
+ const ranges = await language.service.foldingRanges(uri, {
71
70
  signal
72
71
  });
73
72
  if (!signal.aborted) {
@@ -38,10 +38,9 @@ export class FormatOnTypeFeature {
38
38
  /* Whether the server of this document formats on type and the service can ask it. */
39
39
  get supported() {
40
40
  const {
41
- project,
42
41
  uri
43
42
  } = this.language;
44
- return project.service.onTypeFormatting !== undefined && project.service.supports(METHOD, uri);
43
+ return this.language.service.onTypeFormatting !== undefined && this.language.service.supports(METHOD, uri);
45
44
  }
46
45
  configure(enabled) {
47
46
  this.enabled = enabled;
@@ -51,7 +50,7 @@ export class FormatOnTypeFeature {
51
50
  }
52
51
  /* The characters that ask the server, the first one and the more. */
53
52
  triggerCharacters() {
54
- const options = this.language.project.service.providerOptions(METHOD, this.language.uri);
53
+ const options = this.language.service.providerOptions(METHOD, this.language.uri);
55
54
  return options === undefined ? [] : [...(options.firstTriggerCharacter === undefined ? [] : [options.firstTriggerCharacter]), ...(options.moreTriggerCharacter ?? [])];
56
55
  }
57
56
  typed(change) {
@@ -80,14 +79,13 @@ export class FormatOnTypeFeature {
80
79
  async ask(revision, typed, caret, signal) {
81
80
  const {
82
81
  editor,
83
- project,
84
82
  uri,
85
83
  document
86
84
  } = this.language;
87
85
  try {
88
86
  // The change of the typed character reaches the server before this question does.
89
87
  await document.ready;
90
- const edits = await project.service.onTypeFormatting(uri, caret, typed, {
88
+ const edits = await this.language.service.onTypeFormatting(uri, caret, typed, {
91
89
  ...editor.getIndentation()
92
90
  }, {
93
91
  signal
@@ -40,15 +40,14 @@ export class HighlightsFeature {
40
40
  this.language = language;
41
41
  const {
42
42
  editor,
43
- project,
44
43
  uri
45
44
  } = language;
46
45
  this.refresher = new Refresher(async signal => {
47
- if (!project.service.supports(METHOD, uri) || !this.onName()) {
46
+ if (!language.service.supports(METHOD, uri) || !this.onName()) {
48
47
  this.clear();
49
48
  return;
50
49
  }
51
- const result = await project.service.documentHighlights(uri, editor.getCaret(), {
50
+ const result = await language.service.documentHighlights(uri, editor.getCaret(), {
52
51
  signal
53
52
  });
54
53
  if (!signal.aborted) {
package/dist/history.d.ts CHANGED
@@ -1,9 +1,16 @@
1
1
  import type { EditorLanguage } from './editor-language.ts';
2
2
  export declare class HistoryFeature {
3
3
  private readonly language;
4
+ private editing;
4
5
  constructor(language: EditorLanguage);
6
+ lastEdit(): boolean;
7
+ nextEdit(): boolean;
5
8
  back(): boolean;
6
9
  forward(): boolean;
7
10
  recent(): void;
11
+ private currentEdit;
12
+ private edited;
13
+ private moved;
14
+ private visitEdit;
8
15
  private visit;
9
16
  }