@adecore/editor-react 0.0.1 → 0.17.0-beta.2

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 (264) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +60 -0
  3. package/dist/AnchoredPopup.d.ts +12 -0
  4. package/dist/AnchoredPopup.js +27 -0
  5. package/dist/AttributionCard.d.ts +14 -0
  6. package/dist/AttributionCard.js +5 -0
  7. package/dist/BaselineStatus.d.ts +4 -0
  8. package/dist/BaselineStatus.js +13 -0
  9. package/dist/ChangeReview.d.ts +12 -0
  10. package/dist/ChangeReview.js +27 -0
  11. package/dist/CodeAuthorsCard.d.ts +6 -0
  12. package/dist/CodeAuthorsCard.js +22 -0
  13. package/dist/CompletionPopup.d.ts +8 -0
  14. package/dist/CompletionPopup.js +103 -0
  15. package/dist/EditorContextMenu.d.ts +8 -0
  16. package/dist/EditorContextMenu.js +23 -0
  17. package/dist/EditorView.d.ts +16 -0
  18. package/dist/EditorView.js +27 -0
  19. package/dist/FindReplace.d.ts +6 -0
  20. package/dist/FindReplace.js +44 -0
  21. package/dist/HoverCard.d.ts +11 -0
  22. package/dist/HoverCard.js +38 -0
  23. package/dist/HoverSections.d.ts +21 -0
  24. package/dist/HoverSections.js +82 -0
  25. package/dist/LanguagePopups.d.ts +6 -0
  26. package/dist/LanguagePopups.js +38 -0
  27. package/dist/Markdown.d.ts +3 -0
  28. package/dist/Markdown.js +7 -0
  29. package/dist/PathText.d.ts +4 -0
  30. package/dist/PathText.js +12 -0
  31. package/dist/PeekPanel.d.ts +6 -0
  32. package/dist/PeekPanel.js +111 -0
  33. package/dist/PickPopup.d.ts +8 -0
  34. package/dist/PickPopup.js +33 -0
  35. package/dist/RenameCard.d.ts +9 -0
  36. package/dist/RenameCard.js +108 -0
  37. package/dist/SignatureCard.d.ts +4 -0
  38. package/dist/SignatureCard.js +8 -0
  39. package/dist/SymbolPicker.d.ts +6 -0
  40. package/dist/SymbolPicker.js +108 -0
  41. package/dist/code-actions-model.d.ts +23 -0
  42. package/dist/code-actions-model.js +114 -0
  43. package/dist/code-actions.d.ts +30 -0
  44. package/dist/code-actions.js +326 -0
  45. package/dist/code-authors.d.ts +18 -0
  46. package/dist/code-authors.js +79 -0
  47. package/dist/code-vision-model.d.ts +21 -0
  48. package/dist/code-vision-model.js +183 -0
  49. package/dist/code-vision.d.ts +48 -0
  50. package/dist/code-vision.js +236 -0
  51. package/dist/completion-call.d.ts +24 -0
  52. package/dist/completion-call.js +110 -0
  53. package/dist/completion-model.d.ts +51 -0
  54. package/dist/completion-model.js +353 -0
  55. package/dist/completion.d.ts +43 -0
  56. package/dist/completion.js +463 -0
  57. package/dist/conflict-model.d.ts +15 -0
  58. package/dist/conflict-model.js +36 -0
  59. package/dist/context-menu.d.ts +10 -0
  60. package/dist/context-menu.js +55 -0
  61. package/dist/definition-link.d.ts +14 -0
  62. package/dist/definition-link.js +99 -0
  63. package/dist/diagnostics-model.d.ts +15 -0
  64. package/dist/diagnostics-model.js +83 -0
  65. package/dist/diagnostics.d.ts +23 -0
  66. package/dist/diagnostics.js +95 -0
  67. package/dist/editor-language.d.ts +65 -0
  68. package/dist/editor-language.js +170 -0
  69. package/dist/editor-react.css +43 -0
  70. package/dist/find-query.d.ts +26 -0
  71. package/dist/find-query.js +51 -0
  72. package/dist/folding.d.ts +16 -0
  73. package/dist/folding.js +95 -0
  74. package/dist/highlight-layers.d.ts +10 -0
  75. package/dist/highlight-layers.js +30 -0
  76. package/dist/highlights.d.ts +16 -0
  77. package/dist/highlights.js +102 -0
  78. package/dist/history.d.ts +8 -0
  79. package/dist/history.js +46 -0
  80. package/dist/holder.d.ts +5 -0
  81. package/dist/holder.js +19 -0
  82. package/dist/host-types.d.ts +45 -0
  83. package/dist/host-types.js +1 -0
  84. package/dist/hover-content.d.ts +45 -0
  85. package/dist/hover-content.js +198 -0
  86. package/dist/hover.d.ts +32 -0
  87. package/dist/hover.js +260 -0
  88. package/dist/index.d.ts +44 -0
  89. package/dist/index.js +28 -0
  90. package/dist/inlay-hints.d.ts +13 -0
  91. package/dist/inlay-hints.js +71 -0
  92. package/dist/language-ids.d.ts +3 -0
  93. package/dist/language-ids.js +49 -0
  94. package/dist/line-edits.d.ts +8 -0
  95. package/dist/line-edits.js +39 -0
  96. package/dist/locales/en.json +171 -0
  97. package/dist/locales/nl.json +171 -0
  98. package/dist/models.d.ts +20 -0
  99. package/dist/models.js +20 -0
  100. package/dist/navigation-history.d.ts +16 -0
  101. package/dist/navigation-history.js +53 -0
  102. package/dist/navigation.d.ts +23 -0
  103. package/dist/navigation.js +155 -0
  104. package/dist/paths.d.ts +1 -0
  105. package/dist/paths.js +3 -0
  106. package/dist/peek-model.d.ts +28 -0
  107. package/dist/peek-model.js +104 -0
  108. package/dist/peek.d.ts +30 -0
  109. package/dist/peek.js +215 -0
  110. package/dist/pick.d.ts +34 -0
  111. package/dist/pick.js +166 -0
  112. package/dist/popup-placement.d.ts +23 -0
  113. package/dist/popup-placement.js +34 -0
  114. package/dist/popups.d.ts +133 -0
  115. package/dist/popups.js +14 -0
  116. package/dist/project-language.d.ts +36 -0
  117. package/dist/project-language.js +190 -0
  118. package/dist/project-problems.d.ts +24 -0
  119. package/dist/project-problems.js +63 -0
  120. package/dist/proposal-model.d.ts +36 -0
  121. package/dist/proposal-model.js +197 -0
  122. package/dist/recent-choices.d.ts +10 -0
  123. package/dist/recent-choices.js +39 -0
  124. package/dist/refresher.d.ts +13 -0
  125. package/dist/refresher.js +50 -0
  126. package/dist/relative-time.d.ts +1 -0
  127. package/dist/relative-time.js +9 -0
  128. package/dist/rename-model.d.ts +25 -0
  129. package/dist/rename-model.js +93 -0
  130. package/dist/rename.d.ts +21 -0
  131. package/dist/rename.js +257 -0
  132. package/dist/rendering-context.d.ts +6 -0
  133. package/dist/rendering-context.js +5 -0
  134. package/dist/rendering.d.ts +6 -0
  135. package/dist/rendering.js +5 -0
  136. package/dist/review-group.d.ts +16 -0
  137. package/dist/review-group.js +54 -0
  138. package/dist/row-host.d.ts +42 -0
  139. package/dist/row-host.js +120 -0
  140. package/dist/selection-ranges.d.ts +7 -0
  141. package/dist/selection-ranges.js +26 -0
  142. package/dist/semantic-model.d.ts +4 -0
  143. package/dist/semantic-model.js +76 -0
  144. package/dist/semantic-tokens.d.ts +6 -0
  145. package/dist/semantic-tokens.js +38 -0
  146. package/dist/shortcut-keys.d.ts +91 -0
  147. package/dist/shortcut-keys.js +12 -0
  148. package/dist/signature-model.d.ts +18 -0
  149. package/dist/signature-model.js +33 -0
  150. package/dist/signature.d.ts +22 -0
  151. package/dist/signature.js +150 -0
  152. package/dist/snippet-session.d.ts +17 -0
  153. package/dist/snippet-session.js +110 -0
  154. package/dist/snippet.d.ts +11 -0
  155. package/dist/snippet.js +134 -0
  156. package/dist/symbol-links.d.ts +6 -0
  157. package/dist/symbol-links.js +50 -0
  158. package/dist/symbol-picker-model.d.ts +44 -0
  159. package/dist/symbol-picker-model.js +146 -0
  160. package/dist/symbol-picker.d.ts +12 -0
  161. package/dist/symbol-picker.js +76 -0
  162. package/dist/symbols.d.ts +11 -0
  163. package/dist/symbols.js +95 -0
  164. package/dist/testing.d.ts +57 -0
  165. package/dist/testing.js +156 -0
  166. package/dist/timers.d.ts +14 -0
  167. package/dist/timers.js +34 -0
  168. package/dist/use-problem-counts.d.ts +3 -0
  169. package/dist/use-problem-counts.js +20 -0
  170. package/dist/view-state.d.ts +21 -0
  171. package/dist/view-state.js +63 -0
  172. package/dist/word-diff.d.ts +1 -0
  173. package/dist/word-diff.js +32 -0
  174. package/dist/workspace-edit.d.ts +21 -0
  175. package/dist/workspace-edit.js +147 -0
  176. package/package.json +74 -2
  177. package/src/AnchoredPopup.tsx +51 -0
  178. package/src/AttributionCard.tsx +36 -0
  179. package/src/BaselineStatus.tsx +20 -0
  180. package/src/ChangeReview.tsx +43 -0
  181. package/src/CodeAuthorsCard.tsx +81 -0
  182. package/src/CompletionPopup.tsx +196 -0
  183. package/src/EditorContextMenu.tsx +67 -0
  184. package/src/EditorView.tsx +47 -0
  185. package/src/FindReplace.tsx +89 -0
  186. package/src/HoverCard.tsx +121 -0
  187. package/src/HoverSections.tsx +146 -0
  188. package/src/LanguagePopups.tsx +65 -0
  189. package/src/Markdown.tsx +11 -0
  190. package/src/PathText.tsx +18 -0
  191. package/src/PeekPanel.tsx +222 -0
  192. package/src/PickPopup.tsx +102 -0
  193. package/src/RenameCard.tsx +219 -0
  194. package/src/SignatureCard.tsx +42 -0
  195. package/src/SymbolPicker.tsx +219 -0
  196. package/src/code-actions-model.ts +146 -0
  197. package/src/code-actions.ts +384 -0
  198. package/src/code-authors.ts +104 -0
  199. package/src/code-vision-model.ts +230 -0
  200. package/src/code-vision.ts +269 -0
  201. package/src/completion-call.ts +139 -0
  202. package/src/completion-model.ts +440 -0
  203. package/src/completion.ts +500 -0
  204. package/src/conflict-model.ts +56 -0
  205. package/src/context-menu.ts +63 -0
  206. package/src/definition-link.ts +106 -0
  207. package/src/diagnostics-model.ts +102 -0
  208. package/src/diagnostics.ts +117 -0
  209. package/src/editor-language.ts +189 -0
  210. package/src/editor-react.css +43 -0
  211. package/src/find-query.ts +74 -0
  212. package/src/folding.ts +110 -0
  213. package/src/highlight-layers.ts +37 -0
  214. package/src/highlights.ts +123 -0
  215. package/src/history.ts +56 -0
  216. package/src/holder.ts +19 -0
  217. package/src/host-types.ts +52 -0
  218. package/src/hover-content.ts +264 -0
  219. package/src/hover.ts +280 -0
  220. package/src/index.ts +63 -0
  221. package/src/inlay-hints.ts +85 -0
  222. package/src/language-ids.ts +54 -0
  223. package/src/line-edits.ts +48 -0
  224. package/src/locales/en.json +171 -0
  225. package/src/locales/nl.json +171 -0
  226. package/src/models.ts +157 -0
  227. package/src/navigation-history.ts +69 -0
  228. package/src/navigation.ts +168 -0
  229. package/src/paths.ts +3 -0
  230. package/src/peek-model.ts +147 -0
  231. package/src/peek.ts +247 -0
  232. package/src/pick.ts +197 -0
  233. package/src/popup-placement.ts +64 -0
  234. package/src/popups.ts +204 -0
  235. package/src/project-language.ts +223 -0
  236. package/src/project-problems.ts +87 -0
  237. package/src/proposal-model.ts +259 -0
  238. package/src/recent-choices.ts +48 -0
  239. package/src/refresher.ts +56 -0
  240. package/src/relative-time.ts +10 -0
  241. package/src/rename-model.ts +130 -0
  242. package/src/rename.ts +290 -0
  243. package/src/rendering-context.ts +12 -0
  244. package/src/rendering.tsx +6 -0
  245. package/src/review-group.ts +70 -0
  246. package/src/row-host.ts +158 -0
  247. package/src/selection-ranges.ts +32 -0
  248. package/src/semantic-model.ts +80 -0
  249. package/src/semantic-tokens.ts +46 -0
  250. package/src/shortcut-keys.ts +21 -0
  251. package/src/signature-model.ts +49 -0
  252. package/src/signature.ts +164 -0
  253. package/src/snippet-session.ts +122 -0
  254. package/src/snippet.ts +147 -0
  255. package/src/symbol-links.ts +72 -0
  256. package/src/symbol-picker-model.ts +198 -0
  257. package/src/symbol-picker.ts +85 -0
  258. package/src/symbols.ts +110 -0
  259. package/src/testing.ts +240 -0
  260. package/src/timers.ts +45 -0
  261. package/src/use-problem-counts.ts +25 -0
  262. package/src/view-state.ts +88 -0
  263. package/src/word-diff.ts +35 -0
  264. package/src/workspace-edit.ts +194 -0
@@ -0,0 +1,91 @@
1
+ import { type Keymap } from '@adecore/editor/keymap';
2
+ import type { KeyChord } from '@adecore/editor';
3
+ export interface LanguageShortcut extends KeyChord {
4
+ readonly apple: boolean;
5
+ }
6
+ export declare function shortcutsOf(keymap: Keymap, apple: boolean): {
7
+ rename: LanguageShortcut | null;
8
+ acceptGhostWord: LanguageShortcut | null;
9
+ addCaretAbove: LanguageShortcut | null;
10
+ addCaretBelow: LanguageShortcut | null;
11
+ addCaretPerSelectedLine: LanguageShortcut | null;
12
+ autoIndentLines: LanguageShortcut | null;
13
+ codeActions: LanguageShortcut | null;
14
+ collapse: LanguageShortcut | null;
15
+ collapseAll: LanguageShortcut | null;
16
+ collapseDocComments: LanguageShortcut | null;
17
+ collapseRecursively: LanguageShortcut | null;
18
+ deleteLine: LanguageShortcut | null;
19
+ deleteWordLeft: LanguageShortcut | null;
20
+ deleteWordRight: LanguageShortcut | null;
21
+ duplicateLine: LanguageShortcut | null;
22
+ expand: LanguageShortcut | null;
23
+ expandAll: LanguageShortcut | null;
24
+ expandAllToLevel1: LanguageShortcut | null;
25
+ expandAllToLevel2: LanguageShortcut | null;
26
+ expandAllToLevel3: LanguageShortcut | null;
27
+ expandAllToLevel4: LanguageShortcut | null;
28
+ expandAllToLevel5: LanguageShortcut | null;
29
+ expandDocComments: LanguageShortcut | null;
30
+ expandRecursively: LanguageShortcut | null;
31
+ expandSelection: LanguageShortcut | null;
32
+ findNext: LanguageShortcut | null;
33
+ findPrevious: LanguageShortcut | null;
34
+ foldSelection: LanguageShortcut | null;
35
+ formatDocument: LanguageShortcut | null;
36
+ goToDefinition: LanguageShortcut | null;
37
+ goToImplementation: LanguageShortcut | null;
38
+ goToSymbol: LanguageShortcut | null;
39
+ goToTypeDefinition: LanguageShortcut | null;
40
+ historyBack: LanguageShortcut | null;
41
+ historyForward: LanguageShortcut | null;
42
+ inlineEdit: LanguageShortcut | null;
43
+ joinLines: LanguageShortcut | null;
44
+ matchBrace: LanguageShortcut | null;
45
+ moveLineDown: LanguageShortcut | null;
46
+ moveLineUp: LanguageShortcut | null;
47
+ nextHighlight: LanguageShortcut | null;
48
+ nextProblem: LanguageShortcut | null;
49
+ organizeImports: LanguageShortcut | null;
50
+ parameterInfo: LanguageShortcut | null;
51
+ peekDefinition: LanguageShortcut | null;
52
+ peekReferences: LanguageShortcut | null;
53
+ previousHighlight: LanguageShortcut | null;
54
+ previousProblem: LanguageShortcut | null;
55
+ quickInfo: LanguageShortcut | null;
56
+ recentLocations: LanguageShortcut | null;
57
+ redo: LanguageShortcut | null;
58
+ refactorThis: LanguageShortcut | null;
59
+ renameSymbol: LanguageShortcut | null;
60
+ replace: LanguageShortcut | null;
61
+ save: LanguageShortcut | null;
62
+ selectAll: LanguageShortcut | null;
63
+ selectAllOccurrences: LanguageShortcut | null;
64
+ selectNextOccurrence: LanguageShortcut | null;
65
+ selectSmartEnd: LanguageShortcut | null;
66
+ selectSmartHome: LanguageShortcut | null;
67
+ selectTextEnd: LanguageShortcut | null;
68
+ selectTextStart: LanguageShortcut | null;
69
+ selectWordLeft: LanguageShortcut | null;
70
+ selectWordRight: LanguageShortcut | null;
71
+ selectionToChat: LanguageShortcut | null;
72
+ shrinkSelection: LanguageShortcut | null;
73
+ smartEnd: LanguageShortcut | null;
74
+ smartHome: LanguageShortcut | null;
75
+ splitLine: LanguageShortcut | null;
76
+ startNewLine: LanguageShortcut | null;
77
+ startNewLineBefore: LanguageShortcut | null;
78
+ suggestInline: LanguageShortcut | null;
79
+ textEnd: LanguageShortcut | null;
80
+ textStart: LanguageShortcut | null;
81
+ toggleBlockComment: LanguageShortcut | null;
82
+ toggleCase: LanguageShortcut | null;
83
+ toggleColumnMode: LanguageShortcut | null;
84
+ toggleLineComment: LanguageShortcut | null;
85
+ triggerCompletion: LanguageShortcut | null;
86
+ undo: LanguageShortcut | null;
87
+ unselectOccurrence: LanguageShortcut | null;
88
+ wordLeft: LanguageShortcut | null;
89
+ wordRight: LanguageShortcut | null;
90
+ };
91
+ export declare function isShortcut(target: LanguageShortcut | null, event: KeyboardEvent): boolean;
@@ -0,0 +1,12 @@
1
+ import { chordOf, KEYMAP_IDS, parseChord } from '@adecore/editor/keymap';
2
+ import { matchesShortcut } from '@adecore/ui';
3
+ export function shortcutsOf(keymap, apple) {
4
+ const shortcuts = Object.fromEntries(KEYMAP_IDS.map((id) => {
5
+ const text = chordOf(id, apple, keymap);
6
+ return [id, text === null ? null : { ...parseChord(text), apple }];
7
+ }));
8
+ return { ...shortcuts, rename: shortcuts.renameSymbol };
9
+ }
10
+ export function isShortcut(target, event) {
11
+ return target !== null && matchesShortcut(target, event, target.apple);
12
+ }
@@ -0,0 +1,18 @@
1
+ import type { SignatureHelp, SignatureInformation } from '@adecore/lsp';
2
+ export interface SignatureViewModel {
3
+ readonly label: string;
4
+ readonly active: {
5
+ readonly start: number;
6
+ readonly end: number;
7
+ } | null;
8
+ readonly parameterName: string;
9
+ readonly parameterDocumentation: string;
10
+ readonly documentation: string;
11
+ readonly index: number;
12
+ readonly count: number;
13
+ }
14
+ export declare function parameterSpan(signature: SignatureInformation, index: number): {
15
+ start: number;
16
+ end: number;
17
+ } | null;
18
+ export declare function signatureViewOf(help: SignatureHelp | null): SignatureViewModel | null;
@@ -0,0 +1,33 @@
1
+ import { documentationText } from './completion-model.js';
2
+ /* Where a parameter's label stands in the signature's: given as a pair of offsets, or as the text to find in it. */
3
+ export function parameterSpan(signature, index) {
4
+ const parameter = signature.parameters?.[index];
5
+ if (parameter === undefined) {
6
+ return null;
7
+ }
8
+ if (typeof parameter.label !== 'string') {
9
+ const [start, end] = parameter.label;
10
+ return start >= 0 && end <= signature.label.length && start < end ? { start, end } : null;
11
+ }
12
+ const start = signature.label.indexOf(parameter.label);
13
+ return start < 0 ? null : { start, end: start + parameter.label.length };
14
+ }
15
+ export function signatureViewOf(help) {
16
+ if (help === null || help.signatures.length === 0) {
17
+ return null;
18
+ }
19
+ const index = Math.min(Math.max(0, help.activeSignature ?? 0), help.signatures.length - 1);
20
+ const signature = help.signatures[index];
21
+ const parameterIndex = signature.activeParameter ?? help.activeParameter ?? 0;
22
+ const active = parameterSpan(signature, parameterIndex);
23
+ const parameter = signature.parameters?.[parameterIndex];
24
+ return {
25
+ label: signature.label,
26
+ active,
27
+ parameterName: active === null ? '' : signature.label.slice(active.start, active.end).replace(/[?:].*$/s, ''),
28
+ parameterDocumentation: documentationText(parameter?.documentation),
29
+ documentation: documentationText(signature.documentation),
30
+ index,
31
+ count: help.signatures.length
32
+ };
33
+ }
@@ -0,0 +1,22 @@
1
+ import type { EditorLanguage } from './editor-language.ts';
2
+ import { type Timers } from './timers.ts';
3
+ export declare class SignatureFeature {
4
+ private readonly language;
5
+ private readonly timers;
6
+ private timer;
7
+ private request;
8
+ private controller;
9
+ private help;
10
+ private anchor;
11
+ constructor(language: EditorLanguage, timers?: Timers);
12
+ get isOpen(): boolean;
13
+ invoke(): void;
14
+ trigger(): void;
15
+ private cycle;
16
+ close(): void;
17
+ private options;
18
+ private edited;
19
+ private retrigger;
20
+ private ask;
21
+ private fetch;
22
+ }
@@ -0,0 +1,150 @@
1
+ import { signatureViewOf } from './signature-model.js';
2
+ import { isShortcut } from './shortcut-keys.js';
3
+ import { realTimers } from './timers.js';
4
+ const METHOD = 'textDocument/signatureHelp';
5
+ const PAUSE_MS = 60;
6
+ /*
7
+ * The parameters of the call the caret is in, while its arguments are typed. It opens on a trigger
8
+ * character of the server, such as `(` and `,`, and asks again as the caret moves or the text changes,
9
+ * so the active parameter follows; the server answering nothing is the caret leaving the call.
10
+ */
11
+ export class SignatureFeature {
12
+ language;
13
+ timers;
14
+ timer;
15
+ request = 0;
16
+ controller = null;
17
+ help = null;
18
+ anchor = null;
19
+ constructor(language, timers = realTimers) {
20
+ this.language = language;
21
+ this.timers = timers;
22
+ const { editor } = language;
23
+ const offs = [
24
+ editor.onTextChange((change) => this.edited(change)),
25
+ editor.onCaret(() => this.retrigger()),
26
+ editor.onBlur(() => this.close()),
27
+ editor.onKeyDown((event) => {
28
+ if (event.key === 'Escape' && this.isOpen) {
29
+ this.close();
30
+ return true;
31
+ }
32
+ if (isShortcut(this.language.shortcuts.parameterInfo, event)) {
33
+ this.invoke();
34
+ return true;
35
+ }
36
+ if ((event.key === 'ArrowUp' || event.key === 'ArrowDown') && !event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey) {
37
+ return this.cycle(event.key === 'ArrowDown' ? 1 : -1);
38
+ }
39
+ return false;
40
+ })
41
+ ];
42
+ language.onDispose(() => {
43
+ for (const off of offs) {
44
+ off();
45
+ }
46
+ this.close();
47
+ });
48
+ }
49
+ get isOpen() {
50
+ return this.language.popups.getState().signature !== null;
51
+ }
52
+ /* Asks for the parameters of the call at the caret, as its key does. */
53
+ invoke() {
54
+ this.ask({ triggerKind: 1, isRetrigger: false });
55
+ }
56
+ /* Asks for the parameters as if `(` had just been typed, which is what a call opened by a completion is. */
57
+ trigger() {
58
+ this.ask({ triggerKind: 2, triggerCharacter: '(', isRetrigger: false });
59
+ }
60
+ /* Shows the next overload, or the one before, while the card is up and has more than one; false when it has not, which leaves the arrow to the caret. */
61
+ cycle(step) {
62
+ const { help } = this;
63
+ if (help === null || help.signatures.length < 2 || !this.isOpen) {
64
+ return false;
65
+ }
66
+ const count = help.signatures.length;
67
+ const next = { ...help, activeSignature: (Math.min(Math.max(0, help.activeSignature ?? 0), count - 1) + step + count) % count };
68
+ const model = signatureViewOf(next);
69
+ if (model === null || this.anchor === null) {
70
+ return false;
71
+ }
72
+ this.help = next;
73
+ this.language.popups.setState({ signature: { anchor: this.anchor, model } });
74
+ return true;
75
+ }
76
+ close() {
77
+ this.request++;
78
+ this.controller?.abort();
79
+ this.timers.clear(this.timer);
80
+ this.help = null;
81
+ this.anchor = null;
82
+ if (this.isOpen) {
83
+ this.language.popups.setState({ signature: null });
84
+ }
85
+ }
86
+ options() {
87
+ const { project, uri } = this.language;
88
+ const options = project.service.providerOptions(METHOD, uri);
89
+ return options === undefined ? null : { triggers: options.triggerCharacters ?? [], retriggers: options.retriggerCharacters ?? [] };
90
+ }
91
+ edited(change) {
92
+ const options = this.options();
93
+ if (change.source !== 'input' || options === null) {
94
+ this.close();
95
+ return;
96
+ }
97
+ const text = change.changes.length === 1 ? change.changes[0].text : '';
98
+ const last = Array.from(text).at(-1) ?? '';
99
+ if (text.length === 1 && options.triggers.includes(last)) {
100
+ this.ask({ triggerKind: 2, triggerCharacter: last, isRetrigger: this.isOpen });
101
+ }
102
+ else if (this.isOpen) {
103
+ this.ask({
104
+ triggerKind: text.length === 1 && options.retriggers.includes(last) ? 2 : 3,
105
+ ...(options.retriggers.includes(last) ? { triggerCharacter: last } : {}),
106
+ isRetrigger: true
107
+ });
108
+ }
109
+ }
110
+ retrigger() {
111
+ if (this.isOpen) {
112
+ this.ask({ triggerKind: 3, isRetrigger: true });
113
+ }
114
+ }
115
+ ask(context) {
116
+ const { project, uri } = this.language;
117
+ if (!project.service.supports(METHOD, uri)) {
118
+ return;
119
+ }
120
+ this.timers.clear(this.timer);
121
+ const request = ++this.request;
122
+ const full = { ...context, ...(this.help === null ? {} : { activeSignatureHelp: this.help }) };
123
+ this.timer = this.timers.set(() => void this.fetch(full, request), PAUSE_MS);
124
+ }
125
+ async fetch(context, request) {
126
+ const { editor, project, uri } = this.language;
127
+ const caret = editor.getCaret();
128
+ this.controller?.abort();
129
+ const controller = new AbortController();
130
+ this.controller = controller;
131
+ let help;
132
+ try {
133
+ help = await project.service.signatureHelp(uri, caret, context, { signal: controller.signal });
134
+ }
135
+ catch {
136
+ return;
137
+ }
138
+ if (request !== this.request) {
139
+ return;
140
+ }
141
+ const model = signatureViewOf(help);
142
+ if (model === null) {
143
+ this.close();
144
+ return;
145
+ }
146
+ this.help = help;
147
+ this.anchor ??= caret;
148
+ this.language.popups.setState({ signature: { anchor: this.anchor, model } });
149
+ }
150
+ }
@@ -0,0 +1,17 @@
1
+ import type { EditorPosition } from '@adecore/editor';
2
+ import type { EditorLanguage } from './editor-language.ts';
3
+ import type { SnippetStop } from './snippet.ts';
4
+ export declare function positionInInsertion(start: EditorPosition, text: string, offset: number): EditorPosition;
5
+ export declare class SnippetFeature {
6
+ private readonly language;
7
+ private stops;
8
+ private current;
9
+ constructor(language: EditorLanguage);
10
+ get isActive(): boolean;
11
+ begin(start: EditorPosition, text: string, stops: readonly SnippetStop[]): void;
12
+ end(): void;
13
+ private visit;
14
+ private key;
15
+ private edited;
16
+ private moved;
17
+ }
@@ -0,0 +1,110 @@
1
+ import { comparePositions, rangeHolds, shiftRange } from './diagnostics-model.js';
2
+ /* Where an offset of inserted text lands when the text is put in at `start`. */
3
+ export function positionInInsertion(start, text, offset) {
4
+ const lines = text.slice(0, offset).split(/\r\n|\r|\n/);
5
+ return lines.length === 1
6
+ ? { line: start.line, character: start.character + offset }
7
+ : { line: start.line + lines.length - 1, character: lines[lines.length - 1].length };
8
+ }
9
+ /*
10
+ * The tab stops of a snippet that was just inserted. The caret goes to the first one, with its default text
11
+ * selected; Tab goes on to the next and Shift+Tab back, and the last stop (`$0`, or the end of the text)
12
+ * ends it. Escape, a caret that leaves the snippet and any change that is not typing end it too. Typing in
13
+ * a stop does not touch the others: they follow the text, but a mirror of a stop is not edited along.
14
+ */
15
+ export class SnippetFeature {
16
+ language;
17
+ stops = [];
18
+ current = 0;
19
+ constructor(language) {
20
+ this.language = language;
21
+ const { editor } = language;
22
+ const offs = [
23
+ editor.onKeyDown((event) => this.key(event)),
24
+ editor.onTextChange((change) => this.edited(change)),
25
+ editor.onCaret((position) => this.moved(position))
26
+ ];
27
+ language.onDispose(() => {
28
+ for (const off of offs) {
29
+ off();
30
+ }
31
+ });
32
+ }
33
+ get isActive() {
34
+ return this.stops.length > 0;
35
+ }
36
+ /* Puts the caret where the snippet says, and keeps the stops to go through when there is more than the last one. */
37
+ begin(start, text, stops) {
38
+ this.end();
39
+ if (stops.length === 0) {
40
+ return;
41
+ }
42
+ const ranges = stops.map((stop) => ({ start: positionInInsertion(start, text, stop.start), end: positionInInsertion(start, text, stop.end) }));
43
+ if (ranges.length === 1) {
44
+ this.language.editor.setCaret(ranges[0].start);
45
+ return;
46
+ }
47
+ this.stops = ranges;
48
+ this.visit(0);
49
+ }
50
+ end() {
51
+ this.stops = [];
52
+ this.current = 0;
53
+ }
54
+ visit(index) {
55
+ const { editor } = this.language;
56
+ const target = this.stops[index];
57
+ this.current = index;
58
+ if (comparePositions(target.start, target.end) === 0) {
59
+ editor.setCaret(target.start);
60
+ }
61
+ else {
62
+ editor.setSelection(target);
63
+ }
64
+ if (index === this.stops.length - 1) {
65
+ this.end();
66
+ }
67
+ }
68
+ key(event) {
69
+ if (!this.isActive || event.ctrlKey || event.metaKey || event.altKey) {
70
+ return false;
71
+ }
72
+ if (event.key === 'Tab') {
73
+ this.visit(event.shiftKey ? Math.max(0, this.current - 1) : this.current + 1);
74
+ return true;
75
+ }
76
+ if (event.key === 'Escape') {
77
+ this.end();
78
+ return true;
79
+ }
80
+ return false;
81
+ }
82
+ edited(change) {
83
+ if (!this.isActive) {
84
+ return;
85
+ }
86
+ if (change.source !== 'input') {
87
+ this.end();
88
+ return;
89
+ }
90
+ this.stops = this.stops.map((stop) => shiftRange(stop, change.changes));
91
+ }
92
+ /* A caret outside the stretch the stops span is a person moving on. */
93
+ moved(position) {
94
+ if (!this.isActive) {
95
+ return;
96
+ }
97
+ const hull = { start: this.stops[0].start, end: this.stops[this.stops.length - 1].end };
98
+ for (const stop of this.stops) {
99
+ if (comparePositions(stop.start, hull.start) < 0) {
100
+ hull.start = stop.start;
101
+ }
102
+ if (comparePositions(stop.end, hull.end) > 0) {
103
+ hull.end = stop.end;
104
+ }
105
+ }
106
+ if (!rangeHolds(hull, position)) {
107
+ this.end();
108
+ }
109
+ }
110
+ }
@@ -0,0 +1,11 @@
1
+ export interface SnippetStop {
2
+ readonly index: number;
3
+ readonly start: number;
4
+ readonly end: number;
5
+ }
6
+ export interface ParsedSnippet {
7
+ readonly text: string;
8
+ readonly stops: readonly SnippetStop[];
9
+ }
10
+ export declare function parseSnippet(snippet: string): ParsedSnippet;
11
+ export declare function tabOrder(parsed: ParsedSnippet): SnippetStop[];
@@ -0,0 +1,134 @@
1
+ const NAME = '[A-Za-z_]\\w*';
2
+ const TABSTOP = new RegExp(`^\\$(\\d+)|^\\$\\{(\\d+)\\}`);
3
+ const PLACEHOLDER = /^\$\{(\d+):/;
4
+ const CHOICE = /^\$\{(\d+)\|/;
5
+ const DEFAULT_VARIABLE = new RegExp(`^\\$\\{${NAME}:`);
6
+ const TRANSFORMED_VARIABLE = new RegExp(`^\\$\\{${NAME}/`);
7
+ const BARE_VARIABLE = new RegExp(`^\\$${NAME}|^\\$\\{${NAME}\\}`);
8
+ class SnippetParser {
9
+ text = '';
10
+ stops = [];
11
+ at = 0;
12
+ source;
13
+ constructor(source) {
14
+ this.source = source;
15
+ }
16
+ parse() {
17
+ this.sequence(false);
18
+ return { text: this.text, stops: this.stops };
19
+ }
20
+ /* Text up to the `}` that closes the placeholder this is in, which is left for the caller. */
21
+ sequence(nested) {
22
+ while (this.at < this.source.length) {
23
+ const character = this.source[this.at];
24
+ if (character === '}' && nested) {
25
+ return;
26
+ }
27
+ if (character === '\\' && /[\\$}]/.test(this.source[this.at + 1] ?? '')) {
28
+ this.text += this.source[this.at + 1];
29
+ this.at += 2;
30
+ }
31
+ else if (character === '$') {
32
+ this.dollar();
33
+ }
34
+ else {
35
+ this.text += character;
36
+ this.at++;
37
+ }
38
+ }
39
+ }
40
+ dollar() {
41
+ const rest = this.source.slice(this.at);
42
+ const tabstop = TABSTOP.exec(rest);
43
+ const placeholder = PLACEHOLDER.exec(rest);
44
+ const choice = CHOICE.exec(rest);
45
+ if (tabstop !== null) {
46
+ this.at += tabstop[0].length;
47
+ this.stops.push({ index: Number(tabstop[1] ?? tabstop[2]), start: this.text.length, end: this.text.length });
48
+ }
49
+ else if (placeholder !== null) {
50
+ this.at += placeholder[0].length;
51
+ const start = this.text.length;
52
+ this.sequence(true);
53
+ this.closeBrace();
54
+ this.stops.push({ index: Number(placeholder[1]), start, end: this.text.length });
55
+ }
56
+ else if (choice !== null) {
57
+ this.at += choice[0].length;
58
+ const start = this.text.length;
59
+ this.firstChoice();
60
+ this.stops.push({ index: Number(choice[1]), start, end: this.text.length });
61
+ }
62
+ else if (DEFAULT_VARIABLE.test(rest)) {
63
+ this.at += rest.indexOf(':') + 1;
64
+ this.sequence(true);
65
+ this.closeBrace();
66
+ }
67
+ else if (TRANSFORMED_VARIABLE.test(rest)) {
68
+ this.skipBraces();
69
+ }
70
+ else if (BARE_VARIABLE.test(rest)) {
71
+ this.at += BARE_VARIABLE.exec(rest)[0].length;
72
+ }
73
+ else {
74
+ this.text += '$';
75
+ this.at++;
76
+ }
77
+ }
78
+ closeBrace() {
79
+ if (this.source[this.at] === '}') {
80
+ this.at++;
81
+ }
82
+ }
83
+ /* The first of `a,b,c|}`, and the rest dropped. */
84
+ firstChoice() {
85
+ let first = true;
86
+ while (this.at < this.source.length && !(this.source[this.at] === '|' && this.source[this.at + 1] === '}')) {
87
+ const character = this.source[this.at];
88
+ if (character === '\\' && /[\\,|]/.test(this.source[this.at + 1] ?? '')) {
89
+ this.at++;
90
+ if (first) {
91
+ this.text += this.source[this.at];
92
+ }
93
+ }
94
+ else if (character === ',') {
95
+ first = false;
96
+ }
97
+ else if (first) {
98
+ this.text += character;
99
+ }
100
+ this.at++;
101
+ }
102
+ this.at += 2;
103
+ }
104
+ /* Past the `}` that closes a `${name/regex/format/}` variable, which inserts nothing here. */
105
+ skipBraces() {
106
+ let depth = 0;
107
+ while (this.at < this.source.length) {
108
+ const character = this.source[this.at];
109
+ this.at += character === '\\' ? 2 : 1;
110
+ if (character === '{') {
111
+ depth++;
112
+ }
113
+ else if (character === '}' && --depth === 0) {
114
+ return;
115
+ }
116
+ }
117
+ }
118
+ }
119
+ export function parseSnippet(snippet) {
120
+ return new SnippetParser(snippet).parse();
121
+ }
122
+ /* The stops in the order Tab visits them: 1, 2 and on, one per index, then the final stop `$0`, which the end of the text stands in for. */
123
+ export function tabOrder(parsed) {
124
+ const first = new Map();
125
+ for (const stop of parsed.stops) {
126
+ const known = first.get(stop.index);
127
+ if (known === undefined || stop.start < known.start) {
128
+ first.set(stop.index, stop);
129
+ }
130
+ }
131
+ const visited = [...first.values()].filter((stop) => stop.index > 0).sort((left, right) => left.index - right.index);
132
+ const final = first.get(0) ?? { index: 0, start: parsed.text.length, end: parsed.text.length };
133
+ return [...visited, final];
134
+ }
@@ -0,0 +1,6 @@
1
+ import type { Location, WorkspaceSymbolResult } from '@adecore/lsp';
2
+ export declare const SYMBOL_LINK_CLASS = "cursor-pointer hover:text-accent hover:underline";
3
+ export declare function isTypeName(name: string): boolean;
4
+ export declare function linkTypeNames(html: string): string;
5
+ export declare function declaredNameOf(code: string): string | null;
6
+ export declare function placesOfName(result: WorkspaceSymbolResult, name: string, fileText: string): Location[];
@@ -0,0 +1,50 @@
1
+ /* The classes a name in a signature has: a pointer and an underline on hover, and the accent only then, so the signature stays calm. */
2
+ export const SYMBOL_LINK_CLASS = 'cursor-pointer hover:text-accent hover:underline';
3
+ /* A name that reads as a type: it starts with a capital and has a lowercase letter, which leaves `DEFAULT_WEIGHTS` and `T` alone. */
4
+ const TYPE_NAME = /^[A-Z][A-Za-z0-9_]*$/;
5
+ export function isTypeName(name) {
6
+ return TYPE_NAME.test(name) && /[a-z]/.test(name);
7
+ }
8
+ /*
9
+ * Marks the type-like names of a highlighted snippet as links. Shiki writes each token as a span of its own,
10
+ * so a name that is the whole text of a token is one; a string or a comment is never that.
11
+ */
12
+ export function linkTypeNames(html) {
13
+ return html.replace(/>([A-Z][A-Za-z0-9_]*)</g, (match, name) => isTypeName(name) ? `><span data-symbol="${name}" class="${SYMBOL_LINK_CLASS}">${name}</span><` : match);
14
+ }
15
+ const DECLARES = /\b(?:class|interface|enum|trait|type|namespace|struct|function|const|let|var)\s+&?([A-Za-z_$][\w$]*)/;
16
+ /* The name a signature declares, such as `IssueTicketsMessage` in `final class IssueTicketsMessage implements MessageInterface`; null for one that declares nothing, such as a method of a class. */
17
+ export function declaredNameOf(code) {
18
+ return DECLARES.exec(code)?.[1] ?? null;
19
+ }
20
+ /* What a workspace symbol is that a type name can mean. */
21
+ const TYPE_KINDS = new Set([2, 3, 5, 10, 11, 23, 26]);
22
+ /*
23
+ * The places a type name leads to among the symbols a server found for it. Only symbols of exactly that name
24
+ * count, those that are types ahead of the rest, and when several are left the ones whose container (a
25
+ * namespace) the current file names, which is how a file says which `Message` it means.
26
+ */
27
+ export function placesOfName(result, name, fileText) {
28
+ const found = (result ?? []).filter((symbol) => symbol.name === name);
29
+ const types = found.filter((symbol) => TYPE_KINDS.has(symbol.kind));
30
+ let candidates = types.length > 0 ? types : found;
31
+ if (candidates.length > 1) {
32
+ const named = candidates.filter((symbol) => symbol.containerName !== undefined && symbol.containerName !== '' && fileText.includes(symbol.containerName));
33
+ if (named.length > 0) {
34
+ candidates = named;
35
+ }
36
+ }
37
+ const seen = new Set();
38
+ return candidates
39
+ .map((symbol) => 'range' in symbol.location
40
+ ? symbol.location
41
+ : { uri: symbol.location.uri, range: { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } } })
42
+ .filter((location) => {
43
+ const key = `${location.uri}\0${location.range.start.line}`;
44
+ if (seen.has(key)) {
45
+ return false;
46
+ }
47
+ seen.add(key);
48
+ return true;
49
+ });
50
+ }