@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,38 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useTranslation } from 'react-i18next';
3
+ import { CircleX, Info, TriangleAlert } from 'lucide-react';
4
+ import { fileUriToPath } from '@adecore/lsp';
5
+ import { Button, Icon, Tooltip } from '@adecore/ui';
6
+ import { formatNumber } from '@adecore/ui/format';
7
+ import { basenameOf } from './paths.js';
8
+ import { codeLabelOf, severityOf } from './diagnostics-model.js';
9
+ import { SymbolSections } from './HoverSections.js';
10
+ const SEVERITY_ICONS = { error: CircleX, warning: TriangleAlert, info: Info, hint: Info };
11
+ const SEVERITY_COLORS = { error: 'text-status-error', warning: 'text-status-needs-you', info: 'text-status-running', hint: 'text-text-muted' };
12
+ function ProblemSection({ problem, language }) {
13
+ const { t } = useTranslation('editor');
14
+ const { diagnostic } = problem;
15
+ const severity = severityOf(diagnostic);
16
+ const label = diagnostic.source === undefined ? [codeLabelOf(diagnostic), problem.server].filter((part) => part !== '').join(' · ') : codeLabelOf(diagnostic);
17
+ const supported = language.codeActions.supported;
18
+ return (_jsxs("div", { className: "flex flex-col gap-1.5 px-3 py-2.5", children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx(Icon, { icon: SEVERITY_ICONS[severity], size: 14, className: `mt-px shrink-0 ${SEVERITY_COLORS[severity]}` }), _jsx("div", { className: "min-w-0 text-xs/[18px] break-words whitespace-pre-wrap select-text", children: diagnostic.message })] }), label !== '' && _jsx("div", { className: "pl-[22px] text-xs text-text-muted select-text", children: label }), diagnostic.relatedInformation?.map((related, index) => {
19
+ const path = fileUriToPath(related.location.uri);
20
+ return (_jsxs("div", { className: "pl-[22px] text-xs text-text-muted", children: [path !== null && (_jsxs("button", { type: "button", className: "font-mono text-accent hover:underline", onClick: () => language.goTo(related.location), children: [basenameOf(path), ":", formatNumber(related.location.range.start.line + 1)] })), ' ', related.message] }, index));
21
+ }), _jsx("div", { className: "pt-1 pl-[22px]", children: _jsx(Tooltip, { label: t(supported ? 'language.hover.quickFix' : 'language.hover.quickFixNone'), children: _jsx(Button, { size: "xs", variant: "secondary", "aria-disabled": !supported, onClick: () => {
22
+ if (supported) {
23
+ language.hover.hide();
24
+ void language.codeActions.quickFixFor(problem);
25
+ }
26
+ }, children: t('language.hover.quickFix') }) }) })] }));
27
+ }
28
+ export function HoverCard({ language, problems, info, anchor, position }) {
29
+ const { t } = useTranslation('editor');
30
+ const place = info?.definition === null || info === null ? null : fileUriToPath(info.definition.uri);
31
+ return (_jsxs("div", { className: "flex w-max min-w-[280px] max-w-[min(520px,calc(100vw-16px))] flex-col divide-y divide-border", children: [info !== null && (_jsxs(_Fragment, { children: [_jsx(SymbolSections, { text: info.text, onName: (name, declared) => {
32
+ language.hover.hide();
33
+ void language.navigation.goToName(name, anchor, name === (declared ?? info.word) ? info.definition : null);
34
+ } }), _jsxs("div", { className: "flex items-center gap-3 px-3 py-1.5 text-xs", children: [info.definition !== null && (_jsx("button", { type: "button", className: "text-accent hover:underline", onClick: () => language.goTo(info.definition), children: t('language.hover.definition') })), info.references !== null && info.references > 0 && (_jsx("button", { type: "button", className: "text-accent hover:underline", onClick: () => {
35
+ language.hover.hide();
36
+ void language.peek.open(position);
37
+ }, children: t('language.hover.references', { count: info.references, formatted: formatNumber(info.references) }) })), place !== null && info.definition !== null && (_jsxs("span", { className: "ml-auto font-mono text-text-faint", children: [basenameOf(place), ":", formatNumber(info.definition.range.start.line + 1)] }))] })] })), problems.map((problem, index) => (_jsx(ProblemSection, { problem: problem, language: language }, index)))] }));
38
+ }
@@ -0,0 +1,21 @@
1
+ import { type DocTag, type HoverSection, type HoverText } from './hover-content.ts';
2
+ export declare function Signature({ code, language, onName }: {
3
+ code: string;
4
+ language: string;
5
+ onName?: (name: string) => void;
6
+ }): import("react").JSX.Element;
7
+ export declare function DocTags({ tags }: {
8
+ tags: readonly DocTag[];
9
+ }): import("react").JSX.Element;
10
+ export declare function SymbolSection({ section, onName }: {
11
+ section: HoverSection;
12
+ onName?: (name: string, declared: string | null) => void;
13
+ }): import("react").JSX.Element;
14
+ export declare function SymbolSections({ text, onName }: {
15
+ text: HoverText;
16
+ onName?: (name: string, declared: string | null) => void;
17
+ }): import("react").JSX.Element;
18
+ export declare function SourceLine({ text, language }: {
19
+ text: string;
20
+ language: string;
21
+ }): import("react").JSX.Element;
@@ -0,0 +1,82 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEditorRendering } from './rendering-context.js';
3
+ import { useEffect, useState } from 'react';
4
+ import { Markdown } from './Markdown.js';
5
+ import { Tooltip } from '@adecore/ui';
6
+ import { hoverSectionsOf, markdownParts } from './hover-content.js';
7
+ import { BaselineStatus } from './BaselineStatus.js';
8
+ import { declaredNameOf, linkTypeNames } from './symbol-links.js';
9
+ /* Code as the viewer colors it, plain until the grammar is in so the card never changes size under the pointer by much. */
10
+ export function Signature({ code, language, onName }) {
11
+ const { theme, highlight } = useEditorRendering();
12
+ const [html, setHtml] = useState(null);
13
+ const key = `${language}\0${theme}\0${code}\0${onName !== undefined}`;
14
+ // Only whether there are links goes into the highlighting; the handler is read when a name is pressed.
15
+ const linked = onName !== undefined;
16
+ useEffect(() => {
17
+ if (highlight === undefined) {
18
+ return;
19
+ }
20
+ let alive = true;
21
+ highlight(code, language, theme)
22
+ .then((result) => alive && setHtml({ key, html: linked ? linkTypeNames(result) : result }))
23
+ .catch(() => undefined);
24
+ return () => {
25
+ alive = false;
26
+ };
27
+ }, [code, language, theme, key, linked, highlight]);
28
+ const className = 'font-mono text-code break-words whitespace-pre-wrap [&_.line]:block [&_pre]:m-0 [&_pre]:bg-transparent! [&_pre]:whitespace-pre-wrap [&_code]:font-mono';
29
+ // One handler for every name, since the names are part of the highlighted HTML and not elements of ours.
30
+ const follow = (event) => {
31
+ const name = event.target.closest('[data-symbol]')?.dataset.symbol;
32
+ if (name !== undefined && onName !== undefined) {
33
+ onName(name);
34
+ }
35
+ };
36
+ return html?.key === key ? (_jsx("div", { className: className, onClick: follow, dangerouslySetInnerHTML: { __html: html.html } })) : (_jsx("div", { className: className, children: code }));
37
+ }
38
+ /* A docblock's tags as rows: the tag once beside each run of the same tag, so a list of parameters reads as one. */
39
+ export function DocTags({ tags }) {
40
+ return (_jsx("div", { className: "grid grid-cols-[auto_minmax(0,1fr)] gap-x-3 gap-y-0.5 text-xs/[18px] select-text", children: tags.map((tag, index) => (_jsxs("div", { className: "contents", children: [_jsx("span", { className: "font-mono text-text-faint", children: tags[index - 1]?.name === tag.name ? '' : `@${tag.name}` }), _jsx("div", { className: "min-w-0 break-words text-text-muted [&_.adecore-editor-markdown]:text-xs [&_code]:bg-transparent! [&_code]:p-0! [&_p]:m-0", children: _jsx(Markdown, { text: tag.markdown }) })] }, index))) }));
41
+ }
42
+ /* One symbol: its qualified name muted, its signature, the prose and examples as compact blocks, and its tags as rows. Names in the signature are links only where `onName` says what a press does. */
43
+ export function SymbolSection({ section, onName }) {
44
+ const declared = declaredNameOf(section.signatures[0]?.code ?? '');
45
+ const follow = onName === undefined ? undefined : (name) => onName(name, declared);
46
+ return (_jsxs("div", { className: "flex flex-col gap-1.5 px-3 py-2", children: [section.title !== null && _jsx("div", { className: "truncate font-mono text-xs text-text-faint select-text", children: section.title }), section.signatures.map((block, index) => (_jsx(Signature, { code: block.code, language: block.language, onName: follow }, index))), section.baseline !== null && _jsx(BaselineStatus, { baseline: section.baseline }), markdownParts(section.markdown).map((part, index) => part.kind === 'code' ? (_jsx("div", { className: "rounded-md bg-surface-sunken px-2 py-1.5", children: _jsx(Signature, { code: part.block.code, language: part.block.language, onName: follow }) }, index)) : (_jsx("div", { className: "text-text-muted select-text [&_.adecore-editor-markdown]:text-xs [&_p]:my-1", children: _jsx(Markdown, { text: part.text }) }, index))), section.tags.length > 0 && _jsx(DocTags, { tags: section.tags })] }));
47
+ }
48
+ /* What a server says about a symbol as the sections of a hover card, shared by the hover and the documentation beside the suggestions. */
49
+ export function SymbolSections({ text, onName }) {
50
+ return (_jsx(_Fragment, { children: hoverSectionsOf(text).map((section, index) => (_jsx(SymbolSection, { section: section, onName: onName }, index))) }));
51
+ }
52
+ /* PHP highlights only after its opening tag, which is put in front for the grammar and taken off again. */
53
+ function phpWithoutOpenTag(html) {
54
+ return html.replace(/<span[^>]*>&#x3C;\?<\/span><span[^>]*>php<\/span>(<span[^>]*>) /, '$1');
55
+ }
56
+ /*
57
+ * Where a suggestion comes from, such as `use Raxos\Database\Orm\Attribute\PrimaryKey`, on one line that gives
58
+ * way from its start, so the end of the name stays readable and a word is never cut in two. The full text is in the tooltip.
59
+ */
60
+ export function SourceLine({ text, language }) {
61
+ const { theme, highlight } = useEditorRendering();
62
+ const [html, setHtml] = useState(null);
63
+ const highlighted = language === 'php' && text.startsWith('use ');
64
+ const key = `${theme}\0${text}`;
65
+ useEffect(() => {
66
+ if (!highlighted) {
67
+ return;
68
+ }
69
+ if (highlight === undefined) {
70
+ return;
71
+ }
72
+ let alive = true;
73
+ highlight(`<?php ${text}`, 'php', theme)
74
+ .then((result) => alive && setHtml({ key, html: phpWithoutOpenTag(result) }))
75
+ .catch(() => undefined);
76
+ return () => {
77
+ alive = false;
78
+ };
79
+ }, [highlighted, text, theme, key, highlight]);
80
+ const className = 'block overflow-hidden text-left font-mono text-code text-ellipsis whitespace-nowrap [direction:rtl] [&_code]:font-mono [&_pre]:m-0 [&_pre]:inline [&_pre]:bg-transparent! [&_pre]:p-0 [&_code]:inline [&_.line]:inline';
81
+ return (_jsx(Tooltip, { label: text, children: highlighted && html?.key === key ? (_jsx("div", { className: className, children: _jsx("bdi", { dangerouslySetInnerHTML: { __html: html.html } }) })) : (_jsx("div", { className: `${className} text-text-muted`, children: _jsx("bdi", { children: text }) })) }));
82
+ }
@@ -0,0 +1,6 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { EditorLanguage } from './editor-language.ts';
3
+ export declare function LanguagePopups({ language, contextMenuItems }: {
4
+ language: EditorLanguage;
5
+ contextMenuItems?: (language: EditorLanguage) => ReactNode;
6
+ }): import("react").JSX.Element;
@@ -0,0 +1,38 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useReducer } from 'react';
3
+ import { useStore } from 'zustand';
4
+ import { AnchoredPopup } from './AnchoredPopup.js';
5
+ import { CodeAuthorsCard } from './CodeAuthorsCard.js';
6
+ import { CompletionPopup } from './CompletionPopup.js';
7
+ import { EditorContextMenu } from './EditorContextMenu.js';
8
+ import { HoverCard } from './HoverCard.js';
9
+ import { PeekPanel } from './PeekPanel.js';
10
+ import { PickPopup } from './PickPopup.js';
11
+ import { RenameCard } from './RenameCard.js';
12
+ import { SymbolPicker } from './SymbolPicker.js';
13
+ import { SignatureCard } from './SignatureCard.js';
14
+ /*
15
+ * Every card a language feature opens over the editor. Each feature keeps its own state and these
16
+ * only draw it, placed by the screen position of the character it belongs to, which is read again
17
+ * whenever the editor scrolls or resizes. `contextMenuItems` adds rows of the app to the context menu.
18
+ */
19
+ export function LanguagePopups({ language, contextMenuItems }) {
20
+ const [, redraw] = useReducer((count) => count + 1, 0);
21
+ const hover = useStore(language.popups, (state) => state.hover);
22
+ const completion = useStore(language.popups, (state) => state.completion);
23
+ const signature = useStore(language.popups, (state) => state.signature);
24
+ const pick = useStore(language.popups, (state) => state.pick);
25
+ const rename = useStore(language.popups, (state) => state.rename);
26
+ const peek = useStore(language.popups, (state) => state.peek);
27
+ const symbols = useStore(language.popups, (state) => state.symbols);
28
+ const menu = useStore(language.popups, (state) => state.menu);
29
+ const authors = useStore(language.popups, (state) => state.authors);
30
+ useEffect(() => language.editor.onViewChange(redraw), [language]);
31
+ const hoverRect = hover === null ? null : language.editor.rectAt(hover.anchor);
32
+ const completionRect = completion === null ? null : language.editor.rectAt(completion.anchor);
33
+ const signatureRect = signature === null ? null : language.editor.rectAt(signature.anchor);
34
+ const pickRect = pick === null ? null : language.editor.rectAt(pick.anchor);
35
+ const renameRect = rename === null ? null : language.editor.rectAt(rename.range.start);
36
+ const renameEnd = rename === null ? null : language.editor.rectAt(rename.range.end);
37
+ return (_jsxs(_Fragment, { children: [hover !== null && hoverRect !== null && (_jsx(AnchoredPopup, { rect: hoverRect, onPointerEnter: () => language.hover.holdCard(true), onPointerLeave: () => language.hover.holdCard(false), children: _jsx(HoverCard, { language: language, problems: hover.problems, info: hover.info, anchor: hover.anchor, position: hover.position }) })), signature !== null && signatureRect !== null && (_jsx(AnchoredPopup, { rect: signatureRect, placement: { prefer: 'above' }, children: _jsx(SignatureCard, { model: signature.model }) })), completion !== null && completionRect !== null && _jsx(CompletionPopup, { language: language, view: completion, rect: completionRect }), pick !== null && pickRect !== null && _jsx(PickPopup, { language: language, view: pick, rect: pickRect }), menu !== null && (_jsx(EditorContextMenu, { language: language, view: menu, children: contextMenuItems?.(language) })), symbols !== null && _jsx(SymbolPicker, { language: language, view: symbols }), peek !== null && _jsx(PeekPanel, { language: language, view: peek }), authors !== null && _jsx(CodeAuthorsCard, { language: language, view: authors }), rename !== null && renameRect !== null && _jsx(RenameCard, { language: language, view: rename, rect: renameRect, endRect: renameEnd })] }));
38
+ }
@@ -0,0 +1,3 @@
1
+ export declare function Markdown({ text }: {
2
+ text: string;
3
+ }): import("react").JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import ReactMarkdown from 'react-markdown';
3
+ import remarkGfm from 'remark-gfm';
4
+ import remarkBreaks from 'remark-breaks';
5
+ export function Markdown({ text }) {
6
+ return (_jsx("div", { className: "adecore-editor-markdown", children: _jsx(ReactMarkdown, { remarkPlugins: [remarkGfm, remarkBreaks], children: text }) }));
7
+ }
@@ -0,0 +1,4 @@
1
+ export declare function PathText({ path, className }: {
2
+ path: string;
3
+ className?: string;
4
+ }): import("react").JSX.Element | null;
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Tooltip } from '@adecore/ui';
3
+ /*
4
+ * A path that gives way from its start, so the end of it, which names the place, stays readable. The
5
+ * full path is in the tooltip. The `bdi` keeps the slashes in reading order inside the right-to-left box.
6
+ */
7
+ export function PathText({ path, className = '' }) {
8
+ if (path === '') {
9
+ return null;
10
+ }
11
+ return (_jsx(Tooltip, { label: path, children: _jsx("span", { className: `min-w-0 overflow-hidden text-left text-ellipsis whitespace-nowrap [direction:rtl] ${className}`, children: _jsx("bdi", { children: path }) }) }));
12
+ }
@@ -0,0 +1,6 @@
1
+ import type { EditorLanguage } from './editor-language.ts';
2
+ import type { PeekView } from './popups.ts';
3
+ export declare function PeekPanel({ language, view }: {
4
+ language: EditorLanguage;
5
+ view: PeekView;
6
+ }): import("react").ReactPortal | null;
@@ -0,0 +1,111 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEditorRendering } from './rendering-context.js';
3
+ import { useEffect, useMemo, useRef, useState } from 'react';
4
+ import { createPortal } from 'react-dom';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { ChevronDown, ChevronRight, X } from 'lucide-react';
7
+ import { fileUriToPath } from '@adecore/lsp';
8
+ import { FileIcon, IconButton, Tooltip } from '@adecore/ui';
9
+ import { formatNumber } from '@adecore/ui/format';
10
+ import { basenameOf } from './paths.js';
11
+ import { shikiLanguageOfPath } from './language-ids.js';
12
+ import { PathText } from './PathText.js';
13
+ import { PEEK_HEIGHT } from './peek.js';
14
+ import { distinguishingFolders, visualColumnOf } from './peek-model.js';
15
+ const TAB_SIZE = 4;
16
+ const CODE = 'font-mono text-(length:--code-font-size) [tab-size:4] [&_.line]:block [&_.line]:h-(--code-line-height) [&_.line]:leading-(--code-line-height) [&_pre]:m-0 [&_pre]:bg-transparent! [&_pre]:p-0 [&_pre]:whitespace-pre [&_code]:font-mono';
17
+ /* The code around the active reference, drawn like the editor's own rows: its gutter, the line the reference is on and the name marked. */
18
+ function Preview({ view }) {
19
+ const { theme, highlight } = useEditorRendering();
20
+ const scroller = useRef(null);
21
+ const { preview } = view;
22
+ const [html, setHtml] = useState(null);
23
+ const path = preview === null ? null : fileUriToPath(preview.uri);
24
+ const key = preview === null ? '' : `${theme}\0${path}\0${preview.text}`;
25
+ useEffect(() => {
26
+ if (preview === null) {
27
+ return;
28
+ }
29
+ if (highlight === undefined) {
30
+ return;
31
+ }
32
+ let alive = true;
33
+ highlight(preview.text, shikiLanguageOfPath(path ?? preview.uri), theme)
34
+ .then((result) => alive && setHtml({ key, html: result }))
35
+ .catch(() => undefined);
36
+ return () => {
37
+ alive = false;
38
+ };
39
+ }, [preview, path, theme, key, highlight]);
40
+ useEffect(() => {
41
+ scroller.current?.scrollTo(0, 0);
42
+ }, [preview]);
43
+ if (preview === null) {
44
+ return _jsx("div", { className: "min-w-0 flex-1" });
45
+ }
46
+ const lines = preview.text.split('\n');
47
+ const name = preview.name === null ? null : columnsOf(lines[preview.active] ?? '', preview.name);
48
+ return (_jsx("div", { ref: scroller, className: "relative min-w-0 flex-1 overflow-auto pt-1", children: _jsxs("div", { className: "relative flex min-w-max", children: [_jsx("div", { className: "absolute inset-x-0 bg-surface-hover", style: { top: `calc(${preview.active} * var(--code-line-height))`, height: 'var(--code-line-height)' } }), _jsx("div", { className: "sticky left-0 z-10 w-16 shrink-0 pr-4 text-right font-mono text-(length:--code-font-size) text-text-faint select-none", children: lines.map((_, index) => (_jsx("div", { className: `h-(--code-line-height) leading-(--code-line-height) ${index === preview.active ? 'bg-surface-hover text-text' : 'bg-surface'}`, children: preview.startLine + index + 1 }, index))) }), _jsxs("div", { className: "relative", children: [name !== null && (_jsx("div", { className: "absolute rounded-sm bg-(--editor-occurrence) font-mono text-(length:--code-font-size)", style: {
49
+ top: `calc(${preview.active} * var(--code-line-height))`,
50
+ height: 'var(--code-line-height)',
51
+ left: `${name.start}ch`,
52
+ width: `${name.end - name.start}ch`
53
+ } })), html?.key === key ? (_jsx("div", { className: `relative ${CODE}`, dangerouslySetInnerHTML: { __html: html.html } })) : (_jsx("div", { className: `relative ${CODE} whitespace-pre`, children: preview.text }))] })] }) }));
54
+ }
55
+ /* The name's stretch of a line in monospace columns, which is what the code is laid out in. */
56
+ function columnsOf(line, name) {
57
+ const start = visualColumnOf(line, name.start, TAB_SIZE);
58
+ return { start, end: visualColumnOf(line, name.end, TAB_SIZE) };
59
+ }
60
+ function relativeTo(path, folder) {
61
+ return path.startsWith(`${folder}/`) ? path.slice(folder.length + 1) : path;
62
+ }
63
+ function folderOf(path) {
64
+ return path.slice(0, Math.max(0, path.lastIndexOf('/')));
65
+ }
66
+ /* A line of code with its name in the text color and the rest muted. */
67
+ function PlaceText({ place }) {
68
+ if (place.text === '') {
69
+ return _jsx("span", { className: "min-w-0 truncate text-text-muted", children: `:${place.line + 1}` });
70
+ }
71
+ const { name } = place;
72
+ if (name === null) {
73
+ return _jsx("span", { className: "min-w-0 truncate text-text-muted", children: place.text });
74
+ }
75
+ return (_jsxs("span", { className: "min-w-0 truncate text-text-muted", children: [place.text.slice(0, name.start), _jsx("span", { className: "font-semibold text-text", children: place.text.slice(name.start, name.end) }), place.text.slice(name.end)] }));
76
+ }
77
+ function Places({ language, view }) {
78
+ const { t } = useTranslation('editor');
79
+ const activeRow = useRef(null);
80
+ const folder = language.project.folder;
81
+ const paths = useMemo(() => view.files.map((file) => relativeTo(fileUriToPath(file.uri) ?? file.uri, folder)), [view.files, folder]);
82
+ const folders = useMemo(() => distinguishingFolders(paths), [paths]);
83
+ useEffect(() => {
84
+ activeRow.current?.scrollIntoView({ block: 'nearest' });
85
+ }, [view.active]);
86
+ return (_jsx("div", { className: "w-1/3 shrink-0 overflow-y-auto border-l border-border py-1", children: view.files.map((file, fileIndex) => {
87
+ const collapsed = view.collapsed.includes(file.uri);
88
+ return (_jsxs("div", { children: [_jsx(Tooltip, { label: paths[fileIndex], side: "left", children: _jsxs("div", { className: "flex h-[22px] items-center pr-3 pl-1 text-xs", children: [_jsx(IconButton, { icon: collapsed ? ChevronRight : ChevronDown, size: "2xs", label: t(collapsed ? 'language.peek.expand' : 'language.peek.collapse'), onClick: () => language.peek.toggleFile(file.uri) }), _jsx(FileIcon, { path: paths[fileIndex], size: 14, className: "ml-0.5 shrink-0" }), _jsx("span", { className: "ml-1 max-w-full shrink-0 truncate", children: basenameOf(paths[fileIndex]) }), _jsx("span", { className: "ml-1.5 shrink-0 text-text-muted", children: formatNumber(file.places.length) }), folders[fileIndex] !== '' && _jsx("span", { className: "ml-2 min-w-0 flex-1 truncate text-text-faint", children: folders[fileIndex] })] }) }), !collapsed &&
89
+ file.places.map((place) => (_jsx("button", { ref: place.id === view.active ? activeRow : undefined, type: "button", "data-active": place.id === view.active, className: "flex h-[22px] w-full items-center pr-3 pl-9 text-left font-mono text-code hover:bg-surface-hover data-[active=true]:bg-accent-soft", onClick: () => (place.id === view.active ? language.peek.go(place.id) : language.peek.select(place.id)), onDoubleClick: () => language.peek.go(place.id), children: _jsx(PlaceText, { place: place }) }, place.id)))] }, file.uri));
90
+ }) }));
91
+ }
92
+ /*
93
+ * The references of a name drawn in the row the editor leaves under the line: the file of the active
94
+ * reference and where it is, the code around it on the left and every place on the right. It lives in a
95
+ * portal into the editor's own row, which the editor makes again when it scrolls back into view. It has
96
+ * the editor's own ground and no edge of its own, so it reads as part of the file.
97
+ */
98
+ export function PeekPanel({ language, view }) {
99
+ const { t } = useTranslation('editor');
100
+ const active = view.files.flatMap((file) => file.places).find((place) => place.id === view.active);
101
+ const path = active === undefined ? null : fileUriToPath(active.location.uri);
102
+ const folder = language.project.folder;
103
+ const shown = path === null ? '' : folderOf(relativeTo(path, folder));
104
+ if (view.container === null) {
105
+ return null;
106
+ }
107
+ return createPortal(_jsxs("div", { className: "flex flex-col bg-surface pt-[2px] font-sans text-xs text-text", style: { height: PEEK_HEIGHT }, onPointerDown: (event) => event.preventDefault(), children: [_jsx("div", { className: "peek-hatch h-[14px]" }), _jsxs("div", { className: "flex h-[31px] shrink-0 items-center gap-2 border-b border-border pr-2 pl-3 text-sm", children: [_jsx("span", { className: "shrink-0 font-medium", children: path === null ? '' : basenameOf(path) }), _jsx(PathText, { path: shown, className: "text-text-muted" }), _jsxs("div", { className: "ml-auto flex shrink-0 items-center gap-1", children: [_jsx("span", { className: "text-text-muted", children: t(view.kind === 'definitions' ? 'language.peek.definitions' : 'language.peek.references', {
108
+ count: view.count,
109
+ formatted: formatNumber(view.count)
110
+ }) }), _jsx(IconButton, { icon: X, size: "xs", label: t('language.peek.close'), onClick: () => language.peek.close() })] })] }), _jsxs("div", { className: "flex h-[180px] shrink-0", children: [_jsx(Preview, { view: view }), _jsx(Places, { language: language, view: view })] }), _jsx("div", { className: "peek-hatch h-[14px]" })] }), view.container);
111
+ }
@@ -0,0 +1,8 @@
1
+ import type { EditorRect } from '@adecore/editor';
2
+ import type { EditorLanguage } from './editor-language.ts';
3
+ import type { PickView } from './popups.ts';
4
+ export declare function PickPopup({ language, view, rect }: {
5
+ language: EditorLanguage;
6
+ view: PickView;
7
+ rect: EditorRect;
8
+ }): import("react").ReactPortal;
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useLayoutEffect, useRef } from 'react';
3
+ import { createPortal } from 'react-dom';
4
+ import { PathText } from './PathText.js';
5
+ import { placePopup } from './popup-placement.js';
6
+ const CARD = 'fixed top-0 left-0 z-(--z-popup) flex w-[420px] flex-col overflow-hidden rounded-lg border border-border bg-surface-raised text-text shadow-(--float-shadow)';
7
+ function PreviewLines({ preview }) {
8
+ return (_jsxs("div", { className: "flex flex-col gap-px border-t border-border p-1 font-mono text-code", children: [preview.removed.map((line, index) => (_jsxs("div", { className: "flex gap-2 rounded-sm bg-status-error/10 px-1.5 whitespace-pre", children: [_jsx("span", { className: "text-status-error", children: "\u2212" }), _jsx("span", { className: "min-w-0 truncate", children: line })] }, `-${index}`))), preview.added.map((line, index) => (_jsxs("div", { className: "flex gap-2 rounded-sm bg-status-idle/10 px-1.5 whitespace-pre", children: [_jsx("span", { className: "text-status-idle", children: "+" }), _jsx("span", { className: "min-w-0 truncate", children: line })] }, `+${index}`))), preview.note !== null && _jsx("div", { className: "px-1.5 font-sans text-xs text-text-muted", children: preview.note }), preview.refusal !== undefined && (_jsx("div", { role: "alert", className: "px-1.5 py-0.5 font-sans text-xs text-status-error", children: preview.refusal }))] }));
9
+ }
10
+ /*
11
+ * The list of an editor's pick feature under the character it belongs to. A press on a row never takes
12
+ * the focus from the editor, so the keys keep going to it while the list is open.
13
+ */
14
+ export function PickPopup({ language, view, rect }) {
15
+ const card = useRef(null);
16
+ const activeRow = useRef(null);
17
+ // Every render: the character moves with the scroll, and the list changes size with its preview.
18
+ useLayoutEffect(() => {
19
+ const element = card.current;
20
+ if (element === null) {
21
+ return;
22
+ }
23
+ const placed = placePopup(rect, { width: element.offsetWidth, height: element.offsetHeight }, { width: window.innerWidth, height: window.innerHeight }, { gap: 2 });
24
+ element.style.left = `${Math.round(placed.left)}px`;
25
+ element.style.top = `${Math.round(placed.top)}px`;
26
+ element.style.maxHeight = `${Math.floor(placed.maxHeight)}px`;
27
+ element.style.visibility = 'visible';
28
+ });
29
+ useLayoutEffect(() => {
30
+ activeRow.current?.scrollIntoView({ block: 'nearest' });
31
+ }, [view.active]);
32
+ return createPortal(_jsxs("div", { ref: card, className: CARD, style: { visibility: 'hidden' }, onPointerDown: (event) => event.preventDefault(), children: [view.title !== null && _jsx("div", { className: "border-b border-border px-3 py-1.5 text-xs text-text-muted", children: view.title }), _jsx("div", { role: "listbox", className: "min-h-0 flex-1 overflow-y-auto p-1", children: view.groups.map((group, groupIndex) => (_jsxs("div", { role: "group", className: "flex flex-col", children: [group.title !== null && _jsx("div", { className: "px-2 pt-1.5 pb-0.5 text-xs font-medium text-text-faint", children: group.title }), group.rows.map((row) => (_jsxs("button", { ref: row.id === view.active ? activeRow : undefined, type: "button", role: "option", "aria-selected": row.id === view.active, "data-active": row.id === view.active, className: "flex h-7 w-full items-center gap-2 rounded-md px-2 text-left text-xs cursor-row", onClick: () => language.pick.choose(row.id), children: [_jsx("span", { className: row.path ? 'shrink-0' : 'min-w-0 truncate', children: row.label }), row.path ? (_jsx(PathText, { path: row.detail, className: "ml-auto font-mono text-text-faint" })) : (_jsx("span", { className: "ml-auto shrink-0 font-mono text-text-faint", children: row.id === view.active && row.detail === '' ? '↵' : row.detail }))] }, row.id)))] }, group.title ?? groupIndex))) }), view.preview !== null && _jsx(PreviewLines, { preview: view.preview })] }), document.body);
33
+ }
@@ -0,0 +1,9 @@
1
+ import type { EditorRect } from '@adecore/editor';
2
+ import type { EditorLanguage } from './editor-language.ts';
3
+ import type { RenameView } from './popups.ts';
4
+ export declare function RenameCard({ language, view, rect, endRect }: {
5
+ language: EditorLanguage;
6
+ view: RenameView;
7
+ rect: EditorRect;
8
+ endRect: EditorRect | null;
9
+ }): import("react").ReactPortal;
@@ -0,0 +1,108 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useLayoutEffect, useRef, useState } from 'react';
3
+ import { createPortal } from 'react-dom';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { Sparkles } from 'lucide-react';
6
+ import { fileUriToPath } from '@adecore/lsp';
7
+ import { Button, Icon } from '@adecore/ui';
8
+ import { formatNumber } from '@adecore/ui/format';
9
+ import { basenameOf } from './paths.js';
10
+ import { AnchoredPopup } from './AnchoredPopup.js';
11
+ const CARD_ATTRIBUTE = 'data-rename-card';
12
+ /* Whether the focus moved to something that belongs to the rename, which is not leaving it. */
13
+ function staysInside(target) {
14
+ return target instanceof Element && target.closest(`[${CARD_ATTRIBUTE}]`) !== null;
15
+ }
16
+ /*
17
+ * Suggested names from the host, in a list under the input; a press fills the input and the
18
+ * typed name stays what Enter applies. While it is up the card says only its two keys.
19
+ */
20
+ function Suggestions({ names, active, onPick }) {
21
+ const { t } = useTranslation('editor');
22
+ return (_jsxs("div", { className: "flex w-[250px] flex-col p-1.25", children: [_jsxs("div", { className: "flex items-center gap-1.5 px-2.25 pt-1.25 pb-1 text-xs font-semibold text-text-faint", children: [_jsx(Icon, { icon: Sparkles, size: 12, className: "shrink-0" }), t('language.rename.suggestions')] }), names.map((name, index) => (_jsxs("button", { type: "button", tabIndex: -1, className: `flex h-6.5 items-center gap-2 rounded-md px-2.25 text-left font-mono text-code text-text ${index === active ? 'bg-accent/20' : 'hover:bg-surface-hover'}`, onClick: () => onPick(name), children: [_jsx(Icon, { icon: Sparkles, size: 12, className: `shrink-0 ${index === active ? 'text-accent' : 'text-text-faint'}` }), name] }, name))), _jsxs("div", { className: "mt-1 flex gap-3 border-t border-border px-2.25 pt-1.75 pb-0.75 text-xs text-text-muted", children: [_jsxs("span", { children: ["\u21B5 ", t('language.rename.rename')] }), _jsxs("span", { children: ["\u21E5 ", t('language.rename.useSuggestion')] })] })] }));
23
+ }
24
+ function Hints({ view, active, onPick }) {
25
+ const { t } = useTranslation('editor');
26
+ const { occurrences } = view;
27
+ return (_jsxs("div", { className: "flex flex-col", children: [view.error !== null && (_jsx("div", { role: "alert", className: "max-w-[420px] px-2.5 py-1.5 text-xs text-status-error", children: view.error })), view.suggestions.length > 0 ? (_jsx(Suggestions, { names: view.suggestions, active: active, onPick: onPick })) : (_jsxs("div", { className: "flex items-center gap-3 px-2.5 py-1.5 text-xs whitespace-nowrap text-text-muted", children: [occurrences !== null && (_jsx("span", { children: occurrences.files > 1
28
+ ? t('language.rename.occurrencesFiles', {
29
+ count: occurrences.count,
30
+ formatted: formatNumber(occurrences.count),
31
+ files: formatNumber(occurrences.files)
32
+ })
33
+ : t('language.rename.occurrences', { count: occurrences.count, formatted: formatNumber(occurrences.count) }) })), _jsxs("span", { children: ["\u21B5 ", t('language.rename.rename')] }), _jsxs("span", { children: ["\u21E7\u21B5 ", t('language.rename.preview')] })] }))] }));
34
+ }
35
+ function Preview({ language, view }) {
36
+ const { t } = useTranslation('editor');
37
+ const folder = language.project.folder;
38
+ return (_jsxs("div", { className: "flex w-[520px] max-w-full flex-col", onPointerDown: (event) => event.preventDefault(), children: [_jsx("div", { className: "max-h-[320px] overflow-y-auto", children: view.files.map((file) => {
39
+ const path = fileUriToPath(file.uri);
40
+ const shown = path === null ? file.uri : path.startsWith(`${folder}/`) ? path.slice(folder.length + 1) : path;
41
+ return (_jsxs("div", { className: "border-b border-border last:border-b-0", children: [_jsxs("div", { className: "flex items-center gap-2 px-3 py-1.5 text-xs", children: [_jsx("span", { className: "font-medium", children: path === null ? shown : basenameOf(path) }), _jsx("span", { className: "min-w-0 truncate text-text-faint", children: shown })] }), _jsx("div", { className: "flex flex-col gap-px px-1 pb-1.5 font-mono text-code", children: file.rows.map((row) => (_jsxs("div", { className: "flex flex-col gap-px", children: [_jsxs("div", { className: "flex gap-2 rounded-sm bg-status-error/10 px-1.5 whitespace-pre", children: [_jsx("span", { className: "w-8 shrink-0 text-right text-text-faint", children: row.line }), _jsx("span", { className: "min-w-0 truncate", children: row.before })] }), _jsxs("div", { className: "flex gap-2 rounded-sm bg-status-idle/10 px-1.5 whitespace-pre", children: [_jsx("span", { className: "w-8 shrink-0" }), _jsx("span", { className: "min-w-0 truncate", children: row.after })] })] }, row.line))) })] }, file.uri));
42
+ }) }), _jsxs("div", { className: "flex items-center justify-end gap-2 border-t border-border p-2", children: [_jsx(Button, { size: "xs", variant: "ghost", onClick: () => language.rename.cancel(), children: t('language.rename.cancel') }), _jsx(Button, { size: "xs", variant: "primary", onClick: () => void language.rename.confirm(), children: t('language.rename.apply') })] })] }));
43
+ }
44
+ /*
45
+ * The rename of a symbol: an input over the symbol itself, and under it either the count of its places with
46
+ * the keys, or after Shift+Enter the lines the rename would change. The input keeps the focus the whole time,
47
+ * so Enter and Escape mean the same in both, and the focus leaving the card means the person went elsewhere.
48
+ */
49
+ export function RenameCard({ language, view, rect, endRect }) {
50
+ const input = useRef(null);
51
+ const [value, setValue] = useState(view.placeholder);
52
+ const [active, setActive] = useState(0);
53
+ useLayoutEffect(() => {
54
+ input.current?.focus();
55
+ input.current?.select();
56
+ }, []);
57
+ function fill(name) {
58
+ setValue(name);
59
+ language.rename.edited();
60
+ input.current?.focus();
61
+ }
62
+ function onKeyDown(event) {
63
+ const names = view.phase === 'input' ? view.suggestions : [];
64
+ if (names.length > 0 && !event.nativeEvent.isComposing && !event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey) {
65
+ const step = event.key === 'ArrowDown' ? 1 : event.key === 'ArrowUp' ? -1 : 0;
66
+ if (step !== 0 || event.key === 'Tab') {
67
+ event.preventDefault();
68
+ event.stopPropagation();
69
+ if (event.key === 'Tab') {
70
+ fill(names[Math.min(active, names.length - 1)]);
71
+ }
72
+ else {
73
+ setActive((active + step + names.length) % names.length);
74
+ }
75
+ return;
76
+ }
77
+ }
78
+ if (event.key === 'Escape') {
79
+ event.preventDefault();
80
+ event.stopPropagation();
81
+ language.rename.cancel();
82
+ }
83
+ else if (event.key === 'Enter' && !event.nativeEvent.isComposing) {
84
+ event.preventDefault();
85
+ event.stopPropagation();
86
+ if (view.phase === 'preview') {
87
+ void language.rename.confirm();
88
+ }
89
+ else if (!view.busy) {
90
+ void language.rename.submit(value, event.shiftKey);
91
+ }
92
+ }
93
+ }
94
+ const symbolWidth = endRect === null ? 0 : Math.max(0, endRect.left - rect.left);
95
+ return createPortal(_jsxs(_Fragment, { children: [_jsx("input", { ref: input, [CARD_ATTRIBUTE]: '', value: view.phase === 'preview' ? view.name : value, readOnly: view.phase === 'preview' || view.busy, spellCheck: false, autoComplete: "off", "aria-label": view.original, className: "fixed z-(--z-popup) rounded-sm border border-accent bg-surface px-1 font-mono text-code text-text shadow-(--float-shadow) outline-none [field-sizing:content]", style: {
96
+ left: Math.round(rect.left) - 5,
97
+ top: Math.round(rect.top) - 2,
98
+ height: Math.round(rect.bottom - rect.top) + 4,
99
+ minWidth: Math.round(symbolWidth) + 10
100
+ }, onChange: (event) => {
101
+ setValue(event.target.value);
102
+ language.rename.edited();
103
+ }, onKeyDown: onKeyDown, onBlur: (event) => {
104
+ if (!staysInside(event.relatedTarget)) {
105
+ language.rename.cancel();
106
+ }
107
+ } }), _jsx(AnchoredPopup, { rect: { ...rect, top: rect.top - 2, bottom: rect.bottom + 2 }, className: "overflow-hidden", placement: { gap: 2 }, children: _jsx("div", { [CARD_ATTRIBUTE]: '', onPointerDown: (event) => event.preventDefault(), children: view.phase === 'preview' ? _jsx(Preview, { language: language, view: view }) : _jsx(Hints, { view: view, active: active, onPick: fill }) }) })] }), document.body);
108
+ }
@@ -0,0 +1,4 @@
1
+ import type { SignatureViewModel } from './signature-model.ts';
2
+ export declare function SignatureCard({ model }: {
3
+ model: SignatureViewModel;
4
+ }): import("react").JSX.Element;
@@ -0,0 +1,8 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Markdown } from './Markdown.js';
3
+ /* The signature with the parameter being typed underlined, and what is said about that parameter below it. */
4
+ export function SignatureCard({ model }) {
5
+ const { label, active } = model;
6
+ const documentation = model.parameterDocumentation !== '' ? model.parameterDocumentation : model.documentation;
7
+ return (_jsxs("div", { className: "flex w-max max-w-[min(560px,calc(100vw-16px))] min-w-[240px] flex-col divide-y divide-border", children: [_jsxs("div", { className: "px-3 py-2 font-mono text-code break-words whitespace-pre-wrap text-text-muted", children: [active === null ? (label) : (_jsxs(_Fragment, { children: [label.slice(0, active.start), _jsx("span", { className: "font-semibold text-text underline decoration-2 underline-offset-2", children: label.slice(active.start, active.end) }), label.slice(active.end)] })), model.count > 1 && (_jsxs("span", { className: "ml-2 font-sans text-xs text-text-faint", children: [model.index + 1, "/", model.count] }))] }), documentation !== '' && (_jsxs("div", { className: "flex items-baseline gap-1.5 px-3 py-2 text-xs text-text-muted select-text [&_.adecore-editor-markdown]:text-xs [&_.adecore-editor-markdown_p]:m-0", children: [model.parameterName !== '' && model.parameterDocumentation !== '' && (_jsxs(_Fragment, { children: [_jsx("span", { className: "shrink-0 font-semibold text-text", children: model.parameterName }), _jsx("span", { "aria-hidden": true, children: "\u00B7" })] })), _jsx("div", { className: "min-w-0", children: _jsx(Markdown, { text: documentation }) })] }))] }));
8
+ }
@@ -0,0 +1,6 @@
1
+ import type { EditorLanguage } from './editor-language.ts';
2
+ import type { SymbolsView } from './popups.ts';
3
+ export declare function SymbolPicker({ language, view }: {
4
+ language: EditorLanguage;
5
+ view: SymbolsView;
6
+ }): import("react").ReactPortal;