/* Copyright 2026 Marimo. All rights reserved. */ import { type LanguageName, langs, loadLanguage, } from "@uiw/codemirror-extensions-langs"; import ReactCodeMirror, { type Extension, type ReactCodeMirrorProps, } from "@uiw/react-codemirror"; import React, { useMemo } from "react"; import { CopyClipboardIcon } from "@/components/icons/copy-icon"; import type { ResolvedTheme } from "@/theme/useTheme"; import { Logger } from "@/utils/Logger"; import { ErrorBanner } from "../common/error-banner"; export const LANGUAGE_MAP: Record = { python: "py", python3: "py", javascript: "js", typescript: "ts", shell: "sh", bash: "sh", // Other fallbacks unknown: "text", undefined: "text", }; function isSupportedLanguage( language: string | undefined, ): language is LanguageName { if (!language) { return false; } return language in langs; } /** * A code editor that supports any language. * * This lives in a separate file because we want to lazy load the additional * language support. */ const AnyLanguageCodeMirror: React.FC< ReactCodeMirrorProps & { language: string | undefined; hideUnsupportedLanguageErrors?: boolean; theme: ResolvedTheme; showCopyButton?: boolean; } > = ({ language, hideUnsupportedLanguageErrors, showCopyButton, extensions = [], ...props }) => { // Maybe normalize the language to the extension language = LANGUAGE_MAP[language || ""] || language; const isSupported = isSupportedLanguage(language); if (!isSupported) { Logger.warn(`Language ${language} not found in CodeMirror.`); } const finalExtensions = useMemo((): Extension[] => { if (!isSupportedLanguage(language)) { return extensions; } return [loadLanguage(language), ...extensions].filter(Boolean); }, [language, extensions]); return (
{!isSupported && !hideUnsupportedLanguageErrors && ( )} {showCopyButton && isSupported && ( )}
); }; export default AnyLanguageCodeMirror;