Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion zeppelin-web/src/app/notebook/notebook.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl', function($scope, $ro
$scope.startSaveTimer = function() {
$scope.killSaveTimer();
$scope.isNoteDirty = true;
console.log('startSaveTimer called ' + $scope.note.id);
//console.log('startSaveTimer called ' + $scope.note.id);
$scope.saveTimer = $timeout(function(){
$scope.saveNote();
}, 10000);
Expand Down
105 changes: 63 additions & 42 deletions zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@ angular.module('zeppelinWebApp')
$scope.paragraph = null;
$scope.editor = null;

var editorMode = {scala: 'ace/mode/scala', sql: 'ace/mode/sql', markdown: 'ace/mode/markdown',
sh: 'ace/mode/sh'};
var editorModes = {
'ace/mode/scala': /^%spark/,
'ace/mode/sql': /^%(\w*\.)?\wql/,
'ace/mode/markdown': /^%md/,
'ace/mode/sh': /^%sh/
};

// Controller init
$scope.init = function(newParagraph) {
Expand Down Expand Up @@ -420,8 +424,13 @@ angular.module('zeppelinWebApp')
};

$scope.aceChanged = function() {

$scope.dirtyText = $scope.editor.getSession().getValue();
$scope.startSaveTimer();

$timeout(function() {
$scope.setParagraphMode($scope.editor.getSession(), $scope.dirtyText, $scope.editor.getCursorPosition());
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't used Angular.js for some time and I don't quite remember its APIs but shouldn't $timeout accept a second parameter - the duration?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@martin-g, i have no angular experience. As backend dev i don't get exposed often to UI technologies like this and furthermore my favorite amongst them is Google Polymer. But this is another story :)
Here i have used $timeout (with default delay of 0) as a mean to async run the evaluation. This approach has been advised by the ACE-Editor team. And indeed without the $timeout various random errors pop-up during the mode setting.

Anyway this is a critical bit and i am not confident i've resolved it entirely. So by all means try to scrutinize it and help to do it right! ;)

};

$scope.aceLoaded = function(_editor) {
Expand All @@ -448,54 +457,66 @@ angular.module('zeppelinWebApp')
// not applying emacs key binding while the binding override Ctrl-v. default behavior of paste text on windows.
}

var sqlModeTest = /^%(\w*\.)?\wql/;

$scope.setParagraphMode = function(session, paragraphText) {
if (sqlModeTest.test(String(paragraphText))) {
session.setMode(editorMode.sql);
} else if ( String(paragraphText).startsWith('%md')) {
session.setMode(editorMode.markdown);
} else if ( String(paragraphText).startsWith('%sh')) {
session.setMode(editorMode.sh);
} else {
session.setMode(editorMode.scala);
$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;}

pos = session.getTextRange(new Range(0, 0, pos.row, pos.column)).length;
var buf = session.getValue();

// ensure the correct mode is set
$scope.setParagraphMode(session, buf);
websocketMsgSrv.completion($scope.paragraph.id, buf, pos);

$scope.$on('completionList', function(event, data) {
if (data.completions) {
var completions = [];
for (var c in data.completions) {
var v = data.completions[c];
completions.push({
name:v,
value:v,
score:300
});
}
callback(null, completions);
}
});
}
getCompletions : function(editor, session, pos, prefix, callback) {
if (!$scope.editor.isFocused() ){ return;}

pos = session.getTextRange(new Range(0, 0, pos.row, pos.column)).length;
var buf = session.getValue();

websocketMsgSrv.completion($scope.paragraph.id, buf, pos);

$scope.$on('completionList', function(event, data) {
if (data.completions) {
var completions = [];
for (var c in data.completions) {
var v = data.completions[c];
completions.push({
name:v,
value:v,
score:300
});
}
callback(null, completions);
}
});
}
};

langTools.setCompleters([remoteCompleter, langTools.keyWordCompleter, langTools.snippetCompleter, langTools.textCompleter]);

$scope.editor.setOptions({
enableBasicAutocompletion: true,
enableSnippets: false,
enableLiveAutocompletion:false
enableBasicAutocompletion: true,
enableSnippets: false,
enableLiveAutocompletion:false
});

$scope.handleFocus = function(value) {
Expand Down Expand Up @@ -590,7 +611,7 @@ angular.module('zeppelinWebApp')

$scope.getProgress = function() {
return ($scope.currentProgress) ? $scope.currentProgress : 0;
};
};

$scope.getExecutionTime = function() {
var pdata = $scope.paragraph;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ angular.module('zeppelinWebApp').service('websocketMsgSrv', function($rootScope,
websocketEvents.sendNewEvent({op: 'DEL_NOTE', data: {id: noteId}});
},

cloneNotebook: function(noteIdToClone, newNoteName ) {
cloneNotebook: function(noteIdToClone, newNoteName ) {
websocketEvents.sendNewEvent({op: 'CLONE_NOTE', data: {id: noteIdToClone, name: newNoteName}});
},
getNotebookList: function() {
Expand Down