-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Final GSoC Work: Context-Aware Autocomplete, Renaming, and Refactoring Enhancements #3594
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 44 commits
a79c2f1
3e84dd5
5fc1c64
9c4bc52
cf71058
e8bf752
819b9cb
5559967
2c132e6
761657a
b0ec598
87d89a5
d28914a
b0a0feb
5120dce
bdb7d86
e3c7f18
093dccc
ea39d0e
a627fde
990712a
b006d5d
f94b2fb
a7229da
f30b884
01454d0
a67e89d
ce4ae68
2415953
1779145
c50566f
88a9c65
9172902
0665cc6
293b49e
842c46b
a0569d0
fdcd37a
26442f4
ca26d11
77465e9
078b882
63a384a
87dc886
0d5121d
21ed2fa
4b61b10
2830289
c2a2410
4bfbaeb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -73,6 +73,13 @@ import { EditorContainer, EditorHolder } from './MobileEditor'; | |
| import { FolderIcon } from '../../../../common/icons'; | ||
| import IconButton from '../../../../common/IconButton'; | ||
|
|
||
| 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; | ||
|
|
@@ -109,6 +116,7 @@ class Editor extends React.Component { | |
|
|
||
| componentDidMount() { | ||
| this.beep = new Audio(beepUrl); | ||
| ensureAriaLiveRegion(); | ||
| // this.widgets = []; | ||
| this._cm = CodeMirror(this.codemirrorContainer, { | ||
| theme: `p5-${this.props.theme}`, | ||
|
|
@@ -154,6 +162,15 @@ class Editor extends React.Component { | |
|
|
||
| delete this._cm.options.lint.options.errors; | ||
|
|
||
| this._cm.getWrapperElement().addEventListener('click', (e) => { | ||
| const isCtrlClick = isMac() ? e.metaKey : e.ctrlKey; | ||
|
|
||
| if (isCtrlClick) { | ||
| const pos = this._cm.coordsChar({ left: e.clientX, top: e.clientY }); | ||
| jumpToDefinition.call(this, pos); | ||
| } | ||
| }); | ||
|
|
||
| const replaceCommand = | ||
| metaKey === 'Ctrl' ? `${metaKey}-H` : `${metaKey}-Option-F`; | ||
| this._cm.setOption('extraKeys', { | ||
|
|
@@ -172,6 +189,7 @@ class Editor extends React.Component { | |
| [`Shift-${metaKey}-E`]: (cm) => { | ||
| cm.getInputField().blur(); | ||
| }, | ||
| F2: (cm) => this.renameVariable(cm), | ||
|
||
| [`Shift-Tab`]: false, | ||
| [`${metaKey}-Enter`]: () => null, | ||
| [`Shift-${metaKey}-Enter`]: () => null, | ||
|
|
@@ -209,7 +227,13 @@ class Editor extends React.Component { | |
| } | ||
|
|
||
| this._cm.on('keydown', (_cm, e) => { | ||
| // Show hint | ||
| if ( | ||
raclim marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ((e.ctrlKey || e.metaKey) && e.key === 'v') || | ||
| e.ctrlKey || | ||
| e.altKey | ||
| ) { | ||
| return; | ||
| } | ||
| const mode = this._cm.getOption('mode'); | ||
| if (/^[a-z]$/i.test(e.key) && (mode === 'css' || mode === 'javascript')) { | ||
| this.showHint(_cm); | ||
|
|
@@ -395,12 +419,15 @@ class Editor extends React.Component { | |
| } | ||
|
|
||
| showHint(_cm) { | ||
| if (!_cm) return; | ||
|
|
||
| if (!this.props.autocompleteHinter) { | ||
| CodeMirror.showHint(_cm, () => {}, {}); | ||
| return; | ||
| } | ||
|
|
||
| let focusedLinkElement = null; | ||
|
|
||
| const setFocusedLinkElement = (set) => { | ||
| if (set && !focusedLinkElement) { | ||
| const activeItemLink = document.querySelector( | ||
|
|
@@ -415,6 +442,7 @@ class Editor extends React.Component { | |
| } | ||
| } | ||
| }; | ||
|
|
||
| const removeFocusedLinkElement = () => { | ||
| if (focusedLinkElement) { | ||
| focusedLinkElement.classList.remove('focused-hint-link'); | ||
|
|
@@ -437,12 +465,8 @@ class Editor extends React.Component { | |
| ); | ||
| if (activeItemLink) activeItemLink.click(); | ||
| }, | ||
| Right: (cm, e) => { | ||
| setFocusedLinkElement(true); | ||
| }, | ||
| Left: (cm, e) => { | ||
| removeFocusedLinkElement(); | ||
| }, | ||
| Right: (cm, e) => setFocusedLinkElement(true), | ||
| Left: (cm, e) => removeFocusedLinkElement(), | ||
| Up: (cm, e) => { | ||
| const onLink = removeFocusedLinkElement(); | ||
| e.moveFocus(-1); | ||
|
|
@@ -461,30 +485,28 @@ class Editor extends React.Component { | |
| 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); | ||
| } | ||
| 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 | ||
| ); | ||
| } else if (_cm.options.mode === 'css') { | ||
| CodeMirror.showHint(_cm, CodeMirror.hint.css, hintOptions); | ||
| } | ||
| }; | ||
|
|
||
| setTimeout(triggerHints, 0); | ||
| } | ||
|
|
||
| showReplace() { | ||
|
|
@@ -522,6 +544,27 @@ class Editor extends React.Component { | |
| } | ||
| } | ||
|
|
||
| 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; | ||
| } | ||
|
|
||
| const sel = cm.listSelections()[0]; | ||
| const fromPos = | ||
| CodeMirror.cmpPos(sel.anchor, sel.head) <= 0 ? sel.anchor : sel.head; | ||
|
|
||
| showRenameDialog(tokenType, cursorCoords, selection, (newName) => { | ||
| if (newName && newName.trim() !== '' && newName !== selection) { | ||
| handleRename(fromPos, selection, newName, cm); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| initializeDocuments(files) { | ||
| this._docs = {}; | ||
| files.forEach((file) => { | ||
|
|
||

Uh oh!
There was an error while loading. Please reload this page.