Skip to content

Commit

Permalink
Merge pull request #135 from stencila/functions
Browse files Browse the repository at this point in the history
Functions
  • Loading branch information
Nokome Bentley committed Mar 1, 2016
2 parents cbbf189 + d90d38f commit 8176b83
Show file tree
Hide file tree
Showing 9 changed files with 256 additions and 87 deletions.
54 changes: 35 additions & 19 deletions web/sheet/engine/SheetRemoteEngine.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,43 @@
'use strict';

var oo = require('substance/util/oo');
var RemoteEngine = require('../../RemoteEngine');

function SheetRemoteEngine() {
SheetRemoteEngine.super.apply(this, arguments);

window._engine = this;
}

SheetRemoteEngine.Prototype = function() {

/**
* A list of function names currently available in the
* the sheet's context
*/
this._functionList = null;
Gets the list of available functions.
*/
this.getFunctionList = function() {
return this._functionList;
};

/**
A list of function names currently available in the
the sheet's context
TODO: remove hard-coded entries once updateFunction list
strategy is in place.
*/
this._functionList = ['sum', 'mean'];

/**
* A dictionary of functions definitions used as
* a cache
*/
this._functionSpecs = {}
A dictionary of functions definitions used as
a cache
*/
this._functionSpecs = {};

/**
* Get a list of function names
*/
this.functions = function(cb) {
Updates the cache of available functions
TODO: this should be run on app start and when new packages are
imported that expose more functions.
*/
this.updateFunctionList = function(cb) {
if(this._functionList) {
cb(this._functionList);
} else {
Expand All @@ -38,19 +49,24 @@ SheetRemoteEngine.Prototype = function() {
};

/**
* Get a function definition
*/
Get a function definition
*/
this.function = function(name, cb) {
if(this._functionSpecs[name]){
return this._functionSpecs[name];
var cachedFunction = this._functionSpecs[name];
if (cachedFunction) {
cb(null, cachedFunction);
} else {
this._request('PUT', 'function', {name:name}, function(err, result) {
if (err) return cb(err);
this._functionSpecs[name] = result;
cb(result);
cb(null, result);
}.bind(this));
}
};


/*
Updates given cells
*/
this.update = function(cells, cb) {
this._request('PUT', 'update', cells, function(err, result) {
if (err) return cb(err);
Expand Down
28 changes: 14 additions & 14 deletions web/sheet/sheet.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,17 @@ window.Stencila = {};
window.isEditable = true;

function launch() {
Raven
.config(
'https://6329017160394100b21be92165555d72@app.getsentry.com/37250',{
ignoreUrls: [
// Ignore errors generated during development or on local sessions
/localhost/,
/127\.0\.0\.1/,
]
})
.install();
try {
// Raven
// .config(
// 'https://6329017160394100b21be92165555d72@app.getsentry.com/37250',{
// ignoreUrls: [
// // Ignore errors generated during development or on local sessions
// /localhost/,
// /127\.0\.0\.1/,
// ]
// })
// .install();
// try {
var doc = loadDocument();
if (window.isEditable) {
renderStaticReadonlyVersion(doc);
Expand All @@ -80,9 +80,9 @@ function launch() {
} else {
renderInteractiveVersion(doc, 'read');
}
} catch(e) {
Raven.captureException(e)
}
// } catch(e) {
// Raven.captureException(e)
// }
}

window.activate = function() {
Expand Down
1 change: 1 addition & 0 deletions web/sheet/sheet.scss
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ $default-cell-padding: 1px 3px;
@import './styles/_primitive-expression';
@import './styles/_image-expression';
@import './styles/_function';
@import './styles/_select-function';
@import './styles/_cell-teaser';
@import './styles/_save-tool';
@import './styles/_sheet-editor';
Expand Down
10 changes: 10 additions & 0 deletions web/sheet/styles/_select-function.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.sc-select-function {
position: absolute;
min-width: 300px;
z-index: 2;
background: #FFFFFF;
top: 30px;
font-size: 12px;
box-shadow: 0px 2px 4px 0px rgba(0,0,0,0.50);
text-align: left;
}
12 changes: 6 additions & 6 deletions web/sheet/testFunction.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
var testSnippet = {
var testFunction = {
"name": "sum",
"summary": "Returns the sum of values.",
"title": "Returns the sum of values.",
"notes": [
"If only a single number for `values` is supplied, `sum` returns `values`"
],
"parameters": [
{
"name": "value1",
"descr": "The first number or range to sum up."
"description": "The first number or range to sum up."
},
{
"shape": ["one", "block"],
"name": "value2",
"descr": "Additional numbers or ranges to add to value1",
"description": "Additional numbers or ranges to add to value1",
"variadic": true,
"optional": true
}
Expand All @@ -31,7 +31,7 @@ var testSnippet = {
"r",
"py"
]
}
};


module.exports = testSnippet;
module.exports = testFunction;
71 changes: 55 additions & 16 deletions web/sheet/ui/CellEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
var Component = require('substance/ui/Component');
var $$ = Component.$$;
var FunctionComponent = require('./FunctionComponent');
var SelectFunction = require('./SelectFunction');

/*
The CellEditor is different to a regular TextPropertyEditor
Expand All @@ -26,13 +27,16 @@ CellEditor.Prototype = function() {
.on('input', this.onChange)
.ref('editor')
);
if (this.state.func) {
window._engine.function(this.state.func, function(func){
el.append($$(FunctionComponent, {
func: func,
paramIndex: this.state.paramIndex
}));
}.bind(this));
if (this.state.funcName) {
el.append($$(FunctionComponent, {
funcName: this.state.funcName,
paramIndex: this.state.paramIndex
}).ref('function')); // ref is needed so the component is not wiped on each keystroke
} else if (this.state.suggestedFunctions) {
// Render function name suggestor
el.append($$(SelectFunction, {
entries: this.state.suggestedFunctions
}).ref('selectFunction'));
}
return el;
};
Expand Down Expand Up @@ -94,39 +98,74 @@ CellEditor.Prototype = function() {
this._detectFunction();
};

/*
Iterates over available function names and matches the current input string
var _FUNCTIONS = ['sum', 'mean'];
var _FUNCTION_RE_STR = '\\b(' + _FUNCTIONS.join('|') + ')[(]';
TODO: @nokome: the _matcher needs to be improved! Also you may want to
limit the number of suggested function names
*/
this._matchFunctionNames = function(str) {
if (!str) return []; // don't match anything for an empty string
var _matcher = new RegExp('\^'+str, 'gi');

var matches = [];
var funcs = this._getAvailableFunctions();

funcs.forEach(function(funcName) {
if (_matcher.exec('='+funcName)) {
matches.push(funcName);
}
});
return matches;
};

this._getAvailableFunctions = function() {
var engine = this.context.engine;
return engine.getFunctionList();
};

this._detectFunction = function() {
var _availableFuncs = this._getAvailableFunctions();
var _function_re_str = '\\b(' + _availableFuncs.join('|') + ')[(]';

setTimeout(function() {
var el = this._getTextArea();
var source = el.value;
var pos = el.selectionStart;
// only if collapsed
if (pos === el.selectionEnd) {
source = source.slice(0, pos);
var re = new RegExp(_FUNCTION_RE_STR, 'gi');
var re = new RegExp(_function_re_str, 'gi');
var lastMatch, match;
while ( (match = re.exec(source)) ) {
lastMatch = match;
}

if (lastMatch) {
// console.log('DETECTED FUNCTION', lastMatch[1], lastMatch);
var func = lastMatch[1];
var funcName = lastMatch[1];
var startPos = lastMatch.index+1;
var argsPos = startPos + lastMatch[0].length;
var currentArg = this._detectCurrentArg(source.slice(argsPos));
var newState = {
func: func,
funcName: funcName,
paramIndex: currentArg.argIdx
};
// console.log('DETECTED FUNCTION', newState);
this.setState(newState);
} else if (this.state.func) {
this.extendState({
func: false
});
} else {
// Check if any available function name matches partly so we can suggest it
var suggestedFunctions = this._matchFunctionNames(source);

if (suggestedFunctions.length > 0) {
this.setState({
suggestedFunctions: suggestedFunctions
});
} else {
this.setState({
funcName: false
});
}
}
}
}.bind(this));
Expand Down
Loading

0 comments on commit 8176b83

Please sign in to comment.