UNPKG

vzcode

Version:
181 lines (152 loc) 4.84 kB
import { shouldTriggerRun } from '../shouldTriggerRun'; import { JSONOp } from '../../ot'; import { VizFileId, VizContent } from '@vizhub/viz-types'; import { ShareDBDoc } from '../../types'; import { useEffect, useRef, useState } from 'react'; // The time in milliseconds by which auto-saving is debounced. export const autoPrettierDebounceTimeMS = 1200; // Computes a file extension from a file name. // Example: 'foo.js' => '.js' const extension = (fileName: string) => { const match = fileName.match(/\.([^.]+)$/); if (match) { return match[1]; } else { return ''; } }; // export const shouldTriggerRun = (event: KeyboardEvent) => { // if(event.ctrlKey && event.key === 's') { // return true; // } // } export const usePrettier = ({ shareDBDoc, submitOperation, prettierWorker, }: { shareDBDoc: ShareDBDoc<VizContent> | null; submitOperation: ( next: (content: VizContent) => VizContent, ) => void; prettierWorker: Worker; }) => { // The set of files that have been modified // since the last Prettier run. const dirtyFilesRef = useRef<Set<VizFileId>>(new Set()); // State to hold the error from Prettier // `null` means no errors // If this is a string, it's the // error message from Prettier. const [prettierError, setPrettierError] = useState< string | null >(null); // A ref to keep track of whether an op is being applied. // This is used to prevent Prettier from running // when the op is coming from Prettier itself. const isApplyingOpRef = useRef(false); // We use a ref to keep track of the runPrettier function. const runPrettierRef = useRef(null); useEffect(() => { if (!shareDBDoc) { return; } // Get the message that comes back from the worker. // This is the prettified text for a specific file. const handleMessage = (event) => { const { fileId, error, fileTextPrettified } = event.data; // Handle syntax errors if (error) { // If there's an error, set the error state setPrettierError(error); // Return early as there is no text change to apply. return; } else { // Make sure the error overlay goes away // when Prettier runs successfully with no errors. setPrettierError(null); } isApplyingOpRef.current = true; submitOperation((document) => ({ ...document, files: { ...document.files, [fileId]: { ...document.files[fileId], text: fileTextPrettified, }, }, })); isApplyingOpRef.current = false; }; // Listen for messages from the worker. prettierWorker.addEventListener( 'message', handleMessage, ); const runPrettier = async () => { // Get the content of the document const content = shareDBDoc.data; // Get the files const files = content.files; // Get the set of dirty files const dirtyFiles = dirtyFilesRef.current; // Get the dirty files const dirtyFileIds = Array.from(dirtyFiles); // Clear the set of dirty files dirtyFiles.clear(); // Run Prettier on each dirty file for (const fileId of dirtyFileIds) { // Get the file const file = files[fileId]; // Craft the data to send to the worker const data = { fileText: file.text, fileExtension: extension(file.name), fileId, }; // Run Prettier for this file prettierWorker.postMessage(data); } }; runPrettierRef.current = runPrettier; const handleOpBatch = (op: JSONOp) => { // Only act on changes coming from the client // OR changes coming from the AI Assist that we // if (isLocal) { // If the op is coming from Prettier itself, // then do nothing. if (isApplyingOpRef.current) { // console.log('ignoring op from Prettier'); return; } // Check if the path of this op is the text content of a file if ( op && op.length === 4 && op[0] === 'files' && typeof op[1] === 'string' && op[2] === 'text' ) { // Get the file id const fileId: VizFileId = op[1]; // Add the file id to the set of dirty files dirtyFilesRef.current.add(fileId); } }; // Listen for changes shareDBDoc.on('op batch', handleOpBatch); // Clean up the event listeners return () => { prettierWorker.removeEventListener( 'message', handleMessage, ); shareDBDoc.removeListener('op batch', handleOpBatch); }; }, [shareDBDoc]); // Return the errors and run prettier function ref // for use elsewhere. return { prettierError, runPrettierRef }; };