Skip to content

Commit

Permalink
Use compliment for completions, via cider-nrepl.
Browse files Browse the repository at this point in the history
Fixes #134.
  • Loading branch information
JonyEpsilon committed Aug 30, 2014
1 parent 5c87323 commit b168524
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 95 deletions.
3 changes: 2 additions & 1 deletion resources/public/js/codemirrorVM.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ var codemirrorVM = function (id, initialContents, contentType) {
};

self.complete = function (completionFunc) {
CodeMirror.showHint(self.codeMirror, completionFunc, {async: true});
CodeMirror.showHint(self.codeMirror, completionFunc,
{async: true, completeSingle: false, alignWithWord: false});
};

// These can be called to position the CodeMirror cursor appropriately. They are used when the cell is receiving
Expand Down
22 changes: 8 additions & 14 deletions resources/public/js/completions.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,14 @@ var clojureCompleter = function (cm, callback, options) {
var end = token.end;

// we need to know what namespace the user is currently working in, which we get from the evaluator module
var ns = evaluator.currentNamespace;
var ns = repl.currentNamespace;

$.ajax({
type: "GET",
url: "/completions",
data: {stub: word, ns: ns},
success: function (data) {
callback({
list: data.completions,
from: CodeMirror.Pos(cur.line, start),
to: CodeMirror.Pos(cur.line, end)
});

}
repl.getCompletions(word, ns, null, function (compl) {
var completions = {
list: compl,
from: CodeMirror.Pos(cur.line, start),
to: CodeMirror.Pos(cur.line, end)
};
callback(completions);
});

};
70 changes: 0 additions & 70 deletions resources/public/js/evaluator.js

This file was deleted.

116 changes: 107 additions & 9 deletions resources/public/js/repl-ws.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,19 @@
*/

// A websocket connection to the repl. Works with `gorilla-repl.websocket-relay` on the backend.
// This code also keeps track of running evaluations and dispatches responses to the appropriate worksheet segments.

var repl = (function () {

var self = {};

// This is exposed to make it easy to test direct interaction with the nREPL server from the dev tools. It
// shouldn't be considered part of the public API
self.sendREPLCommand = function (message) {
self.ws.send(JSON.stringify(message));
};

// this handles messages coming back from the socket once the connection phase is complete.
var messageHandler = function (message) {
var msg = JSON.parse(message.data);
eventBus.trigger("repl:response", msg);
};

// Connect to the websocket nREPL bridge.
// TODO: handle errors.
self.connect = function (successCallback, failureCallback) {
// hard to believe we have to do this
Expand All @@ -33,7 +31,7 @@ var repl = (function () {
var msg = JSON.parse(message.data);
if (msg['new-session']) {
self.sessionID = msg['new-session'];
self.ws.onmessage = messageHandler;
self.ws.onmessage = handleMessage;
successCallback();
}
};
Expand All @@ -49,11 +47,111 @@ var repl = (function () {
};
};

// This maps evaluation IDs to the IDs of the segment that initiated them.
var evaluationMap = {};

// tracks the namespace that the last evaluation completed in
self.currentNamespace = "user";

// The public interface for executing code on the REPL server.
self.execute = function (command, id) {
var message = {'op': 'eval', 'code': command, id: id, session: self.sessionID};
eventBus.on("evaluator:evaluate", function (e, d) {
// generate an ID to tie the evaluation to its results - when responses are received, we route them to the
// originating segment for display using this ID (see the repl:response event handler below).
var id = UUID.generate();
// store the evaluation ID and the segment ID in the evaluationMap
evaluationMap[id] = d.segmentID;
var message = {'op': 'eval', 'code': d.code, id: id, session: self.sessionID};
self.sendREPLCommand(message);
});

// as well as eval messages, we also send "service" messages to the nREPL server for things like autocomplete,
// docs etc. We maintain a separate map which maps the ID of the service message to the callback function that
// we'd like to run on the returned data.
var serviceMessageMap = {};

// send a service message, and schedule the given callback to run on completion. An ID and the session information
// will be added to the message,
var sendServiceMessage = function (msg, callback) {
var id = UUID.generate();
serviceMessageMap[id] = callback;
msg.id = id;
msg.session = self.sessionID;
self.sendREPLCommand(msg);
};

// query the REPL server for autocompletion suggestions. Relies on the cider-nrepl middleware.
// We call the given callback with the list of symbols once the REPL server replies.
self.getCompletions = function (symbol, ns, context, callback) {
sendServiceMessage({op: "complete", symbol: symbol, ns: ns}, function (d) {
callback(d.value);
});
};

// handle the various different nREPL responses
var handleMessage = function (message) {
var d = JSON.parse(message.data);

// Is this a message relating to an evaluation triggered by the user?
var segID = evaluationMap[d.id];
if (segID != null) {

// - evaluation result (Hopefully no other responses have an ns component!)
if (d.ns) {
self.currentNamespace = d.ns;
eventBus.trigger("evaluator:value-response", {ns: d.ns, value: d.value, segmentID: segID});
return;
}

// - console output
if (d.out) {
eventBus.trigger("evaluator:console-response", {out: d.out, segmentID: segID});
return;
}

// - status response
if (d.status) {
// is this an evaluation done message
if (d.status.indexOf("done") >= 0) {
eventBus.trigger("evaluator:done-response", {segmentID: segID});
// keep the evaluation map clean
delete evaluationMap[d.id];
return;
}
}

// - error message
if (d.err) {
eventBus.trigger("evaluator:error-response", {error: d.err, segmentID: segID});
return;
}

// - root-ex message
if (d['root-ex']) {
// at the minute we just eat (and log) these - I'm not really sure what they're for!
console.log("Root-ex message: " + JSON.stringify(d));
return;
}
}

// If this reply isn't associated with a segment, then it's probably a reply to a service message
if (serviceMessageMap[d.id]) {
// if it's a status "done" message, clean up the service map
if (d.status) {
if (d.status.indexOf("done") >= 0) {
delete serviceMessageMap[d.id];
return;
}
}
// otherwise, get the callback from the service map and run it
serviceMessageMap[d.id](d);
return;
}


// If we get here, then we don't know what the message was for - just log it
console.log("Unknown response: " + JSON.stringify(d));
};


return self;
})();
1 change: 0 additions & 1 deletion resources/public/worksheet.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
<script type="text/javascript" src="js/repl-ws.js"></script>
<script type="text/javascript" src="js/utils.js"></script>
<script type="text/javascript" src="js/worksheetParser.js"></script>
<script type="text/javascript" src="js/evaluator.js"></script>
<script type="text/javascript" src="js/codemirrorVM.js"></script>
<script type="text/javascript" src="js/completions.js"></script>
<script type="text/javascript" src="js/mathJaxViewer.js"></script>
Expand Down

0 comments on commit b168524

Please sign in to comment.