diff --git a/package.json b/package.json index 7e924f4..6a6d930 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "frylord": "^0.6.0", "holovisor": "^0.2.0", "iggins": "^0.2.1", - "irken": "^0.7.0", + "irken": "^0.7.1", "lodash": "^3.9.1", "react": "^0.13.1", "react-loader": "^1.2.0", diff --git a/plugins/editor/index.js b/plugins/editor/index.js index 0d8658d..3250499 100644 --- a/plugins/editor/index.js +++ b/plugins/editor/index.js @@ -9,19 +9,24 @@ require('codemirror/addon/selection/mark-selection'); require('codemirror/lib/codemirror.css'); require('../../assets/theme/parallax.css'); +const React = require('react'); const CodeMirror = require('codemirror'); require('./pbasic')(CodeMirror); const keyExtension = require('./key-extension'); + const consoleStore = require('../../src/stores/console'); const editorStore = require('../../src/stores/editor'); +const deviceStore = require('../../src/stores/device'); const fileStore = require('../../src/stores/file'); + const { handleInput } = require('../../src/actions/editor'); const DocumentsStore = require('../../src/stores/documents'); -const React = require('react'); const TransmissionBar = require('./transmission-bar'); +const makeToasts = require('../../src/lib/toasts'); + function editor(app, opts, done){ var codeEditor; @@ -37,9 +42,39 @@ function editor(app, opts, done){ } } + function highlighter(position, length) { + if(!codeEditor){ + return; + } + + const doc = codeEditor.getDoc(); + + const anchor = doc.posFromIndex(position); + const head = doc.posFromIndex(position + length); + + doc.setSelection(anchor, head, { scroll: false }); + + const charRect = codeEditor.charCoords(anchor, 'local'); + const halfHeight = codeEditor.getScrollerElement().offsetHeight / 2; + const halfTextHeight = Math.floor((charRect.bottom - charRect.top) / 2); + codeEditor.scrollTo(null, charRect.top - halfHeight - halfTextHeight); + } + consoleStore.listen(refreshConsole); - var space = app.workspace; + const space = app.workspace; + const compile = app.compile.bind(app); + // seems strange to pass highlighter to toasts + // maybe this should be named "handlers" or something + const toasts = makeToasts(app.toast, highlighter); + + editorStore.toasts = toasts; + editorStore.compile = compile; + editorStore.workspace = space; + + // really stinks to attach these in here + fileStore.toasts = toasts; + deviceStore.toasts = toasts; app.view('editor', function(el, cb){ console.log('editor render'); @@ -67,7 +102,8 @@ function editor(app, opts, done){ 'Ctrl-Up': false, 'Ctrl-Down': false, 'Tab': false, - 'Shift-Tab': false + 'Shift-Tab': false, + 'Ctrl-T': false }); keyExtension.setup(app); editorStore.cm = codeEditor; diff --git a/plugins/editor/key-extension.js b/plugins/editor/key-extension.js index 705a0ac..97efaaa 100644 --- a/plugins/editor/key-extension.js +++ b/plugins/editor/key-extension.js @@ -6,6 +6,7 @@ const { findNext, findPrevious, replace } = require('../../src/actions/find'); const { moveByScrollUpLine, moveByScrollDownLine } = require('../../src/actions/editor-move'); const { dedent, indent } = require('../../src/actions/text-move'); const { print } = require('../../src/actions/system'); +const { syntaxCheck } = require('../../src/actions/editor'); const { newFile, saveFile } = require('../../src/actions/file'); const { hideOverlays, showSave, showDownload, showProjects } = require('../../src/actions/overlay'); const { disableAuto, enableAuto } = require('../../src/actions/device'); @@ -120,6 +121,13 @@ const keyExtension = { evt.preventDefault(); showProjects(); } + }, + syntaxCheck: { + code: ['CTRL_T', 'F7'], + exec(evt){ + evt.preventDefault(); + syntaxCheck(); + } } }; diff --git a/plugins/overlays/index.js b/plugins/overlays/index.js index 78e3eb5..dda0694 100644 --- a/plugins/overlays/index.js +++ b/plugins/overlays/index.js @@ -11,7 +11,7 @@ const overlayStore = require('../../src/stores/overlay'); const projectStore = require('../../src/stores/project'); const { confirmDelete, changeProject, deleteProject } = require('../../src/actions/project'); -const { handleError, handleSuccess, deleteFile, saveFileAs } = require('../../src/actions/file'); +const { deleteFile, saveFileAs } = require('../../src/actions/file'); const { hideSave, hideDelete, hideDownload, showProjects, hideProjects } = require('../../src/actions/overlay'); function overlays(app, opts, done){ @@ -61,10 +61,7 @@ function overlays(app, opts, done){ if(showDownloadOverlay){ component = ( + onCancel={hideDownload} /> ); } diff --git a/plugins/sidebar/index.js b/plugins/sidebar/index.js index 7d0eaf1..b3cf045 100644 --- a/plugins/sidebar/index.js +++ b/plugins/sidebar/index.js @@ -10,20 +10,18 @@ const FileOperations = require('./file-operations'); const ProjectOperations = require('./project-operations'); const deviceStore = require('../../src/stores/device'); -const editorStore = require('../../src/stores/editor'); const fileStore = require('../../src/stores/file'); const { loadFile } = require('../../src/actions/file'); +const makeToasts = require('../../src/lib/toasts'); + function sidebar(app, opts, done){ const space = app.workspace; - const toast = app.toast; - const overlay = app.overlay; const userConfig = app.userConfig; - const irken = app; - const getBoard = app.getBoard.bind(irken); - const scanBoards = app.scanBoards.bind(irken); + const getBoard = app.getBoard.bind(app); + const scanBoards = app.scanBoards.bind(app); function refreshDirectory(){ // TODO: expose a method to refresh directory without changing it @@ -58,16 +56,11 @@ function sidebar(app, opts, done){ // Store bindings deviceStore.workspace = space; - deviceStore.toast = toast; - deviceStore.overlay = overlay; deviceStore.getBoard = getBoard; deviceStore.scanBoards = scanBoards; - editorStore.workspace = space; - fileStore.workspace = space; fileStore.userConfig = userConfig; - fileStore.toast = toast; done(); } diff --git a/plugins/sidebar/styles.js b/plugins/sidebar/styles.js index 21831a6..bdbb26c 100644 --- a/plugins/sidebar/styles.js +++ b/plugins/sidebar/styles.js @@ -31,12 +31,6 @@ const styles = { }, fileHasTemp: { backgroundColor: red - }, - errorToast: { - backgroundColor: red - }, - successToast: { - backgroundColor: green } }; diff --git a/src/actions/editor.js b/src/actions/editor.js index fcb34f4..15c337b 100644 --- a/src/actions/editor.js +++ b/src/actions/editor.js @@ -6,6 +6,10 @@ class EditorActions { handleInput(inst) { this.dispatch(inst); } + + syntaxCheck(){ + this.dispatch(); + } } module.exports = alt.createActions(EditorActions); diff --git a/src/actions/file.js b/src/actions/file.js index 14a9bb0..7a4e413 100644 --- a/src/actions/file.js +++ b/src/actions/file.js @@ -30,14 +30,6 @@ class FileActions { saveFileAs(name) { this.dispatch(name); } - - handleError(err) { - this.dispatch(err); - } - - handleSuccess(msg) { - this.dispatch(msg); - } } module.exports = alt.createActions(FileActions); diff --git a/src/lib/toasts.js b/src/lib/toasts.js new file mode 100644 index 0000000..bbd4ebe --- /dev/null +++ b/src/lib/toasts.js @@ -0,0 +1,47 @@ +'use strict'; + +const red = '#da2100'; +const green = '#159600'; + +const styles = { + errorToast: { + backgroundColor: red + }, + successToast: { + backgroundColor: green + } +}; + +function toasts(api, highlighter){ + + function success(msg){ + api.show(msg, { style: styles.successToast, timeout: 5000 }); + } + + function error(err){ + // leaving this in for better debugging of errors + console.log(err); + + api.show(err.message, { style: styles.errorToast }); + + if(typeof highlighter !== 'function'){ + return; + } + + if(err && err.errorLength){ + highlighter(err.errorPosition, err.errorLength); + } + } + + function clear(){ + api.clear(); + } + + return { + success, + error, + clear + }; +} + +module.exports = toasts; diff --git a/src/stores/device.js b/src/stores/device.js index a0911dc..43df8ee 100644 --- a/src/stores/device.js +++ b/src/stores/device.js @@ -1,13 +1,13 @@ 'use strict'; -const alt = require('../alt'); const _ = require('lodash'); +const alt = require('../alt'); + const { rx, tx } = require('../actions/transmission'); const { hideDownload, showDownload } = require('../actions/overlay'); const { clearOutput, output } = require('../actions/console'); const { enableAuto, disableAuto, reloadDevices, updateSelected } = require('../actions/device'); -const { handleSuccess, handleError } = require('../actions/file'); class DeviceStore { constructor() { @@ -134,7 +134,7 @@ class DeviceStore { this.setState({ progress: progress }); } - const { workspace, toast, getBoard } = this.getInstance(); + const { workspace, getBoard } = this.getInstance(); const { selectedDevice } = this.state; const name = workspace.filename.deref(); @@ -155,9 +155,9 @@ class DeviceStore { .tap(() => clearOutput()) .then(() => board.on('terminal', output)) .then(() => board.on('terminal', rx)) - .tap(() => toast.clear()) - .tap(() => handleSuccess(`'${name}' downloaded successfully`)) - .catch(handleError) + .tap(() => this._handleClear()) + .tap(() => this._handleSuccess(`'${name}' downloaded successfully`)) + .catch((err) => this._handleError(err)) .finally(() => { board.removeListener('progress', updateProgress); this.setState({ progress: 0 }); @@ -165,6 +165,24 @@ class DeviceStore { }); } + _handleClear(){ + const { toasts } = this.getInstance(); + + toasts.clear(); + } + + _handleError(err){ + const { toasts } = this.getInstance(); + + toasts.error(err); + } + + _handleSuccess(msg){ + const { toasts } = this.getInstance(); + + toasts.success(msg); + } + } DeviceStore.config = { diff --git a/src/stores/editor.js b/src/stores/editor.js index 28354ad..9387dfa 100644 --- a/src/stores/editor.js +++ b/src/stores/editor.js @@ -3,7 +3,7 @@ const alt = require('../alt'); const { findNext, findPrevious, replace } = require('../actions/find'); -const { handleInput } = require('../actions/editor'); +const { handleInput, syntaxCheck } = require('../actions/editor'); const { moveByScrollUpLine, moveByScrollDownLine } = require('../actions/editor-move'); const { dedent, indent } = require('../actions/text-move'); const { print } = require('../actions/system'); @@ -20,7 +20,8 @@ class EditorStore { onMoveByScrollUpLine: moveByScrollUpLine, onMoveByScrollDownLine: moveByScrollDownLine, onPrint: print, - onReplace: replace + onReplace: replace, + onSyntaxCheck: syntaxCheck }); } @@ -77,7 +78,37 @@ class EditorStore { cm.execCommand('replace'); } + onSyntaxCheck() { + const { workspace, compile } = this.getInstance(); + const result = compile({ + type: 'bs2', + source: workspace.current.deref() + }); + if(result.error){ + this._handleError(result.error); + } else { + this._handleClear(); + this._handleSuccess('Tokenization successful!'); + } + } + + _handleClear(){ + const { toasts } = this.getInstance(); + + toasts.clear(); + } + _handleError(err){ + const { toasts } = this.getInstance(); + + toasts.error(err); + } + + _handleSuccess(msg){ + const { toasts } = this.getInstance(); + + toasts.success(msg); + } } EditorStore.config = { diff --git a/src/stores/file.js b/src/stores/file.js index c4bd879..4a772b1 100644 --- a/src/stores/file.js +++ b/src/stores/file.js @@ -5,9 +5,9 @@ const path = require('path'); const _ = require('lodash'); const alt = require('../alt'); -const styles = require('../../plugins/sidebar/styles'); const { hideOverlays, hideSave } = require('../actions/overlay'); + const { clearName, updateName, @@ -15,9 +15,7 @@ const { loadFile, saveFile, saveFileAs, - deleteFile, - handleError, - handleSuccess } = require('../actions/file'); + deleteFile } = require('../actions/file'); class FileStore { constructor() { @@ -25,8 +23,6 @@ class FileStore { this.bindListeners({ onClearName: clearName, onDeleteFile: deleteFile, - onHandleError: handleError, - onHandleSuccess: handleSuccess, onHideOverlay: hideOverlays, onNewFile: newFile, onLoadFile: loadFile, @@ -65,8 +61,8 @@ class FileStore { } workspace.deleteFile(workspace.filename) - .tap(() => this.onHandleSuccess(`'${name}' deleted successfully`)) - .catch(this.onHandleError) + .tap(() => this._handleSuccess(`'${name}' deleted successfully`)) + .catch((err) => this._handleError(err)) .finally(() => this.onNewFile()); } @@ -90,8 +86,8 @@ class FileStore { this.onLoadFile(this.loadQueue.shift()); } }) - .tap(() => this.onHandleSuccess(`'${name}' created successfully`)) - .catch(this.onHandleError); + .tap(() => this._handleSuccess(`'${name}' created successfully`)) + .catch((err) => this._handleError(err)); } onCancelSave(status){ @@ -118,8 +114,8 @@ class FileStore { // TODO: these should transparently accept cursors for all non-function params workspace.saveFile(name, workspace.current) - .tap(() => this.onHandleSuccess(`'${name}' saved successfully`)) - .catch(this.onHandleError); + .tap(() => this._handleSuccess(`'${name}' saved successfully`)) + .catch((err) => this._handleError(err)); } onNewFile() { @@ -185,7 +181,7 @@ class FileStore { workspace.loadFile(filename, (err) => { if(err){ - this.onHandleError(err); + this._handleError(err); return; } @@ -201,18 +197,16 @@ class FileStore { }); } - onHandleError(err){ - // leaving this in for better debugging of errors - console.log(err); - const { toast } = this.getInstance(); + _handleError(err){ + const { toasts } = this.getInstance(); - toast.show(err.message, { style: styles.errorToast }); + toasts.error(err); } - onHandleSuccess(msg){ - const { toast } = this.getInstance(); + _handleSuccess(msg){ + const { toasts } = this.getInstance(); - toast.show(msg, { style: styles.successToast, timeout: 5000 }); + toasts.success(msg); } }