diff --git a/client/modules/IDE/actions/ide.js b/client/modules/IDE/actions/ide.js index 80a43443bc..ad16cd9a5d 100644 --- a/client/modules/IDE/actions/ide.js +++ b/client/modules/IDE/actions/ide.js @@ -236,23 +236,10 @@ export function hideErrorModal() { }; } -export function hideRuntimeErrorWarning() { - return { - type: ActionTypes.HIDE_RUNTIME_ERROR_WARNING - }; -} - -export function showRuntimeErrorWarning() { - return { - type: ActionTypes.SHOW_RUNTIME_ERROR_WARNING - }; -} - export function startSketch() { return (dispatch, getState) => { dispatch(clearConsole()); dispatch(startVisualSketch()); - dispatch(showRuntimeErrorWarning()); const state = getState(); dispatchMessage({ type: MessageTypes.SKETCH, diff --git a/client/modules/IDE/components/ConsoleInput.jsx b/client/modules/IDE/components/ConsoleInput.jsx index 3806db0381..41fc729d4b 100644 --- a/client/modules/IDE/components/ConsoleInput.jsx +++ b/client/modules/IDE/components/ConsoleInput.jsx @@ -1,6 +1,16 @@ import PropTypes from 'prop-types'; -import React, { useRef, useEffect, useState } from 'react'; -import CodeMirror from 'codemirror'; +import React, { useRef, useEffect } from 'react'; +import { EditorState } from '@codemirror/state'; +import { EditorView, highlightSpecialChars, keymap } from '@codemirror/view'; +import { + bracketMatching, + syntaxHighlighting, + defaultHighlightStyle +} from '@codemirror/language'; +import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete'; +import { defaultKeymap, history, historyKeymap } from '@codemirror/commands'; +import { javascript } from '@codemirror/lang-javascript'; + import { useDispatch } from 'react-redux'; import { Encode } from 'console-feed'; @@ -11,31 +21,24 @@ import { dispatchMessage, MessageTypes } from '../../../utils/dispatcher'; // heavily inspired by // https://github.com/codesandbox/codesandbox-client/blob/92a1131f4ded6f7d9c16945dc7c18aa97c8ada27/packages/app/src/app/components/Preview/DevTools/Console/Input/index.tsx +// TODO(connie): Add theme support? function ConsoleInput({ theme, fontSize }) { - const [commandHistory, setCommandHistory] = useState([]); - const [commandCursor, setCommandCursor] = useState(-1); + const commandHistory = useRef([]); + const commandCursor = useRef(-1); const codemirrorContainer = useRef(null); - const cmInstance = useRef(null); + const cmView = useRef(null); const dispatch = useDispatch(); useEffect(() => { - cmInstance.current = CodeMirror(codemirrorContainer.current, { - theme: `p5-${theme}`, - scrollbarStyle: null, - keymap: 'sublime', - mode: 'javascript', - inputStyle: 'contenteditable' - }); - }, []); - - useEffect(() => { - const handleEnterKey = (cm, e) => { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault(); - e.stopPropagation(); - - const value = cm.getValue().trim(); - if (value === '') return; + const enterKeymap = { + key: 'Enter', + shiftKey: () => false, // Treat like a normal Enter key press if the Shift key is held down. + preventDefault: true, + stopPropogation: true, + run: (view) => { + const value = view.state.doc.toString().trim(); + if (value === '' || view.state.selection.main.empty === false) + return false; const messages = [ { log: Encode({ method: 'command', data: [value] }) } @@ -48,77 +51,91 @@ function ConsoleInput({ theme, fontSize }) { }); dispatch(dispatchConsoleEvent(consoleEvent)); - cm.setValue(''); - setCommandHistory([value, ...commandHistory]); - setCommandCursor(-1); - } - }; - - if (cmInstance.current) { - cmInstance.current.on('keydown', handleEnterKey); - } - - return () => { - if (cmInstance.current) { - cmInstance.current.off('keydown', handleEnterKey); + view.dispatch({ + changes: { from: 0, to: view.state.doc.length, insert: '' } + }); + commandHistory.current.unshift(value); + commandCursor.current = -1; + return true; } }; - }, [commandHistory]); - useEffect(() => { - const handleUpArrowKey = (cm, e) => { - if (e.key === 'ArrowUp') { - const lineNumber = cm.getDoc().getCursor().line; - if (lineNumber !== 0) return; + const upArrowKeymap = { + key: 'ArrowUp', + run: (view) => { + // Just let the cursor go up if we have a multiline input + // and the cursor isn't at the first line. + const currentLine = view.state.doc.lineAt( + view.state.selection.main.head + ).number; + // CM lines are 1-indexed, so the first line is 1. + if (currentLine > 1) return false; const newCursor = Math.min( - commandCursor + 1, - commandHistory.length - 1 + commandCursor.current + 1, + commandHistory.current.length - 1 ); - cm.setValue(commandHistory[newCursor] || ''); - const cursorPos = cm.getDoc().getLine(0).length - 1; - cm.getDoc().setCursor({ line: 0, ch: cursorPos }); - setCommandCursor(newCursor); - } - }; - - if (cmInstance.current) { - cmInstance.current.on('keydown', handleUpArrowKey); - } - - return () => { - if (cmInstance.current) { - cmInstance.current.off('keydown', handleUpArrowKey); + const newValue = commandHistory.current[newCursor] || ''; + view.dispatch({ + changes: { from: 0, to: view.state.doc.length, insert: newValue } + }); + const newCursorPos = newValue.length; + view.dispatch({ + selection: { anchor: newCursorPos, head: newCursorPos } + }); + commandCursor.current = newCursor; + return true; } }; - }, [commandCursor, commandHistory]); - useEffect(() => { - const handleArrowDownKey = (cm, e) => { - if (e.key === 'ArrowDown') { - const lineNumber = cm.getDoc().getCursor().line; - const lineCount = cm.lineCount(); - if (lineNumber + 1 !== lineCount) return; - - const newCursor = Math.max(commandCursor - 1, -1); - cm.setValue(commandHistory[newCursor] || ''); - const newLine = cm.getDoc().getLine(lineCount - 1); - const cursorPos = newLine ? newLine.length - 1 : 1; - cm.getDoc().setCursor({ line: lineCount - 1, ch: cursorPos }); - setCommandCursor(newCursor); + const downArrowKeymap = { + key: 'ArrowDown', + run: (view) => { + // Just let the cursor go down if we have a multiline input + // and the cursor isn't at the last line. + const currentLine = view.state.doc.lineAt( + view.state.selection.main.head + ).number; + const docLength = view.state.doc.lines; + if (currentLine !== docLength) return false; + + const newCursor = Math.max(commandCursor.current - 1, -1); + const newValue = commandHistory.current[newCursor] || ''; + view.dispatch({ + changes: { from: 0, to: view.state.doc.length, insert: newValue } + }); + const newCursorPos = newValue.length; + view.dispatch({ + selection: { anchor: newCursorPos, head: newCursorPos } + }); + commandCursor.current = newCursor; + return true; } }; - if (cmInstance.current) { - cmInstance.current.on('keydown', handleArrowDownKey); - } - - return () => { - if (cmInstance.current) { - cmInstance.current.off('keydown', handleArrowDownKey); - } - }; - }, [commandCursor, commandHistory]); + const cmState = EditorState.create({ + extensions: [ + history(), + highlightSpecialChars(), + bracketMatching(), + closeBrackets(), + syntaxHighlighting(defaultHighlightStyle), + javascript(), + keymap.of([ + enterKeymap, + upArrowKeymap, + downArrowKeymap, + ...defaultKeymap, + ...closeBracketsKeymap, + ...historyKeymap + ]) + ] + }); + cmView.current = new EditorView({ + state: cmState, + parent: codemirrorContainer.current + }); + }, []); return (
diff --git a/client/modules/IDE/components/Editor/codemirror.js b/client/modules/IDE/components/Editor/codemirror.js new file mode 100644 index 0000000000..1e44007f84 --- /dev/null +++ b/client/modules/IDE/components/Editor/codemirror.js @@ -0,0 +1,240 @@ +import { useRef, useEffect } from 'react'; +import { EditorView, lineNumbers as lineNumbersExt } from '@codemirror/view'; +import { autocompletion, closeBrackets } from '@codemirror/autocomplete'; +import { debounce } from 'lodash'; +import { openSearchPanel } from '@codemirror/search'; +import { saveLocalBackup } from '../../utils/localBackup'; + +import { + getFileMode, + createNewFileState, + updateFileStates +} from './utils/fileState'; +import { createAutocompleteOptions } from './utils/extensionCustomStyles'; +import { useEffectWithComparison } from '../../hooks/custom-hooks'; +import { tidyCodeWithPrettier } from './utils/tidier'; + +// ----- GENERAL TODOS (in order of priority) ----- +// - revisit keymap differences, esp around sublime +// - emmet doesn't trigger if text is copy pasted in +// - need to re-implement emmet auto rename tag +// - clike addon + +/** This is a custom React hook that manages CodeMirror state. */ +export default function useCodeMirror({ + project, + lineNumbers, + linewrap, + autocloseBracketsQuotes, + setUnsavedChanges, + setCurrentLine, + updateFileContent, + file, + files, + autorefresh, + clearConsole, + startSketch, + autocompleteHinter, + fontSize, + onUpdateLinting, + referenceBaseUrl +}) { + // The codemirror instance. + const cmView = useRef(); + // The current codemirror files. + const fileStates = useRef(); + + // We store values used by the debounced onChange callback in a ref. + // Otherwise it can hold onto stale React state + const onChangeStateRef = useRef({}); + onChangeStateRef.current = { + fileId: file.id, + autorefresh, + project, + files + }; + + // When the file changes, update the file content and save status. + function onChange() { + const state = onChangeStateRef.current; + + setUnsavedChanges(true); + updateFileContent(state.fileId, cmView.current.state.doc.toString()); + + // Save a local backup to localStorage for crash recovery (#3891). + // This ensures work is recoverable even if the tab crashes + // (e.g. from an infinite loop) before the server autosave fires. + const projectId = state.project?.id || 'unsaved'; + saveLocalBackup(projectId, state.files); + + if (state.autorefresh) { + clearConsole(); + startSketch(); + } + } + // Call onChange at most once every second. + const debouncedOnChange = debounce(onChange, 1000); + + // This is called when the CM view updates. + function onViewUpdate(updateView) { + const { state } = updateView; + + // TODO - check if need to subtract one + setCurrentLine(state.doc.lineAt(state.selection.main.head).number); + + if (updateView.docChanged) { + debouncedOnChange(); + } + } + + // When the container component enters the DOM, we want this function + // to be called so we can setup the CodeMirror instance with the container. + function setupCodeMirrorOnContainerMounted(container) { + cmView.current = new EditorView({ + parent: container + }); + } + + // When the component unmounts, we want to clean up the CodeMirror instance. + function teardownCodeMirror() { + if (cmView.current) { + cmView.current.destroy(); + cmView.current = null; + } + } + + // When settings change, we pass those changes into CodeMirror. + useEffect(() => { + const reconfigureEffect = (fileState) => + fileState.fontSizeCpt.reconfigure( + EditorView.theme({ '&': { fontSize: `${fontSize}px` } }) + ); + updateFileStates({ + fileStates: fileStates.current, + cmView: cmView.current, + file, + reconfigureEffect + }); + }, [fontSize]); + useEffect(() => { + const reconfigureEffect = (fileState) => + fileState.lineWrappingCpt.reconfigure( + linewrap ? EditorView.lineWrapping : [] + ); + updateFileStates({ + fileStates: fileStates.current, + cmView: cmView.current, + file, + reconfigureEffect + }); + }, [linewrap]); + useEffect(() => { + const reconfigureEffect = (fileState) => + fileState.lineNumbersCpt.reconfigure(lineNumbers ? lineNumbersExt() : []); + updateFileStates({ + fileStates: fileStates.current, + cmView: cmView.current, + file, + reconfigureEffect + }); + }, [lineNumbers]); + useEffect(() => { + const reconfigureEffect = (fileState) => + fileState.closeBracketsCpt.reconfigure( + autocloseBracketsQuotes ? closeBrackets() : [] + ); + updateFileStates({ + fileStates: fileStates.current, + cmView: cmView.current, + file, + reconfigureEffect + }); + }, [autocloseBracketsQuotes]); + useEffect(() => { + const reconfigureEffect = (fileState) => + fileState.autocompleteCpt.reconfigure( + autocompleteHinter + ? autocompletion(createAutocompleteOptions(referenceBaseUrl)) + : [] + ); + updateFileStates({ + fileStates: fileStates.current, + cmView: cmView.current, + file, + reconfigureEffect + }); + }, [autocompleteHinter, referenceBaseUrl]); + + // Initializes the files as CodeMirror states. + function initializeDocuments() { + if (!fileStates.current) { + fileStates.current = {}; + } + + files.forEach((currentFile) => { + if ( + currentFile.name !== 'root' && + !(currentFile.id in fileStates.current) + ) { + fileStates.current[currentFile.id] = createNewFileState( + currentFile.name, + currentFile.content, + { + linewrap, + lineNumbers, + autocloseBracketsQuotes, + autocomplete: autocompleteHinter, + onUpdateLinting, + onViewUpdate, + referenceBaseUrl, + fontSize + } + ); + } + }); + } + + // When the files change, reinitialize the documents. + useEffect(initializeDocuments, [files]); + + // When the file changes, make the CodeMirror call to swap out the document. + useEffectWithComparison( + (_, prevProps) => { + // We need to save the previous CodeMirror state so we can restore it + // when we switch back to it. + const previousState = cmView.current.state; + if (Array.isArray(prevProps) && prevProps.length > 0 && previousState) { + const prevId = prevProps[0]; + fileStates.current[prevId].cmState = previousState; + } + + const { cmState } = fileStates.current[file.id]; + cmView.current.setState(cmState); + }, + [file.id] + ); + + const getContent = () => { + const content = cmView.current.state.doc.toString(); + const updatedFile = Object.assign({}, file, { content }); + return updatedFile; + }; + + const showSearch = () => { + openSearchPanel(cmView.current); + }; + + const tidyCode = () => { + const fileMode = getFileMode(file.name); + tidyCodeWithPrettier(cmView.current, fileMode); + }; + + return { + setupCodeMirrorOnContainerMounted, + teardownCodeMirror, + getContent, + tidyCode, + showSearch, + codemirrorView: cmView + }; +} diff --git a/client/modules/IDE/components/Editor/index.jsx b/client/modules/IDE/components/Editor/index.jsx index 4bbfffc1f4..f7996020f5 100644 --- a/client/modules/IDE/components/Editor/index.jsx +++ b/client/modules/IDE/components/Editor/index.jsx @@ -1,55 +1,13 @@ -// TODO: convert to functional component - import PropTypes from 'prop-types'; -import React from 'react'; -import CodeMirror from 'codemirror'; -import Fuse from 'fuse.js'; -import emmet from '@emmetio/codemirror-plugin'; -import prettier from 'prettier/standalone'; -import babelParser from 'prettier/parser-babel'; -import htmlParser from 'prettier/parser-html'; -import cssParser from 'prettier/parser-postcss'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { withTranslation } from 'react-i18next'; import StackTrace from 'stacktrace-js'; -import 'codemirror/mode/css/css'; -import 'codemirror/mode/clike/clike'; -import 'codemirror/addon/selection/active-line'; -import 'codemirror/addon/lint/lint'; -import 'codemirror/addon/lint/javascript-lint'; -import 'codemirror/addon/lint/css-lint'; -import 'codemirror/addon/lint/html-lint'; -import 'codemirror/addon/fold/brace-fold'; -import 'codemirror/addon/fold/comment-fold'; -import 'codemirror/addon/fold/foldcode'; -import 'codemirror/addon/fold/foldgutter'; -import 'codemirror/addon/fold/indent-fold'; -import 'codemirror/addon/fold/xml-fold'; -import 'codemirror/addon/comment/comment'; -import 'codemirror/keymap/sublime'; -import 'codemirror/addon/search/searchcursor'; -import 'codemirror/addon/search/matchesonscrollbar'; -import 'codemirror/addon/search/match-highlighter'; -import 'codemirror/addon/search/jump-to-line'; -import 'codemirror/addon/edit/matchbrackets'; -import 'codemirror/addon/edit/closebrackets'; -import 'codemirror/addon/selection/mark-selection'; -import 'codemirror/addon/hint/css-hint'; -import 'codemirror-colorpicker'; -import { JSHINT } from 'jshint'; -import { CSSLint } from 'csslint'; -import { HTMLHint } from 'htmlhint'; import classNames from 'classnames'; import { debounce } from 'lodash'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import MediaQuery from 'react-responsive'; -import '../../../../utils/htmlmixed'; -import '../../../../utils/p5-javascript'; -import { metaKey } from '../../../../utils/metaKey'; -import '../show-hint'; -import * as hinter from '../../../../utils/p5-hinter'; -import '../../../../utils/codemirror-search'; import beepUrl from '../../../../sounds/audioAlert.mp3'; import RightArrowIcon from '../../../../images/right-arrow.svg'; @@ -72,704 +30,219 @@ import UnsavedChangesIndicator from '../UnsavedChangesIndicator'; import { EditorContainer, EditorHolder } from './MobileEditor'; import { FolderIcon } from '../../../../common/icons'; import { IconButton } from '../../../../common/IconButton'; -import { saveLocalBackup } from '../../utils/localBackup'; - -import contextAwareHinter from '../../../../utils/contextAwareHinter'; -import showRenameDialog from '../../../../utils/showRenameDialog'; -import handleRename from '../../../../utils/rename-variable'; -import { jumpToDefinition } from '../../../../utils/jump-to-definition'; -import { ensureAriaLiveRegion } from '../../../../utils/ScreenReaderHelper'; -import { isMac } from '../../../../utils/device'; - -emmet(CodeMirror); - -window.JSHINT = JSHINT; -window.CSSLint = CSSLint; -window.HTMLHint = HTMLHint; -const INDENTATION_AMOUNT = 2; - -class Editor extends React.Component { - constructor(props) { - super(props); - this.state = { - currentLine: 1 - }; - this._cm = null; - this.tidyCode = this.tidyCode.bind(this); - - this.updateLintingMessageAccessibility = debounce((annotations) => { - this.props.clearLintMessage(); - annotations.forEach((x) => { - if (x.from.line > -1) { - this.props.updateLintMessage(x.severity, x.from.line + 1, x.message); - } - }); - if (this.props.lintMessages.length > 0 && this.props.lintWarning) { - this.beep.play(); - } - }, 2000); - this.showFind = this.showFind.bind(this); - this.showReplace = this.showReplace.bind(this); - this.getContent = this.getContent.bind(this); - this.updateFileContent = this.updateFileContent.bind(this); - } +import useCodeMirror from './codemirror'; - componentDidMount() { - this.beep = new Audio(beepUrl); - ensureAriaLiveRegion(); - // this.widgets = []; - this._cm = CodeMirror(this.codemirrorContainer, { - theme: `p5-${this.props.theme}`, - lineNumbers: this.props.lineNumbers, - styleActiveLine: true, - inputStyle: 'contenteditable', - lineWrapping: this.props.linewrap, - fixedGutter: false, - foldGutter: true, - foldOptions: { widget: '\u2026' }, - gutters: ['CodeMirror-foldgutter', 'CodeMirror-lint-markers'], - keyMap: 'sublime', - highlightSelectionMatches: true, // highlight current search match - matchBrackets: true, - emmet: { - preview: ['html'], - markTagPairs: true, - autoRenameTags: true - }, - autoCloseBrackets: this.props.autocloseBracketsQuotes, - styleSelectedText: true, - lint: { - onUpdateLinting: (annotations) => { - this.updateLintingMessageAccessibility(annotations); - }, - options: { - asi: true, - eqeqeq: false, - '-W041': false, - esversion: 11 - } - }, - colorpicker: { - type: 'sketch', - mode: 'edit' - } - }); +import { + addErrorDecoration, + removeErrorDecorations +} from './utils/consoleErrorDecoration'; - this.hinter = new Fuse(hinter.p5Hinter, { - threshold: 0.05, - keys: ['text'] - }); +// temporary until p5.js 2.0 becomes default +// checks if sketch is using p5.js 2.0 to pass correct base url for autocomplete hinter reference +export function getReferenceBaseUrl(htmlFile) { + const html = htmlFile?.content || ''; - delete this._cm.options.lint.options.errors; + const isV2 = + /https:\/\/beta\.p5js\.org\b/i.test(html) || /\bp5(@|-)2\./i.test(html); - this._cm.getWrapperElement().addEventListener('click', (e) => { - const isCtrlClick = isMac() ? e.metaKey : e.ctrlKey; + return isV2 ? 'https://beta.p5js.org' : 'https://p5js.org'; +} - if (isCtrlClick) { - const pos = this._cm.coordsChar({ left: e.clientX, top: e.clientY }); - jumpToDefinition.call(this, pos); +function Editor({ + provideController, + files, + file, + project, + linewrap, + lineNumbers, + closeProjectOptions, + setSelectedFile, + setUnsavedChanges, + lintMessages, + lintWarning, + clearLintMessage, + updateLintMessage, + updateFileContent, + autorefresh, + clearConsole, + startSketch, + autocompleteHinter, + autocloseBracketsQuotes, + fontSize, + consoleEvents, + expandConsole, + isExpanded, + htmlFile, + t, + collapseSidebar, + expandSidebar +}) { + const [currentLine, setCurrentLine] = useState(1); + const beep = useRef(); + + const updateLintingMessageAccessibility = debounce((annotations) => { + clearLintMessage(); + annotations.forEach((x) => { + if (x.from.line > -1) { + updateLintMessage(x.severity, x.from.line + 1, x.message); } }); - - const renameKey = isMac() ? 'Ctrl-F2' : 'F2'; - - const replaceCommand = - metaKey === 'Ctrl' ? `${metaKey}-H` : `${metaKey}-Option-F`; - this._cm.setOption('extraKeys', { - Tab: (cm) => { - if (!cm.execCommand('emmetExpandAbbreviation')) return; - // might need to specify and indent more? - const selection = cm.doc.getSelection(); - if (selection.length > 0) { - cm.execCommand('indentMore'); - } else { - cm.replaceSelection(' '.repeat(INDENTATION_AMOUNT)); - } - }, - Enter: 'emmetInsertLineBreak', - Esc: 'emmetResetAbbreviation', - [`Shift-${metaKey}-E`]: (cm) => { - cm.getInputField().blur(); - }, - [renameKey]: (cm) => this.renameVariable(cm), - [`Shift-Tab`]: false, - [`${metaKey}-Enter`]: () => null, - [`Shift-${metaKey}-Enter`]: () => null, - [`${metaKey}-F`]: 'findPersistent', - [`Shift-${metaKey}-F`]: this.tidyCode, - [`${metaKey}-G`]: 'findPersistentNext', - [`Shift-${metaKey}-G`]: 'findPersistentPrev', - [replaceCommand]: 'replace', - // Cassie Tarakajian: If you don't set a default color, then when you - // choose a color, it deletes characters inline. This is a - // hack to prevent that. - [`${metaKey}-K`]: (cm, event) => - cm.state.colorpicker.popup_color_picker({ length: 0 }), - [`${metaKey}-.`]: 'toggleComment' // Note: most adblockers use the shortcut ctrl+. - }); - - this.initializeDocuments(this.props.files); - this._cm.swapDoc(this._docs[this.props.file.id]); - - this._cm.on( - 'change', - debounce(() => { - this.props.setUnsavedChanges(true); - this.props.hideRuntimeErrorWarning(); - this.props.updateFileContent(this.props.file.id, this._cm.getValue()); - - // Save a local backup to localStorage for crash recovery (#3891). - // This ensures work is recoverable even if the tab crashes - // (e.g. from an infinite loop) before the server autosave fires. - const projectId = this.props.project?.id || 'unsaved'; - saveLocalBackup(projectId, this.props.files); - - if (this.props.autorefresh) { - this.props.clearConsole(); - this.props.startSketch(); - } - }, 1000) - ); - - if (this._cm) { - this._cm.on('keyup', this.handleKeyUp); + if (lintMessages.length > 0 && lintWarning) { + beep.play(); } - - // Mobile autocomplete support (CM5 IME + contenteditable input) - const triggerHint = (cm) => { - const mode = cm.getOption('mode'); - if (mode !== 'css' && mode !== 'javascript') return; - - const cursor = cm.getCursor(); - const token = cm.getTokenAt(cursor); - - // Android keyboards often append a trailing space after each word. - // When that happens, stripping the space so the hinter sees the word. - if (token.string === ' ' && cursor.ch > 0 && cursor.ch === token.end) { - const prevToken = cm.getTokenAt({ - line: cursor.line, - ch: cursor.ch - 1 - }); - if (prevToken.string && /[a-z]/i.test(prevToken.string)) { - cm.replaceRange( - '', - { line: cursor.line, ch: cursor.ch - 1 }, - cursor, - '+trimHint' - ); - this.showHint(cm); - return; - } - } - if (token.string && /[a-z]/i.test(token.string)) { - this.showHint(cm); - } - }; - - // Desktop: fires on each keystroke via CM5's textarea input path. - this._cm.on('change', (_cm, changeObj) => { - if (changeObj.origin !== '+input') return; - if (/[a-z]/i.test(changeObj.text.join(''))) { - triggerHint(_cm); - } + }, 2000); + + // The useCodeMirror hook manages CodeMirror state and returns + // a reference to the actual CM instance. + const { + setupCodeMirrorOnContainerMounted, + teardownCodeMirror, + codemirrorView, + getContent, + tidyCode, + showSearch + } = useCodeMirror({ + project, + lineNumbers, + linewrap, + autocloseBracketsQuotes, + setUnsavedChanges, + updateFileContent, + file, + files, + autorefresh, + clearConsole, + startSketch, + autocompleteHinter, + fontSize, + updateLintingMessageAccessibility, + setCurrentLine, + referenceBaseUrl: getReferenceBaseUrl(htmlFile) + }); + + // Lets the parent component access file content-specific functionality... + useEffect(() => { + provideController({ + tidyCode, + getContent, + showSearch }); + }, [tidyCode, showSearch, getContent]); - // Mobile (word commit): fires when a composed word is accepted. - this._compositionEndHandler = () => { - setTimeout(() => { - if (this._cm) triggerHint(this._cm); - }, 150); - }; - this._cm - .getInputField() - .addEventListener('compositionend', this._compositionEndHandler); - - // Mobile (per-character): forces CM5 to process composing text - // during typing so autocomplete appears before keyboard dismissal. - this._compositionFlushTimer = null; - this._compositionUpdateHandler = (e) => { - if (!e.data || !/[a-z]/i.test(e.data)) return; - clearTimeout(this._compositionFlushTimer); - this._compositionFlushTimer = setTimeout(() => { - const display = this._cm && this._cm.display; - if (display && display.input && display.input.composing) { - display.input.composing.done = true; - display.input.readFromDOMSoon(); - } - }, 200); - }; - this._cm - .getInputField() - .addEventListener('compositionupdate', this._compositionUpdateHandler); - - this._cm.getWrapperElement().style[ - 'font-size' - ] = `${this.props.fontSize}px`; - - this.props.provideController({ - tidyCode: this.tidyCode, - showFind: this.showFind, - showReplace: this.showReplace, - getContent: this.getContent, - updateFileContent: this.updateFileContent - }); - } - - componentWillUpdate(nextProps) { - // check if files have changed - if (this.props.files[0].id !== nextProps.files[0].id) { - // then need to make CodeMirror documents - this.initializeDocuments(nextProps.files); - } - if (this.props.files.length !== nextProps.files.length) { - this.initializeDocuments(nextProps.files); - } - } - - componentDidUpdate(prevProps) { - if (this.props.file.id !== prevProps.file.id) { - const fileMode = this.getFileMode(this.props.file.name); - if (fileMode === 'javascript') { - // Define the new Emmet configuration based on the file mode - const emmetConfig = { - preview: ['html'], - markTagPairs: false, - autoRenameTags: true - }; - this._cm.setOption('emmet', emmetConfig); - } - const oldDoc = this._cm.swapDoc(this._docs[this.props.file.id]); - this._docs[prevProps.file.id] = oldDoc; - this._cm.focus(); - - if (!prevProps.unsavedChanges) { - setTimeout(() => this.props.setUnsavedChanges(false), 400); - } - } else if (this.getContent().content !== this.props.file.content) { - // TODO: make this not break regular edits! - // this._cm.setValue(this.props.file.content); - } - if (this.props.fontSize !== prevProps.fontSize) { - this._cm.getWrapperElement().style[ - 'font-size' - ] = `${this.props.fontSize}px`; - } - if (this.props.linewrap !== prevProps.linewrap) { - this._cm.setOption('lineWrapping', this.props.linewrap); - } - if (this.props.theme !== prevProps.theme) { - this._cm.setOption('theme', `p5-${this.props.theme}`); - } - if (this.props.lineNumbers !== prevProps.lineNumbers) { - this._cm.setOption('lineNumbers', this.props.lineNumbers); - } - if ( - this.props.autocloseBracketsQuotes !== prevProps.autocloseBracketsQuotes - ) { - this._cm.setOption( - 'autoCloseBrackets', - this.props.autocloseBracketsQuotes - ); - } - if (this.props.autocompleteHinter !== prevProps.autocompleteHinter) { - if (!this.props.autocompleteHinter) { - // close the hinter window once the preference is turned off - CodeMirror.showHint(this._cm, () => {}, {}); - } - } - - if (this.props.runtimeErrorWarningVisible) { - if (this.props.consoleEvents.length !== prevProps.consoleEvents.length) { - this.props.consoleEvents.forEach((consoleEvent) => { - if (consoleEvent.method === 'error') { - // It doesn't work if you create a new Error, but this works - // LOL - const errorObj = { stack: consoleEvent.data[0].toString() }; - StackTrace.fromError(errorObj).then((stackLines) => { - this.props.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 = this.props.files.find( - (f) => f.name === fileName && f.filePath === filePath - ); - this.props.setSelectedFile(fileWithError.id); - this._cm.addLineClass( - line.lineNumber - 1, - 'background', - 'line-runtime-error' - ); - }); - } - }); - } else { - for (let i = 0; i < this._cm.lineCount(); i += 1) { - this._cm.removeLineClass(i, 'background', 'line-runtime-error'); - } - } - } - - if (this.props.file.id !== prevProps.file.id) { - for (let i = 0; i < this._cm.lineCount(); i += 1) { - this._cm.removeLineClass(i, 'background', 'line-runtime-error'); - } - } - - this.props.provideController({ - tidyCode: this.tidyCode, - showFind: this.showFind, - showReplace: this.showReplace, - getContent: this.getContent, - updateFileContent: this.updateFileContent - }); - } - - componentWillUnmount() { - if (this._cm) { - this._cm.off('keyup', this.handleKeyUp); - const inputField = this._cm.getInputField(); - if (this._compositionEndHandler) { - inputField.removeEventListener( - 'compositionend', - this._compositionEndHandler - ); - } - if (this._compositionUpdateHandler) { - inputField.removeEventListener( - 'compositionupdate', - this._compositionUpdateHandler - ); - } - clearTimeout(this._compositionFlushTimer); - } - this.props.provideController(null); - } - - 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|mtl)$/i)) { - mode = 'x-shader/x-vertex'; - } else { - mode = 'text/plain'; - } - return mode; - } - - getContent() { - const content = this._cm.getValue(); - const updatedFile = Object.assign({}, this.props.file, { content }); - return updatedFile; - } - - updateFileContent(id, src) { - const file = this._docs[id]; - if (file) { - this._docs[id] = CodeMirror.Doc(src, this._docs[id].modeOption); - if (id === this.props.file.id) { - this._cm.swapDoc(this._docs[id]); - } - } - } - - handleKeyUp = () => { - const lineNumber = parseInt(this._cm.getCursor().line + 1, 10); - this.setState({ currentLine: lineNumber }); - }; - - showFind() { - this._cm.execCommand('findPersistent'); - } - - // temporary until p5.js 2.0 becomes default - // checks if sketch is using p5.js 2.0 to pass correct base url for autocomplete hinter reference - getReferenceBaseUrl = () => { - const html = this.props.htmlFile?.content || ''; - - const isV2 = - /https:\/\/beta\.p5js\.org\b/i.test(html) || /\bp5(@|-)2\./i.test(html); - - return isV2 ? 'https://beta.p5js.org' : 'https://p5js.org'; - }; - - showHint(_cm) { - if (!_cm) return; + // When the CM container div mounts, we set up CodeMirror. + const onContainerMounted = useCallback(setupCodeMirrorOnContainerMounted, []); - if (!this.props.autocompleteHinter) { - CodeMirror.showHint(_cm, () => {}, {}); - return; - } - - let focusedLinkElement = null; + // This is acting as a "componentDidMount" call where it runs once + // at the start and never again. It also provides a cleanup function. + useEffect(() => { + beep.current = new Audio(beepUrl); - 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' - ); - } - } + return () => { + provideController(null); + teardownCodeMirror(); }; - - const removeFocusedLinkElement = () => { - if (focusedLinkElement) { - focusedLinkElement.classList.remove('focused-hint-link'); - focusedLinkElement.parentElement.parentElement.classList.remove( - 'unfocused' + }, []); + + // Updates the runtime error console. + useEffect(() => { + const consoleErrors = consoleEvents.filter((e) => e.method === 'error'); + + if (consoleErrors.length > 0) { + const firstError = consoleErrors[0]; + const errorObj = { stack: firstError.data[0].toString() }; + StackTrace.fromError(errorObj).then((stackLines) => { + expandConsole(); + const line = stackLines.find( + (l) => l.fileName && l.fileName.startsWith('/') ); - focusedLinkElement = null; - return true; - } - return false; - }; - - const hintOptions = { - _fontSize: this.props.fontSize, - referenceBaseUrl: this.getReferenceBaseUrl(), - 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 - }; - - const triggerHints = () => { - if (_cm.options.mode === 'javascript') { - CodeMirror.showHint( - _cm, - () => { - const c = _cm.getCursor(); - const token = _cm.getTokenAt(c); - const hints = contextAwareHinter(_cm, { hinter: this.hinter }); - return { - list: hints, - from: CodeMirror.Pos(c.line, token.start), - to: CodeMirror.Pos(c.line, c.ch) - }; - }, - hintOptions + 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 ); - } else if (_cm.options.mode === 'css') { - CodeMirror.showHint(_cm, CodeMirror.hint.css, hintOptions); - } - }; - - setTimeout(triggerHints, 0); - } - - showReplace() { - this._cm.execCommand('replace'); - } - - prettierFormatWithCursor(parser, plugins) { - try { - const { formatted, cursorOffset } = prettier.formatWithCursor( - this._cm.doc.getValue(), - { - cursorOffset: this._cm.doc.indexFromPos(this._cm.doc.getCursor()), - parser, - plugins - } - ); - const { left, top } = this._cm.getScrollInfo(); - this._cm.doc.setValue(formatted); - this._cm.focus(); - this._cm.doc.setCursor(this._cm.doc.posFromIndex(cursorOffset)); - this._cm.scrollTo(left, top); - } catch (error) { - console.error(error); - } - } - - tidyCode() { - const mode = this._cm.getOption('mode'); - if (mode === 'javascript') { - this.prettierFormatWithCursor('babel', [babelParser]); - } else if (mode === 'css') { - this.prettierFormatWithCursor('css', [cssParser]); - } else if (mode === 'htmlmixed') { - this.prettierFormatWithCursor('html', [htmlParser]); - } - } - - renameVariable(cm) { - const cursorCoords = cm.cursorCoords(true, 'page'); - const selection = cm.getSelection(); - const pos = cm.getCursor(); // or selection start - const token = cm.getTokenAt(pos); - const tokenType = token.type; - if (!selection) { - return; + setSelectedFile(fileWithError.id); + addErrorDecoration(codemirrorView.current, line.lineNumber); + }); + } else { + removeErrorDecorations(codemirrorView.current); } - - const sel = cm.listSelections()[0]; - const fromPos = - CodeMirror.cmpPos(sel.anchor, sel.head) <= 0 ? sel.anchor : sel.head; - - showRenameDialog( - cm, - fromPos, - tokenType, - cursorCoords, - selection, - (newName) => { - if (newName && newName.trim() !== '' && newName !== selection) { - handleRename(fromPos, selection, newName, cm); - } - } - ); - } - - initializeDocuments(files) { - this._docs = {}; - files.forEach((file) => { - if (file.name !== 'root') { - this._docs[file.id] = CodeMirror.Doc( - file.content, - this.getFileMode(file.name) - ); // eslint-disable-line - } - }); - } - - render() { - const editorSectionClass = classNames({ - editor: true, - 'sidebar--contracted': !this.props.isExpanded - }); - - const editorHolderClass = classNames({ - 'editor-holder': true, - 'editor-holder--hidden': - this.props.file.fileType === 'folder' || this.props.file.url - }); - - const { currentLine } = this.state; - - return ( - - {(matches) => - matches ? ( -
-
- - -
- - {this.props.file.name} - - - -
-
-
{ - this.codemirrorContainer = element; + }, [consoleEvents]); + + const editorSectionClass = classNames({ + editor: true, + 'sidebar--contracted': !isExpanded + }); + + const editorHolderClass = classNames({ + 'editor-holder': true, + 'editor-holder--hidden': file.fileType === 'folder' || file.url + }); + + return ( + + {(matches) => + matches ? ( +
+
+ + +
+ + {file.name} + + + +
+
+
+ {file.url ? : null} + +
+ ) : ( + +
+ + + {file.name} + + +
+
+ + {file.url ? ( + ) : null}
- ) : ( - -
- - - {this.props.file.name} - - -
-
- { - this.codemirrorContainer = element; - }} - /> - {this.props.file.url ? ( - - ) : null} - -
-
- ) - } -
- ); - } + + ) + } + + ); } Editor.propTypes = { @@ -807,8 +280,6 @@ Editor.propTypes = { setUnsavedChanges: PropTypes.func.isRequired, startSketch: PropTypes.func.isRequired, autorefresh: PropTypes.bool.isRequired, - theme: PropTypes.string.isRequired, - unsavedChanges: PropTypes.bool.isRequired, files: PropTypes.arrayOf( PropTypes.shape({ id: PropTypes.string.isRequired, @@ -816,16 +287,14 @@ Editor.propTypes = { content: PropTypes.string.isRequired }) ).isRequired, + isExpanded: PropTypes.bool.isRequired, htmlFile: PropTypes.shape({ content: PropTypes.string }), - isExpanded: PropTypes.bool.isRequired, collapseSidebar: PropTypes.func.isRequired, closeProjectOptions: PropTypes.func.isRequired, expandSidebar: PropTypes.func.isRequired, clearConsole: PropTypes.func.isRequired, - hideRuntimeErrorWarning: PropTypes.func.isRequired, - runtimeErrorWarningVisible: PropTypes.bool.isRequired, provideController: PropTypes.func.isRequired, t: PropTypes.func.isRequired, setSelectedFile: PropTypes.func.isRequired, diff --git a/client/modules/IDE/components/Editor/utils/completionPreview.js b/client/modules/IDE/components/Editor/utils/completionPreview.js new file mode 100644 index 0000000000..8293735d7e --- /dev/null +++ b/client/modules/IDE/components/Editor/utils/completionPreview.js @@ -0,0 +1,114 @@ +import { StateField, RangeSetBuilder } from '@codemirror/state'; +import { Decoration, EditorView, WidgetType } from '@codemirror/view'; +import { selectedCompletion, completionStatus } from '@codemirror/autocomplete'; + +/** + * Creates a "ghost" of what the completion would look like, for autocomplete selections. + * + * This is currently only used in conjection with p5Javascript language support, + * but it could be moved to stateUtils and used with other languages as well. + */ +class GhostTextWidget extends WidgetType { + constructor(text) { + super(); + this.text = text; + } + + eq(other) { + return other.text === this.text; + } + + toDOM() { + const span = document.createElement('span'); + span.className = 'cm-ghostCompletion'; + span.textContent = this.text; + return span; + } + + ignoreEvent() { + return true; + } +} + +function getCurrentWord(state) { + const { from, to, empty } = state.selection.main; + if (!empty) return null; + + const line = state.doc.lineAt(from); + const before = line.text.slice(0, from - line.from); + const match = before.match(/\w+$/); + + if (!match) return null; + + const word = match[0]; + return { + text: word, + from: from - word.length, + to + }; +} + +function buildGhostText(state) { + // only show ghost text if autocomplete is on, + // user is typing, and if preview matches typed text + + if (completionStatus(state) !== 'active') return null; + + const selected = selectedCompletion(state); + if (!selected) return null; + + const word = getCurrentWord(state); + if (!word) return null; + + const preview = selected.preview || selected.label; + if (!preview) return null; + + if (!preview.toLowerCase().startsWith(word.text.toLowerCase())) return null; + + const remainder = preview.slice(word.text.length); + if (!remainder) return null; + + return { + pos: word.to, + text: remainder + }; +} + +const ghostTextField = StateField.define({ + create(state) { + return Decoration.none; + }, + + update(deco, tr) { + const decorationBuilder = new RangeSetBuilder(); + const ghost = buildGhostText(tr.state); + + if (ghost) { + decorationBuilder.add( + ghost.pos, + ghost.pos, + Decoration.widget({ + widget: new GhostTextWidget(ghost.text), + side: 1 + }) + ); + } + + return decorationBuilder.finish(); + }, + + provide: (field) => EditorView.decorations.from(field) +}); + +export const completionPreviewTheme = EditorView.theme({ + '.cm-ghostCompletion': { + opacity: '0.55', + fontStyle: 'italic', + pointerEvents: 'none', + whiteSpace: 'pre' + } +}); + +export function completionPreview() { + return [ghostTextField, completionPreviewTheme]; +} diff --git a/client/modules/IDE/components/Editor/utils/consoleErrorDecoration.js b/client/modules/IDE/components/Editor/utils/consoleErrorDecoration.js new file mode 100644 index 0000000000..ce3890bcca --- /dev/null +++ b/client/modules/IDE/components/Editor/utils/consoleErrorDecoration.js @@ -0,0 +1,52 @@ +import { StateField, StateEffect } from '@codemirror/state'; +import { EditorView, Decoration } from '@codemirror/view'; + +// Effects for communicating with the state field +const ADD_ERROR_DECORATION = StateEffect.define(); +const FILTER_ERROR_DECORATION = StateEffect.define(); + +// An extension for managing error line decorations +// Mostly adapted from the Marked Text demo in https://codemirror.net/docs/migration/ +// You can affect this by calling addErrorDecoration and removeErrorDecorations +export const errorDecorationStateField = StateField.define({ + // Start with an empty set of decorations + create() { + return Decoration.none; + }, + // This is called whenever the editor updates + update(value, transaction) { + // Move the decorations to account for document changes + let newValue = value.map(transaction.changes); + for (let i = 0; i < transaction.effects.length; i++) { + const effect = transaction.effects[i]; + if (effect.is(ADD_ERROR_DECORATION)) + newValue = newValue.update({ add: effect.value, sort: true }); + else if (effect.is(FILTER_ERROR_DECORATION)) + newValue = newValue.update({ filter: effect.value }); + } + return newValue; + }, + // Indicate that this field provides a set of decorations + provide: (f) => EditorView.decorations.from(f) +}); + +const ERROR_DECORATION = Decoration.line({ + class: 'cm-errorLine' // Defined in _editor.scss +}); + +// Add an error decoration to a specific line number +export function addErrorDecoration(view, lineNumber) { + const docLineNumber = view.state.doc.line(lineNumber); + view.dispatch({ + effects: ADD_ERROR_DECORATION.of([ + ERROR_DECORATION.range(docLineNumber.from) + ]) + }); +} + +// Remove all error decorations +export function removeErrorDecorations(view) { + view.dispatch({ + effects: FILTER_ERROR_DECORATION.of(() => false) + }); +} diff --git a/client/modules/IDE/components/Editor/utils/extensionCustomStyles.js b/client/modules/IDE/components/Editor/utils/extensionCustomStyles.js new file mode 100644 index 0000000000..47585d6572 --- /dev/null +++ b/client/modules/IDE/components/Editor/utils/extensionCustomStyles.js @@ -0,0 +1,113 @@ +/** + * This file contains utility functions for creating custom styled + * elements for our CodeMirror extensions. */ + +/** + * Returns completion options configured for autocomplete. + * This gets passed into the autocomplete extension in + * stateUtils when creating a new file state. + */ +export const createAutocompleteOptions = (referenceBaseUrl) => ({ + selectOnOpen: false, + tooltipClass: () => 'CodeMirror-hints', + closeOnBlur: false, + icons: false, + + // handle css classes + optionClass(completion) { + let className = 'CodeMirror-hint'; + + if (completion.type) { + className += ` hint-type-${completion.type}`; + } + + if (completion.p5DocPath) { + className += ' has-doc-link'; + } + + return className; + }, + + addToOptions: [ + { + position: 60, + render(completion) { + const kind = document.createElement('span'); + kind.className = 'cm-completionKind'; + kind.textContent = completion.kindLabel || completion.type || ''; + return kind; + } + }, + { + position: 80, + render(completion, state, view) { + if (!completion.p5DocPath) return null; + + const link = document.createElement('a'); + link.className = 'cm-completionRefLink'; + link.href = `${referenceBaseUrl}/reference/p5/${completion.p5DocPath}`; + link.target = '_blank'; + link.rel = 'noopener noreferrer'; + link.tabIndex = -1; + link.setAttribute('aria-label', `Open ${completion.label} reference`); + + link.innerHTML = ` + open ${completion.label} reference + + `; + + link.addEventListener('mousedown', (event) => { + event.preventDefault(); + event.stopPropagation(); + }); + + link.addEventListener('click', (event) => { + event.stopPropagation(); + }); + + link.addEventListener('keydown', (event) => { + if (event.key === 'ArrowLeft' || event.key === 'Escape') { + event.preventDefault(); + event.stopPropagation(); + link.classList.remove('focused-hint-link'); + view.focus(); + } + }); + + return link; + } + }, + { + position: 100, + render(completion) { + if (!completion.blacklisted) return null; + + const warning = document.createElement('div'); + warning.className = 'cm-completionWarning'; + + const icon = document.createElement('span'); + icon.className = 'cm-completionWarningIcon'; + icon.setAttribute('aria-hidden', 'true'); + icon.textContent = '⚠️'; + + const text = document.createElement('span'); + text.className = 'cm-completionWarningText'; + text.textContent = 'use with caution in this context'; + + warning.appendChild(icon); + warning.appendChild(text); + + return warning; + } + } + ] +}); + +/** Used in the fold gutter extension. */ +export function createFoldMarker(open) { + // Uses window.document explicitly to avoid shadowing by the `document` + // parameter in createNewFileState below. + const span = window.document.createElement('span'); + span.className = open ? 'cm-fold-open' : 'cm-fold-closed'; + return span; +} diff --git a/client/modules/IDE/components/Editor/utils/fileState.js b/client/modules/IDE/components/Editor/utils/fileState.js new file mode 100644 index 0000000000..49961dc357 --- /dev/null +++ b/client/modules/IDE/components/Editor/utils/fileState.js @@ -0,0 +1,286 @@ +import { EditorState, Compartment } from '@codemirror/state'; +import { + EditorView, + lineNumbers as lineNumbersExt, + highlightActiveLine, + highlightActiveLineGutter, + gutters, + keymap, + highlightSpecialChars, + drawSelection, + dropCursor, + rectangularSelection, + crosshairCursor +} from '@codemirror/view'; +import { + foldGutter, + bracketMatching, + indentOnInput, + syntaxHighlighting +} from '@codemirror/language'; +import { autocompletion, closeBrackets } from '@codemirror/autocomplete'; +import { highlightSelectionMatches, search } from '@codemirror/search'; +import { history } from '@codemirror/commands'; +import { lintGutter, linter } from '@codemirror/lint'; +import { + expandAbbreviation, + abbreviationTracker +} from '@emmetio/codemirror6-plugin'; + +import { css } from '@codemirror/lang-css'; +import { html } from '@codemirror/lang-html'; +import { json } from '@codemirror/lang-json'; +import { xml } from '@codemirror/lang-xml'; +import { emmetConfig } from '@emmetio/codemirror6-plugin'; +import { color as colorPicker } from '@connieye/codemirror-color-picker'; + +import { p5JavaScript } from './p5JavaScript'; +import { highlightStyle } from './highlightStyle'; +import { errorDecorationStateField } from './consoleErrorDecoration'; +import { + makeCssLinter, + makeHtmlLinter, + makeJsonLinter, + makeJavascriptLinter +} from './linters'; +import { emmetKeymaps, buildKeymaps } from './keymaps'; +import { + createAutocompleteOptions, + createFoldMarker +} from './extensionCustomStyles'; + +// ----- TODOS ----- +// - shader syntax highlighting + +/** Detects what mode the file is based on the name. */ +export function getFileMode(fileName) { + let mode; + if (fileName.match(/.+\.js$/i)) { + mode = 'javascript'; + } else if (fileName.match(/.+\.css$/i)) { + mode = 'css'; + } else if (fileName.match(/.+\.(html)$/i)) { + mode = 'html'; + } else if (fileName.match(/.+\.(xml)$/i)) { + mode = 'xml'; + } 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|mtl)$/i)) { + mode = 'x-shader/x-vertex'; + } else { + mode = 'text/plain'; + } + return mode; +} + +function getFileLanguage(fileName) { + const fileMode = getFileMode(fileName); + + switch (fileMode) { + case 'javascript': + return p5JavaScript; + case 'css': + return css; + case 'html': + return html; + case 'xml': + return xml; + case 'application/json': + return json; + default: + return null; + } +} + +function getFileLinter(fileName, callback) { + const fileMode = getFileMode(fileName); + + switch (fileMode) { + case 'javascript': + return makeJavascriptLinter(); + case 'html': + return linter(makeHtmlLinter(callback)); + case 'css': + return linter(makeCssLinter(callback)); + case 'application/json': + return linter(makeJsonLinter(callback)); + default: + return null; + } +} + +function getFileEmmetConfig(fileName) { + const fileMode = getFileMode(fileName); + + switch (fileMode) { + case 'html': + return emmetConfig.of({ syntax: 'html' }); + case 'css': + return emmetConfig.of({ syntax: 'css' }); + default: + return null; + } +} + +/** + * Creates a new CodeMirror editor state with configurations, + * extensions, and keymaps tailored to the file type and settings. + * + * Returns a "file state" object containing the CodeMirror state and compartments. + */ +export function createNewFileState(filename, document, settings) { + const { + linewrap, + lineNumbers, + autocomplete, + autocloseBracketsQuotes, + onUpdateLinting, + onViewUpdate, + referenceBaseUrl, + fontSize + } = settings; + const lineNumbersCpt = new Compartment(); + const lineWrappingCpt = new Compartment(); + const closeBracketsCpt = new Compartment(); + const autocompleteCpt = new Compartment(); + const fontSizeCpt = new Compartment(); + + // Depending on the file mode, we have a different tidier function. + // Keep this binding local to each file state so modes don't accumulate + // across files via a shared module-level array. + const mode = getFileMode(filename); + + const keymaps = buildKeymaps(mode); + + // https://github.com/codemirror/basic-setup/blob/main/src/codemirror.ts + const extensions = [ + // The first few extensions can be toggled on or off. + fontSizeCpt.of(EditorView.theme({ '&': { fontSize: `${fontSize}px` } })), + lineNumbersCpt.of(lineNumbers ? lineNumbersExt() : []), + lineWrappingCpt.of(linewrap ? EditorView.lineWrapping : []), + closeBracketsCpt.of(autocloseBracketsQuotes ? closeBrackets() : []), + autocompleteCpt.of( + autocomplete + ? autocompletion(createAutocompleteOptions(referenceBaseUrl)) + : [] + ), + + // Everything below here should always be on. + history(), + search(), + // Highlight extensions + highlightActiveLine(), + highlightActiveLineGutter(), + highlightSpecialChars(), + highlightSelectionMatches(), + syntaxHighlighting(highlightStyle), + // Selection extensions + drawSelection(), + rectangularSelection(), + dropCursor(), + crosshairCursor(), + EditorState.allowMultipleSelections.of(true), + // Gutter extensions + gutters({ fixed: false }), + foldGutter({ markerDOM: createFoldMarker }), + // Misc extensions + indentOnInput(), + bracketMatching(), + errorDecorationStateField, + + // Setup the event listeners on the CodeMirror instance. + EditorView.updateListener.of(onViewUpdate) + ]; + + // Only enable the color picker for Javascript and CSS, which + // have both been tested. + if (mode === 'javascript' || mode === 'css') { + extensions.push(colorPicker); + } + + const fileLanguage = getFileLanguage(filename); + const fileLinter = getFileLinter(filename, onUpdateLinting); + const fileEmmetConfig = getFileEmmetConfig(filename); + + if (fileLanguage) { + extensions.push(fileLanguage()); + } + if (fileLinter) { + extensions.push(fileLinter); + extensions.push(lintGutter()); + } + + // If it's HTML or CSS, we add some emmet-specific configs. + if (fileEmmetConfig) { + extensions.push(fileEmmetConfig); + extensions.push(abbreviationTracker()); + extensions.push( + EditorView.domEventHandlers({ + paste(event, view) { + setTimeout(() => { + expandAbbreviation(view); + }, 0); + } + }) + ); + keymaps.push(emmetKeymaps); + } + + // Now add the keymaps... + extensions.push(keymap.of(keymaps.flat())); + + // Create the state with document content if we have it. + const stateOptions = { + extensions + }; + if (document) { + stateOptions.doc = document; + } + + const cmState = EditorState.create(stateOptions); + return { + cmState, + lineNumbersCpt, + lineWrappingCpt, + closeBracketsCpt, + autocompleteCpt, + fontSizeCpt + }; +} + +/** + * Given a reconfigure effect, this function will update all + * of the file states. + * + * We need to do this whenever the settings like line numbers + * change, so it will get called in the useEffect hooks. + */ +export function updateFileStates({ + fileStates, + cmView, + file: currentFile, + reconfigureEffect +}) { + if (!fileStates) return; + + Object.entries(fileStates).forEach(([fileId, fileState]) => { + // Either grab the current state from the view or saved in the fileStates. + let { cmState } = fileState; + if (fileId === currentFile.id) { + cmState = cmView.state; + } + + // Apply the new effects and grab the new state. + const { state: newCmState } = cmState.update({ + effects: reconfigureEffect(fileState) + }); + + // Save the new states and update the view for the currently open file. + fileStates[fileId].cmState = newCmState; + if (fileId === currentFile.id) { + cmView.setState(newCmState); + } + }); +} diff --git a/client/modules/IDE/components/Editor/utils/fileState.test.js b/client/modules/IDE/components/Editor/utils/fileState.test.js new file mode 100644 index 0000000000..6b032fb4ae --- /dev/null +++ b/client/modules/IDE/components/Editor/utils/fileState.test.js @@ -0,0 +1,272 @@ +/** + * @jest-environment jsdom + */ +import { EditorView, runScopeHandlers } from '@codemirror/view'; +import { createNewFileState, getFileMode } from './fileState'; + +const defaultSettings = { + linewrap: false, + lineNumbers: false, + autocomplete: false, + autocloseBracketsQuotes: false, + onUpdateLinting: () => {}, + onViewUpdate: () => {}, + referenceBaseUrl: 'https://p5js.org' +}; + +function mountFile(filename, doc) { + const { cmState } = createNewFileState(filename, doc, defaultSettings); + const parent = document.createElement('div'); + document.body.appendChild(parent); + const view = new EditorView({ state: cmState, parent }); + return view; +} + +function pressTidyShortcut(view) { + const event = new KeyboardEvent('keydown', { + key: 'F', + code: 'KeyF', + shiftKey: true, + ctrlKey: true + }); + runScopeHandlers(view, event, 'editor'); +} + +describe('createNewFileState — Tidy keyboard shortcut', () => { + afterEach(() => { + document.body.innerHTML = ''; + }); + + // Regression test for https://github.com/processing/p5.js-web-editor/issues/4093 + // Each file's Shift-Mod-F binding must only tidy with its own mode, regardless + // of the order in which the file states were created. + it('tidies each file with its own mode regardless of creation order', () => { + const jsView = mountFile('sketch.js', `function foo(){console.log("hi")}`); + const cssView = mountFile('style.css', `body{margin:0;padding:0;}`); + const htmlView = mountFile('index.html', `hello`); + + pressTidyShortcut(jsView); + pressTidyShortcut(cssView); + pressTidyShortcut(htmlView); + + expect(jsView.state.doc.toString()).toBe( + 'function foo() {\n console.log("hi");\n}\n' + ); + expect(cssView.state.doc.toString()).toBe( + 'body {\n margin: 0;\n padding: 0;\n}\n' + ); + expect(htmlView.state.doc.toString()).toBe( + '\n \n hello\n \n\n' + ); + }); +}); + +describe('createNewFileState - Settings', () => { + it('Enables line wrap', () => { + const fileName = 'file1.js'; + const content = ``; + const settings = { + linewrap: true, + lineNumbers: true, + autocomplete: false, + autocloseBracketsQuotes: false, + onUpdateLinting: jest.fn(), + onViewUpdate: jest.fn() + }; + const result = createNewFileState(fileName, content, settings); + + const parent = document.createElement('div'); + const cmView = new EditorView({ state: result.cmState, parent }); + const div = parent.querySelector('.cm-lineWrapping'); + + expect(div).not.toBeNull(); + cmView.destroy(); + }); + + it('Disable line wrap', () => { + const fileName = 'file1.js'; + const content = ``; + const settings = { + linewrap: false, + lineNumbers: true, + autocomplete: false, + autocloseBracketsQuotes: false, + onUpdateLinting: jest.fn(), + onViewUpdate: jest.fn() + }; + const result = createNewFileState(fileName, content, settings); + + const parent = document.createElement('div'); + const cmView = new EditorView({ state: result.cmState, parent }); + const div = parent.querySelector('.cm-lineWrapping'); + + expect(div).toBeNull(); + cmView.destroy(); + }); + + it('Enables line numbers', () => { + const fileName = 'file1.js'; + const content = ``; + const settings = { + linewrap: false, + lineNumbers: true, + autocomplete: false, + autocloseBracketsQuotes: false, + onUpdateLinting: jest.fn(), + onViewUpdate: jest.fn() + }; + const result = createNewFileState(fileName, content, settings); + + const parent = document.createElement('div'); + const cmView = new EditorView({ state: result.cmState, parent }); + const div = parent.querySelector('.cm-lineNumbers'); + + expect(div).not.toBeNull(); + cmView.destroy(); + }); + + it('Disable line numbers', () => { + const fileName = 'file1.js'; + const content = ``; + const settings = { + linewrap: false, + lineNumbers: false, + autocomplete: false, + autocloseBracketsQuotes: false, + onUpdateLinting: jest.fn(), + onViewUpdate: jest.fn() + }; + const result = createNewFileState(fileName, content, settings); + + const parent = document.createElement('div'); + const cmView = new EditorView({ state: result.cmState, parent }); + const div = parent.querySelector('.cm-lineNumbers'); + + expect(div).toBeNull(); + cmView.destroy(); + }); + + it('Enables autoclose brackets and quotes', () => { + const fileName = 'file1.js'; + const content = ``; + const settings = { + linewrap: false, + lineNumbers: false, + autocomplete: false, + autocloseBracketsQuotes: true, + onUpdateLinting: jest.fn(), + onViewUpdate: jest.fn() + }; + + const result = createNewFileState(fileName, content, settings); + + expect(result.closeBracketsCpt.get(result.cmState).length).toBeGreaterThan( + 0 + ); + }); + + it('Disable autoclose brackets and quotes', () => { + const fileName = 'file1.js'; + const content = ``; + const settings = { + linewrap: false, + lineNumbers: false, + autocomplete: false, + autocloseBracketsQuotes: false, + onUpdateLinting: jest.fn(), + onViewUpdate: jest.fn() + }; + + const result = createNewFileState(fileName, content, settings); + + expect(result.closeBracketsCpt.get(result.cmState).length).toBe(0); + }); +}); + +describe('getFileMode', () => { + it('Returns correct javascript file mode', () => { + const fileName = 'file1.js'; + const mode = getFileMode(fileName); + const expectedMode = 'javascript'; + + expect(mode).toBe(expectedMode); + }); + + it('Returns correct css file mode', () => { + const fileName = 'file1.css'; + const mode = getFileMode(fileName); + const expectedMode = 'css'; + + expect(mode).toBe(expectedMode); + }); + + it('Returns correct html file mode', () => { + const fileName = 'file1.html'; + const mode = getFileMode(fileName); + const expectedMode = 'html'; + + expect(mode).toBe(expectedMode); + }); + + it('Returns correct xml file mode', () => { + const fileName = 'file1.xml'; + const mode = getFileMode(fileName); + const expectedMode = 'xml'; + + expect(mode).toBe(expectedMode); + }); + + it('Returns correct json file mode', () => { + const fileName = 'file1.json'; + const mode = getFileMode(fileName); + const expectedMode = 'application/json'; + + expect(mode).toBe(expectedMode); + }); + + it('Returns correct frag|glsl file mode', () => { + const fileName = 'file1.frag'; + const fileName2 = 'file2.glsl'; + const mode = getFileMode(fileName); + const mode2 = getFileMode(fileName2); + const expectedMode = 'x-shader/x-fragment'; + + expect(mode).toBe(expectedMode); + expect(mode2).toBe(expectedMode); + }); + + it('Returns correct vert|stl|mtl file mode', () => { + const fileName = 'file1.vert'; + const fileName2 = 'file2.stl'; + const fileName3 = 'file3.mtl'; + const mode = getFileMode(fileName); + const mode2 = getFileMode(fileName2); + const mode3 = getFileMode(fileName3); + const expectedMode = 'x-shader/x-vertex'; + + expect(mode).toBe(expectedMode); + expect(mode2).toBe(expectedMode); + expect(mode3).toBe(expectedMode); + }); + + it('Returns plain text otherwise file mode', () => { + const fileName = 'file1.py'; + const mode = getFileMode(fileName); + const expectedMode = 'text/plain'; + expect(mode).toBe(expectedMode); + }); + + it('Empty fileName', () => { + const fileName = ''; + const mode = getFileMode(fileName); + const expectedMode = 'text/plain'; + expect(mode).toBe(expectedMode); + }); + + it('Unknown fileName', () => { + const fileName = 'file1.xyz'; + const mode = getFileMode(fileName); + const expectedMode = 'text/plain'; + expect(mode).toBe(expectedMode); + }); +}); diff --git a/client/modules/IDE/components/Editor/utils/highlightStyle.js b/client/modules/IDE/components/Editor/utils/highlightStyle.js new file mode 100644 index 0000000000..c7c589869b --- /dev/null +++ b/client/modules/IDE/components/Editor/utils/highlightStyle.js @@ -0,0 +1,54 @@ +import { HighlightStyle } from '@codemirror/language'; +import { tags } from '@lezer/highlight'; + +export const highlightStyle = HighlightStyle.define([ + { tag: tags.comment, class: 'cm-comment' }, + { tag: tags.lineComment, class: 'cm-comment' }, + { tag: tags.blockComment, class: 'cm-comment' }, + { tag: tags.docComment, class: 'cm-comment' }, + { tag: tags.docString, class: 'cm-comment' }, + { tag: tags.name, class: 'cm-variable' }, + { tag: tags.variableName, class: 'cm-variable' }, + { tag: tags.typeName, class: 'cm-variable' }, + { tag: tags.className, class: 'cm-variable' }, + { tag: tags.string, class: 'cm-string' }, + { tag: tags.character, class: 'cm-string' }, + { tag: tags.attributeName, class: 'cm-string' }, + { tag: tags.regexp, class: 'cm-regexp' }, + { tag: tags.number, class: 'cm-number' }, + { tag: tags.integer, class: 'cm-number' }, + { tag: tags.float, class: 'cm-number' }, + { tag: tags.atom, class: 'cm-atom' }, + { tag: tags.bool, class: 'cm-atom' }, + { tag: tags.null, class: 'cm-atom' }, + { tag: tags.keyword, class: 'cm-keyword' }, + { tag: tags.self, class: 'cm-keyword' }, + { tag: tags.function, class: 'cm-keyword' }, + { tag: tags.operator, class: 'cm-operator' }, + { tag: tags.operatorKeyword, class: 'cm-operator' }, + { tag: tags.controlKeyword, class: 'cm-operator' }, + { tag: tags.derefOperator, class: 'cm-operator' }, + { tag: tags.arithmeticOperator, class: 'cm-operator' }, + { tag: tags.logicOperator, class: 'cm-operator' }, + { tag: tags.bitwiseOperator, class: 'cm-operator' }, + { tag: tags.compareOperator, class: 'cm-operator' }, + { tag: tags.updateOperator, class: 'cm-operator' }, + { tag: tags.typeOperator, class: 'cm-operator' }, + { tag: tags.controlOperator, class: 'cm-operator' }, + { tag: tags.definitionKeyword, class: 'cm-keyword' }, + { tag: tags.tagName, class: 'cm-tag' }, + { tag: tags.heading, class: 'cm-tag' }, + { tag: tags.heading1, class: 'cm-tag' }, + { tag: tags.heading2, class: 'cm-tag' }, + { tag: tags.heading3, class: 'cm-tag' }, + { tag: tags.heading4, class: 'cm-tag' }, + { tag: tags.heading5, class: 'cm-tag' }, + { tag: tags.heading6, class: 'cm-tag' }, + { tag: tags.list, class: 'cm-tag' }, + { tag: tags.quote, class: 'cm-tag' }, + { tag: tags.emphasis, class: 'cm-tag' }, + { tag: tags.strong, class: 'cm-tag' }, + { tag: tags.link, class: 'cm-tag' }, + { tag: tags.propertyName, class: 'cm-property' }, + { tag: tags.attributeName, class: 'cm-attribute' } +]); diff --git a/client/modules/IDE/components/Editor/utils/keymaps.js b/client/modules/IDE/components/Editor/utils/keymaps.js new file mode 100644 index 0000000000..545058bec1 --- /dev/null +++ b/client/modules/IDE/components/Editor/utils/keymaps.js @@ -0,0 +1,136 @@ +import { + completionStatus, + selectedCompletionIndex, + closeBracketsKeymap +} from '@codemirror/autocomplete'; +import { + insertTab, + indentLess, + defaultKeymap, + historyKeymap +} from '@codemirror/commands'; +import { foldKeymap } from '@codemirror/language'; +import { searchKeymap } from '@codemirror/search'; +import { expandAbbreviation } from '@emmetio/codemirror6-plugin'; +import { tidyCodeWithPrettier } from './tidier'; + +function getColorPickerAtSelection(view) { + const { head } = view.state.selection.main; + const { node } = view.domAtPos(head); + + const startEl = + node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement; + + const lineEl = startEl?.closest('.cm-line'); + + return ( + lineEl?.querySelector('input[type="color"]:not(:disabled)') || + view.contentDOM.querySelector('input[type="color"]:not(:disabled)') + ); +} + +export function focusOnReferenceArrow(view) { + if (completionStatus(view.state) !== 'active') return false; + + const selectedIndex = selectedCompletionIndex(view.state); + if (selectedIndex == null || selectedIndex < 0) return false; + + const tooltip = view.dom.querySelector('.cm-tooltip-autocomplete'); + if (!tooltip) return false; + + const options = tooltip.querySelectorAll('li.CodeMirror-hint'); + const selectedOption = options[selectedIndex]; + if (!selectedOption) return false; + + const link = selectedOption.querySelector('.cm-completionRefLink'); + if (!link) return false; + + link.focus(); + link.classList.add('focused-hint-link'); + + const cleanup = () => { + link.classList.remove('focused-hint-link'); + link.removeEventListener('blur', cleanup); + }; + link.addEventListener('blur', cleanup); + + return true; +} + +export function openColorPickerWithKeyboard(view) { + const picker = getColorPickerAtSelection(view); + + if (!picker || picker.disabled) { + return false; + } + + picker.focus(); + + if (typeof picker.showPicker === 'function') { + picker.showPicker(); + } else { + picker.click(); + } + return true; +} + +// Extra custom keymaps. +// TODO: We need to add sublime mappings + other missing extra mappings here. +export const extraKeymaps = [ + { + key: 'Mod-Enter', + run: () => true + }, + { key: 'ArrowRight', run: focusOnReferenceArrow }, + { key: 'Tab', run: insertTab, shift: indentLess } +]; + +export const emmetKeymaps = [{ key: 'Tab', run: expandAbbreviation }]; + +function createColorPickerKeymap(mode) { + const colorPickerKeymap = []; + if (mode === 'css' || mode === 'javascript') { + colorPickerKeymap.push({ + key: 'Mod-k', + run: (view) => openColorPickerWithKeyboard(view) + }); + } + return colorPickerKeymap; +} + +function createFileTidyKeymap(mode) { + // Make a keymap for both uppercase and lowercase F, since + // browsers can differ in which one they send for the Shift-Mod-F shortcut. + return [ + { + key: 'Shift-Mod-F', + run: (cmView) => { + tidyCodeWithPrettier(cmView, mode); + return true; + } + }, + { + key: 'Shift-Mod-f', + run: (cmView) => { + tidyCodeWithPrettier(cmView, mode); + return true; + } + } + ]; +} + +export function buildKeymaps(mode) { + const colorPickerKeymap = createColorPickerKeymap(mode); + const fileTidyKeymap = createFileTidyKeymap(mode); + + return [ + extraKeymaps, + colorPickerKeymap, + fileTidyKeymap, + closeBracketsKeymap, + defaultKeymap, + historyKeymap, + foldKeymap, + searchKeymap + ]; +} diff --git a/client/modules/IDE/components/Editor/utils/linters.js b/client/modules/IDE/components/Editor/utils/linters.js new file mode 100644 index 0000000000..f00afccf67 --- /dev/null +++ b/client/modules/IDE/components/Editor/utils/linters.js @@ -0,0 +1,108 @@ +import { jsonParseLinter } from '@codemirror/lang-json'; +import { linter } from '@codemirror/lint'; +import { HTMLHint } from 'htmlhint'; +import { CSSLint } from 'csslint'; +import { esLint } from '@codemirror/lang-javascript'; +import { Linter as ESLinter } from 'eslint-linter-browserify'; + +// https://github.com/codemirror/codemirror5/blob/master/addon/lint/html-lint.js +const HTMLHINT_OPTIONS = { + 'tagname-lowercase': true, + 'attr-lowercase': true, + 'attr-value-double-quotes': true, + 'doctype-first': false, + 'tag-pair': true, + 'spec-char-escape': true, + 'id-unique': true, + 'src-not-empty': true, + 'attr-no-duplication': true +}; + +const ESLINT_CONFIG = { + languageOptions: { + ecmaVersion: 2021 + }, + rules: { + semi: 'off', + eqeqeq: 'off' + } +}; + +const eslint = new ESLinter(); + +export function makeJavascriptLinter() { + return linter(esLint(eslint, ESLINT_CONFIG)); +} + +export function makeCssLinter(callback) { + return (view) => { + const documentContent = view.state.doc.toString(); + const { messages } = CSSLint.verify(documentContent, {}); + const diagnostics = []; + + messages.forEach((message) => { + if (!message) return; + + const { + line: messageLine, + col: messageCharacter, + type: messageType, + message: messageText + } = message; + const cmLine = view.state.doc.line(messageLine); + + const start = cmLine.from + messageCharacter - 1; + const end = cmLine.to; + + diagnostics.push({ + from: start, + to: end, + severity: messageType, + message: messageText + }); + }); + + if (callback) callback(diagnostics); + return diagnostics; + }; +} + +export function makeHtmlLinter(callback) { + return (view) => { + const documentContent = view.state.doc.toString(); + const messages = HTMLHint.verify(documentContent, HTMLHINT_OPTIONS) || []; + const diagnostics = []; + + messages.forEach((message) => { + if (!message) return; + + const { + line: messageLine, + col: messageCharacter, + type: messageType, + message: messageText + } = message; + const cmLine = view.state.doc.line(messageLine); + + // TODO: Can we to do the to/from smarter? + diagnostics.push({ + from: cmLine.from + messageCharacter - 1, + to: cmLine.from + messageCharacter, + severity: messageType, + message: messageText + }); + }); + + if (callback) callback(diagnostics); + return diagnostics; + }; +} + +export function makeJsonLinter(callback) { + const baseJsonLinter = jsonParseLinter(); + return (view) => { + const diagnostics = baseJsonLinter(view); + if (callback) callback(diagnostics); + return diagnostics; + }; +} diff --git a/client/modules/IDE/components/Editor/utils/p5JavaScript.js b/client/modules/IDE/components/Editor/utils/p5JavaScript.js new file mode 100644 index 0000000000..c24b1de059 --- /dev/null +++ b/client/modules/IDE/components/Editor/utils/p5JavaScript.js @@ -0,0 +1,79 @@ +import { LanguageSupport, syntaxTree } from '@codemirror/language'; +import { javascript } from '@codemirror/lang-javascript'; +import { ViewPlugin, Decoration } from '@codemirror/view'; +import { RangeSetBuilder } from '@codemirror/state'; +import { p5Hinter } from '../../../../../utils/p5-hinter'; +import { completionPreview } from './completionPreview'; +import contextAwareHinter from '../../../../../utils/contextAwareHinter'; +import { + p5FunctionKeywords, + p5VariableKeywords +} from '../../../../../utils/p5-keywords'; + +const p5Functions = new Set(Object.keys(p5FunctionKeywords)); +const p5Variables = new Set(Object.keys(p5VariableKeywords)); + +const p5FunctionMark = Decoration.mark({ class: 'cm-p5-function' }); +const p5VariableMark = Decoration.mark({ class: 'cm-p5-variable' }); + +// Used to add highlighting to the p5-specific keywords. +function buildHighlightDecorations(view) { + const builder = new RangeSetBuilder(); + view.visibleRanges.forEach(({ from, to }) => { + syntaxTree(view.state).iterate({ + from, + to, + enter(node) { + const isVariable = node.name === 'VariableName'; + const isDefinition = node.name === 'VariableDefinition'; + if (!isVariable && !isDefinition) return; + const name = view.state.doc.sliceString(node.from, node.to); + if (p5Functions.has(name)) { + builder.add(node.from, node.to, p5FunctionMark); + } else if (p5Variables.has(name)) { + builder.add(node.from, node.to, p5VariableMark); + } + } + }); + }); + return builder.finish(); +} + +const p5Highlight = ViewPlugin.fromClass( + class { + constructor(view) { + this.decorations = buildHighlightDecorations(view); + } + + update(update) { + if (update.docChanged || update.viewportChanged) { + this.decorations = buildHighlightDecorations(update.view); + } + } + }, + { decorations: (v) => v.decorations } +); + +function addCompletions(context) { + const word = context.matchBefore(/\w*/); + const isValidWord = word?.text && word.text.trim().length >= 2; + if (!isValidWord && !context.explicit) { + return null; + } + + return contextAwareHinter(context, { + hints: p5Hinter + }); +} + +export function p5JavaScript() { + const jsLang = javascript(); + return new LanguageSupport(jsLang.language, [ + jsLang.extension, + jsLang.language.data.of({ + autocomplete: addCompletions + }), + completionPreview(), + p5Highlight + ]); +} diff --git a/client/modules/IDE/components/Editor/utils/tidier.test.ts b/client/modules/IDE/components/Editor/utils/tidier.test.ts new file mode 100644 index 0000000000..babb27c8b2 --- /dev/null +++ b/client/modules/IDE/components/Editor/utils/tidier.test.ts @@ -0,0 +1,67 @@ +import { EditorState } from '@codemirror/state'; +import { EditorView } from '@codemirror/view'; +import { tidyCodeWithPrettier } from './tidier'; + +describe('tidyCodeWithPrettier', () => { + function createEditor(content: string) { + const state = EditorState.create({ doc: content }); + return new EditorView({ state }); + } + + function getDocText(cmView: EditorView) { + return cmView.state.doc.toString(); + } + + function getCursor(cmView: EditorView) { + return cmView.state.selection.main.head; + } + + it('formats JavaScript correctly and keeps cursor stable', () => { + const messyJs = `function foo(){console.log("hi")}`; + const cmView = createEditor(messyJs); + cmView.dispatch({ selection: { anchor: 14 } }); + + tidyCodeWithPrettier(cmView, 'javascript'); + + const expectedJs = 'function foo() {\n console.log("hi");\n}\n'; + expect(getDocText(cmView)).toBe(expectedJs); + + const newCursor = getCursor(cmView); + expect(newCursor).toBeGreaterThan(0); + }); + + it('formats HTML correctly and keeps cursor stable', () => { + const messyHtml = `hello`; + const cmView = createEditor(messyHtml); + tidyCodeWithPrettier(cmView, 'html'); + + const expectedHtml = '\n \n hello\n \n\n'; + expect(getDocText(cmView)).toBe(expectedHtml); + }); + + it('formats CSS correctly and keeps cursor stable', () => { + const messyCss = `body{margin:0;padding:0;}`; + const cmView = createEditor(messyCss); + + tidyCodeWithPrettier(cmView, 'css'); + + const expectedCss = 'body {\n margin: 0;\n padding: 0;\n}\n'; + expect(getDocText(cmView)).toBe(expectedCss); + }); + + it('handles an empty document without errors', () => { + const cmView = createEditor(''); + expect(() => tidyCodeWithPrettier(cmView, 'javascript')).not.toThrow(); + expect(getDocText(cmView)).toBe(''); + }); + + it('leaves the cursor unmoved if the code is already formatted', () => { + const cleanCode = 'function foo() {}\n'; + const cmView = createEditor(cleanCode); + cmView.dispatch({ selection: { anchor: 5 } }); + + tidyCodeWithPrettier(cmView, 'javascript'); + + expect(getCursor(cmView)).toBe(5); + }); +}); diff --git a/client/modules/IDE/components/Editor/utils/tidier.ts b/client/modules/IDE/components/Editor/utils/tidier.ts new file mode 100644 index 0000000000..41fdb24572 --- /dev/null +++ b/client/modules/IDE/components/Editor/utils/tidier.ts @@ -0,0 +1,44 @@ +import prettier from 'prettier/standalone'; +import babelParser from 'prettier/parser-babel'; +import htmlParser from 'prettier/parser-html'; +import cssParser from 'prettier/parser-postcss'; +import type { EditorView } from '@codemirror/view'; +import type { Plugin } from 'prettier'; + +type ParserTypes = 'babel' | 'html' | 'css'; +type FormatMode = 'html' | 'css' | 'javascript'; + +function prettierFormatWithCursor( + parser: ParserTypes, + plugins: Plugin[], + cmView: EditorView +) { + const { doc } = cmView.state; + const cursorOffset = cmView.state.selection.main.head; + const { + formatted, + cursorOffset: newCursorOffset + } = prettier.formatWithCursor(doc.toString(), { + cursorOffset, + parser, + plugins + }); + + cmView.dispatch({ + changes: { from: 0, to: doc.length, insert: formatted }, + selection: { anchor: newCursorOffset } + }); + + cmView.focus(); +} + +/** Runs prettier on the codemirror instance, depending on the mode. */ +export function tidyCodeWithPrettier(cmView: EditorView, mode: FormatMode) { + if (mode === 'javascript') { + prettierFormatWithCursor('babel', [babelParser], cmView); + } else if (mode === 'css') { + prettierFormatWithCursor('css', [cssParser], cmView); + } else if (mode === 'html') { + prettierFormatWithCursor('html', [htmlParser], cmView); + } +} diff --git a/client/modules/IDE/components/Header/MobileNav.jsx b/client/modules/IDE/components/Header/MobileNav.jsx index 2ef0ff3026..b055b62be8 100644 --- a/client/modules/IDE/components/Header/MobileNav.jsx +++ b/client/modules/IDE/components/Header/MobileNav.jsx @@ -427,7 +427,7 @@ const MoreMenu = () => { {t('Nav.Edit.TidyCode')} - + {t('Nav.Edit.Find')} {t('Nav.Sketch.Title')} diff --git a/client/modules/IDE/components/Header/Nav.jsx b/client/modules/IDE/components/Header/Nav.jsx index cc53134bbe..c29a42821a 100644 --- a/client/modules/IDE/components/Header/Nav.jsx +++ b/client/modules/IDE/components/Header/Nav.jsx @@ -169,8 +169,6 @@ const ProjectMenu = () => { shareSketch } = useSketchActions(); - const replaceCommand = - metaKey === 'Ctrl' ? `${metaKeyName}+H` : `${metaKeyName}+⌥+F`; const newFileCommand = metaKey === 'Ctrl' ? `${metaKeyName}+Alt+N` : `${metaKeyName}+⌥+N`; @@ -264,18 +262,24 @@ const ProjectMenu = () => { - + { + cmRef.current?.tidyCode(); + }} + > {t('Nav.Edit.TidyCode')} {metaKeyName}+Shift+F - + { + cmRef.current?.showSearch(); + }} + > {t('Nav.Edit.Find')} {metaKeyName}+F - - {t('Nav.Edit.Replace')} - {replaceCommand} - -
  • - Replace - - Ctrl+H - -
  • 1) return; - // By default, don't allow completion when something is selected. - // A hint function can have a `supportsSelection` property to - // indicate that it can handle selections. - if (this.somethingSelected()) { - if (!options.hint.supportsSelection) return; - // Don't try with cross-line selections - // if selection spans multiple lines, bail out - for (var i = 0; i < selections.length; i++) - if (selections[i].head.line != selections[i].anchor.line) return; - } - - if (this.state.completionActive) this.state.completionActive.close(); // close an already active autocomplete session if active - // create a new completion object and saves it to this.state.completionActive - var completion = (this.state.completionActive = new Completion( - this, - options - )); - if (!completion.options.hint) return; // safety check to ensure hint is valid - - CodeMirror.signal(this, 'startCompletion', this); // emits a signal; fires a startCompletion event on editor instance - completion.update(true); - }); - - CodeMirror.defineExtension('closeHint', function () { - if (this.state.completionActive) this.state.completionActive.close(); - }); - - // defines a constructor function - function Completion(cm, options) { - this.cm = cm; - this.options = options; - this.widget = null; // will hold a reference to the dropdown menu that shows suggestions - this.debounce = 0; - this.tick = 0; - this.startPos = this.cm.getCursor('start'); // startPos is a {line,ch} object used to remember where hinting started - // startLen is the len of the line minus length of any selected text - this.startLen = - this.cm.getLine(this.startPos.line).length - - this.cm.getSelection().length; - - if (this.options.updateOnCursorActivity) { - var self = this; // stores ref to this as self so it can be accessed inside the nested function - // adds an event listener to the editor; called when the cursor moves - cm.on( - 'cursorActivity', - (this.activityFunc = function () { - self.cursorActivity(); - }) - ); - } - } - - var requestAnimationFrame = - window.requestAnimationFrame || - function (fn) { - return setTimeout(fn, 1000 / 60); - }; - var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout; - - Completion.prototype = { - close: function () { - if (!this.active()) return; - this.cm.state.completionActive = null; - this.tick = null; - // removes the current activity listener - if (this.options.updateOnCursorActivity) { - this.cm.off('cursorActivity', this.activityFunc); - } - // signals and removes the widget - if (this.widget && this.data) CodeMirror.signal(this.data, 'close'); - if (this.widget) this.widget.close(); - // emits a completition end event - CodeMirror.signal(this.cm, 'endCompletion', this.cm); - }, - - active: function () { - return this.cm.state.completionActive == this; - }, - - pick: function (data, i) { - // selects an item from the suggestion list - var completion = data.list[i], - self = this; - - this.cm.operation(function () { - const name = completion.item?.text; - - if (completion.hint) { - completion.hint(self.cm, data, completion); - } else { - self.cm.replaceRange( - getText(completion), - completion.from || data.from, - completion.to || data.to, - 'complete' - ); - } - // signals that a hint was picked and scrolls to it - CodeMirror.signal(data, 'pick', completion); - self.cm.scrollIntoView(); - }); - // closes widget if closeOnPick is enabled - if (this.options.closeOnPick) { - this.close(); - } - }, - - cursorActivity: function () { - // if a debounce is scheduled, cancel it to avoid outdated updates - if (this.debounce) { - cancelAnimationFrame(this.debounce); - this.debounce = 0; - } - - var identStart = this.startPos; - if (this.data) { - identStart = this.data.from; - } - - var pos = this.cm.getCursor(), - line = this.cm.getLine(pos.line); - if ( - pos.line != this.startPos.line || - line.length - pos.ch != this.startLen - this.startPos.ch || - pos.ch < identStart.ch || - this.cm.somethingSelected() || - !pos.ch || - this.options.closeCharacters.test(line.charAt(pos.ch - 1)) - ) { - this.close(); - } else { - var self = this; - this.debounce = requestAnimationFrame(function () { - self.update(); - }); - if (this.widget) this.widget.disable(); - } - }, - - update: function (first) { - if (this.tick == null) return; - var self = this, - myTick = ++this.tick; - fetchHints(this.options.hint, this.cm, this.options, function (data) { - if (self.tick == myTick) self.finishUpdate(data, first); - }); - }, - - finishUpdate: function (data, first) { - if (this.data) CodeMirror.signal(this.data, 'update'); - - var picked = - (this.widget && this.widget.picked) || - (first && this.options.completeSingle); - if (this.widget) this.widget.close(); - - this.data = data; - - if (data && data.list.length) { - if (picked && data.list.length == 1) { - this.pick(data, 0); - } else { - this.widget = new Widget(this, data); - CodeMirror.signal(data, 'shown'); - } - } - } - }; - - function parseOptions(cm, pos, options) { - var editor = cm.options.hintOptions; - var out = {}; - // copies all default hint settings into out - for (var prop in defaultOptions) out[prop] = defaultOptions[prop]; - if (editor) - for (var prop in editor) - if (editor[prop] !== undefined) out[prop] = editor[prop]; - if (options) - for (var prop in options) - if (options[prop] !== undefined) out[prop] = options[prop]; - if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos); - return out; - } - // extracts the visible text from a completion entry - function getText(completion) { - if (typeof completion === 'string') return completion; - else return completion.item.text; - } - - // builds a key mapping object to define keyboard behavior for autocomplete - function buildKeyMap(completion, handle) { - var baseMap = { - Up: function () { - handle.moveFocus(-1); - }, - Down: function () { - handle.moveFocus(1); - }, - PageUp: function () { - handle.moveFocus(-handle.menuSize() + 1, true); - }, - PageDown: function () { - handle.moveFocus(handle.menuSize() - 1, true); - }, - Home: function () { - handle.setFocus(0); - }, - End: function () { - handle.setFocus(handle.length - 1); - }, - Enter: handle.pick, - Tab: handle.pick, - Esc: handle.close - }; - // checks if the user is on macOS and adds shortcuts accordingly - var mac = /Mac/.test(navigator.platform); - - if (mac) { - baseMap['Ctrl-P'] = function () { - handle.moveFocus(-1); - }; - baseMap['Ctrl-N'] = function () { - handle.moveFocus(1); - }; - } - - // user defined custom key bindings - var custom = completion.options.customKeys; - var ourMap = custom ? {} : baseMap; - function addBinding(key, val) { - var bound; - if (typeof val != 'string') - bound = function (cm) { - return val(cm, handle); - }; - // This mechanism is deprecated - else if (baseMap.hasOwnProperty(val)) bound = baseMap[val]; - else bound = val; - ourMap[key] = bound; - } - // apply all custom key bindings and extraKeys - if (custom) - for (var key in custom) - if (custom.hasOwnProperty(key)) addBinding(key, custom[key]); - var extra = completion.options.extraKeys; - if (extra) - for (var key in extra) - if (extra.hasOwnProperty(key)) addBinding(key, extra[key]); - return ourMap; - } - - // hintsElement is the parent for hints and el is the clicked element within that container - function displayHint(name, type, p5, isBlacklistedFunction, referenceBaseUrl) { - const base = referenceBaseUrl || 'https://p5js.org'; - const refName = typeof p5 === 'string' ? p5 : name; - - const linkOrPlaceholder = p5 - ? ` - open ${name} reference - - ` - : ` - no reference for ${name} - `; - - const hintHTML = `
    - ${name} - ${type} - ${linkOrPlaceholder} -
    `; - - if (isBlacklistedFunction) { - return `
    - ${hintHTML} -
    ⚠️use with caution in this context
    -
    `; - } else { - return `
    ${hintHTML}
    `; - } - } - - - function getInlineHintSuggestion(cm, focus, token) { - let tokenLength = token.string.length; - if (token.string === '.') { - tokenLength -= 1; - } - const name = focus.item?.text; - - const suggestionItem = focus.item; - // builds the remainder of the suggestion excluding what user already typed - const baseCompletion = `${suggestionItem.text.slice( - tokenLength - )}`; - if (suggestionItem.type !== 'fun') return baseCompletion; - - // for functions - return ( - baseCompletion + - '(' + - (suggestionItem.params && suggestionItem.params.length - ? suggestionItem.params.map(({ p, o }) => (o ? `[${p}]` : p)).join(', ') - : '') + - ')' - ); - } - - // clears existing inline hint (like the part is suggested) - function removeInlineHint(cm) { - if (cm.state.inlineHint) { - cm.state.inlineHint.clear(); - cm.state.inlineHint = null; - } - } - - function changeInlineHint(cm, focus) { - removeInlineHint(cm); - - const cursor = cm.getCursor(); - const token = cm.getTokenAt(cursor); - - if (token && focus.item) { - const suggestionHTML = getInlineHintSuggestion(cm, focus, token); - - const widgetElement = document.createElement('span'); - widgetElement.className = 'autocomplete-inline-hinter'; - widgetElement.innerHTML = suggestionHTML; - - const widget = cm.setBookmark(cursor, { widget: widgetElement }); - cm.state.inlineHint = widget; - cm.setCursor(cursor); - } - } - - // defines the autocomplete dropdown ui; renders the suggestions - // completion = the autocomplete context having cm and options - // data = object with the list of suggestions - function Widget(completion, data) { - this.id = 'cm-complete-' + Math.floor(Math.random(1e6)); - this.completion = completion; - this.data = data; - this.picked = false; - var widget = this, - cm = completion.cm; - var ownerDocument = cm.getInputField().ownerDocument; - var parentWindow = ownerDocument.defaultView || ownerDocument.parentWindow; - - var fontSize = completion.options._fontSize; - - var hints = (this.hints = ownerDocument.createElement('ul')); - hints.setAttribute('role', 'listbox'); - hints.setAttribute('aria-expanded', 'true'); - hints.id = this.id; - var theme = completion.cm.options.theme; - hints.className = 'CodeMirror-hints ' + theme; - this.selectedHint = data.selectedHint || 0; - - const referenceBaseUrl = completion.options.referenceBaseUrl || 'https://p5js.org'; - - // Show inline hint - changeInlineHint(cm, data.list[this.selectedHint]); - - var completions = data.list; - for (var i = 0; i < completions.length; ++i) { - const cur = completions[i]; - - const elt = ownerDocument.createElement('li'); - elt.className = - HINT_ELEMENT_CLASS + - (i !== this.selectedHint ? '' : ' ' + ACTIVE_HINT_ELEMENT_CLASS) + - (cur.isBlacklisted ? ' blacklisted' : ''); - - if (cur.className != null) - elt.className = cur.className + ' ' + elt.className; - - if (i === this.selectedHint) elt.setAttribute('aria-selected', 'true'); - elt.id = this.id + '-' + i; - elt.setAttribute('role', 'option'); - elt.hintId = i; - - if (cur.render) { - cur.render(elt, data, cur); - } else { - const name = getText(cur); - if (cur.item && cur.item.type) { - cur.displayText = displayHint( - name, - cur.item.type, - cur.item.p5, - cur.isBlacklisted, - referenceBaseUrl - ); - } - - elt.innerHTML = - cur.displayText || `${name}`; - } - - hints.appendChild(elt); - } - - var container = completion.options.container || ownerDocument.body; - var pos = cm.cursorCoords( - completion.options.alignWithWord ? data.from : null - ); - var left = pos.left, - top = pos.bottom, - below = true; - var offsetLeft = 0, - offsetTop = 0; - if (container !== ownerDocument.body) { - // We offset the cursor position because left and top are relative to the offsetParent's top left corner. - var isContainerPositioned = - ['absolute', 'relative', 'fixed'].indexOf( - parentWindow.getComputedStyle(container).position - ) !== -1; - var offsetParent = isContainerPositioned - ? container - : container.offsetParent; - var offsetParentPosition = offsetParent.getBoundingClientRect(); - var bodyPosition = ownerDocument.body.getBoundingClientRect(); - offsetLeft = - offsetParentPosition.left - bodyPosition.left - offsetParent.scrollLeft; - offsetTop = - offsetParentPosition.top - bodyPosition.top - offsetParent.scrollTop; - } - hints.style.left = left - offsetLeft + 'px'; - hints.style.top = top - offsetTop + 'px'; - - // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. - var winW = - parentWindow.innerWidth || - Math.max( - ownerDocument.body.offsetWidth, - ownerDocument.documentElement.offsetWidth - ); - var winH = - parentWindow.innerHeight || - Math.max( - ownerDocument.body.offsetHeight, - ownerDocument.documentElement.offsetHeight - ); - container.appendChild(hints); - cm.getInputField().setAttribute('aria-autocomplete', 'list'); - cm.getInputField().setAttribute('aria-owns', this.id); - cm.getInputField().setAttribute( - 'aria-activedescendant', - this.id + '-' + this.selectedHint - ); - - var box = completion.options.moveOnOverlap - ? hints.getBoundingClientRect() - : new DOMRect(); - var scrolls = completion.options.paddingForScrollbar - ? hints.scrollHeight > hints.clientHeight + 1 - : false; - - // Compute in the timeout to avoid reflow on init - var startScroll; - setTimeout(function () { - startScroll = cm.getScrollInfo(); - }); - - var overlapY = box.bottom - winH; - if (overlapY > 0) { - var height = box.bottom - box.top, - curTop = pos.top - (pos.bottom - box.top); - if (curTop - height > 0) { - // Fits above cursor - hints.style.top = (top = pos.top - height - offsetTop) + 'px'; - below = false; - } else if (height > winH) { - hints.style.height = winH - 5 + 'px'; - hints.style.top = (top = pos.bottom - box.top - offsetTop) + 'px'; - var cursor = cm.getCursor(); - if (data.from.ch != cursor.ch) { - pos = cm.cursorCoords(cursor); - hints.style.left = (left = pos.left - offsetLeft) + 'px'; - box = hints.getBoundingClientRect(); - } - } - } - var overlapX = box.right - winW; - if (scrolls) overlapX += cm.display.nativeBarWidth; - if (overlapX > 0) { - if (box.right - box.left > winW) { - hints.style.width = winW - 5 + 'px'; - overlapX -= box.right - box.left - winW; - } - hints.style.left = (left = pos.left - overlapX - offsetLeft) + 'px'; - } - // if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling) - // node.style.paddingRight = cm.display.nativeBarWidth + "px" - - cm.addKeyMap( - (this.keyMap = buildKeyMap(completion, { - moveFocus: function (n, avoidWrap) { - return widget.changeActive(widget.selectedHint + n, avoidWrap); - }, - setFocus: function (n) { - return widget.changeActive(n); - }, - menuSize: function () { - return widget.screenAmount(); - }, - length: completions.length, - close: function () { - completion.close(); - }, - pick: function () { - widget.pick(); - }, - data: data - })) - ); - - if (completion.options.closeOnUnfocus) { - var closingOnBlur; - cm.on( - 'blur', - (this.onBlur = function () { - closingOnBlur = setTimeout(function () { - completion.close(); - }, 100); - }) - ); - cm.on( - 'focus', - (this.onFocus = function () { - clearTimeout(closingOnBlur); - }) - ); - } - - cm.on( - 'scroll', - (this.onScroll = function () { - var curScroll = cm.getScrollInfo(), - editor = cm.getWrapperElement().getBoundingClientRect(); - if (!startScroll) startScroll = cm.getScrollInfo(); - var newTop = top + startScroll.top - curScroll.top; - var point = - newTop - - (parentWindow.pageYOffset || - (ownerDocument.documentElement || ownerDocument.body).scrollTop); - if (!below) point += hints.offsetHeight; - if (point <= editor.top || point >= editor.bottom) - return completion.close(); - hints.style.top = newTop + 'px'; - hints.style.left = left + startScroll.left - curScroll.left + 'px'; - }) - ); - - function getHintElement(container, el) { - while (el && el !== container && el.hintId == null) { - el = el.parentNode; - } - return el; - } - - CodeMirror.on(hints, 'dblclick', function (e) { - var t = getHintElement(hints, e.target || e.srcElement); - if (t && t.hintId != null) { - widget.changeActive(t.hintId); - widget.pick(); - } - }); - - CodeMirror.on(hints, 'click', function (e) { - var t = getHintElement(hints, e.target || e.srcElement); - if (t && t.hintId != null) { - widget.changeActive(t.hintId); - if (completion.options.completeOnSingleClick) widget.pick(); - } - }); - - CodeMirror.on(hints, 'mousedown', function () { - setTimeout(function () { - cm.focus(); - }, 20); - }); - - // The first hint doesn't need to be scrolled to on init - var selectedHintRange = this.getSelectedHintRange(); - if (selectedHintRange.from !== 0 || selectedHintRange.to !== 0) { - this.scrollToActive(); - } - - CodeMirror.signal( - data, - 'select', - completions[this.selectedHint], - hints.childNodes[this.selectedHint] - ); - return true; - } - - Widget.prototype = { - close: function () { - if (this.completion.widget != this) return; - this.completion.widget = null; - if (this.hints.parentNode) this.hints.parentNode.removeChild(this.hints); - this.completion.cm.removeKeyMap(this.keyMap); - var input = this.completion.cm.getInputField(); - input.removeAttribute('aria-activedescendant'); - input.removeAttribute('aria-owns'); - - var cm = this.completion.cm; - if (this.completion.options.closeOnUnfocus) { - cm.off('blur', this.onBlur); - cm.off('focus', this.onFocus); - } - cm.off('scroll', this.onScroll); - - removeInlineHint(cm); - }, - - disable: function () { - this.completion.cm.removeKeyMap(this.keyMap); - var widget = this; - this.keyMap = { - Enter: function () { - widget.picked = true; - } - }; - this.completion.cm.addKeyMap(this.keyMap); - }, - - pick: function () { - this.completion.pick(this.data, this.selectedHint); - }, - - changeActive: function (i, avoidWrap) { - if (i >= this.data.list.length) - i = avoidWrap ? this.data.list.length - 1 : 0; - else if (i < 0) i = avoidWrap ? 0 : this.data.list.length - 1; - - if (this.selectedHint == i) { - changeInlineHint(this.completion.cm, this.data.list[this.selectedHint]); - return this.data.list[this.selectedHint]; - } - - var node = this.hints.childNodes[this.selectedHint]; - if (node) { - node.className = node.className.replace( - ' ' + ACTIVE_HINT_ELEMENT_CLASS, - '' - ); - node.removeAttribute('aria-selected'); - } - node = this.hints.childNodes[(this.selectedHint = i)]; - node.className += ' ' + ACTIVE_HINT_ELEMENT_CLASS; - node.setAttribute('aria-selected', 'true'); - this.completion.cm - .getInputField() - .setAttribute('aria-activedescendant', node.id); - this.scrollToActive(); - CodeMirror.signal( - this.data, - 'select', - this.data.list[this.selectedHint], - node - ); - - changeInlineHint(this.completion.cm, this.data.list[this.selectedHint]); - return this.data.list[this.selectedHint]; - }, - - scrollToActive: function () { - var selectedHintRange = this.getSelectedHintRange(); - var node1 = this.hints.childNodes[selectedHintRange.from]; - var node2 = this.hints.childNodes[selectedHintRange.to]; - var firstNode = this.hints.firstChild; - if (node1.offsetTop < this.hints.scrollTop) - this.hints.scrollTop = node1.offsetTop - firstNode.offsetTop; - else if ( - node2.offsetTop + node2.offsetHeight > - this.hints.scrollTop + this.hints.clientHeight - ) - this.hints.scrollTop = - node2.offsetTop + - node2.offsetHeight - - this.hints.clientHeight + - firstNode.offsetTop; - }, - - screenAmount: function () { - return ( - Math.floor( - this.hints.clientHeight / this.hints.firstChild.offsetHeight - ) || 1 - ); - }, - - getSelectedHintRange: function () { - var margin = this.completion.options.scrollMargin || 0; - return { - from: Math.max(0, this.selectedHint - margin), - to: Math.min(this.data.list.length - 1, this.selectedHint + margin) - }; - } - }; - - function applicableHelpers(cm, helpers) { - if (!cm.somethingSelected()) return helpers; - var result = []; - for (var i = 0; i < helpers.length; i++) - if (helpers[i].supportsSelection) result.push(helpers[i]); - return result; - } - - function fetchHints(hint, cm, options, callback) { - if (hint.async) { - hint(cm, callback, options); - } else { - var result = hint(cm, options); - if (result && result.then) result.then(callback); - else callback(result); - } - } - - function resolveAutoHints(cm, pos) { - var helpers = cm.getHelpers(pos, 'hint'), - words; - if (helpers.length) { - var resolved = function (cm, callback, options) { - var app = applicableHelpers(cm, helpers); - function run(i) { - if (i == app.length) return callback(null); - fetchHints(app[i], cm, options, function (result) { - if (result && result.list.length > 0) callback(result); - else run(i + 1); - }); - } - run(0); - }; - resolved.async = true; - resolved.supportsSelection = true; - return resolved; - } else if ((words = cm.getHelper(cm.getCursor(), 'hintWords'))) { - return function (cm) { - return CodeMirror.hint.fromList(cm, { words: words }); - }; - } else if (CodeMirror.hint.anyword) { - return function (cm, options) { - return CodeMirror.hint.anyword(cm, options); - }; - } else { - return function () {}; - } - } - - CodeMirror.registerHelper('hint', 'auto', { - resolve: resolveAutoHints - }); - - CodeMirror.registerHelper('hint', 'fromList', function (cm, options) { - var cur = cm.getCursor(), - token = cm.getTokenAt(cur); - var term, - from = CodeMirror.Pos(cur.line, token.start), - to = cur; - if ( - token.start < cur.ch && - /\w/.test(token.string.charAt(cur.ch - token.start - 1)) - ) { - term = token.string.substr(0, cur.ch - token.start); - } else { - term = ''; - from = cur; - } - var found = []; - for (var i = 0; i < options.words.length; i++) { - var word = options.words[i]; - if (word.slice(0, term.length) == term) found.push(word); - } - - if (found.length) return { list: found, from: from, to: to }; - }); - - CodeMirror.commands.autocomplete = CodeMirror.showHint; - - var defaultOptions = { - hint: CodeMirror.hint.auto, - completeSingle: true, - alignWithWord: true, - closeCharacters: /[\s()\[\]{};:>,]/, - closeOnPick: true, - closeOnUnfocus: true, - updateOnCursorActivity: true, - completeOnSingleClick: true, - container: null, - customKeys: null, - extraKeys: null, - paddingForScrollbar: true, - moveOnOverlap: true - }; - - CodeMirror.defineOption('hintOptions', null); -}); diff --git a/client/modules/IDE/reducers/ide.js b/client/modules/IDE/reducers/ide.js index 8d45b8b50a..64765cac5b 100644 --- a/client/modules/IDE/reducers/ide.js +++ b/client/modules/IDE/reducers/ide.js @@ -22,7 +22,6 @@ const initialState = { justOpenedProject: false, previousPath: '/', errorType: undefined, - runtimeErrorWarningVisible: false, parentId: undefined }; @@ -111,10 +110,6 @@ const ide = (state = initialState, action) => { return Object.assign({}, state, { errorType: action.modalType }); case ActionTypes.HIDE_ERROR_MODAL: return Object.assign({}, state, { errorType: undefined }); - case ActionTypes.HIDE_RUNTIME_ERROR_WARNING: - return Object.assign({}, state, { runtimeErrorWarningVisible: false }); - case ActionTypes.SHOW_RUNTIME_ERROR_WARNING: - return Object.assign({}, state, { runtimeErrorWarningVisible: true }); case ActionTypes.OPEN_UPLOAD_FILE_MODAL: return Object.assign({}, state, { uploadFileModalVisible: true, diff --git a/client/styles/abstracts/_variables.scss b/client/styles/abstracts/_variables.scss index 46bdd7bc1b..5a595d14a1 100644 --- a/client/styles/abstracts/_variables.scss +++ b/client/styles/abstracts/_variables.scss @@ -6,33 +6,68 @@ $p5js-pink-opacity: #ed225d80; $p5js-active-pink: #f10046; $white: #fff; $black: #000; -$yellow: #F5DC23; -$dodgerblue: #1E90FF; -$p5-contrast-pink: #FFA9D9; +$yellow: #f5dc23; +$dodgerblue: #1e90ff; +$p5-contrast-pink: #ffa9d9; -$outline-color: #0F9DD7; +$outline-color: #0f9dd7; // Grayscale values -$lightest: #FFF; // primary -$lighter: #FBFBFB; +$lightest: #fff; // primary +$lighter: #fbfbfb; -$light: #F0F0F0; // primary -$medium-light: #D9D9D9; -$middle-light: #A6A6A6; +$light: #f0f0f0; // primary +$medium-light: #d9d9d9; +$middle-light: #a6a6a6; // $middle-gray: #7D7D7D; // primary $middle-gray: #747474; // primary $middle-dark: #666; -$medium-dark: #4D4D4D; +$medium-dark: #4d4d4d; $dark: #333; // primary -$darker: #1C1C1C; +$darker: #1c1c1c; $darkest: #000; +// Light highlight styles +$p5-light-selected: $medium-light; +$p5-light-activeline: #cfcfcf44; +$p5-light-brown: #7a5a3a; +$p5-light-black: #333333; +$p5-light-pink: #d52889; +$p5-light-gray: #666; +$p5-light-blue: #0b7ca9; +$p5-light-orange: #a06801; +$p5-light-lightgray: $middle-gray; +$p5-light-green: #47820a; + +// Dark highlight styles +$p5-dark-selected: $medium-dark; +$p5-dark-activeline: #cfcfcf22; +$p5-dark-pink: #de4a9b; +$p5-dark-gray: #9b9b9b; +$p5-dark-lightblue: #0f9dd7; +$p5-dark-white: #fdfdfd; +$p5-dark-orange: #ee9900; +$p5-dark-green: #58a10b; +$p5-dark-goldbrown: #b58318; + +// Contrast highlight styles +$p5-contrast-selected: $middle-dark; +$p5-contrast-activeline: #333333aa; +$p5-contrast-white: #fdfdfd; +$p5-contrast-lightgray: #c1c1c1; +$p5-contrast-blue: #00ffff; +$p5-contrast-green: #2de9b6; +$p5-contrast-yellow: #f5dc23; +$p5-contrast-orange: #ffa95d; +$p5-contrast-pink: #ffa9d9; + $themes: ( light: ( - admonition-background: #E4F8FF, - admonition-border: #22C8ED, + activeline-background-color: $p5-light-activeline, + admonition-background: #e4f8ff, + admonition-border: #22c8ed, admonition-text: #075769, background-color: $lighter, button-active-color: $lightest, @@ -44,9 +79,9 @@ $themes: ( button-hover-color: $lightest, button-nav-inactive-color: $middle-gray, button-secondary-background-color: $medium-light, - codefold-icon-closed: url("../images/triangle-arrow-right.svg?byUrl"), - codefold-icon-open: url("../images/triangle-arrow-down.svg?byUrl"), - console-active-arrow-color: #0071AD, + codefold-icon-closed: url('../images/triangle-arrow-right.svg?byUrl'), + codefold-icon-open: url('../images/triangle-arrow-down.svg?byUrl'), + console-active-arrow-color: #0071ad, console-arrow-color: $middle-gray, console-background-color: $light, console-color: $darker, @@ -63,13 +98,26 @@ $themes: ( form-secondary-title-color: $medium-dark, form-title-color: rgba(51, 51, 51, 0.87), heavy-text-color: $darker, + highlight-style-comment-color: $p5-light-lightgray, + highlight-style-variable-color: $p5-light-blue, + highlight-style-string-color: $p5-light-green, + highlight-style-regexp-color: $p5-light-orange, + highlight-style-number-color: $p5-light-black, + highlight-style-atom-color: $p5-light-pink, + highlight-style-keyword-color: $p5-light-brown, + highlight-style-operator-color: $p5-light-brown, + highlight-style-def-color: $p5-light-blue, + highlight-style-tag-color: $p5-light-pink, + highlight-style-property-color: $p5-light-black, + highlight-style-attribute-color: $p5-light-black, + highlight-style-matching-selection-background-color: $p5js-pink-opacity, hint-arrow-background-active-color: $p5js-active-pink, hint-arrow-background-color: #ed225ddd, hint-arrow-color: $lightest, hint-arrow-focus-outline-color: $middle-dark, hint-background-color: $white, - hint-fun-active-border-bottom-color: #0B7CA9, - hint-fun-text-color: #0B7CA9, + hint-fun-active-border-bottom-color: #0b7ca9, + hint-fun-text-color: #0b7ca9, hint-inline-text-color-light: $middle-light, hint-inline-text-color: $middle-gray, hint-item-active-background-color: $middle-gray, @@ -79,12 +127,12 @@ $themes: ( hint-item-active-type-text-color: $white, hint-item-border-bottom-color: $white, hint-item-hover-background-color: #f4f4f4, - hint-keyword-text-color: #7A5A3A, + hint-keyword-text-color: #7a5a3a, hint-no-link-background-color: $medium-light, hint-text-color: $dark, hint-type-text-color: $medium-dark, - hint-var-active-border-bottom-color: #D52889, - hint-var-text-color: #D52889, + hint-var-active-border-bottom-color: #d52889, + hint-var-text-color: #d52889, icon-color: $middle-gray, icon-hover-color: $darker, icon-toast-hover-color: $lightest, @@ -115,9 +163,10 @@ $themes: ( search-hover-background-color: $medium-dark, search-hover-text-color: $lightest, secondary-text-color: $medium-dark, + selected-background-color: $p5-light-selected, shadow-color: rgba(0, 0, 0, 0.16), table-button-active-color: $lightest, - table-button-background-active-color: #00A1D3, + table-button-background-active-color: #00a1d3, table-button-background-color: $middle-gray, table-button-background-hover-color: $p5js-pink, table-button-color: $lightest, @@ -127,12 +176,13 @@ $themes: ( toast-background-color: $medium-dark, toast-text-color: $lightest, toolbar-button-background-color: $medium-light, - toolbar-button-color: $dark, + toolbar-button-color: $dark ), dark: ( - admonition-background: #105A7F, - admonition-border: #22C8ED, - admonition-text: #FFFFFF, + activeline-background-color: $p5-dark-activeline, + admonition-background: #105a7f, + admonition-border: #22c8ed, + admonition-text: #ffffff, background-color: $darker, button-active-color: $lightest, button-background-active-color: $p5js-active-pink, @@ -143,9 +193,9 @@ $themes: ( button-hover-color: $lightest, button-nav-inactive-color: $middle-light, button-secondary-background-color: $medium-dark, - codefold-icon-closed: url("../images/triangle-arrow-right-white.svg?byUrl"), - codefold-icon-open: url("../images/triangle-arrow-down-white.svg?byUrl"), - console-active-arrow-color: #097BB3, + codefold-icon-closed: url('../images/triangle-arrow-right-white.svg?byUrl'), + codefold-icon-open: url('../images/triangle-arrow-down-white.svg?byUrl'), + console-active-arrow-color: #097bb3, console-arrow-color: $medium-light, console-background-color: $dark, console-color: $lightest, @@ -161,13 +211,26 @@ $themes: ( form-secondary-title-color: $medium-light, form-title-color: $lightest, heavy-text-color: $lightest, + highlight-style-comment-color: $p5-dark-gray, + highlight-style-variable-color: $p5-dark-lightblue, + highlight-style-string-color: $p5-dark-green, + highlight-style-regexp-color: $p5-dark-orange, + highlight-style-number-color: $p5-dark-white, + highlight-style-atom-color: $p5-dark-pink, + highlight-style-keyword-color: $p5-dark-goldbrown, + highlight-style-operator-color: $p5-dark-goldbrown, + highlight-style-def-color: $p5-dark-lightblue, + highlight-style-tag-color: $p5-dark-pink, + highlight-style-property-color: $p5-dark-white, + highlight-style-attribute-color: $p5-dark-lightblue, + highlight-style-matching-selection-background-color: $p5js-pink-opacity, hint-arrow-background-active-color: $p5js-active-pink, hint-arrow-background-color: #ed225ddd, hint-arrow-color: $lightest, hint-arrow-focus-outline-color: #cfcfcf, hint-background-color: $darker, - hint-fun-active-border-bottom-color: #0F9DD7, - hint-fun-text-color: #0F9DD7, + hint-fun-active-border-bottom-color: #0f9dd7, + hint-fun-text-color: #0f9dd7, hint-inline-text-color-light: $middle-gray, hint-inline-text-color: #cfcfcf, hint-item-active-background-color: #cfcfcf, @@ -177,12 +240,12 @@ $themes: ( hint-item-active-type-text-color: $darker, hint-item-border-bottom-color: $darker, hint-item-hover-background-color: $medium-dark, - hint-keyword-text-color: #B58318, + hint-keyword-text-color: #b58318, hint-no-link-background-color: $medium-dark, hint-text-color: $light, hint-type-text-color: $light, - hint-var-active-border-bottom-color: #DE4A9B, - hint-var-text-color: #DE4A9B, + hint-var-active-border-bottom-color: #de4a9b, + hint-var-text-color: #de4a9b, icon-color: $middle-light, icon-hover-color: $lightest, icon-toast-hover-color: $lightest, @@ -213,9 +276,10 @@ $themes: ( search-hover-background-color: $p5js-pink, search-hover-text-color: $lightest, secondary-text-color: $medium-light, + selected-background-color: $p5-dark-selected, shadow-color: rgba(0, 0, 0, 0.16), table-button-active-color: $lightest, - table-button-background-active-color: #00A1D3, + table-button-background-active-color: #00a1d3, table-button-background-color: $middle-gray, table-button-background-hover-color: $p5js-pink, table-button-color: $lightest, @@ -225,11 +289,12 @@ $themes: ( toast-background-color: $medium-light, toast-text-color: $dark, toolbar-button-background-color: $medium-dark, - toolbar-button-color: $lightest, + toolbar-button-color: $lightest ), contrast: ( + activeline-background-color: $p5-contrast-activeline, admonition-background: #000000, - admonition-border: #22C8ED, + admonition-border: #22c8ed, admonition-text: #ffffff, background-color: $darker, button-active-color: $dark, @@ -241,8 +306,8 @@ $themes: ( button-hover-color: $dark, button-nav-inactive-color: $light, button-secondary-background-color: $medium-dark, - codefold-icon-closed: url("../images/triangle-arrow-right-white.svg?byUrl"), - codefold-icon-open: url("../images/triangle-arrow-down-white.svg?byUrl"), + codefold-icon-closed: url('../images/triangle-arrow-right-white.svg?byUrl'), + codefold-icon-open: url('../images/triangle-arrow-down-white.svg?byUrl'), console-active-arrow-color: $dodgerblue, console-arrow-color: $lightest, console-background-color: $dark, @@ -258,13 +323,26 @@ $themes: ( form-secondary-title-color: $medium-light, form-title-color: $lightest, heavy-text-color: $yellow, - hint-arrow-background-active-color: #F5DC23, - hint-arrow-background-color: #F5DC23DD, + highlight-style-comment-color: $p5-contrast-lightgray, + highlight-style-variable-color: $p5-contrast-blue, + highlight-style-string-color: $p5-contrast-green, + highlight-style-regexp-color: $p5-contrast-green, + highlight-style-number-color: $p5-contrast-pink, + highlight-style-atom-color: $p5-contrast-pink, + highlight-style-keyword-color: $p5-contrast-yellow, + highlight-style-operator-color: $p5-contrast-lightgray, + highlight-style-def-color: $p5-contrast-blue, + highlight-style-tag-color: $p5-contrast-orange, + highlight-style-property-color: $p5-contrast-white, + highlight-style-attribute-color: $p5-contrast-white, + highlight-style-matching-selection-background-color: $medium-dark, + hint-arrow-background-active-color: #f5dc23, + hint-arrow-background-color: #f5dc23dd, hint-arrow-color: $darker, hint-arrow-focus-outline-color: $lighter, hint-background-color: $darkest, hint-fun-active-border-bottom-color: none, - hint-fun-text-color: #00FFFF, + hint-fun-text-color: #00ffff, hint-inline-text-color-light: $middle-gray, hint-inline-text-color: #cfcfcf, hint-item-active-background-color: unset, @@ -274,12 +352,12 @@ $themes: ( hint-item-active-type-text-color: $lighter, hint-item-border-bottom-color: $medium-dark, hint-item-hover-background-color: $dark, - hint-keyword-text-color: #F5DC23, + hint-keyword-text-color: #f5dc23, hint-no-link-background-color: $medium-dark, hint-text-color: $medium-light, hint-type-text-color: $middle-light, hint-var-active-border-bottom-color: none, - hint-var-text-color: #FFA9D9, + hint-var-text-color: #ffa9d9, icon-color: $medium-light, icon-hover-color: $yellow, icon-toast-hover-color: $yellow, @@ -310,9 +388,10 @@ $themes: ( search-hover-background-color: $yellow, search-hover-text-color: $dark, secondary-text-color: $lighter, + selected-background-color: $p5-contrast-selected, shadow-color: rgba(0, 0, 0, 0.16), table-button-active-color: $dark, - table-button-background-active-color: #00FFFF, + table-button-background-active-color: #00ffff, table-button-background-color: $middle-gray, table-button-background-hover-color: $yellow, table-button-color: $dark, @@ -322,6 +401,6 @@ $themes: ( toast-background-color: $medium-light, toast-text-color: $darker, toolbar-button-background-color: $medium-light, - toolbar-button-color: $dark, + toolbar-button-color: $dark ) ); diff --git a/client/styles/components/_editor.scss b/client/styles/components/_editor.scss index 0aab1d4b9f..e01e2095f8 100644 --- a/client/styles/components/_editor.scss +++ b/client/styles/components/_editor.scss @@ -1,378 +1,220 @@ @use "sass:math"; -.CodeMirror { +.cm-editor { font-family: Inconsolata, monospace; height: 100%; -} - -.CodeMirror-linenumbers { - padding-right: #{math.div(10, $base-font-size)}rem; -} -.CodeMirror-linenumber { - width: #{math.div(32, $base-font-size)}rem; - left: #{math.div(-3, $base-font-size)}rem !important; @include themify() { - color: getThemifyVariable("inactive-text-color"); + color: getThemifyVariable('primary-text-color'); } -} - -.CodeMirror-lines { - padding-top: #{math.div(25, $base-font-size)}rem; -} - -pre.CodeMirror-line { - padding-left: #{math.div(5, $base-font-size)}rem; -} - -.CodeMirror-gutter-wrapper { - right: 100%; - top: 0; - bottom: 0; -} - -.CodeMirror-lint-marker-warning, -.CodeMirror-lint-marker-error, -.CodeMirror-lint-marker-multiple { - background-image: none; - width: #{math.div(49, $base-font-size)}rem; - position: absolute; - height: 100%; - right: 100%; -} - -.CodeMirror-lint-message-error, -.CodeMirror-lint-message-warning { - background-image: none; - padding-left: inherit; -} -.CodeMirror-lint-marker-warning { - background-color: rgb(255, 190, 5); -} + .cm-scroller { + font-family: Inconsolata, monospace; + line-height: 1.2; + } -.CodeMirror-lint-marker-error { - background-color: rgb(255, 95, 82); -} + .cm-content { + padding-top: #{math.div(25, $base-font-size)}rem; + + .cm-activeLine { + // Needed to place the active line highlight behind the selection highlight. + position: relative; + background-color: transparent; + + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -10; + + @include themify() { + background-color: getThemifyVariable('activeline-background-color'); + } + } + } -.CodeMirror-gutter-elt:not(.CodeMirror-linenumber) { - opacity: 0.2; - width: #{math.div(49, $base-font-size)}rem !important; - height: 100%; - left: 49px !important; - // background-color: rgb(255, 95, 82); -} + .cm-errorLine.cm-activeLine, + .cm-errorLine { + background-color: rgb(255 95 82 / 30%); + } + } + .cm-focused > .cm-scroller .cm-selectionLayer .cm-selectionBackground, + .cm-selectionLayer .cm-selectionBackground, + ::selection { + @include themify() { + background-color: getThemifyVariable( + 'selected-background-color' + ) !important; + } + } -.CodeMirror-lint-tooltip { - @include themify() { - background-color: getThemifyVariable("modal-background-color"); - border: 1px solid getThemifyVariable("modal-border-color"); - box-shadow: 0 12px 12px getThemifyVariable("shadow-color"); - color: getThemifyVariable("primary-text-color"); + .cm-cursorLayer .cm-cursor { + @include themify() { + border-left-color: getThemifyVariable('primary-text-color'); + } } - border-radius: 2px; - font-family: Montserrat, sans-serif; } -.CodeMirror-gutters { +.cm-gutters { @include themify() { - background-color: getThemifyVariable("editor-gutter-color"); - border-color: getThemifyVariable("ide-border-color"); + background-color: getThemifyVariable('editor-gutter-color'); + border-color: getThemifyVariable('ide-border-color'); } - // left: 0 !important; width: #{math.div(48, $base-font-size)}rem; -} - -/* - Search dialog -*/ + position: relative; -.CodeMirror-dialog { - position: fixed; - top: 0; - left: 50%; - margin-left: #{math.div(-552 * 0.5, $base-font-size)}rem; - - @media (max-width: 770px) { - left: 0; - right: 0; - width: 100%; - margin-left: 0; + .cm-gutter { + z-index: 1; } - z-index: 10; - - width: 580px; - font-family: Montserrat, sans-serif; - - padding: #{math.div(8, $base-font-size)}rem #{math.div(10, $base-font-size)}rem #{math.div(5, $base-font-size)}rem #{math.div(9, $base-font-size)}rem; - - border-radius: 2px; - - @include themify() { - background-color: getThemifyVariable("modal-background-color"); - box-shadow: 0 12px 12px 0 getThemifyVariable("shadow-color"); - border: solid 0.5px getThemifyVariable("modal-border-color"); + .cm-lineNumbers { + @include themify() { + color: getThemifyVariable('inactive-text-color'); + } + pointer-events: none; } -} - -.CodeMirror-find-popup-container { - display: flex; - flex-wrap: wrap; - justify-content: space-between; -} - -.Toggle-replace-btn-div { - height: #{math.div(40, $base-font-size)}rem; - padding: 0; -} -.Toggle-replace-btn-div > button { - width: 100%; - height: 100%; -} - -.CodeMirror-search-results { - margin: 0 #{math.div(20, $base-font-size)}rem; - width: #{math.div(75, $base-font-size)}rem; - font-size: #{math.div(12, $base-font-size)}rem; -} - -.CodeMirror-find-controls { - width: 100%; - display: flex; - align-items: center; - justify-content: space-between; - height: #{math.div(35, $base-font-size)}rem; -} -.CodeMirror-search-inputs { - width: 30%; - margin-left: 10px; -} -.CodeMirror-replace-div { - display: flex; - justify-content: flex-start; - align-items: center; -} -.CodeMirror-search-controls { - width: 60%; - display: flex; - flex-wrap: wrap-reverse; - justify-content: flex-start; - align-items: flex-end; -} -.CodeMirror-replace-controls { - display: flex; - margin-left: #{math.div(10, $base-font-size)}rem; -} - -.CodeMirror-replace-options { - width: #{math.div(552, $base-font-size)}rem; - height: #{math.div(65, $base-font-size)}rem; - display: flex; - justify-content: center; - align-items: center; -} -.CodeMirror-replace-options button { - width: #{math.div(200, $base-font-size)}rem; -} + .cm-activeLineGutter { + background-color: transparent; + } -.CodeMirror-search-title { - display: block; - margin-bottom: #{math.div(12, $base-font-size)}rem; + .cm-gutter-lint { + position: absolute; + left: 0; + width: 100%; + z-index: 0; - font-size: #{math.div(21, $base-font-size)}rem; - font-weight: bold; -} + .cm-activeLineGutter { + @include themify() { + background-color: getThemifyVariable('active-line-background-color'); + } + } -.CodeMirror-search-field { - display: block; - width: 100%; - max-width: #{math.div(166, $base-font-size)}rem; - margin-bottom: #{math.div(4, $base-font-size)}rem; - @include themify() { - color: getThemifyVariable("input-text-color"); - background-color: getThemifyVariable("input-secondary-background-color"); - border: solid 0.5px getThemifyVariable("button-border-color"); - &::placeholder { - color: getThemifyVariable("inactive-text-color"); + .cm-gutterElement { + padding: 0; } } -} - -.CodeMirror-search-nav { - display: flex; - align-items: center; -} -.CodeMirror-search-count { - display: block; - height: #{math.div(20, $base-font-size)}rem; - text-align: right; -} - -.CodeMirror-search-actions { - display: flex; - justify-content: space-between; -} + .cm-lint-marker { + content: ''; + width: 100%; + height: 100%; + opacity: 0.2; -.CodeMirror-search-modifiers { - display: flex; - justify-content: flex-end; - align-items: center; - margin-left: #{math.div(10, $base-font-size)}rem; + &.cm-lint-marker-error { + background-color: rgb(255, 95, 82); + } - @media (max-width: 579px) { - display: none; + &.cm-lint-marker-warning { + background-color: rgb(255, 190, 5); + } } } -.CodeMirror-regexp-button, -.CodeMirror-case-button, -.CodeMirror-word-button { +.cm-tooltip-lint { @include themify() { - // @extend %button; - padding: #{math.div(2, $base-font-size)}rem #{math.div(7, $base-font-size)}rem; - border: 2px solid transparent; - &:hover { - border-color: getThemifyVariable("button-border-color"); - } + background-color: getThemifyVariable('modal-background-color'); + border: 1px solid getThemifyVariable('modal-border-color'); + box-shadow: 0 12px 12px getThemifyVariable('shadow-color'); + color: getThemifyVariable('primary-text-color'); } - width: #{math.div(35, $base-font-size)}rem; - height: #{math.div(35, $base-font-size)}rem; - - & + & { - margin-left: #{math.div(3, $base-font-size)}rem; - } - - word-break: keep-all; - white-space: nowrap; -} - -.CodeMirror-regexp-button .label, -.CodeMirror-case-button .label, -.CodeMirror-word-button .label { - @extend %hidden-element; + border-radius: 2px; + font-family: Montserrat, sans-serif; } -[aria-checked="true"] { +/** Search dialog */ +.cm-search.cm-panel { @include themify() { - color: getThemifyVariable("heavy-text-color"); - background-color: getThemifyVariable("button-secondary-background-color"); - border-color: getThemifyVariable("button-border-color"); + color: getThemifyVariable('primary-text-color'); + background-color: getThemifyVariable('modal-background-color'); + box-shadow: 0 12px 12px 0 getThemifyVariable('shadow-color'); + border: solid 0.5px getThemifyVariable('modal-border-color'); } -} - -/* - Previous / Next buttons -*/ + font-family: Montserrat, sans-serif; -// Visually hide button text -.CodeMirror-search-button .label { - @extend %hidden-element; -} + .cm-button { + @include themify() { + @extend %link; + color: getThemifyVariable('secondary-text-color'); + &:hover { + color: getThemifyVariable('logo-color'); + } + } + background: transparent; + border: none; + font-size: 1rem; + } -.CodeMirror-search-button { - margin-right: #{math.div(10, $base-font-size)}rem; + input.cm-textfield { + @include themify() { + color: getThemifyVariable('input-text-color'); + background-color: getThemifyVariable('input-secondary-background-color'); + border: solid 0.5px getThemifyVariable('button-border-color'); + &::placeholder { + color: getThemifyVariable('inactive-text-color'); + } + } + } } -.CodeMirror-search-match { - background: gold; - border-top: #{math.div(1, $base-font-size)}rem solid orange; - border-bottom: #{math.div(1, $base-font-size)}rem solid orange; - box-sizing: border-box; - opacity: 0.5; +// CM5: .CodeMirror-foldgutter-open, .CodeMirror-foldgutter-folded +.cm-foldGutter { + padding-right: #{math.div(3, $base-font-size)}rem; } -/* - Close button -*/ -.CodeMirror-close-button-container { +.cm-foldGutter .cm-gutterElement { + cursor: pointer; display: flex; align-items: center; + justify-content: center; } -// foldgutter -.CodeMirror-foldmarker { - text-shadow: - -1px 0 #ed225d, - 0 1px #ed225d, - 1px 0 #ed225d, - 0 -1px #ed225d; - color: #fff; - /* background-color: rgba(237, 34, 93, 0.42); */ - /* border-radius: 3px; */ - font-weight: bold; - font-family: arial; - line-height: 0.3; - cursor: pointer; - opacity: 0.75; -} -.CodeMirror-foldgutter { - width: 2.7em; -} -.CodeMirror-foldgutter-open, -.CodeMirror-foldgutter-folded { - cursor: pointer; - padding-bottom: 0.4em; - text-align: right; - line-height: 1; -} -.CodeMirror-foldgutter-open:after { - content: "\25BE"; -} -.CodeMirror-foldgutter-folded:after { - content: "\25B8"; -} - -.CodeMirror-foldgutter-open, -.CodeMirror-foldgutter-folded { - position: absolute; - right: 100%; +// CM5: .CodeMirror-foldgutter-open:after / .CodeMirror-foldgutter-folded:after +.cm-fold-open, +.cm-fold-closed { + display: inline-block; + width: #{math.div(10, $base-font-size)}rem; + height: #{math.div(10, $base-font-size)}rem; + background-size: #{math.div(10, $base-font-size)}rem #{math.div( + 10, + $base-font-size + )}rem; + background-repeat: no-repeat; + background-position: center center; + opacity: 0.4; } -.CodeMirror-foldgutter-open:after { +.cm-fold-open { @include themify() { - background-image: getThemifyVariable("codefold-icon-open"); + background-image: getThemifyVariable('codefold-icon-open'); } } -.CodeMirror-foldgutter-folded:after { +.cm-fold-closed { @include themify() { - background-image: getThemifyVariable("codefold-icon-closed"); + background-image: getThemifyVariable('codefold-icon-closed'); } } -.CodeMirror-foldgutter-folded:after, -.CodeMirror-foldgutter-open:after { - background-size: 10px 10px; - content: ""; - padding-left: 15px; - background-repeat: no-repeat; - background-position: center center; -} - -.CodeMirror-foldmarker { - text-shadow: none; - border-radius: 5px; - opacity: 1; - font-weight: normal; +// CM5: .CodeMirror-foldmarker +.cm-foldPlaceholder { display: inline-block; vertical-align: middle; height: 0.85em; line-height: 0.7; + border-radius: 5px; padding: 0 #{math.div(5, $base-font-size)}rem; font-family: serif; -} - -.line-runtime-error + .CodeMirror-activeline-gutter { - background-color: rgb(255, 95, 82); - opacity: 0.3; -} + cursor: pointer; + opacity: 0.75; -.line-runtime-error { - background-color: rgb(255, 95, 82) !important; - opacity: 0.3; + @include themify() { + background-color: getThemifyVariable('primary-text-color'); + color: getThemifyVariable('background-color'); + } } .editor-holder { @@ -380,7 +222,7 @@ pre.CodeMirror-line { width: 100%; position: absolute; @include themify() { - border: 1px solid getThemifyVariable("ide-border-color"); + border: 1px solid getThemifyVariable('ide-border-color'); } &.editor-holder--hidden .CodeMirror { display: none; @@ -393,7 +235,7 @@ pre.CodeMirror-line { .editor__file-name { @include themify() { - color: getThemifyVariable("primary-text-color"); + color: getThemifyVariable('primary-text-color'); } height: #{math.div(29, $base-font-size)}rem; padding-top: #{math.div(7, $base-font-size)}rem; @@ -406,7 +248,7 @@ pre.CodeMirror-line { .editor__library-version { @include themify() { - color: getThemifyVariable("primary-text-color"); + color: getThemifyVariable('primary-text-color'); } position: absolute; top: 0; @@ -422,41 +264,140 @@ pre.CodeMirror-line { } /** Inline abbreviation preview */ - -.emmet-abbreviation-preview { +.emmet-preview.cm-tooltip { @extend %modal; - position: absolute; @include themify() { - background: getThemifyVariable("background-color"); + background: getThemifyVariable('background-color'); } - & .CodeMirror-lines { + .cm-content { padding: 0; } - & .CodeMirror { - height: auto; - max-width: #{math.div(400, $base-font-size)}rem; - max-height: #{math.div(300, $base-font-size)}rem; - border: none; +} + +// Used for comment, lineComment, blockComment, docComment, docString +.cm-comment { + @include themify() { + color: getThemifyVariable('highlight-style-comment-color'); } } -.emmet-abbreviation-preview:not(.has-error) .emmet-abbreviation-preview-error { - display: none; +// Used for name, variableName, typeName +.cm-variable { + @include themify() { + color: getThemifyVariable('highlight-style-variable-color'); + } +} + +// Used for string, character, attributeValue +.cm-string { + @include themify() { + color: getThemifyVariable('highlight-style-string-color'); + } +} + +// Used for regexp +.cm-regexp { + @include themify() { + color: getThemifyVariable('highlight-style-regexp-color'); + } +} + +// Used for number, integer, float +.cm-number { + @include themify() { + color: getThemifyVariable('highlight-style-number-color'); + } +} + +// Used for bool, atom, null +.cm-atom { + @include themify() { + color: getThemifyVariable('highlight-style-atom-color'); + } +} + +// Used for keyword, self, function, className +.cm-keyword { + @include themify() { + color: getThemifyVariable('highlight-style-keyword-color'); + } +} + +// Used for operatorKeyword, controlKeyword, operator, derefOperator, arithmeticOperator, +// logicOperator, bitwiseOperator, compareOperator, updateOperator, typeOperator +// controlOperator +.cm-operator { + @include themify() { + color: getThemifyVariable('highlight-style-operator-color'); + } } -.emmet-abbreviation-preview.has-error .CodeMirror { - display: none; +// Used for definitionKeyword, definition, const, local +.cm-def { + @include themify() { + color: getThemifyVariable('highlight-style-def-color'); + } } -.emmet-abbreviation-preview .CodeMirror-cursors { - visibility: hidden !important; +// tagName, heading, heading1, heading2, heading3, heading4, heading5, heading6, +// list, quote, emphasis, strong, link +.cm-tag { + @include themify() { + color: getThemifyVariable('highlight-style-tag-color'); + } } -.emmet-abbreviation-preview .emmet-error-snippet-message { - padding: 5px; +// Used for propertyName +.cm-property { + @include themify() { + color: getThemifyVariable('highlight-style-property-color'); + } } -.emmet-open-tag, -.emmet-close-tag { - text-decoration: underline; +// Used for attributeName +.cm-attribute { + @include themify() { + color: getThemifyVariable('highlight-style-attribute-color'); + } +} + +// Unassigned Lezer tags: literal, escape, color, url, unit, modifier, punctuation, +// separator, bracket, angleBracket, squareBracket, paren, brace, content +// contentSeparator, monospace, strikethrough, inserted, deleted, changed, invalid, meta, +// documentMeta, annotation, processingInstruction, standard, special, macroName + +// CM6 uses EditorView.theme() instead of cm-s-* classes, so the p5 highlight styles +// from the CM5 theme files do not apply. These rules replicate them for CM6. +.cm-p5-function, +.cm-p5-function .cm-variable { + @include themify() { + color: getThemifyVariable('highlight-style-variable-color'); + font-weight: bold; + } +} + +.cm-p5-variable, +.cm-p5-variable .cm-variable { + @include themify() { + color: getThemifyVariable('highlight-style-atom-color'); + } +} + +// Additional matching selection styling +.cm-matchingBracket { + @include themify() { + outline: 1px solid getThemifyVariable('primary-text-color'); + outline-offset: 1px; + background-color: transparent !important; + } +} + +.cm-selectionMatch, +.cm-searchMatch, +.cm-searchMatch-selected { + @include themify() { + background-color: getThemifyVariable( + 'highlight-style-matching-selection-background-color' + ); + } } diff --git a/client/styles/components/_hints.scss b/client/styles/components/_hints.scss index 7084b460e7..2c22413ed0 100644 --- a/client/styles/components/_hints.scss +++ b/client/styles/components/_hints.scss @@ -1,276 +1,250 @@ @use "sass:math"; -.CodeMirror-hints { - position: absolute; - z-index: 10; - overflow: hidden; - list-style: none; - - margin: 0; - padding: 0; - +.cm-tooltip-autocomplete.CodeMirror-hints { box-shadow: 0 0 #{math.div(18, $base-font-size)}rem 0 rgba(0, 0, 0, 0.16); border: #{math.div(1, $base-font-size)}rem solid #A6A6A6; - - font-size: 100%; font-family: Inconsolata, monospace; - - width: 20rem; - max-height: 20rem; - overflow-y: auto; - - transform-origin: top left; + font-size: 1rem; @include themify() { background: getThemifyVariable('hint-background-color'); + color: getThemifyVariable('hint-text-color'); + } - .CodeMirror-hint { - color: getThemifyVariable('hint-text-color'); - border-bottom: #{math.div(1, $base-font-size)}rem solid getThemifyVariable('hint-item-border-bottom-color'); + ul { + @include themify() { + background: getThemifyVariable('hint-background-color'); } + } - .hint-container { - display: flex; - align-items: center; - flex-direction: column; - justify-content: center; - width: 100%; - height: 100%; + li.CodeMirror-hint { + display: grid; + grid-template-columns: minmax(0, 1fr) auto #{math.div(40, $base-font-size)}rem; + align-items: center; + min-height: #{math.div(24, $base-font-size)}rem; + font-weight: 500; - // Widen the entire row only if a warning is present - &.has-warning { - width: 100%; // Let it fill the parent .CodeMirror-hint width - max-width: 24rem; - } + @include themify() { + background: getThemifyVariable('hint-background-color'); + color: getThemifyVariable('hint-text-color'); + border-bottom: #{math.div(1, $base-font-size)}rem solid + getThemifyVariable('hint-item-border-bottom-color'); } - .hint-main { - display: flex; - justify-content: space-between; - align-items: center; - flex-grow: 1; - padding: 0 0.5rem; - width: 100%; - height: 100%; - // position: relative; // optional, only if you want absolutely-positioned children + &:hover:not([aria-selected='true']) { + @include themify() { + background: getThemifyVariable('hint-item-hover-background-color'); + } } - .hint-main a { - display: flex; - align-items: center; - justify-content: center; - width: 2rem; - height: 100%; - padding: 0; - // margin-left: auto; - text-align: center; - text-decoration: none; - font-size: 1.2rem; - } + &[aria-selected='true'] { + @include themify() { + background: getThemifyVariable('hint-item-active-background-color'); + color: getThemifyVariable('hint-item-active-text-color'); + outline: getThemifyVariable('hint-item-active-outline'); + outline-offset: getThemifyVariable('hint-item-active-outline-offset'); + } - .hint-name { - font-size: 1.2rem; - font-weight: bold; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } + .cm-completionKind { + @include themify() { + color: getThemifyVariable('hint-item-active-type-text-color'); + } + } - .hint-type { - font-size: 1rem; - font-weight: normal; - color: #999; - margin-left: 1rem; - margin-right: 2.5rem; // leaves space for the arrow icon - white-space: nowrap; - flex-shrink: 0; + .cm-completionMatchedText { + color: inherit; + } } - // Warning box - .blacklist-warning { - background-color: #fff3cd; - border: 1px solid #ffc107; - color: #856404; - padding: 6px 10px; - border-radius: 4px; - font-size: 0.85rem; - margin: 0.25rem 0.5rem 0 0.5rem; - width: calc(100% - 1rem); // Match padding - box-sizing: border-box; - } - - .fun-name, .obj-name { - color: getThemifyVariable('hint-fun-text-color'); - } - - .var-name, .boolean-name { - color: getThemifyVariable('hint-var-text-color'); + &.blacklisted { + height: auto; + min-height: 3.2rem; // enough to show the warning + content } + } - .keyword-name { - color: getThemifyVariable('hint-keyword-text-color'); - } - - .hint-type { + .cm-completionLabel { + grid-column: 1; + justify-self: start; + margin-left: #{math.div(12, $base-font-size)}rem; + line-height: 1.1; + font-weight: 600; + color: inherit; + background: transparent; + } + + .cm-completionMatchedText { + color: inherit; + text-decoration: none; + font-weight: inherit; + } + + .cm-completionDetail { + display: none; + } + + .cm-completionKind { + grid-column: 2; + justify-self: end; + padding-right: #{math.div(28, $base-font-size)}rem; + font-size: 0.8rem; + + @include themify() { color: getThemifyVariable('hint-type-text-color'); - margin-right: #{math.div(10, $base-font-size)}rem; } - - a { - color: getThemifyVariable('hint-arrow-color'); + } + + .cm-completionRefLink { + grid-column: 3; + display: flex; + align-items: center; + justify-content: center; + align-self: stretch; + width: 75%; + text-decoration: none; + + @include themify() { background: getThemifyVariable('hint-arrow-background-color'); + color: getThemifyVariable('hint-arrow-color'); + } - &:hover, &:active, &.focused-hint-link { + &:hover, + &:active, + &.focused-hint-link { + @include themify() { background: getThemifyVariable('hint-arrow-background-active-color'); } + } + + &.focused-hint-link { + width: 100%; + height: 100%; + margin: 0; + align-self: stretch; - &.focused-hint-link { - outline: #{math.div(3, $base-font-size)}rem solid getThemifyVariable('hint-arrow-focus-outline-color'); + @include themify() { + outline: #{math.div(3, $base-font-size)}rem solid + getThemifyVariable('hint-arrow-focus-outline-color'); outline-offset: #{math.div(-3, $base-font-size)}rem; } } + } - .no-link-placeholder { + .cm-completionRefLink--disabled, + li:not(.has-doc-link) .cm-completionRefLink { + @include themify() { background: getThemifyVariable('hint-no-link-background-color'); - pointer-events: none; + color: getThemifyVariable('hint-arrow-color'); } - - li.CodeMirror-hint-active:not(.unfocused) { - background: getThemifyVariable('hint-item-active-background-color'); - outline: getThemifyVariable('hint-item-active-outline'); - outline-offset: getThemifyVariable('hint-item-active-outline-offset'); - - // .fun-item { - // border-bottom: #{2 / $base-font-size}rem solid getThemifyVariable('hint-fun-active-border-bottom-color'); - // } - - // .var-item { - // border-bottom: #{2 / $base-font-size}rem solid getThemifyVariable('hint-var-active-border-bottom-color'); - // } + } - .hint-name { - color: getThemifyVariable('hint-item-active-text-color'); + // label colors + li.hint-type-method, + li.hint-type-obj { + .cm-completionLabel, + .cm-completionMatchedText { + @include themify() { + color: getThemifyVariable('hint-fun-text-color'); } + } + } - .fun-name, .obj-name { - background-color: getThemifyVariable('hint-fun-text-color'); - } - - .var-name, .boolean-name { - background-color: getThemifyVariable('hint-var-text-color'); + li.hint-type-variable, + li.hint-type-constant, + li.hint-type-boolean { + .cm-completionLabel, + .cm-completionMatchedText { + @include themify() { + color: getThemifyVariable('hint-var-text-color'); } + } + } - .keyword-name { - background-color: getThemifyVariable('hint-keyword-text-color'); - } - - .hint-type, .plain-hint-item { - color: getThemifyVariable('hint-item-active-type-text-color'); + li.hint-type-keyword { + .cm-completionLabel, + .cm-completionMatchedText { + @include themify() { + color: getThemifyVariable('hint-keyword-text-color'); } } - - .CodeMirror-hint:hover:not(.CodeMirror-hint-active) { - background: getThemifyVariable('hint-item-hover-background-color'); - } } - .CodeMirror-hint { - display: flex; - align-items: center; - justify-content: space-between; - - position: relative; - margin: 0; - padding: 0; - height: 2rem; - white-space: pre; - cursor: pointer; - - &:has(.focused-hint-link) { - z-index: 999; + // highlighted completion label colors + li.hint-type-method[aria-selected='true'], + li.hint-type-obj[aria-selected='true'] { + .cm-completionLabel { + @include themify() { + background: getThemifyVariable('hint-fun-text-color'); + color: getThemifyVariable('hint-item-active-text-color'); + border-bottom: #{math.div(1, $base-font-size)}rem solid + getThemifyVariable('hint-fun-active-border-bottom-color'); + } } + } - &:only-child, &:last-child { - border-bottom: none !important; + li.hint-type-variable[aria-selected='true'], + li.hint-type-constant[aria-selected='true'], + li.hint-type-boolean[aria-selected='true'] { + .cm-completionLabel { + @include themify() { + background: getThemifyVariable('hint-var-text-color'); + color: getThemifyVariable('hint-item-active-text-color'); + border-bottom: #{math.div(1, $base-font-size)}rem solid + getThemifyVariable('hint-var-active-border-bottom-color'); + } } + } - p { - display: flex; - width: 100%; - height: 100%; - } - - .hint-name, .plain-hint-item { - display: flex; - align-items: center; - padding: 0 0.5rem; - width: min-content; - font-size: 1.2rem; - line-height: 100%; - font-weight: bold; - } - - .hint-type { - margin: 0.5rem 2.4rem 0.5rem auto; - font-size: 1rem; - line-height: 100%; - font-weight: normal; + li.hint-type-keyword[aria-selected='true'] { + .cm-completionLabel { + @include themify() { + background: getThemifyVariable('hint-keyword-text-color'); + color: getThemifyVariable('hint-item-active-text-color'); + } } + } - .hint-hidden { - @extend %hidden-element; - } - - a, .no-link-placeholder { - // position: absolute; - top: 0; - right: 0; - height: 100%; - width: calc(2rem - #{math.div(1, $base-font-size)}rem); - margin: 0; - padding-top: 0.4rem; - font-size: 1.2rem; - line-height: 100%; - text-align: center; - outline: none; - z-index: 1; - } - - a:focus, a:active { - outline: 0; - } + .cm-completionWarning { + grid-column: 1 / 4; + display: inline-flex; + align-items: center; + gap: 0.35rem; + width: fit-content; + margin: 0.35rem 0.75rem 0.5rem 0.75rem; + padding: 0.32rem 0.55rem; + border: 1px solid #d9a300; + border-radius: 0.45rem; + background: #f3e7b7; + color: #9a6b00; + font-size: 0.8rem; + line-height: 1.1; + white-space: nowrap; } - .CodeMirror-hint.blacklisted { - height: auto; - min-height: 3.2rem; // enough to show the warning + content + .cm-completionWarningIcon { + flex: 0 0 auto; + font-size: 1rem; } -} -// Inline hinter -.CodeMirror-widget { - line-height: inherit; + .cm-completionWarningText { + line-height: 1.1; + } - @include themify() { - .autocomplete-inline-hinter { - // make the border left look like a cursor and animate like a cursor - // border-left: #{1.2 / $base-font-size}rem solid getThemifyVariable(hint-inline-text-color); - // animation: inline-hint-caret-blink 1s step-end infinite; - pointer-events: none; - - .inline-hinter-suggestion { - color: getThemifyVariable(hint-inline-text-color); - font-style: italic; - } + .cm-ghostCompletion { + @include themify() { + color: getThemifyVariable('hint-inline-text-color'); + } + } - .inline-hinter-suggestion-light { - color: getThemifyVariable(hint-inline-text-color-light); - font-style: italic; - } + .cm-ghostCompletion.cm-ghostCompletion--light { + @include themify() { + color: getThemifyVariable('hint-inline-text-color-light'); } } -} -@keyframes inline-hint-caret-blink { - 50% { border-color: transparent; } + .hint-hidden { + @include themify() { + @extend %hidden-element; + display: none; + } + } } diff --git a/client/styles/components/_p5-contrast-codemirror-theme.scss b/client/styles/components/_p5-contrast-codemirror-theme.scss deleted file mode 100644 index 93c604d0cc..0000000000 --- a/client/styles/components/_p5-contrast-codemirror-theme.scss +++ /dev/null @@ -1,152 +0,0 @@ -// brown: #6C4D13 -// black: #333 -// blue: #0F9DD7 -// pink: #D9328F -// gray: #999999 -// dark blue: #318094 -// white: #fdfdfd - -//numbers -//light gray: #f4f4f4 -//dark gray: #b5b5b5 - -@use "sass:math"; - -$p5-contrast-black: #1C1C1C; -$p5-contrast-gray: #A0A0A0; -$p5-contrast-white: #FDFDFD; -$p5-contrast-darkgray: #333333; -$p5-contrast-lightgray: #C1C1C1; -$p5-contrast-blue: #00FFFF; -$p5-contrast-green: #2DE9B6; -$p5-contrast-yellow: #F5DC23; -$p5-contrast-orange: #FFA95D; -$p5-contrast-pink: #FFA9D9; - -$p5-contrast-gutter: #454545; -$p5-contrast-number: #FDFDFD; -$p5-contrast-selected: $middle-dark; -$p5-contrast-activeline: #999999; - -.cm-s-p5-contrast { - background-color: $p5-contrast-black; - color: $p5-contrast-white; -} - -.cm-s-p5-contrast span .cm-comment { - color: $p5-contrast-lightgray; -} - -.cm-s-p5-contrast span .cm-def { - color: $p5-contrast-blue; -} - -.cm-s-p5-contrast span .cm-string { - color: $p5-contrast-green; -} - -.cm-s-p5-contrast span .cm-string-2 { - color: $p5-contrast-green; -} - -.cm-s-p5-contrast span .cm-number { - color: $p5-contrast-pink; -} - -.cm-s-p5-contrast span .cm-keyword { - color: $p5-contrast-yellow; -} - -.cm-s-p5-contrast span .cm-variable { - color: $p5-contrast-white; -} - -.cm-s-p5-contrast span .cm-variable-2 { - color: $p5-contrast-white; -} - -.cm-s-p5-contrast span .cm-property { - color: $p5-contrast-white; -} - -.cm-s-p5-contrast span .cm-atom { - color: $p5-contrast-pink; -} - -.cm-s-p5-contrast span .cm-operator { - color: $p5-contrast-lightgray; -} - -.cm-s-p5-contrast .cm-linenumber { - color: $p5-contrast-number; -} - -.cm-s-p5-contrast { - .CodeMirror-selected { background: $p5-contrast-selected; } - .CodeMirror-focused .CodeMirror-selected { background: $p5-contrast-selected; } - .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: $p5-contrast-selected; } - .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: $p5-contrast-selected; } -} - -.cm-s-p5-contrast .CodeMirror-activeline-background { - background-color: $dark; -} - -.cm-s-p5-contrast .CodeMirror-activeline-gutter { - background-color: $dark; - border-right: 1px solid $middle-dark; -} - -.cm-s-p5-contrast .cm-error { - color: #f00; -} - -.cm-s-p5-contrast span .CodeMirror-matchingbracket { - outline: 1px solid $p5-contrast-lightgray; - outline-offset: 1px; - color: $p5-contrast-white !important; -} - -.cm-s-p5-contrast span .cm-qualifier { - color: $p5-contrast-yellow; -} - -.cm-s-p5-contrast span .cm-tag { - color: $p5-contrast-orange; -} - -.cm-s-p5-contrast span .cm-builtin { - color: $p5-contrast-yellow; -} - -.cm-s-p5-contrast span .cm-attribute { - color: $p5-contrast-white; -} - -.cm-s-p5-contrast .cm-p5-function { - color: $p5-contrast-blue; -} - -.cm-s-p5-contrast .cm-p5-variable { - color: $p5-contrast-pink; - font-weight: bold; -} - -.cm-s-p5-contrast .CodeMirror-foldmarker { - background-color: white; - color: #333; -} - -.cm-s-p5-contrast .CodeMirror-cursor { - border-left: 1px solid $p5-contrast-white; -} - -.cm-s-p5-contrast .cm-searching { - // background-color: $p5js-pink-opacity; - background-color: $medium-dark; -} - -.cm-s-p5-contrast .cm-searching.CodeMirror-selectedtext { - // background-color: $medium-dark; - outline: #{math.div(1, $base-font-size)}rem solid $p5-contrast-white; -} diff --git a/client/styles/components/_p5-dark-codemirror-theme.scss b/client/styles/components/_p5-dark-codemirror-theme.scss deleted file mode 100644 index 77940fa188..0000000000 --- a/client/styles/components/_p5-dark-codemirror-theme.scss +++ /dev/null @@ -1,151 +0,0 @@ -// brown: #6C4D13 -// black: #333 -// blue: #0F9DD7 -// pink: #D9328F -// gray: #999999 -// dark blue: #318094 -// white: #fdfdfd - -//numbers -//light gray: #f4f4f4 -//dark gray: #b5b5b5 - -$p5-dark-lightbrown: #A67F59; -$p5-light-green: #42F48F; -$p5-dark-black: #1C1C1C; -$p5-dark-pink: #DE4A9B; -$p5-dark-gray: #9B9B9B; -$p5-dark-lightblue: #0F9DD7; -$p5-dark-darkblue: #318094; -$p5-dark-white: #FDFDFD; -$p5-dark-orange: #EE9900; -$p5-dark-lightgray: #E0D7D1; -$p5-dark-darkgray: #666666; -$p5-dark-green: #58a10b; -$p5-dark-goldbrown: #b58318; - -$p5-dark-gutter: #f4f4f4; -$p5-dark-number: #b5b5b5; -$p5-dark-selected: $medium-dark; -$p5-dark-activeline: rgb(207, 207, 207); - -$p5-dark-error: #df3a3d; - -.cm-s-p5-dark { - background-color: $p5-dark-black; - color: $p5-dark-white; -} - -.cm-s-p5-dark span.cm-comment { - color: $p5-dark-gray; -} - -.cm-s-p5-dark span.cm-def { - color: $p5-dark-lightblue; -} - -.cm-s-p5-dark span.cm-string { - color: $p5-dark-green; -} - -.cm-s-p5-dark span.cm-string-2 { - color: $p5-dark-orange; -} - -.cm-s-p5-dark span.cm-number { - color: $p5-dark-white; -} - -.cm-s-p5-dark span.cm-keyword { - color: $p5-dark-goldbrown; -} - -.cm-s-p5-dark span.cm-variable { - color: $p5-dark-lightblue; -} - -.cm-s-p5-dark span.cm-variable-2 { - color: $p5-dark-white; -} - -.cm-s-p5-dark span.cm-property { - color: $p5-dark-white; -} - -.cm-s-p5-dark span.cm-atom { - color: $p5-dark-pink; -} - -.cm-s-p5-dark span.cm-operator { - color: $p5-dark-white; -} - -.cm-s-p5-dark .cm-linenumber { - color: $p5-dark-number; -} - -.cm-s-p5-dark { - .CodeMirror-selected { background: $p5-dark-selected; } - .CodeMirror-focused .CodeMirror-selected { background: $p5-dark-selected; } - .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: $p5-dark-selected; } - .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: $p5-dark-selected; } -} - -.cm-s-p5-dark .CodeMirror-activeline-background { - background-color: $dark; -} - -.cm-s-p5-dark .CodeMirror-activeline-gutter { - background-color: $dark; - border-right: 1px solid $middle-dark; -} - -.cm-s-p5-dark span.CodeMirror-matchingbracket { - outline: 1px solid $p5-dark-gray; - outline-offset: 1px; - color: $p5-dark-white !important; -} - -.cm-s-p5-dark span.cm-qualifier { - color: $p5-dark-lightblue; -} - -.cm-s-p5-dark span.cm-tag { - color: $p5-dark-pink; -} - -.cm-s-p5-dark span.cm-error { - color: $p5-dark-error; -} - -.cm-s-p5-dark span.cm-builtin { - color: $p5-dark-lightblue; - font-weight: bold; -} - -.cm-s-p5-dark span.cm-attribute { - color: $p5-dark-lightblue; -} - -.cm-s-p5-dark .cm-p5-function { - color: $p5-dark-lightblue; - font-weight: bold !important; -} - -.cm-s-p5-dark .cm-p5-variable { - color: $p5-dark-pink; - font-weight: bold; -} - -.cm-s-p5-dark .CodeMirror-foldmarker { - background-color: white; - color: #333; -} - -.cm-s-p5-dark .CodeMirror-cursor { - border-left: 1px solid $p5-dark-white; -} - -.cm-s-p5-dark .cm-searching { - background-color: $p5js-pink-opacity; -} diff --git a/client/styles/components/_p5-light-codemirror-theme.scss b/client/styles/components/_p5-light-codemirror-theme.scss deleted file mode 100644 index c2e8062865..0000000000 --- a/client/styles/components/_p5-light-codemirror-theme.scss +++ /dev/null @@ -1,144 +0,0 @@ -// brown: #6C4D13 -// black: #333 -// blue: #0F9DD7 -// pink: #D9328F -// gray: #999999 -// dark blue: #318094 -// white: #fdfdfd - -//numbers -//light gray: #f4f4f4 -//dark gray: #b5b5b5 - -$p5-light-brown: #7A5A3A; -$p5-light-black: #333333; -$p5-light-pink: #D52889; -$p5-light-gray: #666; -$p5-light-blue: #0B7CA9; -$p5-light-white: $lighter; -$p5-light-orange: #A06801; -$p5-light-lightgray: $middle-gray; -$p5-light-green: #47820A; - - -$p5-light-gutter: #f4f4f4; -$p5-light-number: #b5b5b5; -$p5-light-selected: $medium-light; -$p5-light-activeline: rgb(207, 207, 207); - -.cm-s-p5-light { - background-color: $p5-light-white; - color: $p5-light-black; -} - -.cm-s-p5-light span .cm-comment { - color: $p5-light-lightgray; -} - -.cm-s-p5-light span .cm-def { - color: $p5-light-blue; -} - -.cm-s-p5-light span .cm-string { - color: $p5-light-green; -} - -.cm-s-p5-light span .cm-string-2 { - color: $p5-light-orange; -} - -.cm-s-p5-light span .cm-number { - color: $p5-light-black; -} - -.cm-s-p5-light .cm-keyword { - color: $p5-light-brown; -} - -.cm-s-p5-light span .cm-variable { - color: $p5-light-blue; -} - -.cm-s-p5-light span .cm-variable2 { - color: $p5-light-black; -} - -.cm-s-p5-light span .cm-property { - color: $p5-light-black; -} - -.cm-s-p5-light span .cm-atom { - color: $p5-light-pink; -} - -.cm-s-p5-light span .cm-operator { - color: $p5-light-brown; -} - -.cm-s-p5-light .cm-linenumber { - color: $p5-light-number; -} - -.cm-s-p5-light { - .CodeMirror-selected { background: $p5-light-selected; } - .CodeMirror-focused .CodeMirror-selected { background: $p5-light-selected; } - .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: $p5-light-selected; } - .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: $p5-light-selected; } -} - -.cm-s-p5-light .CodeMirror-activeline-background { - background-color: $light; -} - -.cm-s-p5-light .CodeMirror-activeline-gutter { - background-color: $light; - border-right: 1px solid $medium-light; -} - -.cm-s-p5-light .cm-error { - color: #f00; -} - -.cm-s-p5-light span .CodeMirror-matchingbracket { - outline: 1px solid $p5-light-gray; - outline-offset: 1px; - color: $p5-light-black !important; -} - -.cm-s-p5-light span .cm-qualifier { - color: $p5-light-blue; -} - -.cm-s-p5-light span .cm-tag { - color: $p5-light-pink; -} - -.cm-s-p5-light span .cm-builtin { - color: $p5-light-blue; -} - -.cm-s-p5-light span .cm-attribute { - color: $p5-light-black; -} - -.cm-s-p5-light .cm-p5-function { - color: $p5-light-blue; - font-weight: bold; -} - -.cm-s-p5-light .cm-p5-variable { - color: $p5-light-pink; -} - -.cm-s-p5-light .CodeMirror-foldmarker { - background-color: #333; - color: white; -} - -.cm-s-p5-light .CodeMirror-cursor { - border-left: 1px solid $p5-light-black; -} - -.cm-s-p5-light .cm-searching { - background-color: $p5js-pink-opacity; -} diff --git a/client/styles/main.scss b/client/styles/main.scss index 400da75bfb..3d1a0cbe07 100644 --- a/client/styles/main.scss +++ b/client/styles/main.scss @@ -6,15 +6,9 @@ @import 'base/reset'; @import 'base/base'; -@import '~codemirror/lib/codemirror'; -@import '~codemirror/addon/lint/lint'; -@import '~codemirror-colorpicker/addon/codemirror-colorpicker'; @import '~dropzone/dist/dropzone'; @import '~primer-tooltips/build/build'; -@import 'components/p5-light-codemirror-theme'; -@import 'components/p5-dark-codemirror-theme'; -@import 'components/p5-contrast-codemirror-theme'; @import 'components/account'; @import 'components/api-key'; @import 'components/editor'; diff --git a/client/testData/testReduxStore.ts b/client/testData/testReduxStore.ts index 0a150ea2ef..91b50ff177 100644 --- a/client/testData/testReduxStore.ts +++ b/client/testData/testReduxStore.ts @@ -46,7 +46,6 @@ const initialTestState: RootState = { justOpenedProject: false, previousPath: '/', errorType: undefined, - runtimeErrorWarningVisible: true, parentId: undefined }, files: initialFilesState(), diff --git a/client/utils/codemirror-search.js b/client/utils/codemirror-search.js deleted file mode 100644 index 3ed3116a63..0000000000 --- a/client/utils/codemirror-search.js +++ /dev/null @@ -1,830 +0,0 @@ -/* eslint-disable */ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: http://codemirror.net/LICENSE - -// Define search commands. Depends on dialog.js or another -// implementation of the openDialog method. - -// Replace works a little oddly -- it will do the replace on the next -// Ctrl-G (or whatever is bound to findNext) press. You prevent a -// replace by making sure the match is no longer selected when hitting -// Ctrl-G. -import i18n from '../i18n'; -import CodeMirror from 'codemirror'; -import triangleArrowRight from '../images/triangle-arrow-right.svg?byContent'; -import triangleArrowDown from '../images/triangle-arrow-down.svg?byContent'; -import downArrow from '../images/down-arrow.svg?byContent'; -import upArrow from '../images/up-arrow.svg?byContent'; -import exitIcon from '../images/exit.svg?byContent'; - -function searchOverlay(query, caseInsensitive) { - if (typeof query == 'string') { - query = new RegExp( - query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'), - caseInsensitive ? 'gi' : 'g' - ); - } else if (!query.global) { - query = new RegExp(query.source, query.ignoreCase ? 'gi' : 'g'); - } - - return { - token: function (stream) { - query.lastIndex = stream.pos; - var match = query.exec(stream.string); - if (match && match.index == stream.pos) { - stream.pos += match[0].length || 1; - return 'searching'; - } else if (match) { - stream.pos = match.index; - } else { - stream.skipToEnd(); - } - } - }; -} - -function SearchState() { - this.posFrom = this.posTo = this.lastQuery = this.query = null; - this.overlay = null; - this.regexp = false; - this.caseInsensitive = true; - this.wholeWord = false; - this.replaceStarted = false; - this.lastFileName = - document.querySelector('.editor__file-name span')?.innerText || null; -} - -function getSearchState(cm) { - return cm.state.search || (cm.state.search = new SearchState()); -} - -function getSearchCursor(cm, query, pos) { - // Heuristic: if the query string is all lowercase, do a case insensitive search. - return cm.getSearchCursor(query, pos, getSearchState(cm).caseInsensitive); -} - -function watchFileChanges(cm, searchState, searchField) { - let observer = null; - - function setupObserver() { - var fileNameElement = document.querySelector('.editor__file-name span'); - - if (!fileNameElement) { - setTimeout(setupObserver, 500); - return; - } - - if (observer) { - return; - } - - observer = new MutationObserver(() => { - if (searchField.value.length > 1) { - startSearch(cm, searchState, searchField.value); - } - }); - - observer.observe(fileNameElement, { characterData: true, subtree: true }); - } - - function disconnectObserver() { - if (observer) { - observer.disconnect(); - observer = null; - } - } - - setupObserver(); - - setInterval(() => { - var searchDialog = document.querySelector('.CodeMirror-dialog'); - if (!searchDialog && observer) { - disconnectObserver(); - return; - } else if (searchDialog && !observer) { - setupObserver(); - } - }, 500); -} - -function isMouseClick(event) { - if (event.detail > 0) return true; - else return false; -} - -function persistentDialog(cm, text, deflt, onEnter, replaceOpened, onKeyDown) { - var searchField = document.getElementsByClassName( - 'CodeMirror-search-field' - )[0]; - if (!searchField) { - cm.openDialog(text, onEnter, { - value: deflt, - selectValueOnOpen: true, - closeOnEnter: false, - onClose: function () { - clearSearch(cm); - }, - onKeyDown: onKeyDown, - closeOnBlur: false - }); - - searchField = document.getElementById('Find-input-field'); - - var dialog = document.getElementsByClassName('CodeMirror-dialog')[0]; - var closeButton = dialog.getElementsByClassName('close')[0]; - - var state = getSearchState(cm); - - watchFileChanges(cm, getSearchState(cm), searchField); - - CodeMirror.on(searchField, 'keyup', function (e) { - state.replaceStarted = false; - if (e.keyCode !== 13 && searchField.value.length > 1) { - startSearch(cm, getSearchState(cm), searchField.value); - } else if (searchField.value.length < 1) { - cm.display.wrapper.querySelector( - '.CodeMirror-search-results' - ).innerText = i18n.t('CodemirrorFindAndReplace.NoResults'); - } - }); - - CodeMirror.on(closeButton, 'click', function () { - dialog.parentNode.removeChild(dialog); - clearSearch(cm); - cm.focus(); - }); - - var upArrow = dialog.getElementsByClassName('up-arrow')[0]; - CodeMirror.on(upArrow, 'click', function () { - if (searchField.value.trim() === '') { - searchField.focus(); - } else { - cm.focus(); - CodeMirror.commands.findPrev(cm); - searchField.blur(); - } - }); - - var downArrow = dialog.getElementsByClassName('down-arrow')[0]; - CodeMirror.on(downArrow, 'click', function () { - if (searchField.value.trim() === '') { - searchField.focus(); - } else { - cm.focus(); - CodeMirror.commands.findNext(cm); - searchField.blur(); - } - }); - - var regexpButton = dialog.getElementsByClassName( - 'CodeMirror-regexp-button' - )[0]; - CodeMirror.on(regexpButton, 'click', function (event) { - var state = getSearchState(cm); - state.regexp = toggle(regexpButton); - startSearch(cm, getSearchState(cm), searchField.value); - if (isMouseClick(event)) searchField.focus(); - }); - - toggle(regexpButton, state.regexp); - - var caseSensitiveButton = dialog.getElementsByClassName( - 'CodeMirror-case-button' - )[0]; - CodeMirror.on(caseSensitiveButton, 'click', function (event) { - var state = getSearchState(cm); - state.caseInsensitive = !toggle(caseSensitiveButton); - startSearch(cm, getSearchState(cm), searchField.value); - if (isMouseClick(event)) searchField.focus(); - }); - - toggle(caseSensitiveButton, !state.caseInsensitive); - - var wholeWordButton = dialog.getElementsByClassName( - 'CodeMirror-word-button' - )[0]; - CodeMirror.on(wholeWordButton, 'click', function (event) { - var state = getSearchState(cm); - state.wholeWord = toggle(wholeWordButton); - startSearch(cm, getSearchState(cm), searchField.value); - if (isMouseClick(event)) searchField.focus(); - }); - - toggle(wholeWordButton, state.wholeWord); - - function toggle(el, initialState) { - var currentState, nextState; - - if (initialState == null) { - currentState = el.getAttribute('aria-checked') === 'true'; - nextState = !currentState; - } else { - nextState = initialState; - } - - el.setAttribute('aria-checked', nextState); - return nextState; - } - - function toggleReplace(open) { - var toggleButtonHeightOpened = '80px', - toggleButtonHeightClosed = '40px'; - - if (open) { - replaceFieldDiv.style.display = replaceControlsDiv.style.display = ''; - toggleReplaceBtnDiv.style.height = toggleButtonHeightOpened; - toggleReplaceBtn.style.height = toggleButtonHeightOpened; - toggleReplaceBtn.innerHTML = triangleArrowDown; - } else { - replaceFieldDiv.style.display = replaceControlsDiv.style.display = - 'none'; - toggleReplaceBtnDiv.style.height = toggleButtonHeightClosed; - toggleReplaceBtn.style.height = toggleButtonHeightClosed; - toggleReplaceBtn.innerHTML = triangleArrowRight; - } - } - - var toggleReplaceBtnDiv = document.getElementById('Btn-Toggle-replace-div'); - var toggleReplaceBtn = document.getElementById('Btn-Toggle-replace'); - var replaceFieldDiv = document.getElementById('Replace-input-div'); - var replaceControlsDiv = document.getElementById('Replace-controls-div'); - if (replaceOpened) { - toggleReplace(true); - } - CodeMirror.on(toggleReplaceBtn, 'click', function () { - if (replaceFieldDiv.style.display === 'none') { - toggleReplace(true); - } else { - toggleReplace(false); - } - }); - - var replaceField = document.getElementById('Replace-input-field'); - CodeMirror.on(replaceField, 'keyup', function (e) { - var state = getSearchState(cm); - var query = parseQuery(searchField.value, state); - var withText = parseString(replaceField.value); - if (e.keyCode === 13) { - // if enter - var cursor = getSearchCursor(cm, query, cm.getCursor('from')); - var start = cursor.from(); - var match = cursor.findNext(); - if (!match) { - cursor = getSearchCursor(cm, query); - if ( - !(match = cursor.findNext()) || - (start && - cursor.from().line == start.line && - cursor.from().ch == start.ch) - ) - return; - } - cm.setSelection(cursor.from(), cursor.to()); - state.replaceStarted = true; - doReplace(match, cursor, query, withText); - } - }); - - function doReplace(match, cursor, query, withText) { - cursor.replace( - typeof query == 'string' - ? withText - : withText.replace(/\$(\d)/g, function (_, i) { - return match[i]; - }) - ); - cursor.findNext(); - // cm.focus(); - CodeMirror.commands.findNext(cm); - // searchField.blur(); - } - - var doReplaceButton = document.getElementById('Btn-replace'); - CodeMirror.on(doReplaceButton, 'click', function (e) { - if (!searchField.value) { - searchField.focus(); - return; - } - var state = getSearchState(cm); - var query = parseQuery(searchField.value, state); - var withText = parseString(replaceField.value); - if (state.replaceStarted) { - var cursor = getSearchCursor(cm, query, cm.getCursor('from')); - var match = cursor.findNext(); - if (match) { - cm.setSelection(cursor.from(), cursor.to()); - doReplace(match, cursor, query, withText); - doReplaceButton.focus(); - } - } else { - startSearch(cm, state, searchField.value); - state.replaceStarted = true; - cm.focus(); - CodeMirror.commands.findNext(cm); - searchField.blur(); - doReplaceButton.focus(); - } - }); - - var doReplaceAllButton = document.getElementById('Btn-replace-all'); - CodeMirror.on(doReplaceAllButton, 'click', function (e) { - if (!searchField.value) { - searchField.focus(); - return; - } - var state = getSearchState(cm); - var query = parseQuery(searchField.value, state); - var withText = parseString(replaceField.value); - if (searchField.value.length > 1) { - state.replaceStarted = true; - } - if (state.replaceStarted) { - replaceAll(cm, query, withText); - state.replaceStarted = false; - } else { - startSearch(cm, state, searchField.value); - state.replaceStarted = true; - } - }); - } else { - searchField.value = deflt; - - searchField.focus(); - searchField.select(); - } -} - -function dialog(cm, text, shortText, deflt, f) { - if (cm.openDialog) - cm.openDialog(text, f, { value: deflt, selectValueOnOpen: true }); - else f(prompt(shortText, deflt)); -} - -function parseString(string) { - return string.replace(/\\(.)/g, function (_, ch) { - if (ch == 'n') return '\n'; - if (ch == 'r') return '\r'; - return ch; - }); -} - -function parseQuery(query, state) { - var emptyQuery = 'x^'; // matches nothing - if (query === '') { - query = emptyQuery; - } else { - if (state.regexp === false) { - query = parseString(query); - query = query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); - } - if (state.wholeWord) { - query = '\\b' + query + '\\b'; - } - } - - var regexp; - try { - regexp = new RegExp(query, state.caseInsensitive ? 'gi' : 'g'); - } catch (e) { - regexp = new RegExp(emptyQuery, 'g'); - } - // If the resulting regexp will match everything, do not use it - if (regexp.test('')) { - return new RegExp(emptyQuery, 'g'); - } - return regexp; -} - -function startSearch(cm, state, query) { - var searchDialog = document.querySelector('.CodeMirror-dialog'); - if (searchDialog) { - // check if the file has changed - let currentFileName = document.querySelector('.editor__file-name span') - ?.innerText; - - if (state.lastFileName !== currentFileName) { - state.lastFileName = currentFileName; - state.queryText = null; - state.lastQuery = null; - state.query = null; - cm.removeOverlay(state.overlay); - state.overlay = null; - - if (searchDialog) { - cm.display.wrapper.querySelector( - '.CodeMirror-search-results' - ).innerText = '0/0'; - } - } - - state.queryText = query; - state.lastQuery = query; - state.query = parseQuery(query, state); - cm.removeOverlay(state.overlay, state.caseInsensitive); - state.overlay = searchOverlay(state.query, state.caseInsensitive); - cm.addOverlay(state.overlay); - if (cm.showMatchesOnScrollbar) { - if (state.annotate) { - state.annotate.clear(); - state.annotate = null; - } - state.annotate = cm.showMatchesOnScrollbar( - state.query, - state.caseInsensitive - ); - } - - var cursor = getSearchCursor(cm, state.query); - cursor.findNext(); - var num_match = cm.state.search.annotate.matches.length; - if (num_match == 0) { - cm.display.wrapper.querySelector( - '.CodeMirror-search-results' - ).innerText = i18n.t('CodemirrorFindAndReplace.NoResults'); - cm.removeOverlay(state.overlay, state.caseInsensitive); - } else { - var next = - cm.state.search.annotate.matches.findIndex((s) => { - return ( - s.from.ch === cursor.from().ch && s.from.line === cursor.from().line - ); - }) + 1; - var text_match = next + '/' + num_match; - cm.display.wrapper.querySelector( - '.CodeMirror-search-results' - ).innerText = text_match; - } - } -} - -function doSearch(cm, rev, persistent, immediate, ignoreQuery) { - var state = getSearchState(cm); - if (!ignoreQuery && state.query) { - return findNext(cm, rev); - } - var q = cm.getSelection() || state.lastQuery; - var queryDialog = getQueryDialog(); - if (persistent && cm.openDialog) { - var hiding = null; - var searchNext = function (query, event) { - CodeMirror.e_stop(event); - if (!query) return; - if (query != state.queryText) { - startSearch(cm, state, query); - state.posFrom = state.posTo = cm.getCursor(); - } - if (hiding) hiding.style.opacity = 1; - findNext(cm, event.shiftKey, function (_, to) { - var dialog; - if ( - to.line < 3 && - document.querySelector && - (dialog = cm.display.wrapper.querySelector('.CodeMirror-dialog')) && - dialog.getBoundingClientRect().bottom - 4 > - cm.cursorCoords(to, 'window').top - ) - (hiding = dialog).style.opacity = 0.4; - }); - }; - persistentDialog( - cm, - queryDialog, - q, - searchNext, - false, - function (event, query) { - var keyName = CodeMirror.keyName(event); - var cmd = CodeMirror.keyMap[cm.getOption('keyMap')][keyName]; - if (!cmd) cmd = cm.getOption('extraKeys')[keyName]; - if ( - cmd == 'findNext' || - cmd == 'findPrev' || - cmd == 'findPersistentNext' || - cmd == 'findPersistentPrev' - ) { - CodeMirror.e_stop(event); - startSearch(cm, getSearchState(cm), query); - cm.execCommand(cmd); - } else if (cmd == 'find' || cmd == 'findPersistent') { - CodeMirror.e_stop(event); - searchNext(query, event); - } - } - ); - if (immediate && q) { - startSearch(cm, state, q); - findNext(cm, rev); - } - cm.on('change', function () { - var state = getSearchState(cm); - if (state.query) { - startSearch(cm, state, state.queryText); - } - }); - } else { - dialog(cm, queryDialog, 'Search for:', q, function (query) { - if (query && !state.query) - cm.operation(function () { - startSearch(cm, state, query); - state.posFrom = state.posTo = cm.getCursor(); - findNext(cm, rev); - }); - }); - } -} - -function doFindAndReplace( - cm, - rev, - persistent, - immediate, - ignoreQuery, - replaceOpened -) { - var state = getSearchState(cm); - if (!ignoreQuery && state.query) { - return findNext(cm, rev); - } - var q = cm.getSelection() || state.lastQuery; - var queryDialog = getQueryDialog(); - if (persistent && cm.openDialog) { - var hiding = null; - var searchNext = function (query, event) { - CodeMirror.e_stop(event); - if (!query) return; - if (query != state.queryText) { - startSearch(cm, state, query); - state.posFrom = state.posTo = cm.getCursor(); - } - if (hiding) hiding.style.opacity = 1; - findNext(cm, event.shiftKey, function (_, to) { - var dialog; - if ( - to.line < 3 && - document.querySelector && - (dialog = cm.display.wrapper.querySelector('.CodeMirror-dialog')) && - dialog.getBoundingClientRect().bottom - 4 > - cm.cursorCoords(to, 'window').top - ) - (hiding = dialog).style.opacity = 1; - }); - }; - persistentDialog( - cm, - queryDialog, - q, - searchNext, - replaceOpened, - function (event, query) { - var keyName = CodeMirror.keyName(event); - var cmd = CodeMirror.keyMap[cm.getOption('keyMap')][keyName]; - if (!cmd) cmd = cm.getOption('extraKeys')[keyName]; - if ( - cmd == 'findNext' || - cmd == 'findPrev' || - cmd == 'findPersistentNext' || - cmd == 'findPersistentPrev' - ) { - CodeMirror.e_stop(event); - startSearch(cm, getSearchState(cm), query); - cm.execCommand(cmd); - } else if (cmd == 'find' || cmd == 'findPersistent') { - CodeMirror.e_stop(event); - searchNext(query, event); - } - } - ); - if (immediate && q) { - startSearch(cm, state, q); - findNext(cm, rev); - } - } else { - dialog(cm, queryDialog, 'Search for:', q, function (query) { - if (query && !state.query) - cm.operation(function () { - startSearch(cm, state, query); - state.posFrom = state.posTo = cm.getCursor(); - findNext(cm, rev); - }); - }); - } -} - -function findNext(cm, rev, callback) { - cm.operation(function () { - var state = getSearchState(cm); - var cursor = getSearchCursor( - cm, - state.query, - rev ? state.posFrom : state.posTo - ); - if (!cursor.find(rev)) { - cursor = getSearchCursor( - cm, - state.query, - rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0) - ); - if (!cursor.find(rev)) { - cm.display.wrapper.querySelector( - '.CodeMirror-search-results' - ).innerText = i18n.t('CodemirrorFindAndReplace.NoResults'); - return; - } - } - cm.setSelection(cursor.from(), cursor.to()); - cm.scrollIntoView( - { from: cursor.from(), to: cursor.to() }, - cm.getScrollInfo().clientHeight / 2 - ); - state.posFrom = cursor.from(); - state.posTo = cursor.to(); - var num_match = cm.state.search.annotate.matches.length; - var next = - cm.state.search.annotate.matches.findIndex( - (s) => - s.from.ch === cursor.from().ch && s.from.line === cursor.from().line - ) + 1; - var text_match = next + '/' + num_match; - cm.display.wrapper.querySelector( - '.CodeMirror-search-results' - ).innerText = text_match; - if (callback) callback(cursor.from(), cursor.to()); - }); -} - -function clearSearch(cm) { - cm.operation(function () { - var state = getSearchState(cm); - state.replaceStarted = false; - if (!state.query) return; - state.query = state.queryText = null; - cm.removeOverlay(state.overlay); - if (state.annotate) { - state.annotate.clear(); - state.annotate = null; - } - }); -} - -function replaceAll(cm, query, text) { - cm.operation(function () { - for (var cursor = getSearchCursor(cm, query); cursor.findNext(); ) { - if (typeof query != 'string') { - var match = cm.getRange(cursor.from(), cursor.to()).match(query); - cursor.replace( - text.replace(/\$(\d)/g, function (_, i) { - return match[i]; - }) - ); - } else cursor.replace(text); - } - }); -} - -var getQueryDialog = function () { - return ` -
    -
    - -
    -
    -
    - -
    - -
    -
    - -
    -
    - - - -
    -
    -

    ${i18n.t( - 'CodemirrorFindAndReplace.NoResults' - )}

    - - -
    -
    - -
    -
    -
    -
    - `; -}; - -// CodeMirror.commands.findPersistent = function(cm) {doFindAndReplace(cm, false, true, false, true, false);}; -// CodeMirror.commands.findPersistentNext = function(cm) {doFindAndReplace(cm, false, true, false, true, false);}; -// CodeMirror.commands.findPersistentPrev = function(cm) {doFindAndReplace(cm, false, true, false, true, false);}; -// CodeMirror.commands.findNext = doFindAndReplace; -// CodeMirror.commands.findPrev = function(cm) {doFindAndReplace(cm, true);}; -// CodeMirror.commands.clearSearch = clearSearch; -// CodeMirror.commands.replace = function(cm) { doFindAndReplace(cm, false, true, false, true, true); }; - -CodeMirror.commands.find = function (cm) { - doSearch(cm); -}; -CodeMirror.commands.findPersistent = function (cm) { - doSearch(cm, false, true, false, true); -}; -CodeMirror.commands.findPersistentNext = function (cm) { - doSearch(cm, false, true, true); -}; -CodeMirror.commands.findPersistentPrev = function (cm) { - doSearch(cm, true, true, true); -}; -CodeMirror.commands.findNext = doSearch; -CodeMirror.commands.findPrev = function (cm) { - doSearch(cm, true); -}; -CodeMirror.commands.clearSearch = clearSearch; -CodeMirror.commands.replace = function (cm) { - doFindAndReplace(cm, false, true, false, true, true); -}; -CodeMirror.commands.replaceAll = function (cm) { - doFindAndReplace(cm, true); -}; diff --git a/client/utils/contextAwareHinter.js b/client/utils/contextAwareHinter.js index a90b88cd0e..f9fc11be39 100644 --- a/client/utils/contextAwareHinter.js +++ b/client/utils/contextAwareHinter.js @@ -4,17 +4,145 @@ import classMap from './p5-instance-methods-and-creators.json'; const scopeMap = require('./p5-scope-function-access-map.json'); -function getExpressionBeforeCursor(cm) { - const cursor = cm.getCursor(); - const line = cm.getLine(cursor.line); - const uptoCursor = line.slice(0, cursor.ch); +function getCurrentWordInfo(context) { + const word = context.matchBefore(/\w*/); + + if (!word) { + return { + text: '', + from: context.pos, + to: context.pos + }; + } + + return { + text: word.text || '', + from: word.from, + to: context.pos + }; +} + +function getExpressionBeforeCursor(state, pos) { + const line = state.doc.lineAt(pos); + const uptoCursor = line.text.slice(0, pos - line.from); const match = uptoCursor.match( /([a-zA-Z_$][\w$]*(?:\.[a-zA-Z_$][\w$]*)*)\.(?:[a-zA-Z_$][\w$]*)?$/ ); return match ? match[1] : null; } -export default function contextAwareHinter(cm, options = {}) { +function getTypedMemberInfo(state, pos) { + const line = state.doc.lineAt(pos); + const uptoCursor = line.text.slice(0, pos - line.from); + const dotMatch = uptoCursor.match(/\.([a-zA-Z_$][\w$]*)?$/); + + if (!dotMatch) { + return { + typed: '', + from: pos, + to: pos + }; + } + + const typed = dotMatch[1] || ''; + const methodStart = pos - dotMatch[0].length + 1; + + return { + typed, + from: methodStart, + to: pos + }; +} + +function formatPreview(label, params = []) { + if (!params.length) return `${label}()`; + + return `${label}(${params + .map((param) => (param.o ? `[${param.p}]` : param.p)) + .join(', ')})`; +} + +function makeBaseHintLookup(hints) { + const byLabel = new Map(); + + hints.forEach((hint) => { + if (hint?.label) { + byLabel.set(hint.label, hint); + } + }); + + return byLabel; +} + +function buildMethodOption(methodName, baseHint, range) { + const params = baseHint?.params || []; + + return { + label: methodName, + type: 'method', + kindLabel: baseHint?.kindLabel || 'fun', + params, + p5DocPath: baseHint?.p5DocPath, + preview: baseHint?.preview || formatPreview(methodName, params), + from: range.from, + to: range.to + }; +} + +function buildVarOrFunctionOption({ + name, + isFunc, + userDefinedFunctionMetadata, + blacklist, + range +}) { + const fnMeta = userDefinedFunctionMetadata[name]; + const params = fnMeta?.params || []; + + let preview; + + if (isFunc) { + if (fnMeta?.text) { + preview = formatPreview(fnMeta.text, params); + } else { + preview = formatPreview(name, params); + } + } else { + preview = undefined; + } + + const isBlacklisted = blacklist.includes(name); + + return { + label: fnMeta?.text || name, + type: isFunc ? 'method' : 'variable', + kindLabel: isFunc ? 'fun' : 'var', + params, + p5DocPath: undefined, + preview, + blacklisted: isBlacklisted, + warning: isBlacklisted ? '⚠️ use with caution in this context' : null, + from: range.from, + to: range.to + }; +} + +function buildGlobalHintOption(hint, blacklist, range) { + return { + ...hint, + blacklisted: blacklist.includes(hint.label), + warning: blacklist.includes(hint.label) + ? '⚠️ use with caution in this context' + : null, + from: range.from, + to: range.to + }; +} + +export default function contextAwareHinter(context, { hints = [] } = {}) { + const { state, pos } = context; + const cm = state.doc.toString(); + const { variableToP5ClassMap = {}, scopeToDeclaredVarsMap = {}, @@ -22,12 +150,9 @@ export default function contextAwareHinter(cm, options = {}) { userDefinedClassMetadata = {} } = p5CodeAstAnalyzer(cm) || {}; - const { hinter } = options; - if (!hinter || typeof hinter.search !== 'function') { - return []; - } + const baseHintLookup = makeBaseHintLookup(hints); - const baseExpression = getExpressionBeforeCursor(cm); + const baseExpression = getExpressionBeforeCursor(state, pos); if (baseExpression) { const className = variableToP5ClassMap[baseExpression]; @@ -38,61 +163,36 @@ export default function contextAwareHinter(cm, options = {}) { let methods = []; if (userClassEntry?.methods) { - const { methods: userMethods } = userClassEntry; - methods = userMethods; + methods = userClassEntry.methods; } else if (className && classMap[className]?.methods) { - const { methods: classMethods } = classMap[className]; - methods = classMethods; + methods = classMap[className].methods; } else { return []; } - const cursor = cm.getCursor(); - const lineText = cm.getLine(cursor.line); - const dotMatch = lineText - .slice(0, cursor.ch) - .match(/\.([a-zA-Z_$][\w$]*)?$/); - - let from = cursor; - if (dotMatch) { - const fullMatch = dotMatch[0]; - const methodStart = cursor.ch - fullMatch.length + 1; - from = { line: cursor.line, ch: methodStart }; - } else { - from = cursor; - } - - const to = { line: cursor.line, ch: cursor.ch }; - const typed = dotMatch?.[1]?.toLowerCase() || ''; - - const methodHints = methods - .filter((method) => method.toLowerCase().startsWith(typed)) - .map((method) => ({ - item: { - text: method, - type: 'fun', - isMethod: true - }, - displayText: method, - from, - to - })); - - return methodHints; + const memberInfo = getTypedMemberInfo(state, pos); + const typedLower = memberInfo.typed.toLowerCase(); + + const options = methods + .filter((method) => method.toLowerCase().startsWith(typedLower)) + .map((method) => + buildMethodOption(method, baseHintLookup.get(method), memberInfo) + ); + + return { + from: memberInfo.from, + to: memberInfo.to, + options, + filter: false + }; } - const { line, ch } = cm.getCursor(); - const { string } = cm.getTokenAt({ line, ch }); - const currentWord = string.trim(); - - const currentContext = getContext(cm); - const allHints = hinter.search(currentWord); + const wordInfo = getCurrentWordInfo(context); + const lowerCurrentWord = wordInfo.text.toLowerCase(); - // const whitelist = scopeMap[currentContext]?.whitelist || []; + const currentContext = getContext(cm, pos); const blacklist = scopeMap[currentContext]?.blacklist || []; - const lowerCurrentWord = currentWord.toLowerCase(); - function isInScope(varName) { return Object.entries(scopeToDeclaredVarsMap).some( ([scope, vars]) => @@ -103,13 +203,13 @@ export default function contextAwareHinter(cm, options = {}) { const allVarNames = Array.from( new Set( Object.values(scopeToDeclaredVarsMap) - .map((s) => Object.keys(s)) + .map((scopeVars) => Object.keys(scopeVars)) .flat() .filter((name) => typeof name === 'string') ) ); - const varHints = allVarNames + const localOptions = allVarNames .filter( (varName) => varName.toLowerCase().startsWith(lowerCurrentWord) && isInScope(varName) @@ -120,70 +220,58 @@ export default function contextAwareHinter(cm, options = {}) { (!scopeToDeclaredVarsMap[currentContext]?.[varName] && scopeToDeclaredVarsMap.global?.[varName] === 'fun'); - const baseItem = isFunc - ? { ...userDefinedFunctionMetadata[varName] } - : { - text: varName, - type: 'var', - params: [], - p5: false - }; - - return { - item: baseItem, - isBlacklisted: blacklist.includes(varName) - }; + return buildVarOrFunctionOption({ + name: varName, + isFunc, + userDefinedFunctionMetadata, + blacklist, + range: wordInfo + }); }); - const filteredHints = allHints + const globalOptions = hints .filter( - (h) => - h && - h.item && - typeof h.item.text === 'string' && - h.item.text.toLowerCase().startsWith(lowerCurrentWord) + (hint) => + hint && + typeof hint.label === 'string' && + hint.label.toLowerCase().startsWith(lowerCurrentWord) ) - .map((hint) => { - const name = hint.item?.text || ''; - const isBlacklisted = blacklist.includes(name); - - return { - ...hint, - isBlacklisted - }; - }); + .map((hint) => buildGlobalHintOption(hint, blacklist, wordInfo)); - const combinedHints = [...varHints, ...filteredHints]; + const combinedOptions = [...localOptions, ...globalOptions]; const typePriority = { - fun: 0, - var: 1, + method: 0, + variable: 1, keyword: 2, - other: 3 + constant: 3, + boolean: 4, + obj: 5, + other: 6 }; - const sorted = combinedHints.sort((a, b) => { - const nameA = a.item?.text || ''; - const nameB = b.item?.text || ''; - const typeA = a.item?.type || 'other'; - const typeB = b.item?.type || 'other'; - - const isBlacklistedA = a.isBlacklisted ? 1 : 0; - const isBlacklistedB = b.isBlacklisted ? 1 : 0; - - const typeScoreA = typePriority[typeA] ?? typePriority.other; - const typeScoreB = typePriority[typeB] ?? typePriority.other; + combinedOptions.sort((a, b) => { + const isBlacklistedA = a.blacklisted ? 1 : 0; + const isBlacklistedB = b.blacklisted ? 1 : 0; if (isBlacklistedA !== isBlacklistedB) { return isBlacklistedA - isBlacklistedB; } - if (typeScoreA !== typeScoreB) { - return typeScoreA - typeScoreB; + const typeA = typePriority[a.type] ?? typePriority.other; + const typeB = typePriority[b.type] ?? typePriority.other; + + if (typeA !== typeB) { + return typeA - typeB; } - return nameA.localeCompare(nameB); + return a.label.localeCompare(b.label); }); - return sorted; + return { + from: wordInfo.from, + to: wordInfo.to, + options: combinedOptions, + filter: false + }; } diff --git a/client/utils/getContext.js b/client/utils/getContext.js index beddef71f7..2e9a11ebdc 100644 --- a/client/utils/getContext.js +++ b/client/utils/getContext.js @@ -1,11 +1,7 @@ const parser = require('@babel/parser'); const traverse = require('@babel/traverse').default; -export default function getContext(_cm) { - const code = _cm.getValue(); - const cursor = _cm.getCursor(); - const offset = _cm.indexFromPos(cursor); - +export default function getContext(code, pos) { let ast; try { ast = parser.parse(code, { @@ -21,7 +17,7 @@ export default function getContext(_cm) { traverse(ast, { Function(path) { const { node } = path; - if (offset >= node.start && offset <= node.end) { + if (pos >= node.start && pos <= node.end) { if (node.id && node.id.name) { context = node.id.name; } else { diff --git a/client/utils/htmlmixed.js b/client/utils/htmlmixed.js deleted file mode 100644 index 23a0eacf05..0000000000 --- a/client/utils/htmlmixed.js +++ /dev/null @@ -1,153 +0,0 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: http://codemirror.net/LICENSE -/* eslint-disable */ - -(function(mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("codemirror"), require("codemirror/mode/xml/xml"), require("./p5-javascript"), require("codemirror/mode/css/css")); - else if (typeof define == "function" && define.amd) // AMD - define(["codemirror", "codemirror/mode/xml/xml", "./p5-javascript", "codemirror/mode/css/css"], mod); - else // Plain browser env - mod(CodeMirror); -})(function(CodeMirror) { - "use strict"; - - var defaultTags = { - script: [ - ["lang", /(javascript|babel)/i, "javascript"], - ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^$/i, "javascript"], - ["type", /./, "text/plain"], - [null, null, "javascript"] - ], - style: [ - ["lang", /^css$/i, "css"], - ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"], - ["type", /./, "text/plain"], - [null, null, "css"] - ] - }; - - function maybeBackup(stream, pat, style) { - var cur = stream.current(), close = cur.search(pat); - if (close > -1) { - stream.backUp(cur.length - close); - } else if (cur.match(/<\/?$/)) { - stream.backUp(cur.length); - if (!stream.match(pat, false)) stream.match(cur); - } - return style; - } - - var attrRegexpCache = {}; - function getAttrRegexp(attr) { - var regexp = attrRegexpCache[attr]; - if (regexp) return regexp; - return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*"); - } - - function getAttrValue(text, attr) { - var match = text.match(getAttrRegexp(attr)) - return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : "" - } - - function getTagRegexp(tagName, anchored) { - return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i"); - } - - function addTags(from, to) { - for (var tag in from) { - var dest = to[tag] || (to[tag] = []); - var source = from[tag]; - for (var i = source.length - 1; i >= 0; i--) - dest.unshift(source[i]) - } - } - - function findMatchingMode(tagInfo, tagText) { - for (var i = 0; i < tagInfo.length; i++) { - var spec = tagInfo[i]; - if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2]; - } - } - - CodeMirror.defineMode("htmlmixed", function (config, parserConfig) { - var htmlMode = CodeMirror.getMode(config, { - name: "xml", - htmlMode: true, - multilineTagIndentFactor: parserConfig.multilineTagIndentFactor, - multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag - }); - - var tags = {}; - var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes; - addTags(defaultTags, tags); - if (configTags) addTags(configTags, tags); - if (configScript) for (var i = configScript.length - 1; i >= 0; i--) - tags.script.unshift(["type", configScript[i].matches, configScript[i].mode]) - - function html(stream, state) { - var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName - if (tag && !/[<>\s\/]/.test(stream.current()) && - (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) && - tags.hasOwnProperty(tagName)) { - state.inTag = tagName + " " - } else if (state.inTag && tag && />$/.test(stream.current())) { - var inTag = /^([\S]+) (.*)/.exec(state.inTag) - state.inTag = null - var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2]) - var mode = CodeMirror.getMode(config, modeSpec) - var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false); - state.token = function (stream, state) { - if (stream.match(endTagA, false)) { - state.token = html; - state.localState = state.localMode = null; - return null; - } - return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState)); - }; - state.localMode = mode; - state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "")); - } else if (state.inTag) { - state.inTag += stream.current() - if (stream.eol()) state.inTag += " " - } - return style; - }; - - return { - startState: function () { - var state = CodeMirror.startState(htmlMode); - return {token: html, inTag: null, localMode: null, localState: null, htmlState: state}; - }, - - copyState: function (state) { - var local; - if (state.localState) { - local = CodeMirror.copyState(state.localMode, state.localState); - } - return {token: state.token, inTag: state.inTag, - localMode: state.localMode, localState: local, - htmlState: CodeMirror.copyState(htmlMode, state.htmlState)}; - }, - - token: function (stream, state) { - return state.token(stream, state); - }, - - indent: function (state, textAfter) { - if (!state.localMode || /^\s*<\//.test(textAfter)) - return htmlMode.indent(state.htmlState, textAfter); - else if (state.localMode.indent) - return state.localMode.indent(state.localState, textAfter); - else - return CodeMirror.Pass; - }, - - innerMode: function (state) { - return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode}; - } - }; - }, "xml", "javascript", "css"); - - CodeMirror.defineMIME("text/html", "htmlmixed"); -}); diff --git a/client/utils/p5-hinter.js b/client/utils/p5-hinter.js index 25d68c59f2..dee7c9870b 100644 --- a/client/utils/p5-hinter.js +++ b/client/utils/p5-hinter.js @@ -1,1734 +1,3 @@ /* eslint-disable */ /* generated: do not edit! helper file for hinter. generated by update-p5-hinter script */ -exports.p5Hinter = [ - { - text: 'describe', - type: 'fun', - params: [ - { p: 'text', o: false }, - { p: 'display', o: true } - ], - p5: true - }, - { - text: 'describeElement', - type: 'fun', - params: [ - { p: 'name', o: false }, - { p: 'text', o: false }, - { p: 'display', o: true } - ], - p5: true - }, - { - text: 'textOutput', - type: 'fun', - params: [{ p: 'display', o: true }], - p5: true - }, - { - text: 'gridOutput', - type: 'fun', - params: [{ p: 'display', o: true }], - p5: true - }, - { text: 'alpha', type: 'fun', params: [{ p: 'color', o: false }], p5: true }, - { text: 'blue', type: 'fun', params: [{ p: 'color', o: false }], p5: true }, - { - text: 'brightness', - type: 'fun', - params: [{ p: 'color', o: false }], - p5: true - }, - { text: 'color', type: 'fun', p5: true }, - { text: 'green', type: 'fun', params: [{ p: 'color', o: false }], p5: true }, - { text: 'hue', type: 'fun', params: [{ p: 'color', o: false }], p5: true }, - { - text: 'lerpColor', - type: 'fun', - params: [ - { p: 'c1', o: false }, - { p: 'c2', o: false }, - { p: 'amt', o: false } - ], - p5: true - }, - { - text: 'lightness', - type: 'fun', - params: [{ p: 'color', o: false }], - p5: true - }, - { text: 'red', type: 'fun', params: [{ p: 'color', o: false }], p5: true }, - { - text: 'saturation', - type: 'fun', - params: [{ p: 'color', o: false }], - p5: true - }, - { - text: 'beginClip', - type: 'fun', - params: [{ p: 'options', o: true }], - p5: true - }, - { text: 'endClip', type: 'fun', p5: true }, - { - text: 'clip', - type: 'fun', - params: [ - { p: 'callback', o: false }, - { p: 'options', o: true } - ], - p5: true - }, - { text: 'background', type: 'fun', p5: true }, - { - text: 'clear', - type: 'fun', - params: [ - { p: 'r', o: true }, - { p: 'g', o: true }, - { p: 'b', o: true }, - { p: 'a', o: true } - ], - p5: true - }, - { text: 'colorMode', type: 'fun', p5: true }, - { text: 'fill', type: 'fun', p5: true }, - { text: 'noFill', type: 'fun', p5: true }, - { text: 'noStroke', type: 'fun', p5: true }, - { text: 'stroke', type: 'fun', p5: true }, - { - text: 'erase', - type: 'fun', - params: [ - { p: 'strengthFill', o: true }, - { p: 'strengthStroke', o: true } - ], - p5: true - }, - { text: 'noErase', type: 'fun', p5: true }, - { - text: 'arc', - type: 'fun', - params: [ - { p: 'x', o: false }, - { p: 'y', o: false }, - { p: 'w', o: false }, - { p: 'h', o: false }, - { p: 'start', o: false }, - { p: 'stop', o: false }, - { p: 'mode', o: true }, - { p: 'detail', o: true } - ], - p5: true - }, - { text: 'ellipse', type: 'fun', p5: true }, - { - text: 'circle', - type: 'fun', - params: [ - { p: 'x', o: false }, - { p: 'y', o: false }, - { p: 'd', o: false } - ], - p5: true - }, - { text: 'line', type: 'fun', p5: true }, - { text: 'point', type: 'fun', p5: true }, - { text: 'quad', type: 'fun', p5: true }, - { text: 'rect', type: 'fun', p5: true }, - { - text: 'square', - type: 'fun', - params: [ - { p: 'x', o: false }, - { p: 'y', o: false }, - { p: 's', o: false }, - { p: 'tl', o: true }, - { p: 'tr', o: true }, - { p: 'br', o: true }, - { p: 'bl', o: true } - ], - p5: true - }, - { - text: 'triangle', - type: 'fun', - params: [ - { p: 'x1', o: false }, - { p: 'y1', o: false }, - { p: 'x2', o: false }, - { p: 'y2', o: false }, - { p: 'x3', o: false }, - { p: 'y3', o: false } - ], - p5: true - }, - { - text: 'ellipseMode', - type: 'fun', - params: [{ p: 'mode', o: false }], - p5: true - }, - { text: 'noSmooth', type: 'fun', p5: true }, - { - text: 'rectMode', - type: 'fun', - params: [{ p: 'mode', o: false }], - p5: true - }, - { text: 'smooth', type: 'fun', p5: true }, - { - text: 'strokeCap', - type: 'fun', - params: [{ p: 'cap', o: false }], - p5: true - }, - { - text: 'strokeJoin', - type: 'fun', - params: [{ p: 'join', o: false }], - p5: true - }, - { - text: 'strokeWeight', - type: 'fun', - params: [{ p: 'weight', o: false }], - p5: true - }, - { text: 'bezier', type: 'fun', p5: true }, - { - text: 'bezierDetail', - type: 'fun', - params: [{ p: 'detail', o: false }], - p5: true - }, - { - text: 'bezierPoint', - type: 'fun', - params: [ - { p: 'a', o: false }, - { p: 'b', o: false }, - { p: 'c', o: false }, - { p: 'd', o: false }, - { p: 't', o: false } - ], - p5: true - }, - { - text: 'bezierTangent', - type: 'fun', - params: [ - { p: 'a', o: false }, - { p: 'b', o: false }, - { p: 'c', o: false }, - { p: 'd', o: false }, - { p: 't', o: false } - ], - p5: true - }, - { text: 'curve', type: 'fun', p5: true }, - { - text: 'curveDetail', - type: 'fun', - params: [{ p: 'resolution', o: false }], - p5: true - }, - { - text: 'curveTightness', - type: 'fun', - params: [{ p: 'amount', o: false }], - p5: true - }, - { - text: 'curvePoint', - type: 'fun', - params: [ - { p: 'a', o: false }, - { p: 'b', o: false }, - { p: 'c', o: false }, - { p: 'd', o: false }, - { p: 't', o: false } - ], - p5: true - }, - { - text: 'curveTangent', - type: 'fun', - params: [ - { p: 'a', o: false }, - { p: 'b', o: false }, - { p: 'c', o: false }, - { p: 'd', o: false }, - { p: 't', o: false } - ], - p5: true - }, - { text: 'beginContour', type: 'fun', p5: true }, - { - text: 'beginShape', - type: 'fun', - params: [{ p: 'kind', o: true }], - p5: true - }, - { text: 'bezierVertex', type: 'fun', p5: true }, - { text: 'curveVertex', type: 'fun', p5: true }, - { text: 'endContour', type: 'fun', p5: true }, - { - text: 'endShape', - type: 'fun', - params: [ - { p: 'mode', o: true }, - { p: 'count', o: true } - ], - p5: true - }, - { text: 'quadraticVertex', type: 'fun', p5: true }, - { text: 'vertex', type: 'fun', p5: true }, - { text: 'normal', type: 'fun', p5: true }, - { text: 'VERSION', type: 'var', params: [], p5: true }, - { text: 'P2D', type: 'var', params: [], p5: true }, - { text: 'WEBGL', type: 'var', params: [], p5: true }, - { text: 'WEBGL2', type: 'var', params: [], p5: true }, - { text: 'ARROW', type: 'var', params: [], p5: true }, - { text: 'CROSS', type: 'var', params: [], p5: true }, - { text: 'HAND', type: 'var', params: [], p5: true }, - { text: 'MOVE', type: 'var', params: [], p5: true }, - { text: 'TEXT', type: 'var', params: [], p5: true }, - { text: 'WAIT', type: 'var', params: [], p5: true }, - { text: 'HALF_PI', type: 'var', params: [], p5: true }, - { text: 'PI', type: 'var', params: [], p5: true }, - { text: 'QUARTER_PI', type: 'var', params: [], p5: true }, - { text: 'TAU', type: 'var', params: [], p5: true }, - { text: 'TWO_PI', type: 'var', params: [], p5: true }, - { text: 'DEGREES', type: 'var', params: [], p5: true }, - { text: 'RADIANS', type: 'var', params: [], p5: true }, - { text: 'CORNER', type: 'var', params: [], p5: true }, - { text: 'CORNERS', type: 'var', params: [], p5: true }, - { text: 'RADIUS', type: 'var', params: [], p5: true }, - { text: 'RIGHT', type: 'var', params: [], p5: true }, - { text: 'LEFT', type: 'var', params: [], p5: true }, - { text: 'CENTER', type: 'var', params: [], p5: true }, - { text: 'TOP', type: 'var', params: [], p5: true }, - { text: 'BOTTOM', type: 'var', params: [], p5: true }, - { text: 'BASELINE', type: 'var', params: [], p5: true }, - { text: 'POINTS', type: 'var', params: [], p5: true }, - { text: 'LINES', type: 'var', params: [], p5: true }, - { text: 'LINE_STRIP', type: 'var', params: [], p5: true }, - { text: 'LINE_LOOP', type: 'var', params: [], p5: true }, - { text: 'TRIANGLES', type: 'var', params: [], p5: true }, - { text: 'TRIANGLE_FAN', type: 'var', params: [], p5: true }, - { text: 'TRIANGLE_STRIP', type: 'var', params: [], p5: true }, - { text: 'QUADS', type: 'var', params: [], p5: true }, - { text: 'QUAD_STRIP', type: 'var', params: [], p5: true }, - { text: 'TESS', type: 'var', params: [], p5: true }, - { text: 'CLOSE', type: 'var', params: [], p5: true }, - { text: 'OPEN', type: 'var', params: [], p5: true }, - { text: 'CHORD', type: 'var', params: [], p5: true }, - { text: 'PIE', type: 'var', params: [], p5: true }, - { text: 'PROJECT', type: 'var', params: [], p5: true }, - { text: 'SQUARE', type: 'var', params: [], p5: true }, - { text: 'ROUND', type: 'var', params: [], p5: true }, - { text: 'BEVEL', type: 'var', params: [], p5: true }, - { text: 'MITER', type: 'var', params: [], p5: true }, - { text: 'RGB', type: 'var', params: [], p5: true }, - { text: 'HSB', type: 'var', params: [], p5: true }, - { text: 'HSL', type: 'var', params: [], p5: true }, - { text: 'AUTO', type: 'var', params: [], p5: true }, - { text: 'ALT', type: 'var', params: [], p5: true }, - { text: 'BACKSPACE', type: 'var', params: [], p5: true }, - { text: 'CONTROL', type: 'var', params: [], p5: true }, - { text: 'DELETE', type: 'var', params: [], p5: true }, - { text: 'DOWN_ARROW', type: 'var', params: [], p5: true }, - { text: 'ENTER', type: 'var', params: [], p5: true }, - { text: 'ESCAPE', type: 'var', params: [], p5: true }, - { text: 'LEFT_ARROW', type: 'var', params: [], p5: true }, - { text: 'OPTION', type: 'var', params: [], p5: true }, - { text: 'RETURN', type: 'var', params: [], p5: true }, - { text: 'RIGHT_ARROW', type: 'var', params: [], p5: true }, - { text: 'SHIFT', type: 'var', params: [], p5: true }, - { text: 'TAB', type: 'var', params: [], p5: true }, - { text: 'UP_ARROW', type: 'var', params: [], p5: true }, - { text: 'BLEND', type: 'var', params: [], p5: true }, - { text: 'REMOVE', type: 'var', params: [], p5: true }, - { text: 'ADD', type: 'var', params: [], p5: true }, - { text: 'DARKEST', type: 'var', params: [], p5: true }, - { text: 'LIGHTEST', type: 'var', params: [], p5: true }, - { text: 'DIFFERENCE', type: 'var', params: [], p5: true }, - { text: 'SUBTRACT', type: 'var', params: [], p5: true }, - { text: 'EXCLUSION', type: 'var', params: [], p5: true }, - { text: 'MULTIPLY', type: 'var', params: [], p5: true }, - { text: 'SCREEN', type: 'var', params: [], p5: true }, - { text: 'REPLACE', type: 'var', params: [], p5: true }, - { text: 'OVERLAY', type: 'var', params: [], p5: true }, - { text: 'HARD_LIGHT', type: 'var', params: [], p5: true }, - { text: 'SOFT_LIGHT', type: 'var', params: [], p5: true }, - { text: 'DODGE', type: 'var', params: [], p5: true }, - { text: 'BURN', type: 'var', params: [], p5: true }, - { text: 'THRESHOLD', type: 'var', params: [], p5: true }, - { text: 'GRAY', type: 'var', params: [], p5: true }, - { text: 'OPAQUE', type: 'var', params: [], p5: true }, - { text: 'INVERT', type: 'var', params: [], p5: true }, - { text: 'POSTERIZE', type: 'var', params: [], p5: true }, - { text: 'DILATE', type: 'var', params: [], p5: true }, - { text: 'ERODE', type: 'var', params: [], p5: true }, - { text: 'BLUR', type: 'var', params: [], p5: true }, - { text: 'NORMAL', type: 'var', params: [], p5: true }, - { text: 'ITALIC', type: 'var', params: [], p5: true }, - { text: 'BOLD', type: 'var', params: [], p5: true }, - { text: 'BOLDITALIC', type: 'var', params: [], p5: true }, - { text: 'CHAR', type: 'var', params: [], p5: true }, - { text: 'WORD', type: 'var', params: [], p5: true }, - { text: 'LINEAR', type: 'var', params: [], p5: true }, - { text: 'QUADRATIC', type: 'var', params: [], p5: true }, - { text: 'BEZIER', type: 'var', params: [], p5: true }, - { text: 'CURVE', type: 'var', params: [], p5: true }, - { text: 'STROKE', type: 'var', params: [], p5: true }, - { text: 'FILL', type: 'var', params: [], p5: true }, - { text: 'TEXTURE', type: 'var', params: [], p5: true }, - { text: 'IMMEDIATE', type: 'var', params: [], p5: true }, - { text: 'IMAGE', type: 'var', params: [], p5: true }, - { text: 'NEAREST', type: 'var', params: [], p5: true }, - { text: 'REPEAT', type: 'var', params: [], p5: true }, - { text: 'CLAMP', type: 'var', params: [], p5: true }, - { text: 'MIRROR', type: 'var', params: [], p5: true }, - { text: 'FLAT', type: 'var', params: [], p5: true }, - { text: 'SMOOTH', type: 'var', params: [], p5: true }, - { text: 'LANDSCAPE', type: 'var', params: [], p5: true }, - { text: 'PORTRAIT', type: 'var', params: [], p5: true }, - { text: 'GRID', type: 'var', params: [], p5: true }, - { text: 'AXES', type: 'var', params: [], p5: true }, - { text: 'LABEL', type: 'var', params: [], p5: true }, - { text: 'FALLBACK', type: 'var', params: [], p5: true }, - { text: 'CONTAIN', type: 'var', params: [], p5: true }, - { text: 'COVER', type: 'var', params: [], p5: true }, - { text: 'UNSIGNED_BYTE', type: 'var', params: [], p5: true }, - { text: 'UNSIGNED_INT', type: 'var', params: [], p5: true }, - { text: 'FLOAT', type: 'var', params: [], p5: true }, - { text: 'HALF_FLOAT', type: 'var', params: [], p5: true }, - { text: 'RGBA', type: 'var', params: [], p5: true }, - { - text: 'print', - type: 'fun', - params: [{ p: 'contents', o: false }], - p5: true - }, - { text: 'frameCount', type: 'var', params: [], p5: true }, - { text: 'deltaTime', type: 'var', params: [], p5: true }, - { text: 'focused', type: 'var', params: [], p5: true }, - { - text: 'cursor', - type: 'fun', - params: [ - { p: 'type', o: false }, - { p: 'x', o: true }, - { p: 'y', o: true } - ], - p5: true - }, - { text: 'frameRate', type: 'fun', p5: true }, - { text: 'getTargetFrameRate', type: 'fun', p5: true }, - { text: 'noCursor', type: 'fun', p5: true }, - { text: 'webglVersion', type: 'var', params: [], p5: true }, - { text: 'displayWidth', type: 'var', params: [], p5: true }, - { text: 'displayHeight', type: 'var', params: [], p5: true }, - { text: 'windowWidth', type: 'var', params: [], p5: true }, - { text: 'windowHeight', type: 'var', params: [], p5: true }, - { - text: 'windowResized', - type: 'fun', - params: [{ p: 'event', o: true }], - p5: true - }, - { text: 'width', type: 'var', params: [], p5: true }, - { text: 'height', type: 'var', params: [], p5: true }, - { - text: 'fullscreen', - type: 'fun', - params: [{ p: 'val', o: true }], - p5: true - }, - { text: 'pixelDensity', type: 'fun', p5: true }, - { text: 'displayDensity', type: 'fun', p5: true }, - { text: 'getURL', type: 'fun', p5: true }, - { text: 'getURLPath', type: 'fun', p5: true }, - { text: 'getURLParams', type: 'fun', p5: true }, - { text: 'preload', type: 'fun', p5: true }, - { text: 'setup', type: 'fun', p5: true }, - { text: 'draw', type: 'fun', p5: true }, - { text: 'remove', type: 'fun', p5: true }, - { text: 'disableFriendlyErrors', type: 'var', params: [], p5: true }, - { text: 'createCanvas', type: 'fun', p5: true }, - { - text: 'resizeCanvas', - type: 'fun', - params: [ - { p: 'width', o: false }, - { p: 'height', o: false }, - { p: 'noRedraw', o: true } - ], - p5: true - }, - { text: 'noCanvas', type: 'fun', p5: true }, - { text: 'createGraphics', type: 'fun', p5: true }, - { - text: 'createFramebuffer', - type: 'fun', - params: [{ p: 'options', o: true }], - p5: true - }, - { - text: 'clearDepth', - type: 'fun', - params: [{ p: 'depth', o: true }], - p5: true - }, - { - text: 'blendMode', - type: 'fun', - params: [{ p: 'mode', o: false }], - p5: true - }, - { text: 'drawingContext', type: 'var', params: [], p5: true }, - { text: 'noLoop', type: 'fun', p5: true }, - { text: 'loop', type: 'fun', p5: true }, - { text: 'isLooping', type: 'fun', p5: true }, - { text: 'push', type: 'fun', p5: true }, - { text: 'pop', type: 'fun', p5: true }, - { text: 'redraw', type: 'fun', params: [{ p: 'n', o: true }], p5: true }, - { - text: 'p5', - type: 'fun', - params: [ - { p: 'sketch', o: false }, - { p: 'node', o: false } - ], - p5: true - }, - { text: 'applyMatrix', type: 'fun', p5: true }, - { text: 'resetMatrix', type: 'fun', p5: true }, - { - text: 'rotate', - type: 'fun', - params: [ - { p: 'angle', o: false }, - { p: 'axis', o: true } - ], - p5: true - }, - { - text: 'rotateX', - type: 'fun', - params: [{ p: 'angle', o: false }], - p5: true - }, - { - text: 'rotateY', - type: 'fun', - params: [{ p: 'angle', o: false }], - p5: true - }, - { - text: 'rotateZ', - type: 'fun', - params: [{ p: 'angle', o: false }], - p5: true - }, - { text: 'scale', type: 'fun', p5: true }, - { text: 'shearX', type: 'fun', params: [{ p: 'angle', o: false }], p5: true }, - { text: 'shearY', type: 'fun', params: [{ p: 'angle', o: false }], p5: true }, - { text: 'translate', type: 'fun', p5: true }, - { - text: 'storeItem', - type: 'fun', - params: [ - { p: 'key', o: false }, - { p: 'value', o: false } - ], - p5: true - }, - { text: 'getItem', type: 'fun', params: [{ p: 'key', o: false }], p5: true }, - { text: 'clearStorage', type: 'fun', p5: true }, - { - text: 'removeItem', - type: 'fun', - params: [{ p: 'key', o: false }], - p5: true - }, - { text: 'createStringDict', type: 'fun', p5: true }, - { text: 'createNumberDict', type: 'fun', p5: true }, - { - text: 'select', - type: 'fun', - params: [ - { p: 'selectors', o: false }, - { p: 'container', o: true } - ], - p5: true - }, - { - text: 'selectAll', - type: 'fun', - params: [ - { p: 'selectors', o: false }, - { p: 'container', o: true } - ], - p5: true - }, - { text: 'removeElements', type: 'fun', p5: true }, - { text: 'changed', type: 'fun', params: [{ p: 'fxn', o: false }], p5: true }, - { text: 'input', type: 'fun', params: [{ p: 'fxn', o: false }], p5: true }, - { - text: 'createDiv', - type: 'fun', - params: [{ p: 'html', o: true }], - p5: true - }, - { text: 'createP', type: 'fun', params: [{ p: 'html', o: true }], p5: true }, - { - text: 'createSpan', - type: 'fun', - params: [{ p: 'html', o: true }], - p5: true - }, - { text: 'createImg', type: 'fun', p5: true }, - { - text: 'createA', - type: 'fun', - params: [ - { p: 'href', o: false }, - { p: 'html', o: false }, - { p: 'target', o: true } - ], - p5: true - }, - { - text: 'createSlider', - type: 'fun', - params: [ - { p: 'min', o: false }, - { p: 'max', o: false }, - { p: 'value', o: true }, - { p: 'step', o: true } - ], - p5: true - }, - { - text: 'createButton', - type: 'fun', - params: [ - { p: 'label', o: false }, - { p: 'value', o: true } - ], - p5: true - }, - { - text: 'createCheckbox', - type: 'fun', - params: [ - { p: 'label', o: true }, - { p: 'value', o: true } - ], - p5: true - }, - { text: 'createSelect', type: 'fun', p5: true }, - { text: 'createRadio', type: 'fun', p5: true }, - { - text: 'createColorPicker', - type: 'fun', - params: [{ p: 'value', o: true }], - p5: true - }, - { text: 'createInput', type: 'fun', p5: true }, - { - text: 'createFileInput', - type: 'fun', - params: [ - { p: 'callback', o: false }, - { p: 'multiple', o: true } - ], - p5: true - }, - { - text: 'createVideo', - type: 'fun', - params: [ - { p: 'src', o: false }, - { p: 'callback', o: true } - ], - p5: true - }, - { - text: 'createAudio', - type: 'fun', - params: [ - { p: 'src', o: true }, - { p: 'callback', o: true } - ], - p5: true - }, - { - text: 'createCapture', - type: 'fun', - params: [ - { p: 'type', o: true }, - { p: 'flipped', o: true }, - { p: 'callback', o: true } - ], - p5: true - }, - { - text: 'createElement', - type: 'fun', - params: [ - { p: 'tag', o: false }, - { p: 'content', o: true } - ], - p5: true - }, - { text: 'deviceOrientation', type: 'var', params: [], p5: true }, - { text: 'accelerationX', type: 'var', params: [], p5: true }, - { text: 'accelerationY', type: 'var', params: [], p5: true }, - { text: 'accelerationZ', type: 'var', params: [], p5: true }, - { text: 'pAccelerationX', type: 'var', params: [], p5: true }, - { text: 'pAccelerationY', type: 'var', params: [], p5: true }, - { text: 'pAccelerationZ', type: 'var', params: [], p5: true }, - { text: 'rotationX', type: 'var', params: [], p5: true }, - { text: 'rotationY', type: 'var', params: [], p5: true }, - { text: 'rotationZ', type: 'var', params: [], p5: true }, - { text: 'pRotationX', type: 'var', params: [], p5: true }, - { text: 'pRotationY', type: 'var', params: [], p5: true }, - { text: 'pRotationZ', type: 'var', params: [], p5: true }, - { text: 'turnAxis', type: 'var', params: [], p5: true }, - { - text: 'setMoveThreshold', - type: 'fun', - params: [{ p: 'value', o: false }], - p5: true - }, - { - text: 'setShakeThreshold', - type: 'fun', - params: [{ p: 'value', o: false }], - p5: true - }, - { text: 'deviceMoved', type: 'fun', p5: true }, - { text: 'deviceTurned', type: 'fun', p5: true }, - { text: 'deviceShaken', type: 'fun', p5: true }, - { text: 'keyIsPressed', type: 'var', params: [], p5: true }, - { text: 'key', type: 'var', params: [], p5: true }, - { text: 'keyCode', type: 'var', params: [], p5: true }, - { - text: 'keyPressed', - type: 'fun', - params: [{ p: 'event', o: true }], - p5: true - }, - { - text: 'keyReleased', - type: 'fun', - params: [{ p: 'event', o: true }], - p5: true - }, - { - text: 'keyTyped', - type: 'fun', - params: [{ p: 'event', o: true }], - p5: true - }, - { - text: 'keyIsDown', - type: 'fun', - params: [{ p: 'code', o: false }], - p5: true - }, - { text: 'movedX', type: 'var', params: [], p5: true }, - { text: 'movedY', type: 'var', params: [], p5: true }, - { text: 'mouseX', type: 'var', params: [], p5: true }, - { text: 'mouseY', type: 'var', params: [], p5: true }, - { text: 'pmouseX', type: 'var', params: [], p5: true }, - { text: 'pmouseY', type: 'var', params: [], p5: true }, - { text: 'winMouseX', type: 'var', params: [], p5: true }, - { text: 'winMouseY', type: 'var', params: [], p5: true }, - { text: 'pwinMouseX', type: 'var', params: [], p5: true }, - { text: 'pwinMouseY', type: 'var', params: [], p5: true }, - { text: 'mouseButton', type: 'var', params: [], p5: true }, - { text: 'mouseIsPressed', type: 'var', params: [], p5: true }, - { - text: 'mouseMoved', - type: 'fun', - params: [{ p: 'event', o: true }], - p5: true - }, - { - text: 'mouseDragged', - type: 'fun', - params: [{ p: 'event', o: true }], - p5: true - }, - { - text: 'mousePressed', - type: 'fun', - params: [{ p: 'event', o: true }], - p5: true - }, - { - text: 'mouseReleased', - type: 'fun', - params: [{ p: 'event', o: true }], - p5: true - }, - { - text: 'mouseClicked', - type: 'fun', - params: [{ p: 'event', o: true }], - p5: true - }, - { - text: 'doubleClicked', - type: 'fun', - params: [{ p: 'event', o: true }], - p5: true - }, - { - text: 'mouseWheel', - type: 'fun', - params: [{ p: 'event', o: true }], - p5: true - }, - { text: 'requestPointerLock', type: 'fun', p5: true }, - { text: 'exitPointerLock', type: 'fun', p5: true }, - { text: 'touches', type: 'var', params: [], p5: true }, - { - text: 'touchStarted', - type: 'fun', - params: [{ p: 'event', o: true }], - p5: true - }, - { - text: 'touchMoved', - type: 'fun', - params: [{ p: 'event', o: true }], - p5: true - }, - { - text: 'touchEnded', - type: 'fun', - params: [{ p: 'event', o: true }], - p5: true - }, - { - text: 'createImage', - type: 'fun', - params: [ - { p: 'width', o: false }, - { p: 'height', o: false } - ], - p5: true - }, - { text: 'saveCanvas', type: 'fun', p5: true }, - { - text: 'saveFrames', - type: 'fun', - params: [ - { p: 'filename', o: false }, - { p: 'extension', o: false }, - { p: 'duration', o: false }, - { p: 'framerate', o: false }, - { p: 'callback', o: true } - ], - p5: true - }, - { - text: 'loadImage', - type: 'fun', - params: [ - { p: 'path', o: false }, - { p: 'successCallback', o: true }, - { p: 'failureCallback', o: true } - ], - p5: true - }, - { - text: 'saveGif', - type: 'fun', - params: [ - { p: 'filename', o: false }, - { p: 'duration', o: false }, - { p: 'options', o: true } - ], - p5: true - }, - { text: 'image', type: 'fun', p5: true }, - { text: 'tint', type: 'fun', p5: true }, - { text: 'noTint', type: 'fun', p5: true }, - { - text: 'imageMode', - type: 'fun', - params: [{ p: 'mode', o: false }], - p5: true - }, - { text: 'pixels', type: 'var', params: [], p5: true }, - { text: 'blend', type: 'fun', p5: true }, - { text: 'copy', type: 'fun', p5: true }, - { text: 'filter', type: 'fun', p5: true }, - { text: 'get', type: 'fun', p5: true }, - { text: 'loadPixels', type: 'fun', p5: true }, - { - text: 'set', - type: 'fun', - params: [ - { p: 'x', o: false }, - { p: 'y', o: false }, - { p: 'c', o: false } - ], - p5: true - }, - { - text: 'updatePixels', - type: 'fun', - params: [ - { p: 'x', o: true }, - { p: 'y', o: true }, - { p: 'w', o: true }, - { p: 'h', o: true } - ], - p5: true - }, - { - text: 'loadJSON', - type: 'fun', - params: [ - { p: 'path', o: false }, - { p: 'successCallback', o: true }, - { p: 'errorCallback', o: true } - ], - p5: true - }, - { - text: 'loadStrings', - type: 'fun', - params: [ - { p: 'path', o: false }, - { p: 'successCallback', o: true }, - { p: 'errorCallback', o: true } - ], - p5: true - }, - { - text: 'loadTable', - type: 'fun', - params: [ - { p: 'filename', o: false }, - { p: 'extension', o: true }, - { p: 'header', o: true }, - { p: 'callback', o: true }, - { p: 'errorCallback', o: true } - ], - p5: true - }, - { - text: 'loadXML', - type: 'fun', - params: [ - { p: 'path', o: false }, - { p: 'successCallback', o: true }, - { p: 'errorCallback', o: true } - ], - p5: true - }, - { - text: 'loadBytes', - type: 'fun', - params: [ - { p: 'file', o: false }, - { p: 'callback', o: true }, - { p: 'errorCallback', o: true } - ], - p5: true - }, - { text: 'httpGet', type: 'fun', p5: true }, - { text: 'httpPost', type: 'fun', p5: true }, - { text: 'httpDo', type: 'fun', p5: true }, - { - text: 'createWriter', - type: 'fun', - params: [ - { p: 'name', o: false }, - { p: 'extension', o: true } - ], - p5: true - }, - { - text: 'save', - type: 'fun', - params: [ - { p: 'objectOrFilename', o: true }, - { p: 'filename', o: true }, - { p: 'options', o: true } - ], - p5: true - }, - { - text: 'saveJSON', - type: 'fun', - params: [ - { p: 'json', o: false }, - { p: 'filename', o: false }, - { p: 'optimize', o: true } - ], - p5: true - }, - { - text: 'saveStrings', - type: 'fun', - params: [ - { p: 'list', o: false }, - { p: 'filename', o: false }, - { p: 'extension', o: true }, - { p: 'isCRLF', o: true } - ], - p5: true - }, - { - text: 'saveTable', - type: 'fun', - params: [ - { p: 'Table', o: false }, - { p: 'filename', o: false }, - { p: 'options', o: true } - ], - p5: true - }, - { text: 'abs', type: 'fun', params: [{ p: 'n', o: false }], p5: true }, - { text: 'ceil', type: 'fun', params: [{ p: 'n', o: false }], p5: true }, - { - text: 'constrain', - type: 'fun', - params: [ - { p: 'n', o: false }, - { p: 'low', o: false }, - { p: 'high', o: false } - ], - p5: true - }, - { text: 'dist', type: 'fun', p5: true }, - { text: 'exp', type: 'fun', params: [{ p: 'n', o: false }], p5: true }, - { text: 'floor', type: 'fun', params: [{ p: 'n', o: false }], p5: true }, - { - text: 'lerp', - type: 'fun', - params: [ - { p: 'start', o: false }, - { p: 'stop', o: false }, - { p: 'amt', o: false } - ], - p5: true - }, - { text: 'log', type: 'fun', params: [{ p: 'n', o: false }], p5: true }, - { - text: 'mag', - type: 'fun', - params: [ - { p: 'x', o: false }, - { p: 'y', o: false } - ], - p5: true - }, - { - text: 'map', - type: 'fun', - params: [ - { p: 'value', o: false }, - { p: 'start1', o: false }, - { p: 'stop1', o: false }, - { p: 'start2', o: false }, - { p: 'stop2', o: false }, - { p: 'withinBounds', o: true } - ], - p5: true - }, - { text: 'max', type: 'fun', p5: true }, - { text: 'min', type: 'fun', p5: true }, - { - text: 'norm', - type: 'fun', - params: [ - { p: 'value', o: false }, - { p: 'start', o: false }, - { p: 'stop', o: false } - ], - p5: true - }, - { - text: 'pow', - type: 'fun', - params: [ - { p: 'n', o: false }, - { p: 'e', o: false } - ], - p5: true - }, - { - text: 'round', - type: 'fun', - params: [ - { p: 'n', o: false }, - { p: 'decimals', o: true } - ], - p5: true - }, - { text: 'sq', type: 'fun', params: [{ p: 'n', o: false }], p5: true }, - { text: 'sqrt', type: 'fun', params: [{ p: 'n', o: false }], p5: true }, - { text: 'fract', type: 'fun', params: [{ p: 'n', o: false }], p5: true }, - { - text: 'createVector', - type: 'fun', - params: [ - { p: 'x', o: true }, - { p: 'y', o: true }, - { p: 'z', o: true } - ], - p5: true - }, - { - text: 'noise', - type: 'fun', - params: [ - { p: 'x', o: false }, - { p: 'y', o: true }, - { p: 'z', o: true } - ], - p5: true - }, - { - text: 'noiseDetail', - type: 'fun', - params: [ - { p: 'lod', o: false }, - { p: 'falloff', o: false } - ], - p5: true - }, - { - text: 'noiseSeed', - type: 'fun', - params: [{ p: 'seed', o: false }], - p5: true - }, - { - text: 'randomSeed', - type: 'fun', - params: [{ p: 'seed', o: false }], - p5: true - }, - { text: 'random', type: 'fun', p5: true }, - { - text: 'randomGaussian', - type: 'fun', - params: [ - { p: 'mean', o: true }, - { p: 'sd', o: true } - ], - p5: true - }, - { text: 'acos', type: 'fun', params: [{ p: 'value', o: false }], p5: true }, - { text: 'asin', type: 'fun', params: [{ p: 'value', o: false }], p5: true }, - { text: 'atan', type: 'fun', params: [{ p: 'value', o: false }], p5: true }, - { - text: 'atan2', - type: 'fun', - params: [ - { p: 'y', o: false }, - { p: 'x', o: false } - ], - p5: true - }, - { text: 'cos', type: 'fun', params: [{ p: 'angle', o: false }], p5: true }, - { text: 'sin', type: 'fun', params: [{ p: 'angle', o: false }], p5: true }, - { text: 'tan', type: 'fun', params: [{ p: 'angle', o: false }], p5: true }, - { - text: 'degrees', - type: 'fun', - params: [{ p: 'radians', o: false }], - p5: true - }, - { - text: 'radians', - type: 'fun', - params: [{ p: 'degrees', o: false }], - p5: true - }, - { text: 'angleMode', type: 'fun', p5: true }, - { text: 'textAlign', type: 'fun', p5: true }, - { text: 'textLeading', type: 'fun', p5: true }, - { text: 'textSize', type: 'fun', p5: true }, - { text: 'textStyle', type: 'fun', p5: true }, - { - text: 'textWidth', - type: 'fun', - params: [{ p: 'str', o: false }], - p5: true - }, - { text: 'textAscent', type: 'fun', p5: true }, - { text: 'textDescent', type: 'fun', p5: true }, - { - text: 'textWrap', - type: 'fun', - params: [{ p: 'style', o: false }], - p5: true - }, - { - text: 'loadFont', - type: 'fun', - params: [ - { p: 'path', o: false }, - { p: 'successCallback', o: true }, - { p: 'failureCallback', o: true } - ], - p5: true - }, - { - text: 'text', - type: 'fun', - params: [ - { p: 'str', o: false }, - { p: 'x', o: false }, - { p: 'y', o: false }, - { p: 'maxWidth', o: true }, - { p: 'maxHeight', o: true } - ], - p5: true - }, - { text: 'textFont', type: 'fun', p5: true }, - { - text: 'append', - type: 'fun', - params: [ - { p: 'array', o: false }, - { p: 'value', o: false } - ], - p5: true - }, - { text: 'arrayCopy', type: 'fun', p5: true }, - { - text: 'concat', - type: 'fun', - params: [ - { p: 'a', o: false }, - { p: 'b', o: false } - ], - p5: true - }, - { text: 'reverse', type: 'fun', params: [{ p: 'list', o: false }], p5: true }, - { text: 'shorten', type: 'fun', params: [{ p: 'list', o: false }], p5: true }, - { - text: 'shuffle', - type: 'fun', - params: [ - { p: 'array', o: false }, - { p: 'bool', o: true } - ], - p5: true - }, - { - text: 'sort', - type: 'fun', - params: [ - { p: 'list', o: false }, - { p: 'count', o: true } - ], - p5: true - }, - { - text: 'splice', - type: 'fun', - params: [ - { p: 'list', o: false }, - { p: 'value', o: false }, - { p: 'position', o: false } - ], - p5: true - }, - { - text: 'subset', - type: 'fun', - params: [ - { p: 'list', o: false }, - { p: 'start', o: false }, - { p: 'count', o: true } - ], - p5: true - }, - { text: 'float', type: 'fun', p5: true }, - { text: 'int', type: 'fun', p5: true }, - { text: 'str', type: 'fun', params: [{ p: 'n', o: false }], p5: true }, - { text: 'boolean', type: 'fun', p5: true }, - { text: 'byte', type: 'fun', p5: true }, - { text: 'char', type: 'fun', p5: true }, - { text: 'unchar', type: 'fun', p5: true }, - { text: 'hex', type: 'fun', p5: true }, - { text: 'unhex', type: 'fun', p5: true }, - { - text: 'join', - type: 'fun', - params: [ - { p: 'list', o: false }, - { p: 'separator', o: false } - ], - p5: true - }, - { - text: 'match', - type: 'fun', - params: [ - { p: 'str', o: false }, - { p: 'regexp', o: false } - ], - p5: true - }, - { - text: 'matchAll', - type: 'fun', - params: [ - { p: 'str', o: false }, - { p: 'regexp', o: false } - ], - p5: true - }, - { text: 'nf', type: 'fun', p5: true }, - { text: 'nfc', type: 'fun', p5: true }, - { text: 'nfp', type: 'fun', p5: true }, - { text: 'nfs', type: 'fun', p5: true }, - { - text: 'split', - type: 'fun', - params: [ - { p: 'value', o: false }, - { p: 'delim', o: false } - ], - p5: true - }, - { - text: 'splitTokens', - type: 'fun', - params: [ - { p: 'value', o: false }, - { p: 'delim', o: true } - ], - p5: true - }, - { text: 'trim', type: 'fun', p5: true }, - { text: 'day', type: 'fun', p5: true }, - { text: 'hour', type: 'fun', p5: true }, - { text: 'minute', type: 'fun', p5: true }, - { text: 'millis', type: 'fun', p5: true }, - { text: 'month', type: 'fun', p5: true }, - { text: 'second', type: 'fun', p5: true }, - { text: 'year', type: 'fun', p5: true }, - { text: 'beginGeometry', type: 'fun', p5: true }, - { text: 'endGeometry', type: 'fun', p5: true }, - { - text: 'buildGeometry', - type: 'fun', - params: [{ p: 'callback', o: false }], - p5: true - }, - { - text: 'freeGeometry', - type: 'fun', - params: [{ p: 'geometry', o: false }], - p5: true - }, - { - text: 'plane', - type: 'fun', - params: [ - { p: 'width', o: true }, - { p: 'height', o: true }, - { p: 'detailX', o: true }, - { p: 'detailY', o: true } - ], - p5: true - }, - { - text: 'box', - type: 'fun', - params: [ - { p: 'width', o: true }, - { p: 'height', o: true }, - { p: 'depth', o: true }, - { p: 'detailX', o: true }, - { p: 'detailY', o: true } - ], - p5: true - }, - { - text: 'sphere', - type: 'fun', - params: [ - { p: 'radius', o: true }, - { p: 'detailX', o: true }, - { p: 'detailY', o: true } - ], - p5: true - }, - { - text: 'cylinder', - type: 'fun', - params: [ - { p: 'radius', o: true }, - { p: 'height', o: true }, - { p: 'detailX', o: true }, - { p: 'detailY', o: true }, - { p: 'bottomCap', o: true }, - { p: 'topCap', o: true } - ], - p5: true - }, - { - text: 'cone', - type: 'fun', - params: [ - { p: 'radius', o: true }, - { p: 'height', o: true }, - { p: 'detailX', o: true }, - { p: 'detailY', o: true }, - { p: 'cap', o: true } - ], - p5: true - }, - { - text: 'ellipsoid', - type: 'fun', - params: [ - { p: 'radiusX', o: true }, - { p: 'radiusY', o: true }, - { p: 'radiusZ', o: true }, - { p: 'detailX', o: true }, - { p: 'detailY', o: true } - ], - p5: true - }, - { - text: 'torus', - type: 'fun', - params: [ - { p: 'radius', o: true }, - { p: 'tubeRadius', o: true }, - { p: 'detailX', o: true }, - { p: 'detailY', o: true } - ], - p5: true - }, - { - text: 'orbitControl', - type: 'fun', - params: [ - { p: 'sensitivityX', o: true }, - { p: 'sensitivityY', o: true }, - { p: 'sensitivityZ', o: true }, - { p: 'options', o: true } - ], - p5: true - }, - { text: 'debugMode', type: 'fun', p5: true }, - { text: 'noDebugMode', type: 'fun', p5: true }, - { text: 'ambientLight', type: 'fun', p5: true }, - { text: 'specularColor', type: 'fun', p5: true }, - { text: 'directionalLight', type: 'fun', p5: true }, - { text: 'pointLight', type: 'fun', p5: true }, - { - text: 'imageLight', - type: 'fun', - params: [{ p: 'img', o: false }], - p5: true - }, - { text: 'panorama', type: 'fun', params: [{ p: 'img', o: false }], p5: true }, - { text: 'lights', type: 'fun', p5: true }, - { - text: 'lightFalloff', - type: 'fun', - params: [ - { p: 'constant', o: false }, - { p: 'linear', o: false }, - { p: 'quadratic', o: false } - ], - p5: true - }, - { text: 'spotLight', type: 'fun', p5: true }, - { text: 'noLights', type: 'fun', p5: true }, - { text: 'loadModel', type: 'fun', p5: true }, - { text: 'model', type: 'fun', params: [{ p: 'model', o: false }], p5: true }, - { - text: 'loadShader', - type: 'fun', - params: [ - { p: 'vertFilename', o: false }, - { p: 'fragFilename', o: false }, - { p: 'successCallback', o: true }, - { p: 'failureCallback', o: true } - ], - p5: true - }, - { - text: 'createShader', - type: 'fun', - params: [ - { p: 'vertSrc', o: false }, - { p: 'fragSrc', o: false } - ], - p5: true - }, - { - text: 'createFilterShader', - type: 'fun', - params: [{ p: 'fragSrc', o: false }], - p5: true - }, - { text: 'shader', type: 'fun', params: [{ p: 's', o: false }], p5: true }, - { text: 'resetShader', type: 'fun', p5: true }, - { text: 'texture', type: 'fun', params: [{ p: 'tex', o: false }], p5: true }, - { - text: 'textureMode', - type: 'fun', - params: [{ p: 'mode', o: false }], - p5: true - }, - { - text: 'textureWrap', - type: 'fun', - params: [ - { p: 'wrapX', o: false }, - { p: 'wrapY', o: true } - ], - p5: true - }, - { text: 'normalMaterial', type: 'fun', p5: true }, - { text: 'ambientMaterial', type: 'fun', p5: true }, - { text: 'emissiveMaterial', type: 'fun', p5: true }, - { text: 'specularMaterial', type: 'fun', p5: true }, - { - text: 'shininess', - type: 'fun', - params: [{ p: 'shine', o: false }], - p5: true - }, - { - text: 'metalness', - type: 'fun', - params: [{ p: 'metallic', o: false }], - p5: true - }, - { - text: 'camera', - type: 'fun', - params: [ - { p: 'x', o: true }, - { p: 'y', o: true }, - { p: 'z', o: true }, - { p: 'centerX', o: true }, - { p: 'centerY', o: true }, - { p: 'centerZ', o: true }, - { p: 'upX', o: true }, - { p: 'upY', o: true }, - { p: 'upZ', o: true } - ], - p5: true - }, - { - text: 'perspective', - type: 'fun', - params: [ - { p: 'fovy', o: true }, - { p: 'aspect', o: true }, - { p: 'near', o: true }, - { p: 'far', o: true } - ], - p5: true - }, - { text: 'linePerspective', type: 'fun', p5: true }, - { - text: 'ortho', - type: 'fun', - params: [ - { p: 'left', o: true }, - { p: 'right', o: true }, - { p: 'bottom', o: true }, - { p: 'top', o: true }, - { p: 'near', o: true }, - { p: 'far', o: true } - ], - p5: true - }, - { - text: 'frustum', - type: 'fun', - params: [ - { p: 'left', o: true }, - { p: 'right', o: true }, - { p: 'bottom', o: true }, - { p: 'top', o: true }, - { p: 'near', o: true }, - { p: 'far', o: true } - ], - p5: true - }, - { text: 'createCamera', type: 'fun', p5: true }, - { - text: 'setCamera', - type: 'fun', - params: [{ p: 'cam', o: false }], - p5: true - }, - { text: 'setAttributes', type: 'fun', p5: true }, - { text: 'getAudioContext', type: 'fun', p5: true }, - { - text: 'userStartAudio', - type: 'fun', - params: [ - { p: 'elements', o: true }, - { p: 'callback', o: true } - ], - p5: true - }, - { text: 'getOutputVolume', type: 'fun', p5: true }, - { - text: 'outputVolume', - type: 'fun', - params: [ - { p: 'volume', o: false }, - { p: 'rampTime', o: true }, - { p: 'timeFromNow', o: true } - ], - p5: true - }, - { text: 'soundOut', type: 'var', params: [], p5: true }, - { text: 'sampleRate', type: 'fun', p5: true }, - { - text: 'freqToMidi', - type: 'fun', - params: [{ p: 'frequency', o: false }], - p5: true - }, - { - text: 'midiToFreq', - type: 'fun', - params: [{ p: 'midiNote', o: false }], - p5: true - }, - { - text: 'soundFormats', - type: 'fun', - params: [{ p: 'formats', o: true }], - p5: true - }, - { - text: 'saveSound', - type: 'fun', - params: [ - { p: 'soundFile', o: false }, - { p: 'fileName', o: false } - ], - p5: true - }, - { - text: 'loadSound', - type: 'fun', - params: [ - { p: 'path', o: false }, - { p: 'successCallback', o: true }, - { p: 'errorCallback', o: true }, - { p: 'whileLoading', o: true } - ], - p5: true - }, - { - text: 'createConvolver', - type: 'fun', - params: [ - { p: 'path', o: false }, - { p: 'callback', o: true }, - { p: 'errorCallback', o: true } - ], - p5: true - }, - { - text: 'setBPM', - type: 'fun', - params: [ - { p: 'BPM', o: false }, - { p: 'rampTime', o: false } - ], - p5: true - }, - { text: 'true', type: 'boolean', p5: 'boolean' }, - { text: 'false', type: 'boolean', p5: 'boolean' }, - { text: 'await', type: 'keyword', p5: false }, - { text: 'break', type: 'keyword', p5: false }, - { text: 'case', type: 'keyword', p5: false }, - { text: 'catch', type: 'keyword', p5: false }, - { text: 'class', type: 'keyword', p5: 'class' }, - { text: 'const', type: 'keyword', p5: 'const' }, - { text: 'continue', type: 'keyword', p5: false }, - { text: 'debugger', type: 'keyword', p5: false }, - { text: 'default', type: 'keyword', p5: false }, - { text: 'delete', type: 'keyword', p5: false }, - { text: 'do', type: 'keyword', p5: false }, - { text: 'else', type: 'keyword', p5: 'if-else' }, - { text: 'export', type: 'keyword', p5: false }, - { text: 'extends', type: 'keyword', p5: false }, - { text: 'finally', type: 'keyword', p5: false }, - { text: 'for', type: 'keyword', p5: 'for' }, - { text: 'function', type: 'keyword', p5: 'function' }, - { text: 'if', type: 'keyword', p5: 'if-else' }, - { text: 'import', type: 'keyword', p5: false }, - { text: 'in', type: 'keyword', p5: false }, - { text: 'instanceof', type: 'keyword', p5: false }, - { text: 'new', type: 'keyword', p5: false }, - { text: 'return', type: 'keyword', p5: 'return' }, - { text: 'super', type: 'keyword', p5: false }, - { text: 'switch', type: 'keyword', p5: false }, - { text: 'this', type: 'keyword', p5: false }, - { text: 'throw', type: 'keyword', p5: false }, - { text: 'try', type: 'keyword', p5: false }, - { text: 'typeof', type: 'keyword', p5: false }, - { text: 'var', type: 'keyword', p5: false }, - { text: 'void', type: 'keyword', p5: false }, - { text: 'while', type: 'keyword', p5: 'while' }, - { text: 'with', type: 'keyword', p5: false }, - { text: 'yield', type: 'keyword', p5: false }, - { text: 'let', type: 'keyword', p5: 'let' }, - { text: 'Array', type: 'obj', p5: false }, - { text: 'Boolean', type: 'obj', p5: false }, - { text: 'Date', type: 'obj', p5: false }, - { text: 'Error', type: 'obj', p5: false }, - { text: 'Function', type: 'obj', p5: false }, - { text: 'JSON', type: 'obj', p5: 'JSON' }, - { text: 'Math', type: 'obj', p5: false }, - { text: 'Number', type: 'obj', p5: false }, - { text: 'Object', type: 'obj', p5: false }, - { text: 'RegExp', type: 'obj', p5: false }, - { text: 'String', type: 'obj', p5: false }, - { text: 'Promise', type: 'obj', p5: false }, - { text: 'Set', type: 'obj', p5: false }, - { text: 'Map', type: 'obj', p5: false }, - { text: 'Symbol', type: 'obj', p5: false }, - { text: 'WeakMap', type: 'obj', p5: false }, - { text: 'WeakSet', type: 'obj', p5: false }, - { text: 'ArrayBuffer', type: 'obj', p5: false }, - { text: 'DataView', type: 'obj', p5: false }, - { text: 'Int32Array', type: 'obj', p5: false }, - { text: 'Uint32Array', type: 'obj', p5: false }, - { text: 'Float32Array', type: 'obj', p5: false }, - { text: 'window', type: 'obj', p5: false }, - { text: 'document', type: 'obj', p5: false }, - { text: 'navigator', type: 'obj', p5: false }, - { text: 'console', type: 'obj', p5: 'console' }, - { text: 'localStorage', type: 'obj', p5: false }, - { text: 'sessionStorage', type: 'obj', p5: false }, - { text: 'history', type: 'obj', p5: false }, - { text: 'location', type: 'obj', p5: false } -]; +exports.p5Hinter = [{"label":"describe","type":"method","kindLabel":"fun","params":[{"p":"text","o":false},{"p":"display","o":true}],"preview":"describe(text, [display])","p5DocPath":"describe"},{"label":"describeElement","type":"method","kindLabel":"fun","params":[{"p":"name","o":false},{"p":"text","o":false},{"p":"display","o":true}],"preview":"describeElement(name, text, [display])","p5DocPath":"describeElement"},{"label":"textOutput","type":"method","kindLabel":"fun","params":[{"p":"display","o":true}],"preview":"textOutput([display])","p5DocPath":"textOutput"},{"label":"gridOutput","type":"method","kindLabel":"fun","params":[{"p":"display","o":true}],"preview":"gridOutput([display])","p5DocPath":"gridOutput"},{"label":"alpha","type":"method","kindLabel":"fun","params":[{"p":"color","o":false}],"preview":"alpha(color)","p5DocPath":"alpha"},{"label":"blue","type":"method","kindLabel":"fun","params":[{"p":"color","o":false}],"preview":"blue(color)","p5DocPath":"blue"},{"label":"brightness","type":"method","kindLabel":"fun","params":[{"p":"color","o":false}],"preview":"brightness(color)","p5DocPath":"brightness"},{"label":"color","type":"method","kindLabel":"fun","preview":"color()","p5DocPath":"color"},{"label":"green","type":"method","kindLabel":"fun","params":[{"p":"color","o":false}],"preview":"green(color)","p5DocPath":"green"},{"label":"hue","type":"method","kindLabel":"fun","params":[{"p":"color","o":false}],"preview":"hue(color)","p5DocPath":"hue"},{"label":"lerpColor","type":"method","kindLabel":"fun","params":[{"p":"c1","o":false},{"p":"c2","o":false},{"p":"amt","o":false}],"preview":"lerpColor(c1, c2, amt)","p5DocPath":"lerpColor"},{"label":"paletteLerp","type":"method","kindLabel":"fun","params":[{"p":"colors_stops","o":false},{"p":"amt","o":false}],"preview":"paletteLerp(colors_stops, amt)","p5DocPath":"paletteLerp"},{"label":"lightness","type":"method","kindLabel":"fun","params":[{"p":"color","o":false}],"preview":"lightness(color)","p5DocPath":"lightness"},{"label":"red","type":"method","kindLabel":"fun","params":[{"p":"color","o":false}],"preview":"red(color)","p5DocPath":"red"},{"label":"saturation","type":"method","kindLabel":"fun","params":[{"p":"color","o":false}],"preview":"saturation(color)","p5DocPath":"saturation"},{"label":"beginClip","type":"method","kindLabel":"fun","params":[{"p":"options","o":true}],"preview":"beginClip([options])","p5DocPath":"beginClip"},{"label":"endClip","type":"method","kindLabel":"fun","preview":"endClip()","p5DocPath":"endClip"},{"label":"clip","type":"method","kindLabel":"fun","params":[{"p":"callback","o":false},{"p":"options","o":true}],"preview":"clip(callback, [options])","p5DocPath":"clip"},{"label":"background","type":"method","kindLabel":"fun","preview":"background()","p5DocPath":"background"},{"label":"clear","type":"method","kindLabel":"fun","params":[{"p":"r","o":true},{"p":"g","o":true},{"p":"b","o":true},{"p":"a","o":true}],"preview":"clear([r], [g], [b], [a])","p5DocPath":"clear"},{"label":"colorMode","type":"method","kindLabel":"fun","preview":"colorMode()","p5DocPath":"colorMode"},{"label":"fill","type":"method","kindLabel":"fun","preview":"fill()","p5DocPath":"fill"},{"label":"noFill","type":"method","kindLabel":"fun","preview":"noFill()","p5DocPath":"noFill"},{"label":"noStroke","type":"method","kindLabel":"fun","preview":"noStroke()","p5DocPath":"noStroke"},{"label":"stroke","type":"method","kindLabel":"fun","preview":"stroke()","p5DocPath":"stroke"},{"label":"erase","type":"method","kindLabel":"fun","params":[{"p":"strengthFill","o":true},{"p":"strengthStroke","o":true}],"preview":"erase([strengthFill], [strengthStroke])","p5DocPath":"erase"},{"label":"noErase","type":"method","kindLabel":"fun","preview":"noErase()","p5DocPath":"noErase"},{"label":"arc","type":"method","kindLabel":"fun","params":[{"p":"x","o":false},{"p":"y","o":false},{"p":"w","o":false},{"p":"h","o":false},{"p":"start","o":false},{"p":"stop","o":false},{"p":"mode","o":true},{"p":"detail","o":true}],"preview":"arc(x, y, w, h, start, stop, [mode], [detail])","p5DocPath":"arc"},{"label":"ellipse","type":"method","kindLabel":"fun","preview":"ellipse()","p5DocPath":"ellipse"},{"label":"circle","type":"method","kindLabel":"fun","params":[{"p":"x","o":false},{"p":"y","o":false},{"p":"d","o":false}],"preview":"circle(x, y, d)","p5DocPath":"circle"},{"label":"line","type":"method","kindLabel":"fun","preview":"line()","p5DocPath":"line"},{"label":"point","type":"method","kindLabel":"fun","preview":"point()","p5DocPath":"point"},{"label":"quad","type":"method","kindLabel":"fun","preview":"quad()","p5DocPath":"quad"},{"label":"rect","type":"method","kindLabel":"fun","preview":"rect()","p5DocPath":"rect"},{"label":"square","type":"method","kindLabel":"fun","params":[{"p":"x","o":false},{"p":"y","o":false},{"p":"s","o":false},{"p":"tl","o":true},{"p":"tr","o":true},{"p":"br","o":true},{"p":"bl","o":true}],"preview":"square(x, y, s, [tl], [tr], [br], [bl])","p5DocPath":"square"},{"label":"triangle","type":"method","kindLabel":"fun","params":[{"p":"x1","o":false},{"p":"y1","o":false},{"p":"x2","o":false},{"p":"y2","o":false},{"p":"x3","o":false},{"p":"y3","o":false}],"preview":"triangle(x1, y1, x2, y2, x3, y3)","p5DocPath":"triangle"},{"label":"ellipseMode","type":"method","kindLabel":"fun","params":[{"p":"mode","o":false}],"preview":"ellipseMode(mode)","p5DocPath":"ellipseMode"},{"label":"noSmooth","type":"method","kindLabel":"fun","preview":"noSmooth()","p5DocPath":"noSmooth"},{"label":"rectMode","type":"method","kindLabel":"fun","params":[{"p":"mode","o":false}],"preview":"rectMode(mode)","p5DocPath":"rectMode"},{"label":"smooth","type":"method","kindLabel":"fun","preview":"smooth()","p5DocPath":"smooth"},{"label":"strokeCap","type":"method","kindLabel":"fun","params":[{"p":"cap","o":false}],"preview":"strokeCap(cap)","p5DocPath":"strokeCap"},{"label":"strokeJoin","type":"method","kindLabel":"fun","params":[{"p":"join","o":false}],"preview":"strokeJoin(join)","p5DocPath":"strokeJoin"},{"label":"strokeWeight","type":"method","kindLabel":"fun","params":[{"p":"weight","o":false}],"preview":"strokeWeight(weight)","p5DocPath":"strokeWeight"},{"label":"bezier","type":"method","kindLabel":"fun","preview":"bezier()","p5DocPath":"bezier"},{"label":"bezierDetail","type":"method","kindLabel":"fun","params":[{"p":"detail","o":false}],"preview":"bezierDetail(detail)","p5DocPath":"bezierDetail"},{"label":"bezierPoint","type":"method","kindLabel":"fun","params":[{"p":"a","o":false},{"p":"b","o":false},{"p":"c","o":false},{"p":"d","o":false},{"p":"t","o":false}],"preview":"bezierPoint(a, b, c, d, t)","p5DocPath":"bezierPoint"},{"label":"bezierTangent","type":"method","kindLabel":"fun","params":[{"p":"a","o":false},{"p":"b","o":false},{"p":"c","o":false},{"p":"d","o":false},{"p":"t","o":false}],"preview":"bezierTangent(a, b, c, d, t)","p5DocPath":"bezierTangent"},{"label":"curve","type":"method","kindLabel":"fun","preview":"curve()","p5DocPath":"curve"},{"label":"curveDetail","type":"method","kindLabel":"fun","params":[{"p":"resolution","o":false}],"preview":"curveDetail(resolution)","p5DocPath":"curveDetail"},{"label":"curveTightness","type":"method","kindLabel":"fun","params":[{"p":"amount","o":false}],"preview":"curveTightness(amount)","p5DocPath":"curveTightness"},{"label":"curvePoint","type":"method","kindLabel":"fun","params":[{"p":"a","o":false},{"p":"b","o":false},{"p":"c","o":false},{"p":"d","o":false},{"p":"t","o":false}],"preview":"curvePoint(a, b, c, d, t)","p5DocPath":"curvePoint"},{"label":"curveTangent","type":"method","kindLabel":"fun","params":[{"p":"a","o":false},{"p":"b","o":false},{"p":"c","o":false},{"p":"d","o":false},{"p":"t","o":false}],"preview":"curveTangent(a, b, c, d, t)","p5DocPath":"curveTangent"},{"label":"beginContour","type":"method","kindLabel":"fun","preview":"beginContour()","p5DocPath":"beginContour"},{"label":"beginShape","type":"method","kindLabel":"fun","params":[{"p":"kind","o":true}],"preview":"beginShape([kind])","p5DocPath":"beginShape"},{"label":"bezierVertex","type":"method","kindLabel":"fun","preview":"bezierVertex()","p5DocPath":"bezierVertex"},{"label":"curveVertex","type":"method","kindLabel":"fun","preview":"curveVertex()","p5DocPath":"curveVertex"},{"label":"endContour","type":"method","kindLabel":"fun","preview":"endContour()","p5DocPath":"endContour"},{"label":"endShape","type":"method","kindLabel":"fun","params":[{"p":"mode","o":true},{"p":"count","o":true}],"preview":"endShape([mode], [count])","p5DocPath":"endShape"},{"label":"quadraticVertex","type":"method","kindLabel":"fun","preview":"quadraticVertex()","p5DocPath":"quadraticVertex"},{"label":"vertex","type":"method","kindLabel":"fun","preview":"vertex()","p5DocPath":"vertex"},{"label":"normal","type":"method","kindLabel":"fun","preview":"normal()","p5DocPath":"normal"},{"label":"VERSION","type":"constant","kindLabel":"const","params":[],"preview":"VERSION","p5DocPath":"VERSION"},{"label":"P2D","type":"constant","kindLabel":"const","params":[],"preview":"P2D","p5DocPath":"P2D"},{"label":"WEBGL","type":"constant","kindLabel":"const","params":[],"preview":"WEBGL","p5DocPath":"WEBGL"},{"label":"WEBGL2","type":"constant","kindLabel":"const","params":[],"preview":"WEBGL2","p5DocPath":"WEBGL2"},{"label":"ARROW","type":"constant","kindLabel":"const","params":[],"preview":"ARROW","p5DocPath":"ARROW"},{"label":"CROSS","type":"constant","kindLabel":"const","params":[],"preview":"CROSS","p5DocPath":"CROSS"},{"label":"HAND","type":"constant","kindLabel":"const","params":[],"preview":"HAND","p5DocPath":"HAND"},{"label":"MOVE","type":"constant","kindLabel":"const","params":[],"preview":"MOVE","p5DocPath":"MOVE"},{"label":"TEXT","type":"constant","kindLabel":"const","params":[],"preview":"TEXT","p5DocPath":"TEXT"},{"label":"WAIT","type":"constant","kindLabel":"const","params":[],"preview":"WAIT","p5DocPath":"WAIT"},{"label":"HALF_PI","type":"constant","kindLabel":"const","params":[],"preview":"HALF_PI","p5DocPath":"HALF_PI"},{"label":"PI","type":"constant","kindLabel":"const","params":[],"preview":"PI","p5DocPath":"PI"},{"label":"QUARTER_PI","type":"constant","kindLabel":"const","params":[],"preview":"QUARTER_PI","p5DocPath":"QUARTER_PI"},{"label":"TAU","type":"constant","kindLabel":"const","params":[],"preview":"TAU","p5DocPath":"TAU"},{"label":"TWO_PI","type":"constant","kindLabel":"const","params":[],"preview":"TWO_PI","p5DocPath":"TWO_PI"},{"label":"DEGREES","type":"constant","kindLabel":"const","params":[],"preview":"DEGREES","p5DocPath":"DEGREES"},{"label":"RADIANS","type":"constant","kindLabel":"const","params":[],"preview":"RADIANS","p5DocPath":"RADIANS"},{"label":"CORNER","type":"constant","kindLabel":"const","params":[],"preview":"CORNER","p5DocPath":"CORNER"},{"label":"CORNERS","type":"constant","kindLabel":"const","params":[],"preview":"CORNERS","p5DocPath":"CORNERS"},{"label":"RADIUS","type":"constant","kindLabel":"const","params":[],"preview":"RADIUS","p5DocPath":"RADIUS"},{"label":"RIGHT","type":"constant","kindLabel":"const","params":[],"preview":"RIGHT","p5DocPath":"RIGHT"},{"label":"LEFT","type":"constant","kindLabel":"const","params":[],"preview":"LEFT","p5DocPath":"LEFT"},{"label":"CENTER","type":"constant","kindLabel":"const","params":[],"preview":"CENTER","p5DocPath":"CENTER"},{"label":"TOP","type":"constant","kindLabel":"const","params":[],"preview":"TOP","p5DocPath":"TOP"},{"label":"BOTTOM","type":"constant","kindLabel":"const","params":[],"preview":"BOTTOM","p5DocPath":"BOTTOM"},{"label":"BASELINE","type":"constant","kindLabel":"const","params":[],"preview":"BASELINE","p5DocPath":"BASELINE"},{"label":"POINTS","type":"constant","kindLabel":"const","params":[],"preview":"POINTS","p5DocPath":"POINTS"},{"label":"LINES","type":"constant","kindLabel":"const","params":[],"preview":"LINES","p5DocPath":"LINES"},{"label":"LINE_STRIP","type":"constant","kindLabel":"const","params":[],"preview":"LINE_STRIP","p5DocPath":"LINE_STRIP"},{"label":"LINE_LOOP","type":"constant","kindLabel":"const","params":[],"preview":"LINE_LOOP","p5DocPath":"LINE_LOOP"},{"label":"TRIANGLES","type":"constant","kindLabel":"const","params":[],"preview":"TRIANGLES","p5DocPath":"TRIANGLES"},{"label":"TRIANGLE_FAN","type":"constant","kindLabel":"const","params":[],"preview":"TRIANGLE_FAN","p5DocPath":"TRIANGLE_FAN"},{"label":"TRIANGLE_STRIP","type":"constant","kindLabel":"const","params":[],"preview":"TRIANGLE_STRIP","p5DocPath":"TRIANGLE_STRIP"},{"label":"QUADS","type":"constant","kindLabel":"const","params":[],"preview":"QUADS","p5DocPath":"QUADS"},{"label":"QUAD_STRIP","type":"constant","kindLabel":"const","params":[],"preview":"QUAD_STRIP","p5DocPath":"QUAD_STRIP"},{"label":"TESS","type":"constant","kindLabel":"const","params":[],"preview":"TESS","p5DocPath":"TESS"},{"label":"CLOSE","type":"constant","kindLabel":"const","params":[],"preview":"CLOSE","p5DocPath":"CLOSE"},{"label":"OPEN","type":"constant","kindLabel":"const","params":[],"preview":"OPEN","p5DocPath":"OPEN"},{"label":"CHORD","type":"constant","kindLabel":"const","params":[],"preview":"CHORD","p5DocPath":"CHORD"},{"label":"PIE","type":"constant","kindLabel":"const","params":[],"preview":"PIE","p5DocPath":"PIE"},{"label":"PROJECT","type":"constant","kindLabel":"const","params":[],"preview":"PROJECT","p5DocPath":"PROJECT"},{"label":"SQUARE","type":"constant","kindLabel":"const","params":[],"preview":"SQUARE","p5DocPath":"SQUARE"},{"label":"ROUND","type":"constant","kindLabel":"const","params":[],"preview":"ROUND","p5DocPath":"ROUND"},{"label":"BEVEL","type":"constant","kindLabel":"const","params":[],"preview":"BEVEL","p5DocPath":"BEVEL"},{"label":"MITER","type":"constant","kindLabel":"const","params":[],"preview":"MITER","p5DocPath":"MITER"},{"label":"RGB","type":"constant","kindLabel":"const","params":[],"preview":"RGB","p5DocPath":"RGB"},{"label":"HSB","type":"constant","kindLabel":"const","params":[],"preview":"HSB","p5DocPath":"HSB"},{"label":"HSL","type":"constant","kindLabel":"const","params":[],"preview":"HSL","p5DocPath":"HSL"},{"label":"AUTO","type":"constant","kindLabel":"const","params":[],"preview":"AUTO","p5DocPath":"AUTO"},{"label":"ALT","type":"constant","kindLabel":"const","params":[],"preview":"ALT","p5DocPath":"ALT"},{"label":"BACKSPACE","type":"constant","kindLabel":"const","params":[],"preview":"BACKSPACE","p5DocPath":"BACKSPACE"},{"label":"CONTROL","type":"constant","kindLabel":"const","params":[],"preview":"CONTROL","p5DocPath":"CONTROL"},{"label":"DELETE","type":"constant","kindLabel":"const","params":[],"preview":"DELETE","p5DocPath":"DELETE"},{"label":"DOWN_ARROW","type":"constant","kindLabel":"const","params":[],"preview":"DOWN_ARROW","p5DocPath":"DOWN_ARROW"},{"label":"ENTER","type":"constant","kindLabel":"const","params":[],"preview":"ENTER","p5DocPath":"ENTER"},{"label":"ESCAPE","type":"constant","kindLabel":"const","params":[],"preview":"ESCAPE","p5DocPath":"ESCAPE"},{"label":"LEFT_ARROW","type":"constant","kindLabel":"const","params":[],"preview":"LEFT_ARROW","p5DocPath":"LEFT_ARROW"},{"label":"OPTION","type":"constant","kindLabel":"const","params":[],"preview":"OPTION","p5DocPath":"OPTION"},{"label":"RETURN","type":"constant","kindLabel":"const","params":[],"preview":"RETURN","p5DocPath":"RETURN"},{"label":"RIGHT_ARROW","type":"constant","kindLabel":"const","params":[],"preview":"RIGHT_ARROW","p5DocPath":"RIGHT_ARROW"},{"label":"SHIFT","type":"constant","kindLabel":"const","params":[],"preview":"SHIFT","p5DocPath":"SHIFT"},{"label":"TAB","type":"constant","kindLabel":"const","params":[],"preview":"TAB","p5DocPath":"TAB"},{"label":"UP_ARROW","type":"constant","kindLabel":"const","params":[],"preview":"UP_ARROW","p5DocPath":"UP_ARROW"},{"label":"BLEND","type":"constant","kindLabel":"const","params":[],"preview":"BLEND","p5DocPath":"BLEND"},{"label":"REMOVE","type":"constant","kindLabel":"const","params":[],"preview":"REMOVE","p5DocPath":"REMOVE"},{"label":"ADD","type":"constant","kindLabel":"const","params":[],"preview":"ADD","p5DocPath":"ADD"},{"label":"DARKEST","type":"constant","kindLabel":"const","params":[],"preview":"DARKEST","p5DocPath":"DARKEST"},{"label":"LIGHTEST","type":"constant","kindLabel":"const","params":[],"preview":"LIGHTEST","p5DocPath":"LIGHTEST"},{"label":"DIFFERENCE","type":"constant","kindLabel":"const","params":[],"preview":"DIFFERENCE","p5DocPath":"DIFFERENCE"},{"label":"SUBTRACT","type":"constant","kindLabel":"const","params":[],"preview":"SUBTRACT","p5DocPath":"SUBTRACT"},{"label":"EXCLUSION","type":"constant","kindLabel":"const","params":[],"preview":"EXCLUSION","p5DocPath":"EXCLUSION"},{"label":"MULTIPLY","type":"constant","kindLabel":"const","params":[],"preview":"MULTIPLY","p5DocPath":"MULTIPLY"},{"label":"SCREEN","type":"constant","kindLabel":"const","params":[],"preview":"SCREEN","p5DocPath":"SCREEN"},{"label":"REPLACE","type":"constant","kindLabel":"const","params":[],"preview":"REPLACE","p5DocPath":"REPLACE"},{"label":"OVERLAY","type":"constant","kindLabel":"const","params":[],"preview":"OVERLAY","p5DocPath":"OVERLAY"},{"label":"HARD_LIGHT","type":"constant","kindLabel":"const","params":[],"preview":"HARD_LIGHT","p5DocPath":"HARD_LIGHT"},{"label":"SOFT_LIGHT","type":"constant","kindLabel":"const","params":[],"preview":"SOFT_LIGHT","p5DocPath":"SOFT_LIGHT"},{"label":"DODGE","type":"constant","kindLabel":"const","params":[],"preview":"DODGE","p5DocPath":"DODGE"},{"label":"BURN","type":"constant","kindLabel":"const","params":[],"preview":"BURN","p5DocPath":"BURN"},{"label":"THRESHOLD","type":"constant","kindLabel":"const","params":[],"preview":"THRESHOLD","p5DocPath":"THRESHOLD"},{"label":"GRAY","type":"constant","kindLabel":"const","params":[],"preview":"GRAY","p5DocPath":"GRAY"},{"label":"OPAQUE","type":"constant","kindLabel":"const","params":[],"preview":"OPAQUE","p5DocPath":"OPAQUE"},{"label":"INVERT","type":"constant","kindLabel":"const","params":[],"preview":"INVERT","p5DocPath":"INVERT"},{"label":"POSTERIZE","type":"constant","kindLabel":"const","params":[],"preview":"POSTERIZE","p5DocPath":"POSTERIZE"},{"label":"DILATE","type":"constant","kindLabel":"const","params":[],"preview":"DILATE","p5DocPath":"DILATE"},{"label":"ERODE","type":"constant","kindLabel":"const","params":[],"preview":"ERODE","p5DocPath":"ERODE"},{"label":"BLUR","type":"constant","kindLabel":"const","params":[],"preview":"BLUR","p5DocPath":"BLUR"},{"label":"NORMAL","type":"constant","kindLabel":"const","params":[],"preview":"NORMAL","p5DocPath":"NORMAL"},{"label":"ITALIC","type":"constant","kindLabel":"const","params":[],"preview":"ITALIC","p5DocPath":"ITALIC"},{"label":"BOLD","type":"constant","kindLabel":"const","params":[],"preview":"BOLD","p5DocPath":"BOLD"},{"label":"BOLDITALIC","type":"constant","kindLabel":"const","params":[],"preview":"BOLDITALIC","p5DocPath":"BOLDITALIC"},{"label":"CHAR","type":"constant","kindLabel":"const","params":[],"preview":"CHAR","p5DocPath":"CHAR"},{"label":"WORD","type":"constant","kindLabel":"const","params":[],"preview":"WORD","p5DocPath":"WORD"},{"label":"LINEAR","type":"constant","kindLabel":"const","params":[],"preview":"LINEAR","p5DocPath":"LINEAR"},{"label":"QUADRATIC","type":"constant","kindLabel":"const","params":[],"preview":"QUADRATIC","p5DocPath":"QUADRATIC"},{"label":"BEZIER","type":"constant","kindLabel":"const","params":[],"preview":"BEZIER","p5DocPath":"BEZIER"},{"label":"CURVE","type":"constant","kindLabel":"const","params":[],"preview":"CURVE","p5DocPath":"CURVE"},{"label":"STROKE","type":"constant","kindLabel":"const","params":[],"preview":"STROKE","p5DocPath":"STROKE"},{"label":"FILL","type":"constant","kindLabel":"const","params":[],"preview":"FILL","p5DocPath":"FILL"},{"label":"TEXTURE","type":"constant","kindLabel":"const","params":[],"preview":"TEXTURE","p5DocPath":"TEXTURE"},{"label":"IMMEDIATE","type":"constant","kindLabel":"const","params":[],"preview":"IMMEDIATE","p5DocPath":"IMMEDIATE"},{"label":"IMAGE","type":"constant","kindLabel":"const","params":[],"preview":"IMAGE","p5DocPath":"IMAGE"},{"label":"NEAREST","type":"constant","kindLabel":"const","params":[],"preview":"NEAREST","p5DocPath":"NEAREST"},{"label":"REPEAT","type":"constant","kindLabel":"const","params":[],"preview":"REPEAT","p5DocPath":"REPEAT"},{"label":"CLAMP","type":"constant","kindLabel":"const","params":[],"preview":"CLAMP","p5DocPath":"CLAMP"},{"label":"MIRROR","type":"constant","kindLabel":"const","params":[],"preview":"MIRROR","p5DocPath":"MIRROR"},{"label":"FLAT","type":"constant","kindLabel":"const","params":[],"preview":"FLAT","p5DocPath":"FLAT"},{"label":"SMOOTH","type":"constant","kindLabel":"const","params":[],"preview":"SMOOTH","p5DocPath":"SMOOTH"},{"label":"LANDSCAPE","type":"constant","kindLabel":"const","params":[],"preview":"LANDSCAPE","p5DocPath":"LANDSCAPE"},{"label":"PORTRAIT","type":"constant","kindLabel":"const","params":[],"preview":"PORTRAIT","p5DocPath":"PORTRAIT"},{"label":"GRID","type":"constant","kindLabel":"const","params":[],"preview":"GRID","p5DocPath":"GRID"},{"label":"AXES","type":"constant","kindLabel":"const","params":[],"preview":"AXES","p5DocPath":"AXES"},{"label":"LABEL","type":"constant","kindLabel":"const","params":[],"preview":"LABEL","p5DocPath":"LABEL"},{"label":"FALLBACK","type":"constant","kindLabel":"const","params":[],"preview":"FALLBACK","p5DocPath":"FALLBACK"},{"label":"CONTAIN","type":"constant","kindLabel":"const","params":[],"preview":"CONTAIN","p5DocPath":"CONTAIN"},{"label":"COVER","type":"constant","kindLabel":"const","params":[],"preview":"COVER","p5DocPath":"COVER"},{"label":"UNSIGNED_BYTE","type":"constant","kindLabel":"const","params":[],"preview":"UNSIGNED_BYTE","p5DocPath":"UNSIGNED_BYTE"},{"label":"UNSIGNED_INT","type":"constant","kindLabel":"const","params":[],"preview":"UNSIGNED_INT","p5DocPath":"UNSIGNED_INT"},{"label":"FLOAT","type":"constant","kindLabel":"const","params":[],"preview":"FLOAT","p5DocPath":"FLOAT"},{"label":"HALF_FLOAT","type":"constant","kindLabel":"const","params":[],"preview":"HALF_FLOAT","p5DocPath":"HALF_FLOAT"},{"label":"RGBA","type":"constant","kindLabel":"const","params":[],"preview":"RGBA","p5DocPath":"RGBA"},{"label":"print","type":"method","kindLabel":"fun","params":[{"p":"contents","o":false}],"preview":"print(contents)","p5DocPath":"print"},{"label":"frameCount","type":"variable","kindLabel":"var","params":[],"preview":"frameCount","p5DocPath":"frameCount"},{"label":"deltaTime","type":"variable","kindLabel":"var","params":[],"preview":"deltaTime","p5DocPath":"deltaTime"},{"label":"focused","type":"variable","kindLabel":"var","params":[],"preview":"focused","p5DocPath":"focused"},{"label":"cursor","type":"method","kindLabel":"fun","params":[{"p":"type","o":false},{"p":"x","o":true},{"p":"y","o":true}],"preview":"cursor(type, [x], [y])","p5DocPath":"cursor"},{"label":"frameRate","type":"method","kindLabel":"fun","preview":"frameRate()","p5DocPath":"frameRate"},{"label":"getTargetFrameRate","type":"method","kindLabel":"fun","preview":"getTargetFrameRate()","p5DocPath":"getTargetFrameRate"},{"label":"noCursor","type":"method","kindLabel":"fun","preview":"noCursor()","p5DocPath":"noCursor"},{"label":"webglVersion","type":"variable","kindLabel":"var","params":[],"preview":"webglVersion","p5DocPath":"webglVersion"},{"label":"displayWidth","type":"variable","kindLabel":"var","params":[],"preview":"displayWidth","p5DocPath":"displayWidth"},{"label":"displayHeight","type":"variable","kindLabel":"var","params":[],"preview":"displayHeight","p5DocPath":"displayHeight"},{"label":"windowWidth","type":"variable","kindLabel":"var","params":[],"preview":"windowWidth","p5DocPath":"windowWidth"},{"label":"windowHeight","type":"variable","kindLabel":"var","params":[],"preview":"windowHeight","p5DocPath":"windowHeight"},{"label":"windowResized","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"windowResized([event])","p5DocPath":"windowResized"},{"label":"width","type":"variable","kindLabel":"var","params":[],"preview":"width","p5DocPath":"width"},{"label":"height","type":"variable","kindLabel":"var","params":[],"preview":"height","p5DocPath":"height"},{"label":"fullscreen","type":"method","kindLabel":"fun","params":[{"p":"val","o":true}],"preview":"fullscreen([val])","p5DocPath":"fullscreen"},{"label":"pixelDensity","type":"method","kindLabel":"fun","preview":"pixelDensity()","p5DocPath":"pixelDensity"},{"label":"displayDensity","type":"method","kindLabel":"fun","preview":"displayDensity()","p5DocPath":"displayDensity"},{"label":"getURL","type":"method","kindLabel":"fun","preview":"getURL()","p5DocPath":"getURL"},{"label":"getURLPath","type":"method","kindLabel":"fun","preview":"getURLPath()","p5DocPath":"getURLPath"},{"label":"getURLParams","type":"method","kindLabel":"fun","preview":"getURLParams()","p5DocPath":"getURLParams"},{"label":"preload","type":"method","kindLabel":"fun","preview":"preload()","p5DocPath":"preload"},{"label":"setup","type":"method","kindLabel":"fun","preview":"setup()","p5DocPath":"setup"},{"label":"draw","type":"method","kindLabel":"fun","preview":"draw()","p5DocPath":"draw"},{"label":"remove","type":"method","kindLabel":"fun","preview":"remove()","p5DocPath":"remove"},{"label":"disableFriendlyErrors","type":"variable","kindLabel":"var","params":[],"preview":"disableFriendlyErrors","p5DocPath":"disableFriendlyErrors"},{"label":"createCanvas","type":"method","kindLabel":"fun","preview":"createCanvas()","p5DocPath":"createCanvas"},{"label":"resizeCanvas","type":"method","kindLabel":"fun","params":[{"p":"width","o":false},{"p":"height","o":false},{"p":"noRedraw","o":true}],"preview":"resizeCanvas(width, height, [noRedraw])","p5DocPath":"resizeCanvas"},{"label":"noCanvas","type":"method","kindLabel":"fun","preview":"noCanvas()","p5DocPath":"noCanvas"},{"label":"createGraphics","type":"method","kindLabel":"fun","preview":"createGraphics()","p5DocPath":"createGraphics"},{"label":"createFramebuffer","type":"method","kindLabel":"fun","params":[{"p":"options","o":true}],"preview":"createFramebuffer([options])","p5DocPath":"createFramebuffer"},{"label":"clearDepth","type":"method","kindLabel":"fun","params":[{"p":"depth","o":true}],"preview":"clearDepth([depth])","p5DocPath":"clearDepth"},{"label":"blendMode","type":"method","kindLabel":"fun","params":[{"p":"mode","o":false}],"preview":"blendMode(mode)","p5DocPath":"blendMode"},{"label":"drawingContext","type":"variable","kindLabel":"var","params":[],"preview":"drawingContext","p5DocPath":"drawingContext"},{"label":"noLoop","type":"method","kindLabel":"fun","preview":"noLoop()","p5DocPath":"noLoop"},{"label":"loop","type":"method","kindLabel":"fun","preview":"loop()","p5DocPath":"loop"},{"label":"isLooping","type":"method","kindLabel":"fun","preview":"isLooping()","p5DocPath":"isLooping"},{"label":"push","type":"method","kindLabel":"fun","preview":"push()","p5DocPath":"push"},{"label":"pop","type":"method","kindLabel":"fun","preview":"pop()","p5DocPath":"pop"},{"label":"redraw","type":"method","kindLabel":"fun","params":[{"p":"n","o":true}],"preview":"redraw([n])","p5DocPath":"redraw"},{"label":"p5","type":"method","kindLabel":"fun","params":[{"p":"sketch","o":false},{"p":"node","o":false}],"preview":"p5(sketch, node)","p5DocPath":"p5"},{"label":"applyMatrix","type":"method","kindLabel":"fun","preview":"applyMatrix()","p5DocPath":"applyMatrix"},{"label":"resetMatrix","type":"method","kindLabel":"fun","preview":"resetMatrix()","p5DocPath":"resetMatrix"},{"label":"rotate","type":"method","kindLabel":"fun","params":[{"p":"angle","o":false},{"p":"axis","o":true}],"preview":"rotate(angle, [axis])","p5DocPath":"rotate"},{"label":"rotateX","type":"method","kindLabel":"fun","params":[{"p":"angle","o":false}],"preview":"rotateX(angle)","p5DocPath":"rotateX"},{"label":"rotateY","type":"method","kindLabel":"fun","params":[{"p":"angle","o":false}],"preview":"rotateY(angle)","p5DocPath":"rotateY"},{"label":"rotateZ","type":"method","kindLabel":"fun","params":[{"p":"angle","o":false}],"preview":"rotateZ(angle)","p5DocPath":"rotateZ"},{"label":"scale","type":"method","kindLabel":"fun","preview":"scale()","p5DocPath":"scale"},{"label":"shearX","type":"method","kindLabel":"fun","params":[{"p":"angle","o":false}],"preview":"shearX(angle)","p5DocPath":"shearX"},{"label":"shearY","type":"method","kindLabel":"fun","params":[{"p":"angle","o":false}],"preview":"shearY(angle)","p5DocPath":"shearY"},{"label":"translate","type":"method","kindLabel":"fun","preview":"translate()","p5DocPath":"translate"},{"label":"storeItem","type":"method","kindLabel":"fun","params":[{"p":"key","o":false},{"p":"value","o":false}],"preview":"storeItem(key, value)","p5DocPath":"storeItem"},{"label":"getItem","type":"method","kindLabel":"fun","params":[{"p":"key","o":false}],"preview":"getItem(key)","p5DocPath":"getItem"},{"label":"clearStorage","type":"method","kindLabel":"fun","preview":"clearStorage()","p5DocPath":"clearStorage"},{"label":"removeItem","type":"method","kindLabel":"fun","params":[{"p":"key","o":false}],"preview":"removeItem(key)","p5DocPath":"removeItem"},{"label":"createStringDict","type":"method","kindLabel":"fun","preview":"createStringDict()","p5DocPath":"createStringDict"},{"label":"createNumberDict","type":"method","kindLabel":"fun","preview":"createNumberDict()","p5DocPath":"createNumberDict"},{"label":"select","type":"method","kindLabel":"fun","params":[{"p":"selectors","o":false},{"p":"container","o":true}],"preview":"select(selectors, [container])","p5DocPath":"select"},{"label":"selectAll","type":"method","kindLabel":"fun","params":[{"p":"selectors","o":false},{"p":"container","o":true}],"preview":"selectAll(selectors, [container])","p5DocPath":"selectAll"},{"label":"removeElements","type":"method","kindLabel":"fun","preview":"removeElements()","p5DocPath":"removeElements"},{"label":"changed","type":"method","kindLabel":"fun","params":[{"p":"fxn","o":false}],"preview":"changed(fxn)","p5DocPath":"changed"},{"label":"input","type":"method","kindLabel":"fun","params":[{"p":"fxn","o":false}],"preview":"input(fxn)","p5DocPath":"input"},{"label":"createDiv","type":"method","kindLabel":"fun","params":[{"p":"html","o":true}],"preview":"createDiv([html])","p5DocPath":"createDiv"},{"label":"createP","type":"method","kindLabel":"fun","params":[{"p":"html","o":true}],"preview":"createP([html])","p5DocPath":"createP"},{"label":"createSpan","type":"method","kindLabel":"fun","params":[{"p":"html","o":true}],"preview":"createSpan([html])","p5DocPath":"createSpan"},{"label":"createImg","type":"method","kindLabel":"fun","preview":"createImg()","p5DocPath":"createImg"},{"label":"createA","type":"method","kindLabel":"fun","params":[{"p":"href","o":false},{"p":"html","o":false},{"p":"target","o":true}],"preview":"createA(href, html, [target])","p5DocPath":"createA"},{"label":"createSlider","type":"method","kindLabel":"fun","params":[{"p":"min","o":false},{"p":"max","o":false},{"p":"value","o":true},{"p":"step","o":true}],"preview":"createSlider(min, max, [value], [step])","p5DocPath":"createSlider"},{"label":"createButton","type":"method","kindLabel":"fun","params":[{"p":"label","o":false},{"p":"value","o":true}],"preview":"createButton(label, [value])","p5DocPath":"createButton"},{"label":"createCheckbox","type":"method","kindLabel":"fun","params":[{"p":"label","o":true},{"p":"value","o":true}],"preview":"createCheckbox([label], [value])","p5DocPath":"createCheckbox"},{"label":"createSelect","type":"method","kindLabel":"fun","preview":"createSelect()","p5DocPath":"createSelect"},{"label":"createRadio","type":"method","kindLabel":"fun","preview":"createRadio()","p5DocPath":"createRadio"},{"label":"createColorPicker","type":"method","kindLabel":"fun","params":[{"p":"value","o":true}],"preview":"createColorPicker([value])","p5DocPath":"createColorPicker"},{"label":"createInput","type":"method","kindLabel":"fun","preview":"createInput()","p5DocPath":"createInput"},{"label":"createFileInput","type":"method","kindLabel":"fun","params":[{"p":"callback","o":false},{"p":"multiple","o":true}],"preview":"createFileInput(callback, [multiple])","p5DocPath":"createFileInput"},{"label":"createVideo","type":"method","kindLabel":"fun","params":[{"p":"src","o":false},{"p":"callback","o":true}],"preview":"createVideo(src, [callback])","p5DocPath":"createVideo"},{"label":"createAudio","type":"method","kindLabel":"fun","params":[{"p":"src","o":true},{"p":"callback","o":true}],"preview":"createAudio([src], [callback])","p5DocPath":"createAudio"},{"label":"createCapture","type":"method","kindLabel":"fun","params":[{"p":"type","o":true},{"p":"flipped","o":true},{"p":"callback","o":true}],"preview":"createCapture([type], [flipped], [callback])","p5DocPath":"createCapture"},{"label":"createElement","type":"method","kindLabel":"fun","params":[{"p":"tag","o":false},{"p":"content","o":true}],"preview":"createElement(tag, [content])","p5DocPath":"createElement"},{"label":"deviceOrientation","type":"variable","kindLabel":"var","params":[],"preview":"deviceOrientation","p5DocPath":"deviceOrientation"},{"label":"accelerationX","type":"variable","kindLabel":"var","params":[],"preview":"accelerationX","p5DocPath":"accelerationX"},{"label":"accelerationY","type":"variable","kindLabel":"var","params":[],"preview":"accelerationY","p5DocPath":"accelerationY"},{"label":"accelerationZ","type":"variable","kindLabel":"var","params":[],"preview":"accelerationZ","p5DocPath":"accelerationZ"},{"label":"pAccelerationX","type":"variable","kindLabel":"var","params":[],"preview":"pAccelerationX","p5DocPath":"pAccelerationX"},{"label":"pAccelerationY","type":"variable","kindLabel":"var","params":[],"preview":"pAccelerationY","p5DocPath":"pAccelerationY"},{"label":"pAccelerationZ","type":"variable","kindLabel":"var","params":[],"preview":"pAccelerationZ","p5DocPath":"pAccelerationZ"},{"label":"rotationX","type":"variable","kindLabel":"var","params":[],"preview":"rotationX","p5DocPath":"rotationX"},{"label":"rotationY","type":"variable","kindLabel":"var","params":[],"preview":"rotationY","p5DocPath":"rotationY"},{"label":"rotationZ","type":"variable","kindLabel":"var","params":[],"preview":"rotationZ","p5DocPath":"rotationZ"},{"label":"pRotationX","type":"variable","kindLabel":"var","params":[],"preview":"pRotationX","p5DocPath":"pRotationX"},{"label":"pRotationY","type":"variable","kindLabel":"var","params":[],"preview":"pRotationY","p5DocPath":"pRotationY"},{"label":"pRotationZ","type":"variable","kindLabel":"var","params":[],"preview":"pRotationZ","p5DocPath":"pRotationZ"},{"label":"turnAxis","type":"variable","kindLabel":"var","params":[],"preview":"turnAxis","p5DocPath":"turnAxis"},{"label":"setMoveThreshold","type":"method","kindLabel":"fun","params":[{"p":"value","o":false}],"preview":"setMoveThreshold(value)","p5DocPath":"setMoveThreshold"},{"label":"setShakeThreshold","type":"method","kindLabel":"fun","params":[{"p":"value","o":false}],"preview":"setShakeThreshold(value)","p5DocPath":"setShakeThreshold"},{"label":"deviceMoved","type":"method","kindLabel":"fun","preview":"deviceMoved()","p5DocPath":"deviceMoved"},{"label":"deviceTurned","type":"method","kindLabel":"fun","preview":"deviceTurned()","p5DocPath":"deviceTurned"},{"label":"deviceShaken","type":"method","kindLabel":"fun","preview":"deviceShaken()","p5DocPath":"deviceShaken"},{"label":"keyIsPressed","type":"variable","kindLabel":"var","params":[],"preview":"keyIsPressed","p5DocPath":"keyIsPressed"},{"label":"key","type":"variable","kindLabel":"var","params":[],"preview":"key","p5DocPath":"key"},{"label":"keyCode","type":"variable","kindLabel":"var","params":[],"preview":"keyCode","p5DocPath":"keyCode"},{"label":"keyPressed","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"keyPressed([event])","p5DocPath":"keyPressed"},{"label":"keyReleased","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"keyReleased([event])","p5DocPath":"keyReleased"},{"label":"keyTyped","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"keyTyped([event])","p5DocPath":"keyTyped"},{"label":"keyIsDown","type":"method","kindLabel":"fun","params":[{"p":"code","o":false}],"preview":"keyIsDown(code)","p5DocPath":"keyIsDown"},{"label":"movedX","type":"variable","kindLabel":"var","params":[],"preview":"movedX","p5DocPath":"movedX"},{"label":"movedY","type":"variable","kindLabel":"var","params":[],"preview":"movedY","p5DocPath":"movedY"},{"label":"mouseX","type":"variable","kindLabel":"var","params":[],"preview":"mouseX","p5DocPath":"mouseX"},{"label":"mouseY","type":"variable","kindLabel":"var","params":[],"preview":"mouseY","p5DocPath":"mouseY"},{"label":"pmouseX","type":"variable","kindLabel":"var","params":[],"preview":"pmouseX","p5DocPath":"pmouseX"},{"label":"pmouseY","type":"variable","kindLabel":"var","params":[],"preview":"pmouseY","p5DocPath":"pmouseY"},{"label":"winMouseX","type":"variable","kindLabel":"var","params":[],"preview":"winMouseX","p5DocPath":"winMouseX"},{"label":"winMouseY","type":"variable","kindLabel":"var","params":[],"preview":"winMouseY","p5DocPath":"winMouseY"},{"label":"pwinMouseX","type":"variable","kindLabel":"var","params":[],"preview":"pwinMouseX","p5DocPath":"pwinMouseX"},{"label":"pwinMouseY","type":"variable","kindLabel":"var","params":[],"preview":"pwinMouseY","p5DocPath":"pwinMouseY"},{"label":"mouseButton","type":"variable","kindLabel":"var","params":[],"preview":"mouseButton","p5DocPath":"mouseButton"},{"label":"mouseIsPressed","type":"variable","kindLabel":"var","params":[],"preview":"mouseIsPressed","p5DocPath":"mouseIsPressed"},{"label":"mouseMoved","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"mouseMoved([event])","p5DocPath":"mouseMoved"},{"label":"mouseDragged","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"mouseDragged([event])","p5DocPath":"mouseDragged"},{"label":"mousePressed","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"mousePressed([event])","p5DocPath":"mousePressed"},{"label":"mouseReleased","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"mouseReleased([event])","p5DocPath":"mouseReleased"},{"label":"mouseClicked","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"mouseClicked([event])","p5DocPath":"mouseClicked"},{"label":"doubleClicked","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"doubleClicked([event])","p5DocPath":"doubleClicked"},{"label":"mouseWheel","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"mouseWheel([event])","p5DocPath":"mouseWheel"},{"label":"requestPointerLock","type":"method","kindLabel":"fun","preview":"requestPointerLock()","p5DocPath":"requestPointerLock"},{"label":"exitPointerLock","type":"method","kindLabel":"fun","preview":"exitPointerLock()","p5DocPath":"exitPointerLock"},{"label":"touches","type":"variable","kindLabel":"var","params":[],"preview":"touches","p5DocPath":"touches"},{"label":"touchStarted","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"touchStarted([event])","p5DocPath":"touchStarted"},{"label":"touchMoved","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"touchMoved([event])","p5DocPath":"touchMoved"},{"label":"touchEnded","type":"method","kindLabel":"fun","params":[{"p":"event","o":true}],"preview":"touchEnded([event])","p5DocPath":"touchEnded"},{"label":"createImage","type":"method","kindLabel":"fun","params":[{"p":"width","o":false},{"p":"height","o":false}],"preview":"createImage(width, height)","p5DocPath":"createImage"},{"label":"saveCanvas","type":"method","kindLabel":"fun","preview":"saveCanvas()","p5DocPath":"saveCanvas"},{"label":"saveFrames","type":"method","kindLabel":"fun","params":[{"p":"filename","o":false},{"p":"extension","o":false},{"p":"duration","o":false},{"p":"framerate","o":false},{"p":"callback","o":true}],"preview":"saveFrames(filename, extension, duration, framerate, [callback])","p5DocPath":"saveFrames"},{"label":"loadImage","type":"method","kindLabel":"fun","params":[{"p":"path","o":false},{"p":"successCallback","o":true},{"p":"failureCallback","o":true}],"preview":"loadImage(path, [successCallback], [failureCallback])","p5DocPath":"loadImage"},{"label":"saveGif","type":"method","kindLabel":"fun","params":[{"p":"filename","o":false},{"p":"duration","o":false},{"p":"options","o":true}],"preview":"saveGif(filename, duration, [options])","p5DocPath":"saveGif"},{"label":"image","type":"method","kindLabel":"fun","preview":"image()","p5DocPath":"image"},{"label":"tint","type":"method","kindLabel":"fun","preview":"tint()","p5DocPath":"tint"},{"label":"noTint","type":"method","kindLabel":"fun","preview":"noTint()","p5DocPath":"noTint"},{"label":"imageMode","type":"method","kindLabel":"fun","params":[{"p":"mode","o":false}],"preview":"imageMode(mode)","p5DocPath":"imageMode"},{"label":"pixels","type":"variable","kindLabel":"var","params":[],"preview":"pixels","p5DocPath":"pixels"},{"label":"blend","type":"method","kindLabel":"fun","preview":"blend()","p5DocPath":"blend"},{"label":"copy","type":"method","kindLabel":"fun","preview":"copy()","p5DocPath":"copy"},{"label":"filter","type":"method","kindLabel":"fun","preview":"filter()","p5DocPath":"filter"},{"label":"get","type":"method","kindLabel":"fun","preview":"get()","p5DocPath":"get"},{"label":"loadPixels","type":"method","kindLabel":"fun","preview":"loadPixels()","p5DocPath":"loadPixels"},{"label":"set","type":"method","kindLabel":"fun","params":[{"p":"x","o":false},{"p":"y","o":false},{"p":"c","o":false}],"preview":"set(x, y, c)","p5DocPath":"set"},{"label":"updatePixels","type":"method","kindLabel":"fun","params":[{"p":"x","o":true},{"p":"y","o":true},{"p":"w","o":true},{"p":"h","o":true}],"preview":"updatePixels([x], [y], [w], [h])","p5DocPath":"updatePixels"},{"label":"loadJSON","type":"method","kindLabel":"fun","params":[{"p":"path","o":false},{"p":"successCallback","o":true},{"p":"errorCallback","o":true}],"preview":"loadJSON(path, [successCallback], [errorCallback])","p5DocPath":"loadJSON"},{"label":"loadStrings","type":"method","kindLabel":"fun","params":[{"p":"path","o":false},{"p":"successCallback","o":true},{"p":"errorCallback","o":true}],"preview":"loadStrings(path, [successCallback], [errorCallback])","p5DocPath":"loadStrings"},{"label":"loadTable","type":"method","kindLabel":"fun","params":[{"p":"filename","o":false},{"p":"extension","o":true},{"p":"header","o":true},{"p":"callback","o":true},{"p":"errorCallback","o":true}],"preview":"loadTable(filename, [extension], [header], [callback], [errorCallback])","p5DocPath":"loadTable"},{"label":"loadXML","type":"method","kindLabel":"fun","params":[{"p":"path","o":false},{"p":"successCallback","o":true},{"p":"errorCallback","o":true}],"preview":"loadXML(path, [successCallback], [errorCallback])","p5DocPath":"loadXML"},{"label":"loadBytes","type":"method","kindLabel":"fun","params":[{"p":"file","o":false},{"p":"callback","o":true},{"p":"errorCallback","o":true}],"preview":"loadBytes(file, [callback], [errorCallback])","p5DocPath":"loadBytes"},{"label":"httpGet","type":"method","kindLabel":"fun","preview":"httpGet()","p5DocPath":"httpGet"},{"label":"httpPost","type":"method","kindLabel":"fun","preview":"httpPost()","p5DocPath":"httpPost"},{"label":"httpDo","type":"method","kindLabel":"fun","preview":"httpDo()","p5DocPath":"httpDo"},{"label":"createWriter","type":"method","kindLabel":"fun","params":[{"p":"name","o":false},{"p":"extension","o":true}],"preview":"createWriter(name, [extension])","p5DocPath":"createWriter"},{"label":"save","type":"method","kindLabel":"fun","params":[{"p":"objectOrFilename","o":true},{"p":"filename","o":true},{"p":"options","o":true}],"preview":"save([objectOrFilename], [filename], [options])","p5DocPath":"save"},{"label":"saveJSON","type":"method","kindLabel":"fun","params":[{"p":"json","o":false},{"p":"filename","o":false},{"p":"optimize","o":true}],"preview":"saveJSON(json, filename, [optimize])","p5DocPath":"saveJSON"},{"label":"saveStrings","type":"method","kindLabel":"fun","params":[{"p":"list","o":false},{"p":"filename","o":false},{"p":"extension","o":true},{"p":"isCRLF","o":true}],"preview":"saveStrings(list, filename, [extension], [isCRLF])","p5DocPath":"saveStrings"},{"label":"saveTable","type":"method","kindLabel":"fun","params":[{"p":"Table","o":false},{"p":"filename","o":false},{"p":"options","o":true}],"preview":"saveTable(Table, filename, [options])","p5DocPath":"saveTable"},{"label":"abs","type":"method","kindLabel":"fun","params":[{"p":"n","o":false}],"preview":"abs(n)","p5DocPath":"abs"},{"label":"ceil","type":"method","kindLabel":"fun","params":[{"p":"n","o":false}],"preview":"ceil(n)","p5DocPath":"ceil"},{"label":"constrain","type":"method","kindLabel":"fun","params":[{"p":"n","o":false},{"p":"low","o":false},{"p":"high","o":false}],"preview":"constrain(n, low, high)","p5DocPath":"constrain"},{"label":"dist","type":"method","kindLabel":"fun","preview":"dist()","p5DocPath":"dist"},{"label":"exp","type":"method","kindLabel":"fun","params":[{"p":"n","o":false}],"preview":"exp(n)","p5DocPath":"exp"},{"label":"floor","type":"method","kindLabel":"fun","params":[{"p":"n","o":false}],"preview":"floor(n)","p5DocPath":"floor"},{"label":"lerp","type":"method","kindLabel":"fun","params":[{"p":"start","o":false},{"p":"stop","o":false},{"p":"amt","o":false}],"preview":"lerp(start, stop, amt)","p5DocPath":"lerp"},{"label":"log","type":"method","kindLabel":"fun","params":[{"p":"n","o":false}],"preview":"log(n)","p5DocPath":"log"},{"label":"mag","type":"method","kindLabel":"fun","params":[{"p":"x","o":false},{"p":"y","o":false}],"preview":"mag(x, y)","p5DocPath":"mag"},{"label":"map","type":"method","kindLabel":"fun","params":[{"p":"value","o":false},{"p":"start1","o":false},{"p":"stop1","o":false},{"p":"start2","o":false},{"p":"stop2","o":false},{"p":"withinBounds","o":true}],"preview":"map(value, start1, stop1, start2, stop2, [withinBounds])","p5DocPath":"map"},{"label":"max","type":"method","kindLabel":"fun","preview":"max()","p5DocPath":"max"},{"label":"min","type":"method","kindLabel":"fun","preview":"min()","p5DocPath":"min"},{"label":"norm","type":"method","kindLabel":"fun","params":[{"p":"value","o":false},{"p":"start","o":false},{"p":"stop","o":false}],"preview":"norm(value, start, stop)","p5DocPath":"norm"},{"label":"pow","type":"method","kindLabel":"fun","params":[{"p":"n","o":false},{"p":"e","o":false}],"preview":"pow(n, e)","p5DocPath":"pow"},{"label":"round","type":"method","kindLabel":"fun","params":[{"p":"n","o":false},{"p":"decimals","o":true}],"preview":"round(n, [decimals])","p5DocPath":"round"},{"label":"sq","type":"method","kindLabel":"fun","params":[{"p":"n","o":false}],"preview":"sq(n)","p5DocPath":"sq"},{"label":"sqrt","type":"method","kindLabel":"fun","params":[{"p":"n","o":false}],"preview":"sqrt(n)","p5DocPath":"sqrt"},{"label":"fract","type":"method","kindLabel":"fun","params":[{"p":"n","o":false}],"preview":"fract(n)","p5DocPath":"fract"},{"label":"createVector","type":"method","kindLabel":"fun","params":[{"p":"x","o":true},{"p":"y","o":true},{"p":"z","o":true}],"preview":"createVector([x], [y], [z])","p5DocPath":"createVector"},{"label":"noise","type":"method","kindLabel":"fun","params":[{"p":"x","o":false},{"p":"y","o":true},{"p":"z","o":true}],"preview":"noise(x, [y], [z])","p5DocPath":"noise"},{"label":"noiseDetail","type":"method","kindLabel":"fun","params":[{"p":"lod","o":false},{"p":"falloff","o":false}],"preview":"noiseDetail(lod, falloff)","p5DocPath":"noiseDetail"},{"label":"noiseSeed","type":"method","kindLabel":"fun","params":[{"p":"seed","o":false}],"preview":"noiseSeed(seed)","p5DocPath":"noiseSeed"},{"label":"randomSeed","type":"method","kindLabel":"fun","params":[{"p":"seed","o":false}],"preview":"randomSeed(seed)","p5DocPath":"randomSeed"},{"label":"random","type":"method","kindLabel":"fun","preview":"random()","p5DocPath":"random"},{"label":"randomGaussian","type":"method","kindLabel":"fun","params":[{"p":"mean","o":true},{"p":"sd","o":true}],"preview":"randomGaussian([mean], [sd])","p5DocPath":"randomGaussian"},{"label":"acos","type":"method","kindLabel":"fun","params":[{"p":"value","o":false}],"preview":"acos(value)","p5DocPath":"acos"},{"label":"asin","type":"method","kindLabel":"fun","params":[{"p":"value","o":false}],"preview":"asin(value)","p5DocPath":"asin"},{"label":"atan","type":"method","kindLabel":"fun","params":[{"p":"value","o":false}],"preview":"atan(value)","p5DocPath":"atan"},{"label":"atan2","type":"method","kindLabel":"fun","params":[{"p":"y","o":false},{"p":"x","o":false}],"preview":"atan2(y, x)","p5DocPath":"atan2"},{"label":"cos","type":"method","kindLabel":"fun","params":[{"p":"angle","o":false}],"preview":"cos(angle)","p5DocPath":"cos"},{"label":"sin","type":"method","kindLabel":"fun","params":[{"p":"angle","o":false}],"preview":"sin(angle)","p5DocPath":"sin"},{"label":"tan","type":"method","kindLabel":"fun","params":[{"p":"angle","o":false}],"preview":"tan(angle)","p5DocPath":"tan"},{"label":"degrees","type":"method","kindLabel":"fun","params":[{"p":"radians","o":false}],"preview":"degrees(radians)","p5DocPath":"degrees"},{"label":"radians","type":"method","kindLabel":"fun","params":[{"p":"degrees","o":false}],"preview":"radians(degrees)","p5DocPath":"radians"},{"label":"angleMode","type":"method","kindLabel":"fun","preview":"angleMode()","p5DocPath":"angleMode"},{"label":"textAlign","type":"method","kindLabel":"fun","preview":"textAlign()","p5DocPath":"textAlign"},{"label":"textLeading","type":"method","kindLabel":"fun","preview":"textLeading()","p5DocPath":"textLeading"},{"label":"textSize","type":"method","kindLabel":"fun","preview":"textSize()","p5DocPath":"textSize"},{"label":"textStyle","type":"method","kindLabel":"fun","preview":"textStyle()","p5DocPath":"textStyle"},{"label":"textWidth","type":"method","kindLabel":"fun","params":[{"p":"str","o":false}],"preview":"textWidth(str)","p5DocPath":"textWidth"},{"label":"textAscent","type":"method","kindLabel":"fun","preview":"textAscent()","p5DocPath":"textAscent"},{"label":"textDescent","type":"method","kindLabel":"fun","preview":"textDescent()","p5DocPath":"textDescent"},{"label":"textWrap","type":"method","kindLabel":"fun","params":[{"p":"style","o":false}],"preview":"textWrap(style)","p5DocPath":"textWrap"},{"label":"loadFont","type":"method","kindLabel":"fun","params":[{"p":"path","o":false},{"p":"successCallback","o":true},{"p":"failureCallback","o":true}],"preview":"loadFont(path, [successCallback], [failureCallback])","p5DocPath":"loadFont"},{"label":"text","type":"method","kindLabel":"fun","params":[{"p":"str","o":false},{"p":"x","o":false},{"p":"y","o":false},{"p":"maxWidth","o":true},{"p":"maxHeight","o":true}],"preview":"text(str, x, y, [maxWidth], [maxHeight])","p5DocPath":"text"},{"label":"textFont","type":"method","kindLabel":"fun","preview":"textFont()","p5DocPath":"textFont"},{"label":"append","type":"method","kindLabel":"fun","params":[{"p":"array","o":false},{"p":"value","o":false}],"preview":"append(array, value)","p5DocPath":"append"},{"label":"arrayCopy","type":"method","kindLabel":"fun","preview":"arrayCopy()","p5DocPath":"arrayCopy"},{"label":"concat","type":"method","kindLabel":"fun","params":[{"p":"a","o":false},{"p":"b","o":false}],"preview":"concat(a, b)","p5DocPath":"concat"},{"label":"reverse","type":"method","kindLabel":"fun","params":[{"p":"list","o":false}],"preview":"reverse(list)","p5DocPath":"reverse"},{"label":"shorten","type":"method","kindLabel":"fun","params":[{"p":"list","o":false}],"preview":"shorten(list)","p5DocPath":"shorten"},{"label":"shuffle","type":"method","kindLabel":"fun","params":[{"p":"array","o":false},{"p":"bool","o":true}],"preview":"shuffle(array, [bool])","p5DocPath":"shuffle"},{"label":"sort","type":"method","kindLabel":"fun","params":[{"p":"list","o":false},{"p":"count","o":true}],"preview":"sort(list, [count])","p5DocPath":"sort"},{"label":"splice","type":"method","kindLabel":"fun","params":[{"p":"list","o":false},{"p":"value","o":false},{"p":"position","o":false}],"preview":"splice(list, value, position)","p5DocPath":"splice"},{"label":"subset","type":"method","kindLabel":"fun","params":[{"p":"list","o":false},{"p":"start","o":false},{"p":"count","o":true}],"preview":"subset(list, start, [count])","p5DocPath":"subset"},{"label":"float","type":"method","kindLabel":"fun","preview":"float()","p5DocPath":"float"},{"label":"int","type":"method","kindLabel":"fun","preview":"int()","p5DocPath":"int"},{"label":"str","type":"method","kindLabel":"fun","params":[{"p":"n","o":false}],"preview":"str(n)","p5DocPath":"str"},{"label":"boolean","type":"method","kindLabel":"fun","preview":"boolean()","p5DocPath":"boolean"},{"label":"byte","type":"method","kindLabel":"fun","preview":"byte()","p5DocPath":"byte"},{"label":"char","type":"method","kindLabel":"fun","preview":"char()","p5DocPath":"char"},{"label":"unchar","type":"method","kindLabel":"fun","preview":"unchar()","p5DocPath":"unchar"},{"label":"hex","type":"method","kindLabel":"fun","preview":"hex()","p5DocPath":"hex"},{"label":"unhex","type":"method","kindLabel":"fun","preview":"unhex()","p5DocPath":"unhex"},{"label":"join","type":"method","kindLabel":"fun","params":[{"p":"list","o":false},{"p":"separator","o":false}],"preview":"join(list, separator)","p5DocPath":"join"},{"label":"match","type":"method","kindLabel":"fun","params":[{"p":"str","o":false},{"p":"regexp","o":false}],"preview":"match(str, regexp)","p5DocPath":"match"},{"label":"matchAll","type":"method","kindLabel":"fun","params":[{"p":"str","o":false},{"p":"regexp","o":false}],"preview":"matchAll(str, regexp)","p5DocPath":"matchAll"},{"label":"nf","type":"method","kindLabel":"fun","preview":"nf()","p5DocPath":"nf"},{"label":"nfc","type":"method","kindLabel":"fun","preview":"nfc()","p5DocPath":"nfc"},{"label":"nfp","type":"method","kindLabel":"fun","preview":"nfp()","p5DocPath":"nfp"},{"label":"nfs","type":"method","kindLabel":"fun","preview":"nfs()","p5DocPath":"nfs"},{"label":"split","type":"method","kindLabel":"fun","params":[{"p":"value","o":false},{"p":"delim","o":false}],"preview":"split(value, delim)","p5DocPath":"split"},{"label":"splitTokens","type":"method","kindLabel":"fun","params":[{"p":"value","o":false},{"p":"delim","o":true}],"preview":"splitTokens(value, [delim])","p5DocPath":"splitTokens"},{"label":"trim","type":"method","kindLabel":"fun","preview":"trim()","p5DocPath":"trim"},{"label":"day","type":"method","kindLabel":"fun","preview":"day()","p5DocPath":"day"},{"label":"hour","type":"method","kindLabel":"fun","preview":"hour()","p5DocPath":"hour"},{"label":"minute","type":"method","kindLabel":"fun","preview":"minute()","p5DocPath":"minute"},{"label":"millis","type":"method","kindLabel":"fun","preview":"millis()","p5DocPath":"millis"},{"label":"month","type":"method","kindLabel":"fun","preview":"month()","p5DocPath":"month"},{"label":"second","type":"method","kindLabel":"fun","preview":"second()","p5DocPath":"second"},{"label":"year","type":"method","kindLabel":"fun","preview":"year()","p5DocPath":"year"},{"label":"beginGeometry","type":"method","kindLabel":"fun","preview":"beginGeometry()","p5DocPath":"beginGeometry"},{"label":"endGeometry","type":"method","kindLabel":"fun","preview":"endGeometry()","p5DocPath":"endGeometry"},{"label":"buildGeometry","type":"method","kindLabel":"fun","params":[{"p":"callback","o":false}],"preview":"buildGeometry(callback)","p5DocPath":"buildGeometry"},{"label":"freeGeometry","type":"method","kindLabel":"fun","params":[{"p":"geometry","o":false}],"preview":"freeGeometry(geometry)","p5DocPath":"freeGeometry"},{"label":"plane","type":"method","kindLabel":"fun","params":[{"p":"width","o":true},{"p":"height","o":true},{"p":"detailX","o":true},{"p":"detailY","o":true}],"preview":"plane([width], [height], [detailX], [detailY])","p5DocPath":"plane"},{"label":"box","type":"method","kindLabel":"fun","params":[{"p":"width","o":true},{"p":"height","o":true},{"p":"depth","o":true},{"p":"detailX","o":true},{"p":"detailY","o":true}],"preview":"box([width], [height], [depth], [detailX], [detailY])","p5DocPath":"box"},{"label":"sphere","type":"method","kindLabel":"fun","params":[{"p":"radius","o":true},{"p":"detailX","o":true},{"p":"detailY","o":true}],"preview":"sphere([radius], [detailX], [detailY])","p5DocPath":"sphere"},{"label":"cylinder","type":"method","kindLabel":"fun","params":[{"p":"radius","o":true},{"p":"height","o":true},{"p":"detailX","o":true},{"p":"detailY","o":true},{"p":"bottomCap","o":true},{"p":"topCap","o":true}],"preview":"cylinder([radius], [height], [detailX], [detailY], [bottomCap], [topCap])","p5DocPath":"cylinder"},{"label":"cone","type":"method","kindLabel":"fun","params":[{"p":"radius","o":true},{"p":"height","o":true},{"p":"detailX","o":true},{"p":"detailY","o":true},{"p":"cap","o":true}],"preview":"cone([radius], [height], [detailX], [detailY], [cap])","p5DocPath":"cone"},{"label":"ellipsoid","type":"method","kindLabel":"fun","params":[{"p":"radiusX","o":true},{"p":"radiusY","o":true},{"p":"radiusZ","o":true},{"p":"detailX","o":true},{"p":"detailY","o":true}],"preview":"ellipsoid([radiusX], [radiusY], [radiusZ], [detailX], [detailY])","p5DocPath":"ellipsoid"},{"label":"torus","type":"method","kindLabel":"fun","params":[{"p":"radius","o":true},{"p":"tubeRadius","o":true},{"p":"detailX","o":true},{"p":"detailY","o":true}],"preview":"torus([radius], [tubeRadius], [detailX], [detailY])","p5DocPath":"torus"},{"label":"orbitControl","type":"method","kindLabel":"fun","params":[{"p":"sensitivityX","o":true},{"p":"sensitivityY","o":true},{"p":"sensitivityZ","o":true},{"p":"options","o":true}],"preview":"orbitControl([sensitivityX], [sensitivityY], [sensitivityZ], [options])","p5DocPath":"orbitControl"},{"label":"debugMode","type":"method","kindLabel":"fun","preview":"debugMode()","p5DocPath":"debugMode"},{"label":"noDebugMode","type":"method","kindLabel":"fun","preview":"noDebugMode()","p5DocPath":"noDebugMode"},{"label":"ambientLight","type":"method","kindLabel":"fun","preview":"ambientLight()","p5DocPath":"ambientLight"},{"label":"specularColor","type":"method","kindLabel":"fun","preview":"specularColor()","p5DocPath":"specularColor"},{"label":"directionalLight","type":"method","kindLabel":"fun","preview":"directionalLight()","p5DocPath":"directionalLight"},{"label":"pointLight","type":"method","kindLabel":"fun","preview":"pointLight()","p5DocPath":"pointLight"},{"label":"imageLight","type":"method","kindLabel":"fun","params":[{"p":"img","o":false}],"preview":"imageLight(img)","p5DocPath":"imageLight"},{"label":"panorama","type":"method","kindLabel":"fun","params":[{"p":"img","o":false}],"preview":"panorama(img)","p5DocPath":"panorama"},{"label":"lights","type":"method","kindLabel":"fun","preview":"lights()","p5DocPath":"lights"},{"label":"lightFalloff","type":"method","kindLabel":"fun","params":[{"p":"constant","o":false},{"p":"linear","o":false},{"p":"quadratic","o":false}],"preview":"lightFalloff(constant, linear, quadratic)","p5DocPath":"lightFalloff"},{"label":"spotLight","type":"method","kindLabel":"fun","preview":"spotLight()","p5DocPath":"spotLight"},{"label":"noLights","type":"method","kindLabel":"fun","preview":"noLights()","p5DocPath":"noLights"},{"label":"loadModel","type":"method","kindLabel":"fun","preview":"loadModel()","p5DocPath":"loadModel"},{"label":"model","type":"method","kindLabel":"fun","params":[{"p":"model","o":false}],"preview":"model(model)","p5DocPath":"model"},{"label":"createModel","type":"method","kindLabel":"fun","preview":"createModel()","p5DocPath":"createModel"},{"label":"loadShader","type":"method","kindLabel":"fun","params":[{"p":"vertFilename","o":false},{"p":"fragFilename","o":false},{"p":"successCallback","o":true},{"p":"failureCallback","o":true}],"preview":"loadShader(vertFilename, fragFilename, [successCallback], [failureCallback])","p5DocPath":"loadShader"},{"label":"createShader","type":"method","kindLabel":"fun","params":[{"p":"vertSrc","o":false},{"p":"fragSrc","o":false},{"p":"options","o":true}],"preview":"createShader(vertSrc, fragSrc, [options])","p5DocPath":"createShader"},{"label":"createFilterShader","type":"method","kindLabel":"fun","params":[{"p":"fragSrc","o":false}],"preview":"createFilterShader(fragSrc)","p5DocPath":"createFilterShader"},{"label":"shader","type":"method","kindLabel":"fun","params":[{"p":"s","o":false}],"preview":"shader(s)","p5DocPath":"shader"},{"label":"baseMaterialShader","type":"method","kindLabel":"fun","preview":"baseMaterialShader()","p5DocPath":"baseMaterialShader"},{"label":"baseNormalShader","type":"method","kindLabel":"fun","preview":"baseNormalShader()","p5DocPath":"baseNormalShader"},{"label":"baseColorShader","type":"method","kindLabel":"fun","preview":"baseColorShader()","p5DocPath":"baseColorShader"},{"label":"baseStrokeShader","type":"method","kindLabel":"fun","preview":"baseStrokeShader()","p5DocPath":"baseStrokeShader"},{"label":"resetShader","type":"method","kindLabel":"fun","preview":"resetShader()","p5DocPath":"resetShader"},{"label":"texture","type":"method","kindLabel":"fun","params":[{"p":"tex","o":false}],"preview":"texture(tex)","p5DocPath":"texture"},{"label":"textureMode","type":"method","kindLabel":"fun","params":[{"p":"mode","o":false}],"preview":"textureMode(mode)","p5DocPath":"textureMode"},{"label":"textureWrap","type":"method","kindLabel":"fun","params":[{"p":"wrapX","o":false},{"p":"wrapY","o":true}],"preview":"textureWrap(wrapX, [wrapY])","p5DocPath":"textureWrap"},{"label":"normalMaterial","type":"method","kindLabel":"fun","preview":"normalMaterial()","p5DocPath":"normalMaterial"},{"label":"ambientMaterial","type":"method","kindLabel":"fun","preview":"ambientMaterial()","p5DocPath":"ambientMaterial"},{"label":"emissiveMaterial","type":"method","kindLabel":"fun","preview":"emissiveMaterial()","p5DocPath":"emissiveMaterial"},{"label":"specularMaterial","type":"method","kindLabel":"fun","preview":"specularMaterial()","p5DocPath":"specularMaterial"},{"label":"shininess","type":"method","kindLabel":"fun","params":[{"p":"shine","o":false}],"preview":"shininess(shine)","p5DocPath":"shininess"},{"label":"metalness","type":"method","kindLabel":"fun","params":[{"p":"metallic","o":false}],"preview":"metalness(metallic)","p5DocPath":"metalness"},{"label":"camera","type":"method","kindLabel":"fun","params":[{"p":"x","o":true},{"p":"y","o":true},{"p":"z","o":true},{"p":"centerX","o":true},{"p":"centerY","o":true},{"p":"centerZ","o":true},{"p":"upX","o":true},{"p":"upY","o":true},{"p":"upZ","o":true}],"preview":"camera([x], [y], [z], [centerX], [centerY], [centerZ], [upX], [upY], [upZ])","p5DocPath":"camera"},{"label":"perspective","type":"method","kindLabel":"fun","params":[{"p":"fovy","o":true},{"p":"aspect","o":true},{"p":"near","o":true},{"p":"far","o":true}],"preview":"perspective([fovy], [aspect], [near], [far])","p5DocPath":"perspective"},{"label":"linePerspective","type":"method","kindLabel":"fun","preview":"linePerspective()","p5DocPath":"linePerspective"},{"label":"ortho","type":"method","kindLabel":"fun","params":[{"p":"left","o":true},{"p":"right","o":true},{"p":"bottom","o":true},{"p":"top","o":true},{"p":"near","o":true},{"p":"far","o":true}],"preview":"ortho([left], [right], [bottom], [top], [near], [far])","p5DocPath":"ortho"},{"label":"frustum","type":"method","kindLabel":"fun","params":[{"p":"left","o":true},{"p":"right","o":true},{"p":"bottom","o":true},{"p":"top","o":true},{"p":"near","o":true},{"p":"far","o":true}],"preview":"frustum([left], [right], [bottom], [top], [near], [far])","p5DocPath":"frustum"},{"label":"createCamera","type":"method","kindLabel":"fun","preview":"createCamera()","p5DocPath":"createCamera"},{"label":"setCamera","type":"method","kindLabel":"fun","params":[{"p":"cam","o":false}],"preview":"setCamera(cam)","p5DocPath":"setCamera"},{"label":"setAttributes","type":"method","kindLabel":"fun","preview":"setAttributes()","p5DocPath":"setAttributes"},{"label":"getAudioContext","type":"method","kindLabel":"fun","preview":"getAudioContext()","p5DocPath":"getAudioContext"},{"label":"userStartAudio","type":"method","kindLabel":"fun","params":[{"p":"elements","o":true},{"p":"callback","o":true}],"preview":"userStartAudio([elements], [callback])","p5DocPath":"userStartAudio"},{"label":"getOutputVolume","type":"method","kindLabel":"fun","preview":"getOutputVolume()","p5DocPath":"getOutputVolume"},{"label":"outputVolume","type":"method","kindLabel":"fun","params":[{"p":"volume","o":false},{"p":"rampTime","o":true},{"p":"timeFromNow","o":true}],"preview":"outputVolume(volume, [rampTime], [timeFromNow])","p5DocPath":"outputVolume"},{"label":"soundOut","type":"variable","kindLabel":"var","params":[],"preview":"soundOut","p5DocPath":"soundOut"},{"label":"sampleRate","type":"method","kindLabel":"fun","preview":"sampleRate()","p5DocPath":"sampleRate"},{"label":"freqToMidi","type":"method","kindLabel":"fun","params":[{"p":"frequency","o":false}],"preview":"freqToMidi(frequency)","p5DocPath":"freqToMidi"},{"label":"midiToFreq","type":"method","kindLabel":"fun","params":[{"p":"midiNote","o":false}],"preview":"midiToFreq(midiNote)","p5DocPath":"midiToFreq"},{"label":"soundFormats","type":"method","kindLabel":"fun","params":[{"p":"formats","o":true}],"preview":"soundFormats([formats])","p5DocPath":"soundFormats"},{"label":"saveSound","type":"method","kindLabel":"fun","params":[{"p":"soundFile","o":false},{"p":"fileName","o":false}],"preview":"saveSound(soundFile, fileName)","p5DocPath":"saveSound"},{"label":"loadSound","type":"method","kindLabel":"fun","params":[{"p":"path","o":false},{"p":"successCallback","o":true},{"p":"errorCallback","o":true},{"p":"whileLoading","o":true}],"preview":"loadSound(path, [successCallback], [errorCallback], [whileLoading])","p5DocPath":"loadSound"},{"label":"createConvolver","type":"method","kindLabel":"fun","params":[{"p":"path","o":false},{"p":"callback","o":true},{"p":"errorCallback","o":true}],"preview":"createConvolver(path, [callback], [errorCallback])","p5DocPath":"createConvolver"},{"label":"setBPM","type":"method","kindLabel":"fun","params":[{"p":"BPM","o":false},{"p":"rampTime","o":false}],"preview":"setBPM(BPM, rampTime)","p5DocPath":"setBPM"},{"label":"true","type":"boolean","kindLabel":"bool","params":[],"preview":"true","p5DocPath":"boolean"},{"label":"false","type":"boolean","kindLabel":"bool","params":[],"preview":"false","p5DocPath":"boolean"},{"label":"await","type":"keyword","kindLabel":"keyword","params":[],"preview":"await"},{"label":"class","type":"keyword","kindLabel":"keyword","params":[],"preview":"class","p5DocPath":"class"},{"label":"const","type":"keyword","kindLabel":"keyword","params":[],"preview":"const","p5DocPath":"const"},{"label":"else","type":"keyword","kindLabel":"keyword","params":[],"preview":"else","p5DocPath":"if-else"},{"label":"export","type":"keyword","kindLabel":"keyword","params":[],"preview":"export"},{"label":"for","type":"keyword","kindLabel":"keyword","params":[],"preview":"for","p5DocPath":"for"},{"label":"function","type":"keyword","kindLabel":"keyword","params":[],"preview":"function","p5DocPath":"function"},{"label":"if","type":"keyword","kindLabel":"keyword","params":[],"preview":"if","p5DocPath":"if-else"},{"label":"return","type":"keyword","kindLabel":"keyword","params":[],"preview":"return","p5DocPath":"return"},{"label":"while","type":"keyword","kindLabel":"keyword","params":[],"preview":"while","p5DocPath":"while"},{"label":"with","type":"keyword","kindLabel":"keyword","params":[],"preview":"with"},{"label":"let","type":"keyword","kindLabel":"keyword","params":[],"preview":"let","p5DocPath":"let"},{"label":"Array","type":"obj","kindLabel":"obj","params":[],"preview":"Array"},{"label":"Boolean","type":"obj","kindLabel":"obj","params":[],"preview":"Boolean"},{"label":"Date","type":"obj","kindLabel":"obj","params":[],"preview":"Date"},{"label":"Error","type":"obj","kindLabel":"obj","params":[],"preview":"Error"},{"label":"Function","type":"obj","kindLabel":"obj","params":[],"preview":"Function"},{"label":"JSON","type":"obj","kindLabel":"obj","params":[],"preview":"JSON","p5DocPath":"JSON"},{"label":"Math","type":"obj","kindLabel":"obj","params":[],"preview":"Math"},{"label":"Number","type":"obj","kindLabel":"obj","params":[],"preview":"Number"},{"label":"Object","type":"obj","kindLabel":"obj","params":[],"preview":"Object"},{"label":"RegExp","type":"obj","kindLabel":"obj","params":[],"preview":"RegExp"},{"label":"String","type":"obj","kindLabel":"obj","params":[],"preview":"String"},{"label":"Promise","type":"obj","kindLabel":"obj","params":[],"preview":"Promise"},{"label":"Set","type":"obj","kindLabel":"obj","params":[],"preview":"Set"},{"label":"Map","type":"obj","kindLabel":"obj","params":[],"preview":"Map"},{"label":"Symbol","type":"obj","kindLabel":"obj","params":[],"preview":"Symbol"},{"label":"WeakMap","type":"obj","kindLabel":"obj","params":[],"preview":"WeakMap"},{"label":"WeakSet","type":"obj","kindLabel":"obj","params":[],"preview":"WeakSet"},{"label":"ArrayBuffer","type":"obj","kindLabel":"obj","params":[],"preview":"ArrayBuffer"},{"label":"DataView","type":"obj","kindLabel":"obj","params":[],"preview":"DataView"},{"label":"Int32Array","type":"obj","kindLabel":"obj","params":[],"preview":"Int32Array"},{"label":"Uint32Array","type":"obj","kindLabel":"obj","params":[],"preview":"Uint32Array"},{"label":"Float32Array","type":"obj","kindLabel":"obj","params":[],"preview":"Float32Array"},{"label":"window","type":"obj","kindLabel":"obj","params":[],"preview":"window"},{"label":"document","type":"obj","kindLabel":"obj","params":[],"preview":"document"},{"label":"navigator","type":"obj","kindLabel":"obj","params":[],"preview":"navigator"},{"label":"console","type":"obj","kindLabel":"obj","params":[],"preview":"console","p5DocPath":"console"},{"label":"localStorage","type":"obj","kindLabel":"obj","params":[],"preview":"localStorage"},{"label":"sessionStorage","type":"obj","kindLabel":"obj","params":[],"preview":"sessionStorage"},{"label":"history","type":"obj","kindLabel":"obj","params":[],"preview":"history"},{"label":"location","type":"obj","kindLabel":"obj","params":[],"preview":"location"}]; diff --git a/client/utils/p5CodeAstAnalyzer.js b/client/utils/p5CodeAstAnalyzer.js index 5aaa5ec7be..3307af8c5a 100644 --- a/client/utils/p5CodeAstAnalyzer.js +++ b/client/utils/p5CodeAstAnalyzer.js @@ -24,8 +24,7 @@ let lastValidResult = { userDefinedClassMetadata: {} }; -function _p5CodeAstAnalyzer(_cm) { - const code = _cm.getValue(); +function _p5CodeAstAnalyzer(code) { let ast; try { @@ -153,8 +152,7 @@ function _p5CodeAstAnalyzer(_cm) { expr.left.object.type === 'ThisExpression' && expr.left.property.type === 'Identifier' ) { - const propName = expr.left.property.name; - classInfo.fields.add(propName); + classInfo.fields.add(expr.left.property.name); } }, @@ -165,8 +163,7 @@ function _p5CodeAstAnalyzer(_cm) { callee.object.type === 'ThisExpression' && callee.property.type === 'Identifier' ) { - const methodName = callee.property.name; - classInfo.fields.add(methodName); + classInfo.fields.add(callee.property.name); } } }, diff --git a/package-lock.json b/package-lock.json index a0757fe864..f199bc3e2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,8 +15,23 @@ "@babel/parser": "^7.27.5", "@babel/register": "^7.14.5", "@babel/traverse": "^7.27.4", + "@codemirror/autocomplete": "^6.18.6", + "@codemirror/commands": "^6.8.1", + "@codemirror/lang-css": "^6.3.1", + "@codemirror/lang-html": "^6.4.9", + "@codemirror/lang-javascript": "^6.2.3", + "@codemirror/lang-json": "^6.0.1", + "@codemirror/lang-xml": "^6.1.0", + "@codemirror/language": "^6.11.0", + "@codemirror/lint": "^6.8.5", + "@codemirror/search": "^6.5.11", + "@codemirror/state": "^6.5.2", + "@codemirror/view": "^6.40.0", + "@connieye/codemirror-color-picker": "^1.0.3", "@emmetio/codemirror-plugin": "^1.2.4", + "@emmetio/codemirror6-plugin": "^0.4.0", "@gatsbyjs/webpack-hot-middleware": "^2.25.3", + "@lezer/highlight": "^1.2.3", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", "@redux-devtools/core": "^3.11.0", "@redux-devtools/dock-monitor": "^3.0.1", @@ -34,8 +49,7 @@ "bson-objectid": "^2.0.3", "classnames": "^2.3.1", "clipboard": "^1.7.1", - "codemirror": "^5.62.0", - "codemirror-colorpicker": "^1.9.72", + "codemirror": "^6.0.1", "connect-mongo": "^5.1.0", "console-feed": "^3.2.0", "cookie-parser": "^1.4.5", @@ -49,6 +63,7 @@ "dropzone": "^4.3.0", "escape-string-regexp": "^1.0.5", "escodegen": "^2.1.0", + "eslint-linter-browserify": "^10.0.3", "eslint-scope": "^8.4.0", "eslint-webpack-plugin": "^3.1.1", "express": "^4.22.1", @@ -58,7 +73,7 @@ "friendly-words": "^1.2.1", "fuse.js": "^6.6.2", "history": "^4.10.1", - "htmlhint": "^0.15.1", + "htmlhint": "^0.15.2", "i18next": "^19.9.2", "i18next-http-backend": "^1.2.6", "is-url": "^1.2.4", @@ -171,6 +186,7 @@ "@types/nodemailer": "^7.0.1", "@types/nodemailer-mailgun-transport": "^1.4.6", "@types/passport": "^1.0.17", + "@types/prettier": "^2.7.3", "@types/react": "^16.14.0", "@types/react-dom": "^16.9.25", "@types/react-helmet": "^6.1.11", @@ -5967,6 +5983,172 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@codemirror/autocomplete": { + "version": "6.20.3", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.3.tgz", + "integrity": "sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.3.tgz", + "integrity": "sha512-JFRiqhKu+bvSkDLI+rUhJwSxQxYb759W5GBezE8Uc8mHLqC9aV/9aTC7yJSqCtB3F00pylrLCwnyS91Ap5ej4Q==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.6.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-css": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz", + "integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.2", + "@lezer/css": "^1.1.7" + } + }, + "node_modules/@codemirror/lang-html": { + "version": "6.4.11", + "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.11.tgz", + "integrity": "sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/lang-css": "^6.0.0", + "@codemirror/lang-javascript": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/css": "^1.1.0", + "@lezer/html": "^1.3.12" + } + }, + "node_modules/@codemirror/lang-javascript": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.5.tgz", + "integrity": "sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-json": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz", + "integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/json": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-xml": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.1.0.tgz", + "integrity": "sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/xml": "^1.0.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.3.tgz", + "integrity": "sha512-QwCZW6Tt1siP37Jet9Tb02Zs81TQt6qQrZR2H+eGMcFsL1zMrk2/b9CLC7/9ieP1fjIUMgviLWMmgiHoJrj+ZA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.5.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.6.tgz", + "integrity": "sha512-6Kp7r6XfCi/D/5sdXieMfg9pJU1bUEx96WITuLU6ESaKizCz0QHFMjY/TaFSbigDdEAIgi93itLBIUETP4oK+A==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.42.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.7.0.tgz", + "integrity": "sha512-ZvGm99wc/s2cITtMT15LFdn8aH/aS+V+DqyGq/N5ZlV5vWtH+nILvC2nw0zX7ByNoHHDZ2IxxdW38O0tc5nVHg==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.37.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.6.0.tgz", + "integrity": "sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==", + "license": "MIT", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.43.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.0.tgz", + "integrity": "sha512-V7ZCLQO3Jus9hzh2jVCCPW3mO4IBMr43O37PqSUYautJSnnJF41YlgLw21x0fLJTYvJ+Vkm6Gp+qKGH9pltgXA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.6.0", + "crelt": "^1.0.6", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "node_modules/@connieye/codemirror-color-picker": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@connieye/codemirror-color-picker/-/codemirror-color-picker-1.0.3.tgz", + "integrity": "sha512-e22W5t1kT8l0MCvYayax51QFdhEr/6P+TMb6+gEpDuEeNpIoxK9NE1aLrFUCgN8fhsefv1qo8bfr4BbL/od2Pw==", + "license": "MIT", + "dependencies": { + "colors-named": "^1.0.0", + "colors-named-hex": "^1.0.0", + "hsl-matcher": "^1.2.3" + }, + "peerDependencies": { + "@codemirror/language": ">=6.0.0", + "@codemirror/state": ">=6.0.0", + "@codemirror/view": ">=6.40.0" + } + }, "node_modules/@csstools/convert-colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz", @@ -5985,11 +6167,62 @@ "node": ">=10.0.0" } }, + "node_modules/@emmetio/abbreviation": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@emmetio/abbreviation/-/abbreviation-2.3.3.tgz", + "integrity": "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==", + "license": "MIT", + "dependencies": { + "@emmetio/scanner": "^1.0.4" + } + }, "node_modules/@emmetio/codemirror-plugin": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@emmetio/codemirror-plugin/-/codemirror-plugin-1.2.4.tgz", "integrity": "sha512-wVw2gqI6X+uVWYVRtTVymzTgbo4hEZIcPCNj4xrXw4l/+L3Qa+tAC/yf+Xy9nenRPCqRq0RLqGiQL+Qf/wYE9Q==" }, + "node_modules/@emmetio/codemirror6-plugin": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emmetio/codemirror6-plugin/-/codemirror6-plugin-0.4.0.tgz", + "integrity": "sha512-ZP3W8JvN0cEFTdsrcKPIBg/K9sadE15g//TfubPXfM28ZxUp3SwNASbRfRLRQmPyP73+pAZzLqeOwACUUz/oAw==", + "dependencies": { + "@emmetio/math-expression": "^1.0.5", + "emmet": "^2.4.11" + }, + "peerDependencies": { + "@codemirror/autocomplete": "^6.17.0", + "@codemirror/commands": "^6.6.0", + "@codemirror/lang-css": "^6.2.1", + "@codemirror/lang-html": "^6.4.9", + "@codemirror/language": "^6.10.2", + "@codemirror/state": "^6.4.1", + "@codemirror/view": "^6.29.1" + } + }, + "node_modules/@emmetio/css-abbreviation": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@emmetio/css-abbreviation/-/css-abbreviation-2.1.8.tgz", + "integrity": "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==", + "license": "MIT", + "dependencies": { + "@emmetio/scanner": "^1.0.4" + } + }, + "node_modules/@emmetio/math-expression": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@emmetio/math-expression/-/math-expression-1.0.5.tgz", + "integrity": "sha512-qf5SXD/ViS04rXSeDg9CRGM10xLC9dVaKIbMHrrwxYr5LNB/C0rOfokhGSBwnVQKcidLmdRJeNWH1V1tppZ84Q==", + "license": "MIT", + "dependencies": { + "@emmetio/scanner": "^1.0.4" + } + }, + "node_modules/@emmetio/scanner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emmetio/scanner/-/scanner-1.0.4.tgz", + "integrity": "sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==", + "license": "MIT" + }, "node_modules/@emotion/cache": { "version": "10.0.29", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz", @@ -7763,6 +7996,91 @@ "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==", "dev": true }, + "node_modules/@lezer/common": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz", + "integrity": "sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==", + "license": "MIT" + }, + "node_modules/@lezer/css": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.3.tgz", + "integrity": "sha512-RzBo8r+/6QJeow7aPHIpGVIH59xTcJXp399820gZoMo9noQDRVpJLheIBUicYwKcsbOYoBRoLZlf2720dG/4Tg==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/highlight": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", + "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.3.0" + } + }, + "node_modules/@lezer/html": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.13.tgz", + "integrity": "sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/javascript": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz", + "integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/json": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz", + "integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.10.tgz", + "integrity": "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/xml": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.6.tgz", + "integrity": "sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "license": "MIT" + }, "node_modules/@mdx-js/react": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-2.3.0.tgz", @@ -13718,6 +14036,13 @@ "@types/express": "*" } }, + "node_modules/@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@types/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", @@ -16760,16 +17085,18 @@ } }, "node_modules/codemirror": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.62.0.tgz", - "integrity": "sha512-Xnl3304iCc8nyVZuRkzDVVwc794uc9QNX0UcPGeNic1fbzkSrO4l4GVXho9tRNKBgPYZXgocUqXyfIv3BILhCQ==" - }, - "node_modules/codemirror-colorpicker": { - "version": "1.9.80", - "resolved": "https://registry.npmjs.org/codemirror-colorpicker/-/codemirror-colorpicker-1.9.80.tgz", - "integrity": "sha512-7lGqNxf5haBJXLnVR1ynPiPkN2d1Whm0jdy8Z9QsSOhRWVyK2C2ihgm1dX4DCks57ht/jKMdpL9lYv+zAphxWQ==", - "peerDependencies": { - "codemirror": "^5.48.0" + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", + "integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" } }, "node_modules/collect-v8-coverage": { @@ -16821,6 +17148,30 @@ "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", "dev": true }, + "node_modules/colors-named": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/colors-named/-/colors-named-1.0.5.tgz", + "integrity": "sha512-xaspf9oddAOqP2LYNOgp8E3BwAzugrdO9J1kDNS5ySrzTgV9hrXGBt5w87ioLEr2pM4Ukt+GKedvzaLRxpv8pA==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, + "node_modules/colors-named-hex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/colors-named-hex/-/colors-named-hex-1.0.4.tgz", + "integrity": "sha512-X+Enw/2fFAgDRhUac69cRO/RJvHnWDBBrP8J1sJuEU16Buiiu8PPpJP4abSo0V+fJbkfwmQITE6zKx/SBJERGw==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -17327,6 +17678,12 @@ "node": ">=8" } }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "license": "MIT" + }, "node_modules/cross-env": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.1.tgz", @@ -19302,6 +19659,22 @@ "url": "https://github.com/sindresorhus/emittery?sponsor=1" } }, + "node_modules/emmet": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/emmet/-/emmet-2.4.11.tgz", + "integrity": "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==", + "license": "MIT", + "workspaces": [ + "./packages/scanner", + "./packages/abbreviation", + "./packages/css-abbreviation", + "./" + ], + "dependencies": { + "@emmetio/abbreviation": "^2.3.3", + "@emmetio/css-abbreviation": "^2.1.8" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -19863,6 +20236,12 @@ "ms": "^2.1.1" } }, + "node_modules/eslint-linter-browserify": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/eslint-linter-browserify/-/eslint-linter-browserify-10.4.1.tgz", + "integrity": "sha512-mIs4xbUDjkijfVNs3F973k7HPJ0MhQZ77PotlaCX9xUbI/jwgv5sAh+O2mgKr6j3CM2aaGlbdvRFOyycN5VZxg==", + "license": "MIT" + }, "node_modules/eslint-module-utils": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", @@ -21964,6 +22343,18 @@ "react-is": "^16.7.0" } }, + "node_modules/hsl-matcher": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/hsl-matcher/-/hsl-matcher-1.2.4.tgz", + "integrity": "sha512-tS7XnJS33Egirm+6cI+Z/kH/aVZt94uxGlJxOZlGql2/yqbAzPg3zHHnTnVN4cVpoJnEYEGq+LE3iXbuUIe8BA==", + "license": "MIT", + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, "node_modules/html-encoding-sniffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", @@ -34521,6 +34912,12 @@ "webpack": "^5.0.0" } }, + "node_modules/style-mod": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", + "license": "MIT" + }, "node_modules/style-to-object": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz", @@ -35614,6 +36011,12 @@ "browser-process-hrtime": "^1.0.0" } }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -40928,6 +41331,154 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@codemirror/autocomplete": { + "version": "6.20.3", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.3.tgz", + "integrity": "sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==", + "requires": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "@codemirror/commands": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.3.tgz", + "integrity": "sha512-JFRiqhKu+bvSkDLI+rUhJwSxQxYb759W5GBezE8Uc8mHLqC9aV/9aTC7yJSqCtB3F00pylrLCwnyS91Ap5ej4Q==", + "requires": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.6.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "@codemirror/lang-css": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz", + "integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==", + "requires": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.2", + "@lezer/css": "^1.1.7" + } + }, + "@codemirror/lang-html": { + "version": "6.4.11", + "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.11.tgz", + "integrity": "sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==", + "requires": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/lang-css": "^6.0.0", + "@codemirror/lang-javascript": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/css": "^1.1.0", + "@lezer/html": "^1.3.12" + } + }, + "@codemirror/lang-javascript": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.5.tgz", + "integrity": "sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==", + "requires": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" + } + }, + "@codemirror/lang-json": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz", + "integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==", + "requires": { + "@codemirror/language": "^6.0.0", + "@lezer/json": "^1.0.0" + } + }, + "@codemirror/lang-xml": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.1.0.tgz", + "integrity": "sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==", + "requires": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/xml": "^1.0.0" + } + }, + "@codemirror/language": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.3.tgz", + "integrity": "sha512-QwCZW6Tt1siP37Jet9Tb02Zs81TQt6qQrZR2H+eGMcFsL1zMrk2/b9CLC7/9ieP1fjIUMgviLWMmgiHoJrj+ZA==", + "requires": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.5.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "@codemirror/lint": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.6.tgz", + "integrity": "sha512-6Kp7r6XfCi/D/5sdXieMfg9pJU1bUEx96WITuLU6ESaKizCz0QHFMjY/TaFSbigDdEAIgi93itLBIUETP4oK+A==", + "requires": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.42.0", + "crelt": "^1.0.5" + } + }, + "@codemirror/search": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.7.0.tgz", + "integrity": "sha512-ZvGm99wc/s2cITtMT15LFdn8aH/aS+V+DqyGq/N5ZlV5vWtH+nILvC2nw0zX7ByNoHHDZ2IxxdW38O0tc5nVHg==", + "requires": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.37.0", + "crelt": "^1.0.5" + } + }, + "@codemirror/state": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.6.0.tgz", + "integrity": "sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==", + "requires": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "@codemirror/view": { + "version": "6.43.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.0.tgz", + "integrity": "sha512-V7ZCLQO3Jus9hzh2jVCCPW3mO4IBMr43O37PqSUYautJSnnJF41YlgLw21x0fLJTYvJ+Vkm6Gp+qKGH9pltgXA==", + "requires": { + "@codemirror/state": "^6.6.0", + "crelt": "^1.0.6", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "@connieye/codemirror-color-picker": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@connieye/codemirror-color-picker/-/codemirror-color-picker-1.0.3.tgz", + "integrity": "sha512-e22W5t1kT8l0MCvYayax51QFdhEr/6P+TMb6+gEpDuEeNpIoxK9NE1aLrFUCgN8fhsefv1qo8bfr4BbL/od2Pw==", + "requires": { + "colors-named": "^1.0.0", + "colors-named-hex": "^1.0.0", + "hsl-matcher": "^1.2.3" + } + }, "@csstools/convert-colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz", @@ -40940,11 +41491,49 @@ "integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==", "dev": true }, + "@emmetio/abbreviation": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@emmetio/abbreviation/-/abbreviation-2.3.3.tgz", + "integrity": "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==", + "requires": { + "@emmetio/scanner": "^1.0.4" + } + }, "@emmetio/codemirror-plugin": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@emmetio/codemirror-plugin/-/codemirror-plugin-1.2.4.tgz", "integrity": "sha512-wVw2gqI6X+uVWYVRtTVymzTgbo4hEZIcPCNj4xrXw4l/+L3Qa+tAC/yf+Xy9nenRPCqRq0RLqGiQL+Qf/wYE9Q==" }, + "@emmetio/codemirror6-plugin": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emmetio/codemirror6-plugin/-/codemirror6-plugin-0.4.0.tgz", + "integrity": "sha512-ZP3W8JvN0cEFTdsrcKPIBg/K9sadE15g//TfubPXfM28ZxUp3SwNASbRfRLRQmPyP73+pAZzLqeOwACUUz/oAw==", + "requires": { + "@emmetio/math-expression": "^1.0.5", + "emmet": "^2.4.11" + } + }, + "@emmetio/css-abbreviation": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@emmetio/css-abbreviation/-/css-abbreviation-2.1.8.tgz", + "integrity": "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==", + "requires": { + "@emmetio/scanner": "^1.0.4" + } + }, + "@emmetio/math-expression": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@emmetio/math-expression/-/math-expression-1.0.5.tgz", + "integrity": "sha512-qf5SXD/ViS04rXSeDg9CRGM10xLC9dVaKIbMHrrwxYr5LNB/C0rOfokhGSBwnVQKcidLmdRJeNWH1V1tppZ84Q==", + "requires": { + "@emmetio/scanner": "^1.0.4" + } + }, + "@emmetio/scanner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emmetio/scanner/-/scanner-1.0.4.tgz", + "integrity": "sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==" + }, "@emotion/cache": { "version": "10.0.29", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz", @@ -42213,6 +42802,82 @@ "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==", "dev": true }, + "@lezer/common": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz", + "integrity": "sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==" + }, + "@lezer/css": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.3.tgz", + "integrity": "sha512-RzBo8r+/6QJeow7aPHIpGVIH59xTcJXp399820gZoMo9noQDRVpJLheIBUicYwKcsbOYoBRoLZlf2720dG/4Tg==", + "requires": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.0" + } + }, + "@lezer/highlight": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", + "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", + "requires": { + "@lezer/common": "^1.3.0" + } + }, + "@lezer/html": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.13.tgz", + "integrity": "sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==", + "requires": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "@lezer/javascript": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz", + "integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==", + "requires": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "@lezer/json": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz", + "integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==", + "requires": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "@lezer/lr": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.10.tgz", + "integrity": "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==", + "requires": { + "@lezer/common": "^1.0.0" + } + }, + "@lezer/xml": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.6.tgz", + "integrity": "sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==", + "requires": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==" + }, "@mdx-js/react": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-2.3.0.tgz", @@ -46503,6 +47168,12 @@ "@types/express": "*" } }, + "@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "dev": true + }, "@types/pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@types/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", @@ -48779,15 +49450,18 @@ "dev": true }, "codemirror": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.62.0.tgz", - "integrity": "sha512-Xnl3304iCc8nyVZuRkzDVVwc794uc9QNX0UcPGeNic1fbzkSrO4l4GVXho9tRNKBgPYZXgocUqXyfIv3BILhCQ==" - }, - "codemirror-colorpicker": { - "version": "1.9.80", - "resolved": "https://registry.npmjs.org/codemirror-colorpicker/-/codemirror-colorpicker-1.9.80.tgz", - "integrity": "sha512-7lGqNxf5haBJXLnVR1ynPiPkN2d1Whm0jdy8Z9QsSOhRWVyK2C2ihgm1dX4DCks57ht/jKMdpL9lYv+zAphxWQ==", - "requires": {} + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", + "integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==", + "requires": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } }, "collect-v8-coverage": { "version": "1.0.2", @@ -48838,6 +49512,16 @@ "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", "dev": true }, + "colors-named": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/colors-named/-/colors-named-1.0.5.tgz", + "integrity": "sha512-xaspf9oddAOqP2LYNOgp8E3BwAzugrdO9J1kDNS5ySrzTgV9hrXGBt5w87ioLEr2pM4Ukt+GKedvzaLRxpv8pA==" + }, + "colors-named-hex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/colors-named-hex/-/colors-named-hex-1.0.4.tgz", + "integrity": "sha512-X+Enw/2fFAgDRhUac69cRO/RJvHnWDBBrP8J1sJuEU16Buiiu8PPpJP4abSo0V+fJbkfwmQITE6zKx/SBJERGw==" + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -49204,6 +49888,11 @@ } } }, + "crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" + }, "cross-env": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.1.tgz", @@ -50595,6 +51284,15 @@ "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true }, + "emmet": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/emmet/-/emmet-2.4.11.tgz", + "integrity": "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==", + "requires": { + "@emmetio/abbreviation": "^2.3.3", + "@emmetio/css-abbreviation": "^2.1.8" + } + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -51216,6 +51914,11 @@ } } }, + "eslint-linter-browserify": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/eslint-linter-browserify/-/eslint-linter-browserify-10.4.1.tgz", + "integrity": "sha512-mIs4xbUDjkijfVNs3F973k7HPJ0MhQZ77PotlaCX9xUbI/jwgv5sAh+O2mgKr6j3CM2aaGlbdvRFOyycN5VZxg==" + }, "eslint-module-utils": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", @@ -52531,6 +53234,11 @@ "react-is": "^16.7.0" } }, + "hsl-matcher": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/hsl-matcher/-/hsl-matcher-1.2.4.tgz", + "integrity": "sha512-tS7XnJS33Egirm+6cI+Z/kH/aVZt94uxGlJxOZlGql2/yqbAzPg3zHHnTnVN4cVpoJnEYEGq+LE3iXbuUIe8BA==" + }, "html-encoding-sniffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", @@ -61686,6 +62394,11 @@ "dev": true, "requires": {} }, + "style-mod": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==" + }, "style-to-object": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz", @@ -62483,6 +63196,11 @@ "browser-process-hrtime": "^1.0.0" } }, + "w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" + }, "walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", diff --git a/package.json b/package.json index 2a8c3ca370..7034823448 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,12 @@ "displayName": "client", "testEnvironment": "jsdom", "transform": { - "^.+\\.[jt]sx?$": "babel-jest" + "\\.[jt]sx?$": [ + "babel-jest", + { + "configFile": "./.babelrc" + } + ] }, "moduleFileExtensions": [ "ts", @@ -83,6 +88,9 @@ "^.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)(|\\?byContent|\\?byUrl)$": "/client/__mocks__/fileMock.js", "\\.(css|less|scss)$": "/client/__mocks__/styleMock.js" }, + "transformIgnorePatterns": [ + "/node_modules/(?!colors-named|colors-named-hex|hsl-matcher|@emmetio/)" + ], "testMatch": [ "/client/**/*.test.(js|jsx|ts|tsx)" ] @@ -145,6 +153,7 @@ "@types/nodemailer": "^7.0.1", "@types/nodemailer-mailgun-transport": "^1.4.6", "@types/passport": "^1.0.17", + "@types/prettier": "^2.7.3", "@types/react": "^16.14.0", "@types/react-dom": "^16.9.25", "@types/react-helmet": "^6.1.11", @@ -210,8 +219,23 @@ "@babel/parser": "^7.27.5", "@babel/register": "^7.14.5", "@babel/traverse": "^7.27.4", + "@codemirror/autocomplete": "^6.18.6", + "@codemirror/commands": "^6.8.1", + "@codemirror/lang-css": "^6.3.1", + "@codemirror/lang-html": "^6.4.9", + "@codemirror/lang-javascript": "^6.2.3", + "@codemirror/lang-json": "^6.0.1", + "@codemirror/lang-xml": "^6.1.0", + "@codemirror/language": "^6.11.0", + "@codemirror/lint": "^6.8.5", + "@codemirror/search": "^6.5.11", + "@codemirror/state": "^6.5.2", + "@codemirror/view": "^6.40.0", + "@connieye/codemirror-color-picker": "^1.0.3", "@emmetio/codemirror-plugin": "^1.2.4", + "@emmetio/codemirror6-plugin": "^0.4.0", "@gatsbyjs/webpack-hot-middleware": "^2.25.3", + "@lezer/highlight": "^1.2.3", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", "@redux-devtools/core": "^3.11.0", "@redux-devtools/dock-monitor": "^3.0.1", @@ -229,8 +253,7 @@ "bson-objectid": "^2.0.3", "classnames": "^2.3.1", "clipboard": "^1.7.1", - "codemirror": "^5.62.0", - "codemirror-colorpicker": "^1.9.72", + "codemirror": "^6.0.1", "connect-mongo": "^5.1.0", "console-feed": "^3.2.0", "cookie-parser": "^1.4.5", @@ -243,6 +266,7 @@ "dotenv": "^2.0.0", "dropzone": "^4.3.0", "escape-string-regexp": "^1.0.5", + "eslint-linter-browserify": "^10.0.3", "escodegen": "^2.1.0", "eslint-scope": "^8.4.0", "eslint-webpack-plugin": "^3.1.1", @@ -253,7 +277,7 @@ "friendly-words": "^1.2.1", "fuse.js": "^6.6.2", "history": "^4.10.1", - "htmlhint": "^0.15.1", + "htmlhint": "^0.15.2", "i18next": "^19.9.2", "i18next-http-backend": "^1.2.6", "is-url": "^1.2.4", diff --git a/server/scripts/update-p5-hinter.js b/server/scripts/update-p5-hinter.js index a6a667eac4..d438c28ca0 100644 --- a/server/scripts/update-p5-hinter.js +++ b/server/scripts/update-p5-hinter.js @@ -2,81 +2,90 @@ const fs = require('fs'); const process = require('process'); const axios = require('axios'); -// const getDescription = (d) => { -// return d.split('\n')[0].replace('

    ', ''); -// }; - +// TODO: Currently this makes duplicate entries because +// the default Javascript hinter also has these, +// but should we keep them around for the p5 reference links? const reservedKeywords = [ - { name: 'await', p5DocPath: false }, - { name: 'break', p5DocPath: false }, - { name: 'case', p5DocPath: false }, - { name: 'catch', p5DocPath: false }, + { name: 'await', p5DocPath: undefined }, { name: 'class', p5DocPath: 'class' }, { name: 'const', p5DocPath: 'const' }, - { name: 'continue', p5DocPath: false }, - { name: 'debugger', p5DocPath: false }, - { name: 'default', p5DocPath: false }, - { name: 'delete', p5DocPath: false }, - { name: 'do', p5DocPath: false }, { name: 'else', p5DocPath: 'if-else' }, - { name: 'export', p5DocPath: false }, - { name: 'extends', p5DocPath: false }, - { name: 'finally', p5DocPath: false }, + { name: 'export', p5DocPath: undefined }, { name: 'for', p5DocPath: 'for' }, { name: 'function', p5DocPath: 'function' }, { name: 'if', p5DocPath: 'if-else' }, - { name: 'import', p5DocPath: false }, - { name: 'in', p5DocPath: false }, - { name: 'instanceof', p5DocPath: false }, - { name: 'new', p5DocPath: false }, { name: 'return', p5DocPath: 'return' }, - { name: 'super', p5DocPath: false }, - { name: 'switch', p5DocPath: false }, - { name: 'this', p5DocPath: false }, - { name: 'throw', p5DocPath: false }, - { name: 'try', p5DocPath: false }, - { name: 'typeof', p5DocPath: false }, - { name: 'var', p5DocPath: false }, - { name: 'void', p5DocPath: false }, { name: 'while', p5DocPath: 'while' }, - { name: 'with', p5DocPath: false }, - { name: 'yield', p5DocPath: false }, + { name: 'with', p5DocPath: undefined }, { name: 'let', p5DocPath: 'let' } ]; const reservedObjects = [ - { name: 'Array', p5DocPath: false }, - { name: 'Boolean', p5DocPath: false }, - { name: 'Date', p5DocPath: false }, - { name: 'Error', p5DocPath: false }, - { name: 'Function', p5DocPath: false }, + { name: 'Array', p5DocPath: undefined }, + { name: 'Boolean', p5DocPath: undefined }, + { name: 'Date', p5DocPath: undefined }, + { name: 'Error', p5DocPath: undefined }, + { name: 'Function', p5DocPath: undefined }, { name: 'JSON', p5DocPath: 'JSON' }, - { name: 'Math', p5DocPath: false }, - { name: 'Number', p5DocPath: false }, - { name: 'Object', p5DocPath: false }, - { name: 'RegExp', p5DocPath: false }, - { name: 'String', p5DocPath: false }, - { name: 'Promise', p5DocPath: false }, - { name: 'Set', p5DocPath: false }, - { name: 'Map', p5DocPath: false }, - { name: 'Symbol', p5DocPath: false }, - { name: 'WeakMap', p5DocPath: false }, - { name: 'WeakSet', p5DocPath: false }, - { name: 'ArrayBuffer', p5DocPath: false }, - { name: 'DataView', p5DocPath: false }, - { name: 'Int32Array', p5DocPath: false }, - { name: 'Uint32Array', p5DocPath: false }, - { name: 'Float32Array', p5DocPath: false }, - { name: 'window', p5DocPath: false }, - { name: 'document', p5DocPath: false }, - { name: 'navigator', p5DocPath: false }, + { name: 'Math', p5DocPath: undefined }, + { name: 'Number', p5DocPath: undefined }, + { name: 'Object', p5DocPath: undefined }, + { name: 'RegExp', p5DocPath: undefined }, + { name: 'String', p5DocPath: undefined }, + { name: 'Promise', p5DocPath: undefined }, + { name: 'Set', p5DocPath: undefined }, + { name: 'Map', p5DocPath: undefined }, + { name: 'Symbol', p5DocPath: undefined }, + { name: 'WeakMap', p5DocPath: undefined }, + { name: 'WeakSet', p5DocPath: undefined }, + { name: 'ArrayBuffer', p5DocPath: undefined }, + { name: 'DataView', p5DocPath: undefined }, + { name: 'Int32Array', p5DocPath: undefined }, + { name: 'Uint32Array', p5DocPath: undefined }, + { name: 'Float32Array', p5DocPath: undefined }, + { name: 'window', p5DocPath: undefined }, + { name: 'document', p5DocPath: undefined }, + { name: 'navigator', p5DocPath: undefined }, { name: 'console', p5DocPath: 'console' }, - { name: 'localStorage', p5DocPath: false }, - { name: 'sessionStorage', p5DocPath: false }, - { name: 'history', p5DocPath: false }, - { name: 'location', p5DocPath: false } + { name: 'localStorage', p5DocPath: undefined }, + { name: 'sessionStorage', p5DocPath: undefined }, + { name: 'history', p5DocPath: undefined }, + { name: 'location', p5DocPath: undefined } ]; +function getKindLabel(type) { + switch (type) { + case 'method': + return 'fun'; + case 'variable': + return 'var'; + case 'constant': + return 'const'; + case 'keyword': + return 'kw'; + case 'boolean': + return 'bool'; + case 'obj': + return 'obj'; + default: + return type; + } +} + +// create ghost text preview for methods +function makePreview(label, type, params = []) { + const formattedParams = params + .map((param) => (param.o ? `[${param.p}]` : param.p)) + .join(', '); + + if (type === 'method') { + return `${label}(${formattedParams})`; + } + + return label; +} + +// TODO: add back in reference version switching depending user's p5.js version axios .get('https://p5js.org/reference/data.json') .then((response) => { @@ -92,49 +101,60 @@ axios obj.name && obj.itemtype ) { - let type; + let itemType; let params = []; if (obj.itemtype === 'method') { - type = 'fun'; + itemType = 'method'; params = obj.params?.map((param) => ({ p: param.name, // param name o: param.optional ?? false // optional })); } else if (obj.itemtype === 'property') { - type = 'var'; - } else type = 'attr'; + itemType = obj.module === 'Constants' ? 'constant' : 'variable'; + } else itemType = 'attr'; p5Keywords.push({ - text: obj.name, - type, + label: obj.name, + type: itemType, + kindLabel: getKindLabel(itemType), params, - p5: true + preview: makePreview(obj.name, itemType, params), + p5DocPath: obj.name }); } }); ['true', 'false'].forEach((bol) => { p5Keywords.push({ - text: bol, + label: bol, type: 'boolean', - p5: 'boolean' + kindLabel: 'bool', + params: [], + preview: bol, + p5DocPath: 'boolean' }); }); reservedKeywords.forEach((keyword) => { p5Keywords.push({ - text: keyword.name, + label: keyword.name, type: 'keyword', - p5: keyword.p5DocPath + kindLabel: 'keyword', + params: [], + preview: keyword.name, + p5DocPath: keyword.p5DocPath }); }); reservedObjects.forEach((keyword) => { p5Keywords.push({ - text: keyword.name, + label: keyword.name, type: 'obj', - p5: keyword.p5DocPath + kindLabel: 'obj', + params: [], + preview: keyword.name, + p5DocPath: keyword.p5DocPath }); });