Created
March 11, 2024 16:59
-
-
Save Pratyush-Dehury/df87564dc172019ab1b5349baaa36025 to your computer and use it in GitHub Desktop.
Sample code changes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState, useEffect, useRef } from 'react'; | |
const Editor = ({ | |
files, | |
file, | |
lintMessages, | |
clearLintMessage, | |
updateLintMessage, | |
lintWarning, | |
collapseSidebar, | |
expandSidebar, | |
expandConsole, | |
hideRuntimeErrorWarning, | |
setUnsavedChanges, | |
unsavedChanges, | |
updateFileContent, | |
provideController, | |
setSelectedFile, | |
startSketch, | |
clearConsole, | |
consoleEvents, | |
isPlaying, | |
closeProjectOptions, | |
fontSize, | |
lineNumbers, | |
linewrap, | |
theme, | |
autocloseBracketsQuotes, | |
autocompleteHinter, | |
runtimeErrorWarningVisible, | |
isExpanded, | |
}) => { | |
const codemirrorContainer = useRef(null); | |
const [cm, setCm] = useState(null); | |
const hinter = useRef(null); | |
useEffect(() => { | |
const beep = new Audio(beepUrl); | |
const initializeEditor = () => { | |
const _cm = CodeMirror(codemirrorContainer.current, { | |
theme: `p5-${theme}`, | |
lineNumbers, | |
styleActiveLine: true, | |
inputStyle: 'contenteditable', | |
lineWrapping: linewrap, | |
fixedGutter: false, | |
foldGutter: true, | |
foldOptions: { widget: '\u2026' }, | |
gutters: ['CodeMirror-foldgutter', 'CodeMirror-lint-markers'], | |
keyMap: 'sublime', | |
highlightSelectionMatches: true, | |
matchBrackets: true, | |
emmet: { | |
preview: ['html'], | |
markTagPairs: true, | |
autoRenameTags: true, | |
}, | |
autoCloseBrackets: autocloseBracketsQuotes, | |
styleSelectedText: true, | |
lint: { | |
onUpdateLinting: (annotations) => { | |
updateLintingMessageAccessibility(annotations); | |
}, | |
options: { | |
asi: true, | |
eqeqeq: false, | |
'-W041': false, | |
esversion: 7, | |
}, | |
}, | |
colorpicker: { | |
type: 'sketch', | |
mode: 'edit', | |
}, | |
}); | |
setCm(_cm); | |
hinter.current = new Fuse(hinter.p5Hinter, { | |
threshold: 0.05, | |
keys: ['text'], | |
}); | |
delete _cm.options.lint.options.errors; | |
const replaceCommand = metaKey === 'Ctrl' ? `${metaKey}-H` : `${metaKey}-Option-F`; | |
_cm.setOption('extraKeys', { | |
Tab: (cm) => { | |
if (!cm.execCommand('emmetExpandAbbreviation')) return; | |
const selection = cm.doc.getSelection(); | |
if (selection.length > 0) { | |
cm.execCommand('indentMore'); | |
} else { | |
cm.replaceSelection(' '.repeat(INDENTATION_AMOUNT)); | |
} | |
}, | |
Enter: 'emmetInsertLineBreak', | |
Esc: 'emmetResetAbbreviation', | |
[`${metaKey}-Enter`]: () => null, | |
[`Shift-${metaKey}-Enter`]: () => null, | |
[`${metaKey}-F`]: 'findPersistent', | |
[`Shift-${metaKey}-F`]: tidyCode, | |
[`${metaKey}-G`]: 'findPersistentNext', | |
[`Shift-${metaKey}-G`]: 'findPersistentPrev', | |
[replaceCommand]: 'replace', | |
[`${metaKey}-K`]: (cm, event) => cm.state.colorpicker.popup_color_picker({ length: 0 }), | |
[`${metaKey}-.`]: 'toggleComment', | |
}); | |
_cm.on('change', debounce(() => { | |
setUnsavedChanges(true); | |
hideRuntimeErrorWarning(); | |
updateFileContent(file.id, _cm.getValue()); | |
if (autorefresh && isPlaying) { | |
clearConsole(); | |
startSketch(); | |
} | |
}, 1000)); | |
_cm.on('keyup', () => { | |
const temp = t('Editor.KeyUpLineNumber', { | |
lineNumber: parseInt(_cm.getCursor().line + 1, 10), | |
}); | |
document.getElementById('current-line').innerHTML = temp; | |
}); | |
_cm.on('keydown', (_cm, e) => { | |
const mode = _cm.getOption('mode'); | |
if (/^[a-z]$/i.test(e.key) && (mode === 'css' || mode === 'javascript')) { | |
showHint(_cm); | |
} | |
}); | |
_cm.getWrapperElement().style['font-size'] = `${fontSize}px`; | |
provideController({ | |
tidyCode, | |
showFind, | |
showReplace, | |
getContent, | |
}); | |
}; | |
initializeEditor(); | |
return () => { | |
if (cm) { | |
cm.toTextArea(); | |
} | |
provideController(null); | |
}; | |
}, []); | |
useEffect(() => { | |
if (cm) { | |
cm.setOption('mode', getFileMode(file.name)); | |
const oldDoc = cm.swapDoc(docs[file.id]); | |
docs[prevProps.file.id] = oldDoc; | |
cm.focus(); | |
if (!unsavedChanges) { | |
setTimeout(() => setUnsavedChanges(false), 400); | |
} | |
} | |
}, [file]); | |
useEffect(() => { | |
if (cm) { | |
cm.getWrapperElement().style['font-size'] = `${fontSize}px`; | |
} | |
}, [fontSize]); | |
useEffect(() => { | |
if (cm) { | |
cm.setOption('lineWrapping', linewrap); | |
} | |
}, [linewrap]); | |
useEffect(() => { | |
if (cm) { | |
cm.setOption('theme', `p5-${theme}`); | |
} | |
}, [theme]); | |
useEffect(() => { | |
if (cm) { | |
cm.setOption('lineNumbers', lineNumbers); | |
} | |
}, [lineNumbers]); | |
useEffect(() => { | |
if (cm) { | |
cm.setOption('autoCloseBrackets', autocloseBracketsQuotes); | |
} | |
}, [autocloseBracketsQuotes]); | |
useEffect(() => { | |
if (cm) { | |
if (!autocompleteHinter) { | |
CodeMirror.showHint(cm, () => {}, {}); | |
} | |
} | |
}, [autocompleteHinter]); | |
useEffect(() => { | |
if (cm && runtimeErrorWarningVisible) { | |
if (consoleEvents.length !== 0) { | |
consoleEvents.forEach((consoleEvent) => { | |
if (consoleEvent.method === 'error') { | |
const errorObj = { stack: consoleEvent.data[0].toString() }; | |
StackTrace.fromError(errorObj).then((stackLines) => { | |
expandConsole(); | |
const line = stackLines.find((l) => l.fileName && l.fileName.startsWith('/')); | |
if (!line) return; | |
const fileNameArray = line.fileName.split('/'); | |
const fileName = fileNameArray.slice(-1)[0]; | |
const filePath = fileNameArray.slice(0, -1).join('/'); | |
const fileWithError = files.find((f) => f.name === fileName && f.filePath === filePath); | |
setSelectedFile(fileWithError.id); | |
cm.addLineClass(line.lineNumber - 1, 'background', 'line-runtime-error'); | |
}); | |
} | |
}); | |
} else { | |
for (let i = 0; i < cm.lineCount(); i += 1) { | |
cm.removeLineClass(i, 'background', 'line-runtime-error'); | |
} | |
} | |
} | |
return () => { | |
if (cm) { | |
for (let i = 0; i < cm.lineCount(); i += 1) { | |
cm.removeLineClass(i, 'background', 'line-runtime-error'); | |
} | |
} | |
}; | |
}, [runtimeErrorWarningVisible, consoleEvents]); | |
const getFileMode = (fileName) => { | |
let mode; | |
if (fileName.match(/.+\.js$/i)) { | |
mode = 'javascript'; | |
} else if (fileName.match(/.+\.css$/i)) { | |
mode = 'css'; | |
} else if (fileName.match(/.+\.(html|xml)$/i)) { | |
mode = 'htmlmixed'; | |
} else if (fileName.match(/.+\.json$/i)) { | |
mode = 'application/json'; | |
} else if (fileName.match(/.+\.(frag|glsl)$/i)) { | |
mode = 'x-shader/x-fragment'; | |
} else if (fileName.match(/.+\.(vert|stl)$/i)) { | |
mode = 'x-shader/x-vertex'; | |
} else { | |
mode = 'text/plain'; | |
} | |
return mode; | |
}; | |
const getContent = () => { | |
const content = cm.getValue(); | |
const updatedFile = Object.assign({}, file, { content }); | |
return updatedFile; | |
}; | |
const showFind = () => { | |
cm.execCommand('findPersistent'); | |
}; | |
const showHint = (_cm) => { | |
if (!autocompleteHinter) { | |
CodeMirror.showHint(_cm, () => {}, {}); | |
return; | |
} | |
let focusedLinkElement = null; | |
const setFocusedLinkElement = (set) => { | |
if (set && !focusedLinkElement) { | |
const activeItemLink = document.querySelector(`.CodeMirror-hint-active a`); | |
if (activeItemLink) { | |
focusedLinkElement = activeItemLink; | |
focusedLinkElement.classList.add('focused-hint-link'); | |
focusedLinkElement.parentElement.parentElement.classList.add('unfocused'); | |
} | |
} | |
}; | |
const removeFocusedLinkElement = () => { | |
if (focusedLinkElement) { | |
focusedLinkElement.classList.remove('focused-hint-link'); | |
focusedLinkElement.parentElement.parentElement.classList.remove('unfocused'); | |
focusedLinkElement = null; | |
return true; | |
} | |
return false; | |
}; | |
const hintOptions = { | |
_fontSize: fontSize, | |
completeSingle: false, | |
extraKeys: { | |
'Shift-Right': (cm, e) => { | |
const activeItemLink = document.querySelector(`.CodeMirror-hint-active a`); | |
if (activeItemLink) activeItemLink.click(); | |
}, | |
Right: (cm, e) => { | |
setFocusedLinkElement(true); | |
}, | |
Left: (cm, e) => { | |
removeFocusedLinkElement(); | |
}, | |
Up: (cm, e) => { | |
const onLink = removeFocusedLinkElement(); | |
e.moveFocus(-1); | |
setFocusedLinkElement(onLink); | |
}, | |
Down: (cm, e) => { | |
const onLink = removeFocusedLinkElement(); | |
e.moveFocus(1); | |
setFocusedLinkElement(onLink); | |
}, | |
Enter: (cm, e) => { | |
if (focusedLinkElement) focusedLinkElement.click(); | |
else e.pick(); | |
}, | |
}, | |
closeOnUnfocus: false, | |
}; | |
if (_cm.options.mode === 'javascript') { | |
const c = _cm.getCursor(); | |
const token = _cm.getTokenAt(c); | |
const hints = hinter.current | |
.search(token.string) | |
.filter((h) => h.item.text[0] === token.string[0]); | |
return { | |
list: hints, | |
from: CodeMirror.Pos(c.line, token.start), | |
to: CodeMirror.Pos(c.line, c.ch), | |
}; | |
} else if (_cm.options.mode === 'css') { | |
return CodeMirror.hint.css; | |
} | |
}; | |
const showReplace = () => { | |
cm.execCommand('replace'); | |
}; | |
const prettierFormatWithCursor = (parser, plugins) => { | |
try { | |
const { formatted, cursorOffset } = prettier.formatWithCursor( | |
cm.doc.getValue(), | |
{ | |
cursorOffset: cm.doc.indexFromPos(cm.doc.getCursor()), | |
parser, | |
plugins, | |
} | |
); | |
const { left, top } = cm.getScrollInfo(); | |
cm.doc.setValue(formatted); | |
cm.focus(); | |
cm.doc.setCursor(cm.doc.posFromIndex(cursorOffset)); | |
cm.scrollTo(left, top); | |
} catch (error) { | |
console.error(error); | |
} | |
}; | |
const tidyCode = () => { | |
const mode = cm.getOption('mode'); | |
if (mode === 'javascript') { | |
prettierFormatWithCursor('babel', [babelParser]); | |
} else if (mode === 'css') { | |
prettierFormatWithCursor('css', [cssParser]); | |
} else if (mode === 'htmlmixed') { | |
prettierFormatWithCursor('html', [htmlParser]); | |
} | |
}; | |
const editorSectionClass = classNames({ | |
editor: true, | |
'sidebar--contracted': !isExpanded, | |
}); | |
const editorHolderClass = classNames({ | |
'editor-holder': true, | |
'editor-holder--hidden': | |
file.fileType === 'folder' || file.url, | |
}); | |
return ( | |
<MediaQuery minWidth={770}> | |
{(matches) => | |
matches ? ( | |
<section className={editorSectionClass}> | |
<header className="editor__header"> | |
<button | |
aria-label={t('Editor.OpenSketchARIA')} | |
className="sidebar__contract" | |
onClick={() => { | |
collapseSidebar(); | |
closeProjectOptions(); | |
}} | |
> | |
<LeftArrowIcon focusable="false" aria-hidden="true" /> | |
</button> | |
<button | |
aria-label={t('Editor.CloseSketchARIA')} | |
className="sidebar__expand" | |
onClick={expandSidebar} | |
> | |
<RightArrowIcon focusable="false" aria-hidden="true" /> | |
</button> | |
<div className="editor__file-name"> | |
<span> | |
{file.name} | |
<UnsavedChangesIndicator /> | |
</span> | |
<Timer /> | |
</div> | |
</header> | |
<article | |
ref={codemirrorContainer} | |
className={editorHolderClass} | |
/> | |
{file.url ? ( | |
<AssetPreview | |
url={file.url} | |
name={file.name} | |
/> | |
) : null} | |
<EditorAccessibility lintMessages={lintMessages} /> | |
</section> | |
) : ( | |
<EditorContainer expanded={isExpanded}> | |
<header> | |
<IconButton | |
onClick={expandSidebar} | |
icon={FolderIcon} | |
/> | |
<span> | |
{file.name} | |
<UnsavedChangesIndicator /> | |
</span> | |
</header> | |
<section> | |
<EditorHolder | |
ref={codemirrorContainer} | |
/> | |
{file.url ? ( | |
<AssetPreview | |
url={file.url} | |
name={file.name} | |
/> | |
) : null} | |
<EditorAccessibility lintMessages={lintMessages} /> | |
</section> | |
</EditorContainer> | |
) | |
} | |
</MediaQuery> | |
); | |
}; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment