in zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js [774:1041]
$scope.aceLoaded = function(_editor) {
let langTools = ace.require('ace/ext/language_tools');
let Range = ace.require('ace/range').Range;
_editor.$blockScrolling = Infinity;
$scope.editor = _editor;
$scope.editor.on('input', $scope.aceChanged);
if (_editor.container.id !== '{{paragraph.id}}_editor') {
$scope.editor.renderer.setShowGutter($scope.paragraph.config.lineNumbers);
$scope.editor.setShowFoldWidgets(false);
$scope.editor.setHighlightActiveLine(false);
$scope.editor.getSession().setUseWrapMode(true);
$scope.editor.setTheme('ace/theme/chrome');
$scope.editor.setReadOnly($scope.isRunning($scope.paragraph) || $scope.isNoteRunning);
$scope.editor.setHighlightActiveLine($scope.paragraphFocused);
if ($scope.paragraphFocused) {
let prefix = getParagraphMagic($scope.paragraph.text);
let paragraphText = $scope.paragraph.text ? $scope.paragraph.text.trim() : '';
$scope.editor.focus();
$scope.goToEnd($scope.editor);
if (prefix === paragraphText) {
$timeout(function() {
$scope.editor.gotoLine(2, 0);
}, 0);
}
}
autoAdjustEditorHeight(_editor);
let adjustEditorListener = () => autoAdjustEditorHeight(_editor);
angular.element(window).resize(adjustEditorListener);
$scope.$on('$destroy', () => angular.element(window).unbind('resize', adjustEditorListener));
if (navigator.appVersion.indexOf('Mac') !== -1) {
$scope.editor.setKeyboardHandler('ace/keyboard/emacs');
$rootScope.isMac = true;
} else if (navigator.appVersion.indexOf('Win') !== -1 ||
navigator.appVersion.indexOf('X11') !== -1 ||
navigator.appVersion.indexOf('Linux') !== -1) {
$rootScope.isMac = false;
// not applying emacs key binding while the binding override Ctrl-v. default behavior of paste text on windows.
}
$scope.$on('completionListLength', function(event, data) {
completionListLength = data;
});
$scope.$on('callCompletion', function(event, data) {
if($scope.paragraphFocused) {
websocketMsgSrv.completion($scope.paragraph.id, data.buf, data.pos);
}
});
let remoteCompleter = {
getCompletions: function(editor, session, pos, prefix, callback) {
let langTools = ace.require('ace/ext/language_tools');
let defaultKeywords = new Set();
// eslint-disable-next-line handle-callback-err
let getDefaultKeywords = function(err, completions) {
if (completions !== undefined) {
completions.forEach(function(c) {
defaultKeywords.add(c.value);
});
}
};
if (langTools.keyWordCompleter !== undefined) {
langTools.keyWordCompleter.getCompletions(editor, session, pos, prefix, getDefaultKeywords);
}
if (!editor.isFocused()) {
return;
}
pos = session.getTextRange(new Range(0, 0, pos.row, pos.column)).length;
let buf = session.getValue();
$rootScope.$broadcast('callCompletion', {buf: buf, pos: pos});
$scope.$on('completionList', function(event, data) {
let computeCaption = function(value, meta) {
let metaLength = meta !== undefined ? meta.length : 0;
let length = 42;
let whitespaceLength = 3;
let ellipses = '...';
let maxLengthCaption = length - metaLength - whitespaceLength - ellipses.length;
if (value !== undefined && value.length > maxLengthCaption) {
return value.substr(0, maxLengthCaption) + ellipses;
}
return value;
};
if (data.completions) {
let completions = [];
for (let c in data.completions) {
if (data.completions.hasOwnProperty(c)) {
let v = data.completions[c];
if (v.meta !== undefined && v.meta === 'keyword' && defaultKeywords.has(v.value.trim())) {
continue;
}
completions.push({
name: v.name,
value: v.value,
meta: v.meta,
caption: computeCaption(v.name, v.meta),
score: 300,
});
}
}
$rootScope.$broadcast('completionListLength', completions.length);
callback(null, completions);
}
});
},
};
langTools.setCompleters([remoteCompleter, langTools.keyWordCompleter, langTools.snippetCompleter,
langTools.textCompleter]);
$scope.editor.setOptions({
fontSize: $scope.paragraph.config.fontSize + 'pt',
enableBasicAutocompletion: true,
enableSnippets: false,
enableLiveAutocompletion: false,
});
$scope.editor.on('focus', function() {
handleFocus(true);
});
$scope.editor.on('blur', function() {
handleFocus(false);
$scope.saveParagraph($scope.paragraph);
});
$scope.editor.on('paste', function(e) {
if (e.text.indexOf('%') === 0) {
pastePercentSign = true;
}
});
$scope.editor.getSession().on('change', function(e, editSession) {
autoAdjustEditorHeight(_editor);
});
setParagraphMode($scope.editor.getSession(), $scope.editor.getSession().getValue());
// autocomplete on '.'
/*
$scope.editor.commands.on("afterExec", function(e, t) {
if (e.command.name == "insertstring" && e.args == "." ) {
var all = e.editor.completers;
//e.editor.completers = [remoteCompleter];
e.editor.execCommand("startAutocomplete");
//e.editor.completers = all;
}
});
*/
// remove binding
$scope.editor.commands.removeCommand('showSettingsMenu');
$scope.editor.commands.removeCommand('find');
$scope.editor.commands.removeCommand('replace');
let isOption = $rootScope.isMac ? 'option' : 'alt';
$scope.editor.commands.bindKey('ctrl-' + isOption + '-n.', null);
$scope.editor.commands.bindKey('ctrl-' + isOption + '-l', null);
$scope.editor.commands.bindKey('ctrl-' + isOption + '-w', null);
$scope.editor.commands.bindKey('ctrl-' + isOption + '-a', null);
$scope.editor.commands.bindKey('ctrl-' + isOption + '-k', null);
$scope.editor.commands.bindKey('ctrl-' + isOption + '-e', null);
$scope.editor.commands.bindKey('ctrl-' + isOption + '-t', null);
$scope.editor.commands.bindKey('ctrl-space', null);
if ($rootScope.isMac) {
$scope.editor.commands.bindKey('command-l', null);
} else {
$scope.editor.commands.bindKey('ctrl-l', null);
}
// autocomplete on 'ctrl+.'
$scope.editor.commands.bindKey('ctrl-.', 'startAutocomplete');
// Show autocomplete on tab
$scope.editor.commands.addCommand({
name: 'tabAutocomplete',
bindKey: {
win: 'tab',
mac: 'tab',
sender: 'editor|cli',
},
exec: function(env, args, request) {
let iCursor = $scope.editor.getCursorPosition();
let currentLine = $scope.editor.session.getLine(iCursor.row);
let isAllTabs = currentLine.substring(0, iCursor.column - 1).split('').every(function(char) {
return (char === '\t' || char === ' ');
});
// If user has pressed tab on first line char or if isTabCompletion() is false, keep existing behavior
// If user has pressed tab anywhere in between and editor mode is not %md, show autocomplete
if (!isAllTabs && iCursor.column && isTabCompletion()) {
$scope.editor.execCommand('startAutocomplete');
} else {
ace.config.loadModule('ace/ext/language_tools', function() {
$scope.editor.indent();
});
}
},
});
let keyBindingEditorFocusAction = function(scrollValue) {
let numRows = $scope.editor.getSession().getLength();
let currentRow = $scope.editor.getCursorPosition().row;
if (currentRow === 0 && scrollValue <= 0) {
// move focus to previous paragraph
$scope.$emit('moveFocusToPreviousParagraph', $scope.paragraph.id);
} else if (currentRow === numRows - 1 && scrollValue >= 0) {
$scope.$emit('moveFocusToNextParagraph', $scope.paragraph.id);
} else {
$scope.scrollToCursor($scope.paragraph.id, scrollValue);
}
};
// handle cursor moves
$scope.editor.keyBinding.origOnCommandKey = $scope.editor.keyBinding.onCommandKey;
$scope.editor.keyBinding.onCommandKey = function(e, hashId, keyCode) {
if ($scope.editor.completer && $scope.editor.completer.activated) { // if autocompleter is active
} else {
// fix ace editor focus issue in chrome (textarea element goes to top: -1000px after focused by cursor move)
if (parseInt(angular.element('#' + $scope.paragraph.id + '_editor > textarea')
.css('top').replace('px', '')) < 0) {
let position = $scope.editor.getCursorPosition();
let cursorPos = $scope.editor.renderer.$cursorLayer.getPixelPosition(position, true);
angular.element('#' + $scope.paragraph.id + '_editor > textarea').css('top', cursorPos.top);
}
let ROW_UP = -1;
let ROW_DOWN = 1;
switch (keyCode) {
case 38:
if (!e.shiftKey) {
keyBindingEditorFocusAction(ROW_UP);
}
break;
case 80:
if (e.ctrlKey && !e.altKey) {
keyBindingEditorFocusAction(ROW_UP);
}
break;
case 40:
if (!e.shiftKey) {
keyBindingEditorFocusAction(ROW_DOWN);
}
break;
case 78:
if (e.ctrlKey && !e.altKey) {
keyBindingEditorFocusAction(ROW_DOWN);
}
break;
}
}
this.origOnCommandKey(e, hashId, keyCode);
};
}
};