Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions client/modules/IDE/components/Editor/hinter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import Fuse from 'fuse.js';
import CodeMirror from 'codemirror';
import { JSHINT } from 'jshint';
import { HTMLHint } from 'htmlhint';
import { CSSLint } from 'csslint';

import 'codemirror/addon/hint/css-hint';
import * as hinterDefinition from '../../../../utils/p5-hinter';
import '../show-hint'; // TODO: Remove for codemirror v6?

window.JSHINT = JSHINT;
window.CSSLint = CSSLint;
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 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, () => {}, {});
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);
}
}
115 changes: 4 additions & 111 deletions client/modules/IDE/components/Editor/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -33,12 +32,8 @@ 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';
Expand All @@ -47,8 +42,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';
Expand All @@ -73,11 +66,9 @@ import { EditorContainer, EditorHolder } from './MobileEditor';
import { FolderIcon } from '../../../../common/icons';
import IconButton from '../../../../common/IconButton';

emmet(CodeMirror);
import { showHint, hideHinter } from './hinter';

window.JSHINT = JSHINT;
window.CSSLint = CSSLint;
window.HTMLHint = HTMLHint;
emmet(CodeMirror);

const INDENTATION_AMOUNT = 2;

Expand Down Expand Up @@ -146,11 +137,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 =
Expand Down Expand Up @@ -207,7 +193,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();
Expand Down Expand Up @@ -283,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
CodeMirror.showHint(this._cm, () => {}, {});
hideHinter();
}
}

Expand Down Expand Up @@ -378,99 +364,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');
}
Expand Down
Loading