@adecore/editor-react 0.0.1 → 0.17.0-beta.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 (264) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +55 -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 +15 -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 +4 -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 +29 -0
  44. package/dist/code-actions.js +329 -0
  45. package/dist/code-authors.d.ts +17 -0
  46. package/dist/code-authors.js +80 -0
  47. package/dist/code-vision-model.d.ts +20 -0
  48. package/dist/code-vision-model.js +184 -0
  49. package/dist/code-vision.d.ts +48 -0
  50. package/dist/code-vision.js +234 -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 +464 -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 +64 -0
  68. package/dist/editor-language.js +148 -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 +97 -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 +47 -0
  80. package/dist/holder.d.ts +5 -0
  81. package/dist/holder.js +19 -0
  82. package/dist/host-types.d.ts +43 -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 +261 -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 +73 -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 +22 -0
  103. package/dist/navigation.js +156 -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 +29 -0
  109. package/dist/peek.js +216 -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 +34 -0
  117. package/dist/project-language.js +187 -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 +20 -0
  131. package/dist/rename.js +258 -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 +40 -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 +77 -0
  162. package/dist/symbols.d.ts +11 -0
  163. package/dist/symbols.js +97 -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 +45 -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 +61 -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 +387 -0
  198. package/src/code-authors.ts +104 -0
  199. package/src/code-vision-model.ts +230 -0
  200. package/src/code-vision.ts +267 -0
  201. package/src/completion-call.ts +139 -0
  202. package/src/completion-model.ts +440 -0
  203. package/src/completion.ts +501 -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 +163 -0
  210. package/src/editor-react.css +43 -0
  211. package/src/find-query.ts +74 -0
  212. package/src/folding.ts +112 -0
  213. package/src/highlight-layers.ts +37 -0
  214. package/src/highlights.ts +123 -0
  215. package/src/history.ts +57 -0
  216. package/src/holder.ts +19 -0
  217. package/src/host-types.ts +49 -0
  218. package/src/hover-content.ts +264 -0
  219. package/src/hover.ts +281 -0
  220. package/src/index.ts +63 -0
  221. package/src/inlay-hints.ts +87 -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 +169 -0
  229. package/src/paths.ts +3 -0
  230. package/src/peek-model.ts +147 -0
  231. package/src/peek.ts +248 -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 +220 -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 +291 -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 +48 -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 +86 -0
  258. package/src/symbols.ts +112 -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,97 @@
1
+ import { Refresher } from './refresher.js';
2
+ import { realTimers } from './timers.js';
3
+ const METHOD = 'textDocument/documentSymbol';
4
+ const PAUSE_MS = 500;
5
+ /* The names a breadcrumb and a sticky header know a symbol kind by; a kind left out is not a block worth pinning. */
6
+ const KINDS = {
7
+ 2: 'module',
8
+ 3: 'namespace',
9
+ 4: 'package',
10
+ 5: 'class',
11
+ 6: 'method',
12
+ 9: 'constructor',
13
+ 10: 'enum',
14
+ 11: 'interface',
15
+ 12: 'function',
16
+ 13: 'variable',
17
+ 14: 'constant',
18
+ 23: 'struct'
19
+ };
20
+ function isHierarchical(symbols) {
21
+ return symbols.length > 0 && 'range' in symbols[0];
22
+ }
23
+ function blockOf(name, kind, startLine, endLine) {
24
+ const label = KINDS[kind];
25
+ // A one-line symbol has no body to pin, and a property or an enum member is part of the block around it.
26
+ return label === undefined || endLine <= startLine ? null : { startLine: startLine + 1, endLine: endLine + 1, name, kind: label };
27
+ }
28
+ function walk(symbols, blocks) {
29
+ for (const symbol of symbols) {
30
+ const block = blockOf(symbol.name, symbol.kind, symbol.range.start.line, symbol.range.end.line);
31
+ if (block !== null) {
32
+ blocks.push(block);
33
+ }
34
+ walk(symbol.children ?? [], blocks);
35
+ }
36
+ }
37
+ /* The multi-line symbols of a file as blocks, outermost first, in one-based lines. */
38
+ export function blocksOf(result) {
39
+ const blocks = [];
40
+ if (result === null || result.length === 0) {
41
+ return blocks;
42
+ }
43
+ if (isHierarchical(result)) {
44
+ walk(result, blocks);
45
+ }
46
+ else {
47
+ for (const symbol of result) {
48
+ const block = blockOf(symbol.name, symbol.kind, symbol.location.range.start.line, symbol.location.range.end.line);
49
+ if (block !== null) {
50
+ blocks.push(block);
51
+ }
52
+ }
53
+ }
54
+ return blocks.sort((left, right) => left.startLine - right.startLine || right.endLine - left.endLine);
55
+ }
56
+ /*
57
+ * The symbols of the file as the blocks sticky scroll and the breadcrumb go by, with their real names. Until
58
+ * the first answer, and wherever no server has symbols, the editor reads blocks off the text itself.
59
+ */
60
+ export class SymbolsFeature {
61
+ refresher;
62
+ listeners = new Set();
63
+ constructor(language, timers = realTimers) {
64
+ const { editor, project, uri } = language;
65
+ this.refresher = new Refresher(async (signal) => {
66
+ if (!project.service.supports(METHOD, uri)) {
67
+ return;
68
+ }
69
+ const result = await project.service.documentSymbols(uri, { signal });
70
+ if (!signal.aborted) {
71
+ editor.setBlocks(result === null ? null : blocksOf(result));
72
+ for (const listener of [...this.listeners]) {
73
+ listener(result);
74
+ }
75
+ }
76
+ }, PAUSE_MS, timers);
77
+ const edits = editor.onTextChange(() => this.refresher.later());
78
+ const providers = project.service.onProvidersChanged((changed) => {
79
+ if (changed === uri) {
80
+ this.refresher.now();
81
+ }
82
+ });
83
+ language.onDispose(() => {
84
+ edits();
85
+ providers.dispose();
86
+ this.refresher.dispose();
87
+ editor.setBlocks(null);
88
+ });
89
+ }
90
+ /* Called with every answer that was not replaced before it came. */
91
+ onResult(listener) {
92
+ this.listeners.add(listener);
93
+ return () => {
94
+ this.listeners.delete(listener);
95
+ };
96
+ }
97
+ }
@@ -0,0 +1,57 @@
1
+ import { type LanguageService, type LanguageDocument, type LanguageRequestOptions, type DiagnosticsReport, type ContentChange, type Disposable, type ProviderOptions, type CodeAction, type CodeActionContext, type CodeLens, type Command, type CompletionContext, type CompletionItem, type CompletionResult, type DocumentHighlight, type DocumentSymbolResult, type FoldingRange, type FormattingOptions, type Hover, type InlayHint, type Location, type NavigationResult, type Position, type PrepareRenameResult, type Range, type SelectionRange, type SemanticTokens, type SemanticTokensDelta, type SignatureHelp, type SignatureHelpContext, type TextEdit, type WorkspaceEdit, type WorkspaceSymbolResult } from '@adecore/lsp';
2
+ export { ManualTimers } from './timers.ts';
3
+ export interface LanguageCall {
4
+ method: string;
5
+ uri: string;
6
+ params: unknown;
7
+ options: LanguageRequestOptions;
8
+ }
9
+ export declare class FakeLanguageService implements LanguageService {
10
+ readonly documents: Map<string, LanguageDocument & {
11
+ version: number;
12
+ }>;
13
+ readonly calls: LanguageCall[];
14
+ readonly providers: Map<string, ProviderOptions>;
15
+ private readonly answers;
16
+ private readonly diagnostics;
17
+ private readonly changes;
18
+ respond(method: string, answer: (call: LanguageCall) => unknown, provider?: ProviderOptions): void;
19
+ openDocument(document: LanguageDocument): Promise<void>;
20
+ changeDocument(uri: string, changes: readonly ContentChange[]): Promise<void>;
21
+ closeDocument(uri: string): Promise<void>;
22
+ supports(method: string, _uri: string): boolean;
23
+ providerOptions(method: string, _uri: string): ProviderOptions | undefined;
24
+ onProvidersChanged(listener: (uri: string) => void): Disposable;
25
+ onDiagnostics(listener: (report: DiagnosticsReport) => void): Disposable;
26
+ report(report: DiagnosticsReport): void;
27
+ refresh(uri: string): void;
28
+ private request;
29
+ completion(uri: string, position: Position, context?: CompletionContext, options?: LanguageRequestOptions): Promise<CompletionResult>;
30
+ resolveCompletion(uri: string, item: CompletionItem, options?: LanguageRequestOptions): Promise<CompletionItem>;
31
+ hover(uri: string, position: Position, options?: LanguageRequestOptions): Promise<Hover | null>;
32
+ signatureHelp(uri: string, position: Position, context?: SignatureHelpContext, options?: LanguageRequestOptions): Promise<SignatureHelp | null>;
33
+ definition(uri: string, position: Position, options?: LanguageRequestOptions): Promise<NavigationResult>;
34
+ declaration(uri: string, position: Position, options?: LanguageRequestOptions): Promise<NavigationResult>;
35
+ typeDefinition(uri: string, position: Position, options?: LanguageRequestOptions): Promise<NavigationResult>;
36
+ implementation(uri: string, position: Position, options?: LanguageRequestOptions): Promise<NavigationResult>;
37
+ references(uri: string, position: Position, includeDeclaration?: boolean, options?: LanguageRequestOptions): Promise<Location[] | null>;
38
+ documentHighlights(uri: string, position: Position, options?: LanguageRequestOptions): Promise<DocumentHighlight[] | null>;
39
+ documentSymbols(uri: string, options?: LanguageRequestOptions): Promise<DocumentSymbolResult>;
40
+ prepareRename(uri: string, position: Position, options?: LanguageRequestOptions): Promise<PrepareRenameResult>;
41
+ rename(uri: string, position: Position, newName: string, options?: LanguageRequestOptions): Promise<WorkspaceEdit | null>;
42
+ codeActions(uri: string, range: Range, context?: CodeActionContext, options?: LanguageRequestOptions): Promise<(CodeAction | Command)[] | null>;
43
+ resolveCodeAction(uri: string, action: CodeAction, options?: LanguageRequestOptions): Promise<CodeAction>;
44
+ executeCommand(uri: string, command: Command, options?: LanguageRequestOptions): Promise<unknown>;
45
+ formatting(uri: string, formatting: FormattingOptions, options?: LanguageRequestOptions): Promise<TextEdit[] | null>;
46
+ rangeFormatting(uri: string, range: Range, formatting: FormattingOptions, options?: LanguageRequestOptions): Promise<TextEdit[] | null>;
47
+ semanticTokens(uri: string, options?: LanguageRequestOptions): Promise<SemanticTokens | null>;
48
+ semanticTokensDelta(uri: string, previousResultId: string, options?: LanguageRequestOptions): Promise<SemanticTokens | SemanticTokensDelta | null>;
49
+ semanticTokensRange(uri: string, range: Range, options?: LanguageRequestOptions): Promise<SemanticTokens | null>;
50
+ inlayHints(uri: string, range: Range, options?: LanguageRequestOptions): Promise<InlayHint[] | null>;
51
+ resolveInlayHint(uri: string, hint: InlayHint, options?: LanguageRequestOptions): Promise<InlayHint>;
52
+ workspaceSymbols(uri: string, query: string, options?: LanguageRequestOptions): Promise<WorkspaceSymbolResult>;
53
+ foldingRanges(uri: string, options?: LanguageRequestOptions): Promise<FoldingRange[] | null>;
54
+ selectionRanges(uri: string, positions: readonly Position[], options?: LanguageRequestOptions): Promise<SelectionRange[] | null>;
55
+ codeLenses(uri: string, options?: LanguageRequestOptions): Promise<CodeLens[] | null>;
56
+ resolveCodeLens(uri: string, lens: CodeLens, options?: LanguageRequestOptions): Promise<CodeLens>;
57
+ }
@@ -0,0 +1,156 @@
1
+ import { applyContentChanges, LspError, StaleResultError } from '@adecore/lsp';
2
+ export { ManualTimers } from './timers.js';
3
+ export class FakeLanguageService {
4
+ documents = new Map();
5
+ calls = [];
6
+ providers = new Map();
7
+ answers = new Map();
8
+ diagnostics = new Set();
9
+ changes = new Set();
10
+ respond(method, answer, provider = {}) {
11
+ this.answers.set(method, answer);
12
+ this.providers.set(method, provider);
13
+ }
14
+ async openDocument(document) {
15
+ const held = this.documents.get(document.uri);
16
+ this.documents.set(document.uri, { ...document, version: held === undefined ? 1 : held.version + (held.text === document.text ? 0 : 1) });
17
+ this.calls.push({ method: 'open', uri: document.uri, params: document, options: {} });
18
+ await this.answers.get('open')?.(this.calls.at(-1));
19
+ }
20
+ async changeDocument(uri, changes) {
21
+ const held = this.documents.get(uri);
22
+ if (held === undefined) {
23
+ throw new LspError('Document is not open', -32002);
24
+ }
25
+ held.text = applyContentChanges(held.text, changes);
26
+ held.version++;
27
+ this.calls.push({ method: 'change', uri, params: changes, options: {} });
28
+ }
29
+ async closeDocument(uri) {
30
+ this.documents.delete(uri);
31
+ this.calls.push({ method: 'close', uri, params: null, options: {} });
32
+ }
33
+ supports(method, _uri) {
34
+ return this.providers.has(method);
35
+ }
36
+ providerOptions(method, _uri) {
37
+ return this.providers.get(method);
38
+ }
39
+ onProvidersChanged(listener) {
40
+ this.changes.add(listener);
41
+ return { dispose: () => this.changes.delete(listener) };
42
+ }
43
+ onDiagnostics(listener) {
44
+ this.diagnostics.add(listener);
45
+ return { dispose: () => this.diagnostics.delete(listener) };
46
+ }
47
+ report(report) {
48
+ for (const listener of this.diagnostics) {
49
+ listener(report);
50
+ }
51
+ }
52
+ refresh(uri) {
53
+ for (const listener of this.changes) {
54
+ listener(uri);
55
+ }
56
+ }
57
+ async request(method, uri, params, options = {}) {
58
+ if (!this.supports(method, uri)) {
59
+ throw new LspError(`${method} is unavailable`, -32601);
60
+ }
61
+ options.signal?.throwIfAborted();
62
+ const version = this.documents.get(uri)?.version;
63
+ const call = { method, uri, params, options };
64
+ this.calls.push(call);
65
+ const result = await this.answers.get(method)?.(call);
66
+ options.signal?.throwIfAborted();
67
+ if (this.documents.get(uri)?.version !== version) {
68
+ throw new StaleResultError(uri);
69
+ }
70
+ return result;
71
+ }
72
+ completion(uri, position, context, options = {}) {
73
+ return this.request('textDocument/completion', uri, { position, context }, options);
74
+ }
75
+ resolveCompletion(uri, item, options = {}) {
76
+ return this.request('completionItem/resolve', uri, item, options);
77
+ }
78
+ hover(uri, position, options = {}) {
79
+ return this.request('textDocument/hover', uri, { position }, options);
80
+ }
81
+ signatureHelp(uri, position, context, options = {}) {
82
+ return this.request('textDocument/signatureHelp', uri, { position, context }, options);
83
+ }
84
+ definition(uri, position, options = {}) {
85
+ return this.request('textDocument/definition', uri, { position }, options);
86
+ }
87
+ declaration(uri, position, options = {}) {
88
+ return this.request('textDocument/declaration', uri, { position }, options);
89
+ }
90
+ typeDefinition(uri, position, options = {}) {
91
+ return this.request('textDocument/typeDefinition', uri, { position }, options);
92
+ }
93
+ implementation(uri, position, options = {}) {
94
+ return this.request('textDocument/implementation', uri, { position }, options);
95
+ }
96
+ references(uri, position, includeDeclaration, options = {}) {
97
+ return this.request('textDocument/references', uri, { position, context: { includeDeclaration } }, options);
98
+ }
99
+ documentHighlights(uri, position, options = {}) {
100
+ return this.request('textDocument/documentHighlight', uri, { position }, options);
101
+ }
102
+ documentSymbols(uri, options = {}) {
103
+ return this.request('textDocument/documentSymbol', uri, {}, options);
104
+ }
105
+ prepareRename(uri, position, options = {}) {
106
+ return this.request('textDocument/prepareRename', uri, { position }, options);
107
+ }
108
+ rename(uri, position, newName, options = {}) {
109
+ return this.request('textDocument/rename', uri, { position, newName }, options);
110
+ }
111
+ codeActions(uri, range, context, options = {}) {
112
+ return this.request('textDocument/codeAction', uri, { range, context }, options);
113
+ }
114
+ resolveCodeAction(uri, action, options = {}) {
115
+ return this.request('codeAction/resolve', uri, action, options);
116
+ }
117
+ executeCommand(uri, command, options = {}) {
118
+ return this.request('workspace/executeCommand', uri, command, options);
119
+ }
120
+ formatting(uri, formatting, options = {}) {
121
+ return this.request('textDocument/formatting', uri, { options: formatting }, options);
122
+ }
123
+ rangeFormatting(uri, range, formatting, options = {}) {
124
+ return this.request('textDocument/rangeFormatting', uri, { range, options: formatting }, options);
125
+ }
126
+ semanticTokens(uri, options = {}) {
127
+ return this.request('textDocument/semanticTokens/full', uri, {}, options);
128
+ }
129
+ semanticTokensDelta(uri, previousResultId, options = {}) {
130
+ return this.request('textDocument/semanticTokens/full/delta', uri, { previousResultId }, options);
131
+ }
132
+ semanticTokensRange(uri, range, options = {}) {
133
+ return this.request('textDocument/semanticTokens/range', uri, { range }, options);
134
+ }
135
+ inlayHints(uri, range, options = {}) {
136
+ return this.request('textDocument/inlayHint', uri, { range }, options);
137
+ }
138
+ resolveInlayHint(uri, hint, options = {}) {
139
+ return this.request('inlayHint/resolve', uri, hint, options);
140
+ }
141
+ workspaceSymbols(uri, query, options = {}) {
142
+ return this.request('workspace/symbol', uri, { query }, options);
143
+ }
144
+ foldingRanges(uri, options = {}) {
145
+ return this.request('textDocument/foldingRange', uri, {}, options);
146
+ }
147
+ selectionRanges(uri, positions, options = {}) {
148
+ return this.request('textDocument/selectionRange', uri, { positions }, options);
149
+ }
150
+ codeLenses(uri, options = {}) {
151
+ return this.request('textDocument/codeLens', uri, {}, options);
152
+ }
153
+ resolveCodeLens(uri, lens, options = {}) {
154
+ return this.request('codeLens/resolve', uri, lens, options);
155
+ }
156
+ }
@@ -0,0 +1,14 @@
1
+ export interface Timers {
2
+ set(callback: () => void, delay: number): unknown;
3
+ clear(handle: unknown): void;
4
+ }
5
+ export declare const realTimers: Timers;
6
+ export declare class ManualTimers implements Timers {
7
+ private now;
8
+ private next;
9
+ private readonly pending;
10
+ set(callback: () => void, delay: number): unknown;
11
+ clear(handle: unknown): void;
12
+ advance(milliseconds: number): void;
13
+ get size(): number;
14
+ }
package/dist/timers.js ADDED
@@ -0,0 +1,34 @@
1
+ export const realTimers = {
2
+ set: (callback, delay) => setTimeout(callback, delay),
3
+ clear: (handle) => clearTimeout(handle)
4
+ };
5
+ /* Runs what was scheduled when a test says how much time has passed. */
6
+ export class ManualTimers {
7
+ now = 0;
8
+ next = 1;
9
+ pending = new Map();
10
+ set(callback, delay) {
11
+ const id = this.next++;
12
+ this.pending.set(id, { at: this.now + delay, callback });
13
+ return id;
14
+ }
15
+ clear(handle) {
16
+ this.pending.delete(handle);
17
+ }
18
+ advance(milliseconds) {
19
+ const until = this.now + milliseconds;
20
+ for (;;) {
21
+ const due = [...this.pending].filter(([, entry]) => entry.at <= until).sort((left, right) => left[1].at - right[1].at)[0];
22
+ if (due === undefined) {
23
+ break;
24
+ }
25
+ this.now = due[1].at;
26
+ this.pending.delete(due[0]);
27
+ due[1].callback();
28
+ }
29
+ this.now = until;
30
+ }
31
+ get size() {
32
+ return this.pending.size;
33
+ }
34
+ }
@@ -0,0 +1,3 @@
1
+ import type { EditorLanguage } from './editor-language.ts';
2
+ import type { ProblemCounts } from './diagnostics.ts';
3
+ export declare function useProblemCounts(language: EditorLanguage | null): ProblemCounts;
@@ -0,0 +1,20 @@
1
+ import { useMemo, useSyncExternalStore } from 'react';
2
+ const NONE = { error: 0, warning: 0, info: 0 };
3
+ function countsStore(language) {
4
+ let current = language?.diagnostics.counts() ?? NONE;
5
+ return {
6
+ get: () => {
7
+ const next = language?.diagnostics.counts() ?? NONE;
8
+ if (next.error !== current.error || next.warning !== current.warning || next.info !== current.info) {
9
+ current = next;
10
+ }
11
+ return current;
12
+ },
13
+ subscribe: (listener) => language?.diagnostics.onChange(listener) ?? (() => undefined)
14
+ };
15
+ }
16
+ /* How many errors, warnings and infos the file has, said again when the servers report. */
17
+ export function useProblemCounts(language) {
18
+ const store = useMemo(() => countsStore(language), [language]);
19
+ return useSyncExternalStore(store.subscribe, store.get);
20
+ }
@@ -0,0 +1,21 @@
1
+ import type { EditorFolds, FoldRole } from '@adecore/editor';
2
+ export interface RevealLineRequest {
3
+ readonly line: number;
4
+ }
5
+ export interface ViewState {
6
+ readonly scrollTop: number;
7
+ readonly line: number;
8
+ readonly column: number;
9
+ readonly folds: EditorFolds;
10
+ }
11
+ export declare const viewStates: Map<string, ViewState>;
12
+ export declare function openingPlace(reveal: RevealLineRequest | null, last: ViewState | undefined, placeholderScroll: number, foldDefaults: readonly FoldRole[]): {
13
+ line?: number;
14
+ column?: number;
15
+ scrollTop?: number;
16
+ folds?: EditorFolds;
17
+ foldDefaults?: readonly FoldRole[];
18
+ };
19
+ export declare function followViewStates(endpointId: string, from: string, to: string): void;
20
+ export declare function viewStateKey(key: string): string;
21
+ export declare function forgetMovesFrom(key: string): void;
@@ -0,0 +1,63 @@
1
+ function endpointKey(namespace, path) {
2
+ return `${namespace}:${path}`;
3
+ }
4
+ /* Where each file's editor was when it went, so switching tabs or a node scrolling back into view opens it there again. Per window, never stored. */
5
+ export const viewStates = new Map();
6
+ /*
7
+ * A line asked for wins; otherwise where this file's editor last was, and for a file opened the first time, where the
8
+ * viewer before it was scrolled and with the kinds of fold the settings choose folded. What was folded comes back
9
+ * whichever way it opens.
10
+ */
11
+ export function openingPlace(reveal, last, placeholderScroll, foldDefaults) {
12
+ const folds = last === undefined ? { foldDefaults } : { folds: last.folds };
13
+ if (reveal !== null) {
14
+ return { line: reveal.line, ...folds };
15
+ }
16
+ if (last !== undefined) {
17
+ return { line: last.line, column: last.column, scrollTop: last.scrollTop, ...folds };
18
+ }
19
+ return { scrollTop: placeholderScroll, ...folds };
20
+ }
21
+ /* Where a place the editors know by the key it had goes now: a file, or the files of a folder that moved. */
22
+ const moves = [];
23
+ function movedKey(key, from, to) {
24
+ if (key === from) {
25
+ return to;
26
+ }
27
+ return key.startsWith(`${from}/`) ? `${to}${key.slice(from.length)}` : null;
28
+ }
29
+ /*
30
+ * A file or folder moved, so where its editors were follows it. An editor that is still open writes its place once
31
+ * more under the key it mounted with as it closes, which `viewStateKey` sends to the new one; a new editor on the
32
+ * old path forgets the move.
33
+ */
34
+ export function followViewStates(endpointId, from, to) {
35
+ const oldKey = endpointKey(endpointId, from);
36
+ const newKey = endpointKey(endpointId, to);
37
+ for (const [key, state] of [...viewStates]) {
38
+ const target = movedKey(key, oldKey, newKey);
39
+ if (target !== null) {
40
+ viewStates.delete(key);
41
+ viewStates.set(target, state);
42
+ }
43
+ }
44
+ moves.push({ from: oldKey, to: newKey });
45
+ }
46
+ /* The key an editor that mounted under `key` leaves its place under. */
47
+ export function viewStateKey(key) {
48
+ for (const { from, to } of moves) {
49
+ const target = movedKey(key, from, to);
50
+ if (target !== null) {
51
+ return target;
52
+ }
53
+ }
54
+ return key;
55
+ }
56
+ /* An editor mounts under `key`, which is a file of its own again whatever moved away from there. */
57
+ export function forgetMovesFrom(key) {
58
+ for (let i = moves.length - 1; i >= 0; i--) {
59
+ if (movedKey(key, moves[i].from, moves[i].to) !== null) {
60
+ moves.splice(i, 1);
61
+ }
62
+ }
63
+ }
@@ -0,0 +1 @@
1
+ export declare function replacedWords(removed: readonly string[], added: readonly string[]): Array<Array<[number, number]>>;
@@ -0,0 +1,32 @@
1
+ import { diffLines } from '@adecore/merge';
2
+ /* A word, a run of spaces or one other character, so a change inside a line is found by word and not by letter. */
3
+ const TOKEN = /\w+|\s+|[^\w\s]/g;
4
+ /*
5
+ * The character ranges of each removed line that the added lines changed, `[start, end)` per line, for
6
+ * the stronger tint on the words a change replaced. Lines are only compared when as many were added as
7
+ * removed, since then each removed line has the line it became; any other change has no such pairing
8
+ * and draws no words.
9
+ */
10
+ export function replacedWords(removed, added) {
11
+ return removed.map((line, index) => (removed.length === added.length ? replacedRanges(line, added[index]) : []));
12
+ }
13
+ function replacedRanges(removed, added) {
14
+ const before = removed.match(TOKEN) ?? [];
15
+ const after = added.match(TOKEN) ?? [];
16
+ const starts = [];
17
+ let offset = 0;
18
+ for (const token of before) {
19
+ starts.push(offset);
20
+ offset += token.length;
21
+ }
22
+ starts.push(offset);
23
+ const ranges = [];
24
+ for (const change of diffLines(before, after)) {
25
+ const text = removed.slice(starts[change.baseStart], starts[change.baseEnd]);
26
+ if (text.trim() !== '') {
27
+ const start = starts[change.baseStart] + (text.length - text.trimStart().length);
28
+ ranges.push([start, start + text.trim().length]);
29
+ }
30
+ }
31
+ return ranges;
32
+ }
@@ -0,0 +1,21 @@
1
+ import type { Editor } from '@adecore/editor';
2
+ import { type ApplyWorkspaceEditResult, type RenameFile, type TextEdit, type WorkspaceEdit } from '@adecore/lsp';
3
+ import type { DiskText } from './host-types.ts';
4
+ export interface StagedFile {
5
+ readonly path: string;
6
+ readonly disk: DiskText;
7
+ readonly text: string;
8
+ }
9
+ export interface ProjectFiles {
10
+ read(path: string): Promise<DiskText | null>;
11
+ stage(files: readonly StagedFile[]): void;
12
+ save(files: readonly StagedFile[]): Promise<string | null>;
13
+ rename(from: string, to: string): Promise<string | null>;
14
+ }
15
+ export interface WorkspaceEditHost {
16
+ editorOf(uri: string): Editor | undefined;
17
+ readonly files: ProjectFiles | null;
18
+ }
19
+ export declare function entriesOf(edit: WorkspaceEdit): Map<string, TextEdit[][]> | null;
20
+ export declare function renamesOf(edit: WorkspaceEdit): RenameFile[];
21
+ export declare function applyWorkspaceEdit(edit: WorkspaceEdit, host: WorkspaceEditHost): Promise<ApplyWorkspaceEditResult>;