Skip to content

Commit

Permalink
fix: auto code
Browse files Browse the repository at this point in the history
  • Loading branch information
qinluhe committed Jul 5, 2024
1 parent a869c43 commit ed82d18
Show file tree
Hide file tree
Showing 18 changed files with 248 additions and 205 deletions.
5 changes: 4 additions & 1 deletion frontend/appflowy_web_app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@
<meta name="twitter:site" content="@appflowy" />
<meta name="twitter:creator" content="@appflowy" />
<link href="https://fonts.googleapis.com/css2?family=Noto+Color+Emoji&display=swap" rel="stylesheet">

</head>
<body id="body">
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js"></script>

<script>
document.addEventListener('DOMContentLoaded', () => {
const userAgent = window.navigator.userAgent.toLowerCase();
Expand Down Expand Up @@ -64,5 +64,8 @@
}
});
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.26.0/prism.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.26.0/plugins/autoloader/prism-autoloader.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/highlight.min.js"></script>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as Y from 'yjs';

export interface DatabaseContextState {
readOnly: boolean;
isDark?: boolean;
databaseDoc: YDoc;
viewId: string;
rowDocMap: Y.Map<YDoc>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ import { DatabaseContextProvider } from './DatabaseContext';

export interface Database2Props extends ViewMetaProps {
doc: YDoc;
isDark?: boolean;
getViewRowsMap?: (viewId: string, rowIds: string[]) => Promise<{ rows: Y.Map<YDoc>; destroy: () => void }>;
loadView?: (viewId: string) => Promise<YDoc>;
navigateToView?: (viewId: string) => Promise<void>;
loadViewMeta?: (viewId: string) => Promise<ViewMeta>;
}

function Database({ doc, getViewRowsMap, navigateToView, loadViewMeta, loadView, ...viewMeta }: Database2Props) {
function Database({ doc, isDark, getViewRowsMap, navigateToView, loadViewMeta, loadView, ...viewMeta }: Database2Props) {
const [search, setSearch] = useSearchParams();

const viewId = search.get('v') || viewMeta.viewId;
Expand Down Expand Up @@ -73,6 +74,7 @@ function Database({ doc, getViewRowsMap, navigateToView, loadViewMeta, loadView,
<div className={'flex w-full justify-center'}>
<Suspense fallback={<ComponentLoading />}>
<DatabaseContextProvider
isDark={isDark}
isDatabaseRowPage={!!rowId}
navigateToRow={handleNavigateToRow}
viewId={viewId}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import React, { useCallback, useContext, useEffect, useState } from 'react';
export function DatabaseRowSubDocument({ rowId }: { rowId: string }) {
const meta = useRowMetaSelector(rowId);
const documentId = meta?.documentId;
const isDark = useContext(DatabaseContext)?.isDark || false;
const loadView = useContext(DatabaseContext)?.loadView;
const getViewRowsMap = useContext(DatabaseContext)?.getViewRowsMap;
const navigateToView = useContext(DatabaseContext)?.navigateToView;
Expand Down Expand Up @@ -46,6 +47,7 @@ export function DatabaseRowSubDocument({ rowId }: { rowId: string }) {

return (
<Editor
isDark={isDark}
doc={doc}
loadViewMeta={loadViewMeta}
navigateToView={navigateToView}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Y from 'yjs';

export interface DocumentProps extends ViewMetaProps {
doc: YDoc;
isDark: boolean;
navigateToView?: (viewId: string) => Promise<void>;
loadViewMeta?: (viewId: string) => Promise<ViewMeta>;
loadView?: (viewId: string) => Promise<YDoc>;
Expand All @@ -20,6 +21,7 @@ export const Document = ({
navigateToView,
loadViewMeta,
getViewRowsMap,
isDark,
...viewMeta
}: DocumentProps) => {
return (
Expand All @@ -28,6 +30,7 @@ export const Document = ({
<Suspense fallback={<ComponentLoading />}>
<div className={'mx-16 w-[964px] min-w-0 max-w-full'}>
<Editor
isDark={isDark}
loadView={loadView}
loadViewMeta={loadViewMeta}
navigateToView={navigateToView}
Expand Down
22 changes: 9 additions & 13 deletions frontend/appflowy_web_app/src/components/editor/Editable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,23 @@ const EditorEditable = ({ editor }: { editor: ReactEditor }) => {
const { readOnly } = useEditorContext();
const codeDecorate = useDecorate(editor);

const decorate = useCallback(
(entry: NodeEntry) => {
return [...codeDecorate(entry)];
},
[codeDecorate]
);

const renderElement = useCallback(
(props: RenderElementProps) => (
const renderElement = useCallback((props: RenderElementProps) => {
return (
<Suspense fallback={<Skeleton width={'100%'} height={24} />}>
<Element {...props} />
</Suspense>
),
[]
);
);
}, []);

return (
<>
<Editable
role={'textbox'}
decorate={decorate}
decorate={(entry: NodeEntry) => {
const decoration = codeDecorate?.(entry);

return decoration || [];
}}
className={'px-16 outline-none focus:outline-none max-md:px-4'}
renderLeaf={Leaf}
renderElement={renderElement}
Expand Down
13 changes: 12 additions & 1 deletion frontend/appflowy_web_app/src/components/editor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,19 @@ export interface EditorProps extends EditorContextState {
}

export const Editor = memo(({ doc, layoutStyle = defaultLayoutStyle, ...props }: EditorProps) => {
const [codeGrammars, setCodeGrammars] = React.useState<Record<string, string>>({});

const handleAddCodeGrammars = React.useCallback((blockId: string, grammar: string) => {
setCodeGrammars((prev) => ({ ...prev, [blockId]: grammar }));
}, []);

return (
<EditorContextProvider {...props} layoutStyle={layoutStyle}>
<EditorContextProvider
{...props}
codeGrammars={codeGrammars}
addCodeGrammars={handleAddCodeGrammars}
layoutStyle={layoutStyle}
>
<CollaborativeEditor doc={doc} />
</EditorContextProvider>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ export const defaultLayoutStyle: EditorLayoutStyle = {

export interface EditorContextState {
readOnly: boolean;
isDark: boolean;
layoutStyle?: EditorLayoutStyle;
codeGrammars?: Record<string, string>;
addCodeGrammars?: (blockId: string, grammar: string) => void;
navigateToView?: (viewId: string) => Promise<void>;
loadViewMeta?: (viewId: string) => Promise<ViewMeta>;
loadView?: (viewId: string) => Promise<YDoc>;
Expand All @@ -27,6 +30,8 @@ export interface EditorContextState {
export const EditorContext = createContext<EditorContextState>({
readOnly: true,
layoutStyle: defaultLayoutStyle,
codeGrammars: {},
isDark: false,
});

export const EditorContextProvider = ({ children, ...props }: EditorContextState & { children: React.ReactNode }) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,43 @@
import { CodeNode } from '@/components/editor/editor.type';
import { useCallback } from 'react';
import { useEditorContext } from '@/components/editor/EditorContext';
import { useCallback, useEffect } from 'react';
import { ReactEditor, useSlateStatic } from 'slate-react';
import { Element as SlateElement, Transforms } from 'slate';

const Prism = window.Prism;
const hljs = window.hljs;

export function useCodeBlock(node: CodeNode) {
const language = node.data.language;
const editor = useSlateStatic() as ReactEditor;

const addCodeGrammars = useEditorContext().addCodeGrammars;

useEffect(() => {
const path = ReactEditor.findPath(editor, node);
let detectedLanguage = language;

if (!language) {
const codeSnippet = editor.string(path);

detectedLanguage = hljs.highlightAuto(codeSnippet).language;
}

const prismLanguage = Prism.languages[detectedLanguage.toLowerCase()];

if (!prismLanguage) {
const script = document.createElement('script');

script.src = `https://cdnjs.cloudflare.com/ajax/libs/prism/1.26.0/components/prism-${detectedLanguage.toLowerCase()}.min.js`;
document.head.appendChild(script);
script.onload = () => {
addCodeGrammars?.(node.blockId, detectedLanguage);
};
} else {
addCodeGrammars?.(node.blockId, detectedLanguage);
}
}, [addCodeGrammars, editor, language, node]);

const handleChangeLanguage = useCallback(
(newLang: string) => {
const path = ReactEditor.findPath(editor, node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,4 @@ export const CodeBlock = memo(
}),
(prevProps, nextProps) => JSON.stringify(prevProps.node) === JSON.stringify(nextProps.node)
);
export default CodeBlock;
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';

function SelectLanguage({
readOnly,
language = 'json',
language = 'Auto',
}: {
readOnly?: boolean;
language: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import { BlockType } from '@/application/collab.type';
import { decorateCode } from '@/components/editor/components/blocks/code/utils';
import { useEditorContext } from '@/components/editor/EditorContext';
import { decorateCode } from './utils';
import { CodeNode } from '@/components/editor/editor.type';
import { useCallback } from 'react';
import { useMemo } from 'react';
import { BaseRange, Editor, NodeEntry, Element } from 'slate';
import { ReactEditor } from 'slate-react';

export function useDecorate(editor: ReactEditor) {
return useCallback(
(entry: NodeEntry): BaseRange[] => {
const grammars = useEditorContext().codeGrammars;
const isDark = useEditorContext().isDark;

return useMemo(() => {
return (entry: NodeEntry): BaseRange[] => {
if (!entry) return [];
const path = entry[1];

const blockEntry = editor.above({
Expand All @@ -20,14 +25,11 @@ export function useDecorate(editor: ReactEditor) {

const block = blockEntry[0] as CodeNode;

if (block.type === BlockType.CodeBlock) {
const language = block.data.language;

return decorateCode(entry, language, false);
if (block.type === BlockType.CodeBlock && grammars?.[block.blockId]) {
return decorateCode(entry, grammars[block.blockId], isDark);
}

return [];
},
[editor]
);
};
}, [editor, grammars, isDark]);
}
Original file line number Diff line number Diff line change
@@ -1,46 +1,9 @@
import Prism from 'prismjs';

import 'prismjs/components/prism-bash';
import 'prismjs/components/prism-basic';
import 'prismjs/components/prism-c';
import 'prismjs/components/prism-clojure';
import 'prismjs/components/prism-cpp';
import 'prismjs/components/prism-csp';
import 'prismjs/components/prism-css';
import 'prismjs/components/prism-dart';
import 'prismjs/components/prism-elixir';
import 'prismjs/components/prism-elm';
import 'prismjs/components/prism-erlang';
import 'prismjs/components/prism-fortran';
import 'prismjs/components/prism-go';
import 'prismjs/components/prism-graphql';
import 'prismjs/components/prism-haskell';
import 'prismjs/components/prism-java';
import 'prismjs/components/prism-javascript';
import 'prismjs/components/prism-json';
import 'prismjs/components/prism-kotlin';
import 'prismjs/components/prism-lisp';
import 'prismjs/components/prism-lua';
import 'prismjs/components/prism-markdown';
import 'prismjs/components/prism-matlab';
import 'prismjs/components/prism-ocaml';
import 'prismjs/components/prism-perl';
import 'prismjs/components/prism-php';
import 'prismjs/components/prism-powershell';
import 'prismjs/components/prism-python';
import 'prismjs/components/prism-r';
import 'prismjs/components/prism-ruby';
import 'prismjs/components/prism-rust';
import 'prismjs/components/prism-scala';
import 'prismjs/components/prism-shell-session';
import 'prismjs/components/prism-sql';
import 'prismjs/components/prism-swift';
import 'prismjs/components/prism-typescript';
import 'prismjs/components/prism-xml-doc';
import 'prismjs/components/prism-yaml';

import { BaseRange, NodeEntry, Text, Path } from 'slate';

const Prism = window.Prism;

Prism.plugins.autoloader.languages_path = 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.26.0/components/';

const push_string = (
token: string | Prism.Token,
path: Path,
Expand Down Expand Up @@ -121,17 +84,21 @@ export const decorateCode = ([node, path]: NodeEntry, language: string, isDark:
return ranges;
}

try {
const tokens = Prism.tokenize(node.text, Prism.languages[language.toLowerCase()]);
const highlightCode = (code: string, language: string) => {
try {
const tokens = Prism.tokenize(code, language);

let start = 0;

let start = 0;
for (const token of tokens) {
start = recurseTokenize(token, path, ranges, start) || 0;
}

for (const token of tokens) {
start = recurseTokenize(token, path, ranges, start) || 0;
return ranges;
} catch {
return ranges;
}
};

return ranges;
} catch {
return ranges;
}
return highlightCode(node.text, Prism.languages[language.toLowerCase()]);
};
Loading

0 comments on commit ed82d18

Please sign in to comment.