From 84b135bd014a782ea63aa027a5963a260341e548 Mon Sep 17 00:00:00 2001 From: Connie Ye Date: Fri, 24 Jan 2025 00:15:47 -0800 Subject: [PATCH 1/4] Split off hinter functionality into a separate file --- .../modules/IDE/components/Editor/hinter.js | 114 ++++++++++++++++++ .../modules/IDE/components/Editor/index.jsx | 112 +---------------- 2 files changed, 118 insertions(+), 108 deletions(-) create mode 100644 client/modules/IDE/components/Editor/hinter.js diff --git a/client/modules/IDE/components/Editor/hinter.js b/client/modules/IDE/components/Editor/hinter.js new file mode 100644 index 0000000000..21e2b80d35 --- /dev/null +++ b/client/modules/IDE/components/Editor/hinter.js @@ -0,0 +1,114 @@ +import Fuse from 'fuse.js'; +import CodeMirror from 'codemirror'; +import { JSHINT } from 'jshint'; +import { HTMLHint } from 'htmlhint'; +import 'codemirror/addon/hint/css-hint'; + +import * as hinterDefinition from '../../../../utils/p5-hinter'; +import '../show-hint'; // Remove for codemirror v6? + +// Are we using these????? +window.JSHINT = JSHINT; +window.HTMLHint = HTMLHint; + +const hinter = new Fuse(hinterDefinition.p5Hinter, { + threshold: 0.05, + keys: ['text'] +}); + +/** Hides the hinter. */ +export function hideHinter() { + CodeMirror.showHint(this._cm, () => {}, {}); +} + +/** Shows a hint in the codemirror instance. */ +export function showHint(cmInstance, autocompleteHinter, fontSize) { + if (!autocompleteHinter) { + CodeMirror.showHint(cmInstance, () => {}, {}); + return; + } + + let focusedLinkElement = null; + const setFocusedLinkElement = (set) => { + if (set && !focusedLinkElement) { + const activeItemLink = document.querySelector( + `.CodeMirror-hint-active a` + ); + if (activeItemLink) { + focusedLinkElement = activeItemLink; + focusedLinkElement.classList.add('focused-hint-link'); + focusedLinkElement.parentElement.parentElement.classList.add( + 'unfocused' + ); + } + } + }; + const removeFocusedLinkElement = () => { + if (focusedLinkElement) { + focusedLinkElement.classList.remove('focused-hint-link'); + focusedLinkElement.parentElement.parentElement.classList.remove( + 'unfocused' + ); + focusedLinkElement = null; + return true; + } + return false; + }; + + const hintOptions = { + _fontSize: fontSize, + completeSingle: false, + extraKeys: { + 'Shift-Right': (cm, e) => { + const activeItemLink = document.querySelector( + `.CodeMirror-hint-active a` + ); + if (activeItemLink) activeItemLink.click(); + }, + Right: (cm, e) => { + setFocusedLinkElement(true); + }, + Left: (cm, e) => { + removeFocusedLinkElement(); + }, + Up: (cm, e) => { + const onLink = removeFocusedLinkElement(); + e.moveFocus(-1); + setFocusedLinkElement(onLink); + }, + Down: (cm, e) => { + const onLink = removeFocusedLinkElement(); + e.moveFocus(1); + setFocusedLinkElement(onLink); + }, + Enter: (cm, e) => { + if (focusedLinkElement) focusedLinkElement.click(); + else e.pick(); + } + }, + closeOnUnfocus: false + }; + + if (cmInstance.options.mode === 'javascript') { + CodeMirror.showHint( + cmInstance, + () => { + const cursor = cmInstance.getCursor(); + const token = cmInstance.getTokenAt(cursor); + + const hints = hinter + .search(token.string) + .filter((h) => h.item.text[0] === token.string[0]); + + return { + list: hints, + from: CodeMirror.Pos(cursor.line, token.start), + to: CodeMirror.Pos(cursor.line, cursor.ch) + }; + }, + hintOptions + ); + } else if (cmInstance.options.mode === 'css') { + CodeMirror.showHint(cmInstance, CodeMirror.hint.css, hintOptions); + } +} diff --git a/client/modules/IDE/components/Editor/index.jsx b/client/modules/IDE/components/Editor/index.jsx index 8393116308..3e5bce5268 100644 --- a/client/modules/IDE/components/Editor/index.jsx +++ b/client/modules/IDE/components/Editor/index.jsx @@ -3,7 +3,6 @@ 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'; @@ -33,12 +32,9 @@ 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'; @@ -47,8 +43,6 @@ 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'; @@ -73,11 +67,11 @@ import { EditorContainer, EditorHolder } from './MobileEditor'; import { FolderIcon } from '../../../../common/icons'; import IconButton from '../../../../common/IconButton'; +import { showHint, hideHinter } from './hinter'; + emmet(CodeMirror); -window.JSHINT = JSHINT; window.CSSLint = CSSLint; -window.HTMLHint = HTMLHint; const INDENTATION_AMOUNT = 2; @@ -146,11 +140,6 @@ class Editor extends React.Component { } }); - this.hinter = new Fuse(hinter.p5Hinter, { - threshold: 0.05, - keys: ['text'] - }); - delete this._cm.options.lint.options.errors; const replaceCommand = @@ -207,7 +196,7 @@ class Editor extends React.Component { // Show hint const mode = this._cm.getOption('mode'); if (/^[a-z]$/i.test(e.key) && (mode === 'css' || mode === 'javascript')) { - this.showHint(_cm); + showHint(_cm, this.props.autocompleteHinter, this.props.fontSize); } if (e.key === 'Escape') { e.preventDefault(); @@ -283,7 +272,7 @@ class Editor extends React.Component { if (this.props.autocompleteHinter !== prevProps.autocompleteHinter) { if (!this.props.autocompleteHinter) { // close the hinter window once the preference is turned off - CodeMirror.showHint(this._cm, () => {}, {}); + hideHinter(); } } @@ -378,99 +367,6 @@ class Editor extends React.Component { this._cm.execCommand('findPersistent'); } - showHint(_cm) { - if (!this.props.autocompleteHinter) { - CodeMirror.showHint(_cm, () => {}, {}); - return; - } - - let focusedLinkElement = null; - const setFocusedLinkElement = (set) => { - if (set && !focusedLinkElement) { - const activeItemLink = document.querySelector( - `.CodeMirror-hint-active a` - ); - if (activeItemLink) { - focusedLinkElement = activeItemLink; - focusedLinkElement.classList.add('focused-hint-link'); - focusedLinkElement.parentElement.parentElement.classList.add( - 'unfocused' - ); - } - } - }; - const removeFocusedLinkElement = () => { - if (focusedLinkElement) { - focusedLinkElement.classList.remove('focused-hint-link'); - focusedLinkElement.parentElement.parentElement.classList.remove( - 'unfocused' - ); - focusedLinkElement = null; - return true; - } - return false; - }; - - const hintOptions = { - _fontSize: this.props.fontSize, - completeSingle: false, - extraKeys: { - 'Shift-Right': (cm, e) => { - const activeItemLink = document.querySelector( - `.CodeMirror-hint-active a` - ); - if (activeItemLink) activeItemLink.click(); - }, - Right: (cm, e) => { - setFocusedLinkElement(true); - }, - Left: (cm, e) => { - removeFocusedLinkElement(); - }, - Up: (cm, e) => { - const onLink = removeFocusedLinkElement(); - e.moveFocus(-1); - setFocusedLinkElement(onLink); - }, - Down: (cm, e) => { - const onLink = removeFocusedLinkElement(); - e.moveFocus(1); - setFocusedLinkElement(onLink); - }, - Enter: (cm, e) => { - if (focusedLinkElement) focusedLinkElement.click(); - else e.pick(); - } - }, - closeOnUnfocus: false - }; - - if (_cm.options.mode === 'javascript') { - // JavaScript - CodeMirror.showHint( - _cm, - () => { - const c = _cm.getCursor(); - const token = _cm.getTokenAt(c); - - const hints = this.hinter - .search(token.string) - .filter((h) => h.item.text[0] === token.string[0]); - - return { - list: hints, - from: CodeMirror.Pos(c.line, token.start), - to: CodeMirror.Pos(c.line, c.ch) - }; - }, - hintOptions - ); - } else if (_cm.options.mode === 'css') { - // CSS - CodeMirror.showHint(_cm, CodeMirror.hint.css, hintOptions); - } - } - showReplace() { this._cm.execCommand('replace'); } From 10da30e192f6b5cfd45fdd78a7f82a3917d77f65 Mon Sep 17 00:00:00 2001 From: Connie Ye Date: Fri, 24 Jan 2025 00:45:54 -0800 Subject: [PATCH 2/4] update docstring in hinter --- client/modules/IDE/components/Editor/hinter.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/modules/IDE/components/Editor/hinter.js b/client/modules/IDE/components/Editor/hinter.js index 21e2b80d35..43e23a9dcf 100644 --- a/client/modules/IDE/components/Editor/hinter.js +++ b/client/modules/IDE/components/Editor/hinter.js @@ -21,7 +21,10 @@ export function hideHinter() { CodeMirror.showHint(this._cm, () => {}, {}); } -/** Shows a hint in the codemirror instance. */ +/** + * Shows a hint popup in the codemirror instance. + * It will only be visible if the user has autocomplete on in the settings. + */ export function showHint(cmInstance, autocompleteHinter, fontSize) { if (!autocompleteHinter) { CodeMirror.showHint(cmInstance, () => {}, {}); From 44e5cfc68cc2c8aa019542105c9aac208cda3ce6 Mon Sep 17 00:00:00 2001 From: Connie Ye Date: Mon, 27 Jan 2025 21:49:08 -0800 Subject: [PATCH 3/4] move csslint to hint file --- client/modules/IDE/components/Editor/hinter.js | 7 ++++--- client/modules/IDE/components/Editor/index.jsx | 3 --- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/client/modules/IDE/components/Editor/hinter.js b/client/modules/IDE/components/Editor/hinter.js index 43e23a9dcf..00e4236c1d 100644 --- a/client/modules/IDE/components/Editor/hinter.js +++ b/client/modules/IDE/components/Editor/hinter.js @@ -2,13 +2,14 @@ import Fuse from 'fuse.js'; import CodeMirror from 'codemirror'; import { JSHINT } from 'jshint'; import { HTMLHint } from 'htmlhint'; -import 'codemirror/addon/hint/css-hint'; +import { CSSLint } from 'csslint'; +import 'codemirror/addon/hint/css-hint'; import * as hinterDefinition from '../../../../utils/p5-hinter'; -import '../show-hint'; // Remove for codemirror v6? +import '../show-hint'; // TODO: Remove for codemirror v6? -// Are we using these????? window.JSHINT = JSHINT; +window.CSSLint = CSSLint; window.HTMLHint = HTMLHint; const hinter = new Fuse(hinterDefinition.p5Hinter, { diff --git a/client/modules/IDE/components/Editor/index.jsx b/client/modules/IDE/components/Editor/index.jsx index 3e5bce5268..8f5a723671 100644 --- a/client/modules/IDE/components/Editor/index.jsx +++ b/client/modules/IDE/components/Editor/index.jsx @@ -34,7 +34,6 @@ import 'codemirror/addon/edit/closebrackets'; import 'codemirror/addon/selection/mark-selection'; import 'codemirror-colorpicker'; -import { CSSLint } from 'csslint'; import classNames from 'classnames'; import { debounce } from 'lodash'; import { connect } from 'react-redux'; @@ -71,8 +70,6 @@ import { showHint, hideHinter } from './hinter'; emmet(CodeMirror); -window.CSSLint = CSSLint; - const INDENTATION_AMOUNT = 2; class Editor extends React.Component { From 850845a5edae900d7b8c4c3a607f17f8dd8ab21b Mon Sep 17 00:00:00 2001 From: Connie Ye Date: Fri, 31 Jan 2025 08:41:05 -0800 Subject: [PATCH 4/4] fix bug where i forgot to pass in the cm instance to hide the hinter --- client/modules/IDE/components/Editor/hinter.js | 4 ++-- client/modules/IDE/components/Editor/index.jsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/modules/IDE/components/Editor/hinter.js b/client/modules/IDE/components/Editor/hinter.js index 00e4236c1d..81bb73d353 100644 --- a/client/modules/IDE/components/Editor/hinter.js +++ b/client/modules/IDE/components/Editor/hinter.js @@ -18,8 +18,8 @@ const hinter = new Fuse(hinterDefinition.p5Hinter, { }); /** Hides the hinter. */ -export function hideHinter() { - CodeMirror.showHint(this._cm, () => {}, {}); +export function hideHinter(cmInstance) { + CodeMirror.showHint(cmInstance, () => {}, {}); } /** diff --git a/client/modules/IDE/components/Editor/index.jsx b/client/modules/IDE/components/Editor/index.jsx index 8f5a723671..5f51ca9ccd 100644 --- a/client/modules/IDE/components/Editor/index.jsx +++ b/client/modules/IDE/components/Editor/index.jsx @@ -269,7 +269,7 @@ class Editor extends React.Component { if (this.props.autocompleteHinter !== prevProps.autocompleteHinter) { if (!this.props.autocompleteHinter) { // close the hinter window once the preference is turned off - hideHinter(); + hideHinter(this._cm); } }