Get paragraph editor mode from backend

This commit is contained in:
Mina Lee 2016-08-24 22:57:08 +02:00
parent 52f42071d2
commit 20132ca9ff
9 changed files with 111 additions and 50 deletions

View file

@ -251,6 +251,7 @@ public abstract class Interpreter {
private String className;
private boolean defaultInterpreter;
private Map<String, InterpreterProperty> properties;
private Map<String, Object> editor;
private String path;
public RegisteredInterpreter(String name, String group, String className,
@ -266,6 +267,7 @@ public abstract class Interpreter {
this.className = className;
this.defaultInterpreter = defaultInterpreter;
this.properties = properties;
this.editor = new HashMap<>();
}
public String getName() {
@ -292,6 +294,10 @@ public abstract class Interpreter {
return properties;
}
public Map<String, Object> getEditor() {
return editor;
}
public void setPath(String path) {
this.path = path;
}

View file

@ -250,6 +250,9 @@ public class NotebookServer extends WebSocketServlet implements
case SAVE_INTERPRETER_BINDINGS:
saveInterpreterBindings(conn, messagereceived);
break;
case EDITOR_SETTING:
getEditorSetting(conn, notebook, messagereceived);
break;
default:
break;
}
@ -1457,7 +1460,7 @@ public class NotebookServer extends WebSocketServlet implements
}
/**
* This callback is for praragraph that runs on RemoteInterpreterProcess
* This callback is for paragraph that runs on RemoteInterpreterProcess
* @param paragraph
* @param out
* @param output
@ -1569,11 +1572,24 @@ public class NotebookServer extends WebSocketServlet implements
if (id.equals(interpreterGroupId)) {
broadcast(
note.getId(),
new Message(OP.ANGULAR_OBJECT_REMOVE).put("name", name).put(
"noteId", noteId).put("paragraphId", paragraphId));
new Message(OP.ANGULAR_OBJECT_REMOVE)
.put("name", name)
.put("noteId", noteId)
.put("paragraphId", paragraphId));
}
}
}
}
private void getEditorSetting(NotebookSocket conn, Notebook notebook, Message fromMessage)
throws IOException {
String replName = (String) fromMessage.get("magic");
String noteId = getOpenNoteId(conn);
Note note = notebook.getNote(noteId);
Message resp = new Message(OP.EDITOR_SETTING);
resp.put("editor", note.getEditorSetting(replName));
conn.send(serializeMessage(resp));
return;
}
}

View file

@ -24,7 +24,7 @@
"angular-elastic": "~2.4.2",
"angular-elastic-input": "~2.2.0",
"angular-xeditable": "0.1.12",
"highlightjs": "^9.2.0",
"highlightjs": "^9.4.0",
"lodash": "~3.9.3",
"angular-filter": "~0.5.4",
"ngtoast": "~2.0.0",
@ -62,7 +62,7 @@
"highlight.pack.js",
"styles/github.css"
],
"version": "8.4.0",
"version": "9.4.0",
"name": "highlightjs"
}
}

View file

@ -15,7 +15,7 @@
angular.module('zeppelinWebApp').controller('ParagraphCtrl', function($scope, $rootScope, $route, $window,
$routeParams, $location, $timeout, $compile,
$http, websocketMsgSrv, baseUrlSrv, ngToast,
$http, $q, websocketMsgSrv, baseUrlSrv, ngToast,
saveAsService, esriLoader) {
var ANGULAR_FUNCTION_OBJECT_NAME_PREFIX = '_Z_ANGULAR_FUNC_';
$scope.parentNote = null;
@ -78,15 +78,6 @@ angular.module('zeppelinWebApp').controller('ParagraphCtrl', function($scope, $r
var angularObjectRegistry = {};
var editorModes = {
'ace/mode/python': /^%(\w*\.)?(pyspark|python)\s*$/,
'ace/mode/scala': /^%(\w*\.)?spark\s*$/,
'ace/mode/r': /^%(\w*\.)?(r|sparkr|knitr)\s*$/,
'ace/mode/sql': /^%(\w*\.)?\wql/,
'ace/mode/markdown': /^%md/,
'ace/mode/sh': /^%sh/
};
// Controller init
$scope.init = function(newParagraph, note) {
$scope.paragraph = newParagraph;
@ -538,7 +529,7 @@ angular.module('zeppelinWebApp').controller('ParagraphCtrl', function($scope, $r
$scope.aceChanged = function() {
$scope.dirtyText = $scope.editor.getSession().getValue();
$scope.startSaveTimer();
$scope.setParagraphMode($scope.editor.getSession(), $scope.dirtyText, $scope.editor.getCursorPosition());
setParagraphMode($scope.editor.getSession(), $scope.dirtyText, $scope.editor.getCursorPosition());
};
$scope.aceLoaded = function(_editor) {
@ -576,37 +567,11 @@ angular.module('zeppelinWebApp').controller('ParagraphCtrl', function($scope, $r
// not applying emacs key binding while the binding override Ctrl-v. default behavior of paste text on windows.
}
$scope.setParagraphMode = function(session, paragraphText, pos) {
// Evaluate the mode only if the first 30 characters of the paragraph have been modified or the the position is undefined.
if ((typeof pos === 'undefined') || (pos.row === 0 && pos.column < 30)) {
// If paragraph loading, use config value if exists
if ((typeof pos === 'undefined') && $scope.paragraph.config.editorMode) {
session.setMode($scope.paragraph.config.editorMode);
} else {
// Defaults to spark mode
var newMode = 'ace/mode/scala';
// Test first against current mode
var oldMode = session.getMode().$id;
if (!editorModes[oldMode] || !editorModes[oldMode].test(paragraphText)) {
for (var key in editorModes) {
if (key !== oldMode) {
if (editorModes[key].test(paragraphText)) {
$scope.paragraph.config.editorMode = key;
session.setMode(key);
return true;
}
}
}
$scope.paragraph.config.editorMode = newMode;
session.setMode(newMode);
}
}
}
};
var remoteCompleter = {
getCompletions: function(editor, session, pos, prefix, callback) {
if (!$scope.editor.isFocused()) { return;}
if (!$scope.editor.isFocused()) {
return;
}
pos = session.getTextRange(new Range(0, 0, pos.row, pos.column)).length;
var buf = session.getValue();
@ -662,7 +627,7 @@ angular.module('zeppelinWebApp').controller('ParagraphCtrl', function($scope, $r
autoAdjustEditorHeight(_editor.container.id);
});
$scope.setParagraphMode($scope.editor.getSession(), $scope.editor.getSession().getValue());
setParagraphMode($scope.editor.getSession(), $scope.editor.getSession().getValue());
// autocomplete on '.'
/*
@ -737,6 +702,51 @@ angular.module('zeppelinWebApp').controller('ParagraphCtrl', function($scope, $r
}
};
var getEditorSetting = function(interpreterName) {
var deferred = $q.defer();
websocketMsgSrv.getEditorSetting(interpreterName);
$timeout(
$scope.$on('editorSetting', function(event, data) {
deferred.resolve(data);
}
), 1000);
return deferred.promise;
};
var setParagraphMode = function(session, paragraphText, pos) {
// Evaluate the mode only if the the position is undefined
// or the first 30 characters of the paragraph have been modified
// or cursor position is at beginning of second line.(in case user hit enter after typing %magic)
if ((typeof pos === 'undefined') || (pos.row === 0 && pos.column < 30) || (pos.row === 1 && pos.column === 0)) {
// If paragraph loading, use config value if exists
if ((typeof pos === 'undefined') && $scope.paragraph.config.editorMode) {
session.setMode($scope.paragraph.config.editorMode);
} else {
var magic;
// set editor mode to default interpreter syntax if paragraph text doesn't start with '%'
if (!paragraphText.startsWith('%')) {
magic = $scope.$parent.interpreterBindings[0].group;
} else {
var replNameRegexp = /%(.+?)\s/g;
var match = replNameRegexp.exec(paragraphText);
if (match) {
magic = match[1];
}
}
if (magic) {
var promise = getEditorSetting(magic);
promise.then(function(editorSetting) {
if (!_.isEmpty(editorSetting.editor)) {
var mode = 'ace/mode/' + editorSetting.editor.language;
$scope.paragraph.config.editorMode = mode;
session.setMode(mode);
}
});
}
}
}
};
var autoAdjustEditorHeight = function(id) {
var editor = $scope.editor;
var height = editor.getSession().getScreenLength() * editor.renderer.lineHeight +
@ -2259,7 +2269,6 @@ angular.module('zeppelinWebApp').controller('ParagraphCtrl', function($scope, $r
var noteId = $route.current.pathParams.noteId;
$http.get(baseUrlSrv.getRestApiBase() + '/helium/suggest/' + noteId + '/' + $scope.paragraph.id)
.success(function(data, status, headers, config) {
console.log('Suggested apps %o', data);
$scope.suggestion = data.body;
})
.error(function(err, status, headers, config) {

View file

@ -76,8 +76,8 @@ angular.module('zeppelinWebApp').factory('websocketEvents',
action: function(dialog) {
dialog.close();
angular.element('#loginModal').modal({
show: 'true'
});
show: 'true'
});
}
}, {
label: 'Cancel',
@ -97,6 +97,8 @@ angular.module('zeppelinWebApp').factory('websocketEvents',
$rootScope.$broadcast('updateProgress', data);
} else if (op === 'COMPLETION_LIST') {
$rootScope.$broadcast('completionList', data);
} else if (op === 'EDITOR_SETTING') {
$rootScope.$broadcast('editorSetting', data);
} else if (op === 'ANGULAR_OBJECT_UPDATE') {
$rootScope.$broadcast('angularObjectUpdate', data);
} else if (op === 'ANGULAR_OBJECT_REMOVE') {

View file

@ -180,6 +180,15 @@ angular.module('zeppelinWebApp').service('websocketMsgSrv', function($rootScope,
});
},
getEditorSetting: function(replName) {
websocketEvents.sendNewEvent({
op: 'EDITOR_SETTING',
data: {
magic: replName
}
});
},
isConnected: function() {
return websocketEvents.isConnected();
},

View file

@ -650,6 +650,21 @@ public class Note implements Serializable, ParagraphJobListener {
this.info = info;
}
public Interpreter getRepl(String name) {
return factory.getInterpreter(id(), name);
}
public Map<String, Object> getEditorSetting(String replName) {
Interpreter intp = getRepl(replName);
Map<String, Object> editor = new HashMap<>();
try {
editor = intp.findRegisteredInterpreterByClassName(intp.getClassName()).getEditor();
} catch (NullPointerException e) {
editor.put("language", "text");
}
return editor;
}
@Override
public void beforeStatusChange(Job job, Status before, Status after) {
if (jobListenerFactory != null) {

View file

@ -193,7 +193,7 @@ public class Paragraph extends Job implements Serializable, Cloneable {
}
public Interpreter getRepl(String name) {
return factory.getInterpreter(note.getId(), name);
return note.getRepl(name);
}
public Interpreter getCurrentRepl() {

View file

@ -76,6 +76,10 @@ public class Message {
INSERT_PARAGRAPH, // [c-s] create new paragraph below current paragraph
// @param target index
EDITOR_SETTING, // [c-s] ask paragraph editor setting
// @param magic magic keyword written in paragraph
// ex) spark.spark or spark
COMPLETION, // [c-s] ask completion candidates
// @param id
// @param buf current code