import type { LucideIcon } from 'lucide-react' import type { CSSProperties, ReactElement } from 'react' import type { GroupStyle, Inline, SegmentGroup } from './types' import { useContext, useState } from 'react' import { DisplayNameContext, shownName } from './displayNames' import { KIND_ICON_COMPONENTS, KIND_ICONS } from './kindIcons' import { isCurrent, segmentOccurrence, sourceOf } from './occurrences' import { HighlightContext } from './useOccurrences' /** * 按词性给每一段上色,颜色在 codeReader.css 的 .cr-role-* 里按主题切换,带自己颜色(color)的段用它(.cr-colored);带源码下标的名字记在 data-offset 上 * (讲别处名字的段记那个名字的下标,见 sourceOf),跳到定义、高亮各处出现用;是眼下高亮着的名字的一处出现时 data-occurrence 标出底色,是当前那一处(点中的、用键盘跳到的)时 * 再标 data-current。起了中文名的名字显示中文名(data-name),悬停看原名,定义处本身在后面淡淡写原名;下标照旧,跳到定义、高亮照常。 * 框起来的一组词画成小框,框里的词同样处理。 */ export function SegmentText({ segments }: { segments: Inline[] }): ReactElement { const highlight = useContext(HighlightContext) const names = useContext(DisplayNameContext) return ( <> {segments.map((piece, index) => { if (!('role' in piece)) return const shown = shownName(piece, names) return ( {shown ? shown.name : piece.text} {shown?.definition && {piece.text}} ) })} ) } /** * 框的样式 → 框头图标:遍历同「循环」块、按条件同「条件」块、不渲染同「返回」块的图标;条件的一支、小标签、类名不带图标。 * 颜色在 codeReader.css 的 .cr-group[data-style] 里,框自己给了颜色(color)时用它。 */ const GROUP_ICONS: Record = { iteration: KIND_ICON_COMPONENTS[KIND_ICONS.loop], condition: KIND_ICON_COMPONENTS[KIND_ICONS.if], branch: undefined, skip: KIND_ICON_COMPONENTS[KIND_ICONS.return], tag: undefined, classes: undefined, } /** 框自己的颜色,暗色、亮色两个都写在 style 上,CSS 按主题挑一个(见 .cr-group[data-colored])。 */ function groupColor(group: SegmentGroup): CSSProperties | undefined { return group.color && { '--group-dark': group.color.dark, '--group-light': group.color.light } as CSSProperties } /** * 框起来的一组词:嵌在前后文字里的小框,放不下时整个挪到下一行,比一行还长时在框里换行。框头是图标和 label,只有图标、只有字、都没有都行; * 收起来的类名另见 ClassesChip。 */ function GroupBox({ group }: { group: SegmentGroup }): ReactElement { if (group.style === 'classes') return const Icon = GROUP_ICONS[group.style] return ( {(Icon || group.label) && ( {Icon && } {group.label} )} ) } /** 收起来的一串类名(「样式 N 个类」):悬停看全部类名,点一下在它下面弹出、盖住后面的字,再点收起。 */ function ClassesChip({ group }: { group: SegmentGroup }): ReactElement { const [open, setOpen] = useState(false) return ( {open && {group.detail}} ) }