diff --git a/.gitignore b/.gitignore index 7aeb361fef7..ea16e75839d 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ conf/interpreter.json conf/notebook-authorization.json conf/shiro.ini conf/credentials.json +conf/code-editor.json # other generated files spark/dependency-reduced-pom.xml diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java index 0f7d8a1b9fc..4c7895bcd6b 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java @@ -45,6 +45,7 @@ import org.apache.zeppelin.search.SearchService; import org.apache.zeppelin.socket.NotebookServer; import org.apache.zeppelin.user.Credentials; +import org.apache.zeppelin.util.CodeEditorWebSettings; import org.apache.zeppelin.utils.SecurityUtils; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.server.*; @@ -70,6 +71,7 @@ public class ZeppelinServer extends Application { public static NotebookServer notebookWsServer; public static Helium helium; public static HeliumApplicationFactory heliumApplicationFactory; + public static CodeEditorWebSettings webEditorSetting; private SchedulerFactory schedulerFactory; private InterpreterFactory replFactory; @@ -103,6 +105,10 @@ public ZeppelinServer() throws Exception { // to update fire websocket event on application event. heliumApplicationFactory.setApplicationEventListener(notebookWsServer); + webEditorSetting = new CodeEditorWebSettings(conf); + // user web editor settings + webEditorSetting.deserialize(); + notebook.addNotebookEventListener(heliumApplicationFactory); } diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java index 0f8ce70fc39..57bfc924fb2 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java @@ -53,6 +53,8 @@ import org.apache.zeppelin.scheduler.Job.Status; import org.apache.zeppelin.server.ZeppelinServer; import org.apache.zeppelin.ticket.TicketContainer; +import org.apache.zeppelin.util.CodeEditorWebSettings; +import org.apache.zeppelin.util.CodeEditorWebSettings.EditorConfig; import org.apache.zeppelin.utils.SecurityUtils; import org.eclipse.jetty.websocket.servlet.WebSocketServlet; import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; @@ -88,6 +90,7 @@ String getKey() { private Notebook notebook() { return ZeppelinServer.notebook; } + private CodeEditorWebSettings codeEditorWebSettings() { return ZeppelinServer.webEditorSetting; } @Override public void configure(WebSocketServletFactory factory) { @@ -234,6 +237,12 @@ public void onMessage(NotebookSocket conn, String msg) { case LIST_UPDATE_NOTEBOOK_JOBS: unicastUpdateNotebookJobInfo(conn, messagereceived); break; + case GET_USER_CODE_EDITOR_SETTING: + getUserCodeEditorSetting(conn, messagereceived); + break; + case SAVE_USER_CODE_EDITOR_SETTING: + saveUserCodeEditorSetting(conn, messagereceived); + break; default: break; } @@ -743,6 +752,40 @@ private void completion(NotebookSocket conn, HashSet userAndRoles, Noteb conn.send(serializeMessage(resp)); } + // clover + private void getUserCodeEditorSetting(NotebookSocket conn, Message fromMessage) + throws IOException { + String userName = (String) fromMessage.get("principal"); + + if (userName == null) { + throw new IOException("principal is null"); + } + + List userEditorSetting = codeEditorWebSettings().getUserEditorConfig(userName); + + if (userEditorSetting == null) { + LOG.info("does not exists [{}] user code editor settings", userName); + conn.send(serializeMessage(new Message(OP.GET_USER_CODE_EDITOR_SETTING) + .put("editorSettings", new LinkedList<>()))); + } else { + conn.send(serializeMessage(new Message(OP.GET_USER_CODE_EDITOR_SETTING) + .put("editorSettings", userEditorSetting))); + } + } + + private void saveUserCodeEditorSetting(NotebookSocket conn, Message fromMessage) + throws IOException { + List userEditorSetting = fromMessage.getType("editorSettings"); + String userName = (String) fromMessage.get("principal"); + if (userName == null) { + throw new IOException("principal is null"); + } + codeEditorWebSettings().setUserEditorConfig(userName, userEditorSetting); + codeEditorWebSettings().serialize(); + + conn.send(serializeMessage(new Message(OP.SAVE_USER_CODE_EDITOR_SETTING))); + } + /** * When angular object updated from client * diff --git a/zeppelin-web/bower.json b/zeppelin-web/bower.json index 5d849b3490d..9bc4e7db427 100644 --- a/zeppelin-web/bower.json +++ b/zeppelin-web/bower.json @@ -41,15 +41,50 @@ "ace-builds": { "main": [ "src-noconflict/ace.js", - "src-noconflict/mode-scala.js", - "src-noconflict/mode-python.js", - "src-noconflict/mode-sql.js", - "src-noconflict/mode-markdown.js", - "src-noconflict/mode-sh.js", - "src-noconflict/mode-r.js", + "src-noconflict/mode-scala.js", + "src-noconflict/mode-python.js", + "src-noconflict/mode-sql.js", + "src-noconflict/mode-markdown.js", + "src-noconflict/mode-sh.js", + "src-noconflict/mode-r.js", + "src-noconflict/mode-mysql.js", + "src-noconflict/mode-pgsql.js", + "src-noconflict/mode-javascript.js", + "src-noconflict/mode-java.js", "src-noconflict/keybinding-emacs.js", "src-noconflict/ext-language_tools.js", - "src-noconflict/theme-chrome.js" + "src-noconflict/theme-ambiance.js", + "src-noconflict/theme-chaos.js", + "src-noconflict/theme-chrome.js", + "src-noconflict/theme-clouds.js", + "src-noconflict/theme-clouds_midnight.js", + "src-noconflict/theme-cobalt.js", + "src-noconflict/theme-crimson_editor.js", + "src-noconflict/theme-dawn.js", + "src-noconflict/theme-dreamweaver.js", + "src-noconflict/theme-eclipse.js", + "src-noconflict/theme-github.js", + "src-noconflict/theme-idle_fingers.js", + "src-noconflict/theme-katzenmilch.js", + "src-noconflict/theme-kr_theme.js", + "src-noconflict/theme-kuroir.js", + "src-noconflict/theme-merbivore.js", + "src-noconflict/theme-merbivore_soft.js", + "src-noconflict/theme-mono_industrial.js", + "src-noconflict/theme-monokai.js", + "src-noconflict/theme-pastel_on_dark.js", + "src-noconflict/theme-solarized_dark.js", + "src-noconflict/theme-solarized_light.js", + "src-noconflict/theme-terminal.js", + "src-noconflict/theme-textmate.js", + "src-noconflict/theme-tomorrow.js", + "src-noconflict/theme-tomorrow_night.js", + "src-noconflict/theme-tomorrow_night_blue.js", + "src-noconflict/theme-tomorrow_night_bright.js", + "src-noconflict/theme-tomorrow_night_eighties.js", + "src-noconflict/theme-twilight.js", + "src-noconflict/theme-vibrant_ink.js", + "src-noconflict/theme-xcode.js" ], "version": "1.1.9", "name": "ace-builds" diff --git a/zeppelin-web/src/app/app.js b/zeppelin-web/src/app/app.js index 98d6b879f09..7dc91f53d7a 100644 --- a/zeppelin-web/src/app/app.js +++ b/zeppelin-web/src/app/app.js @@ -78,6 +78,10 @@ templateUrl: 'app/configuration/configuration.html', controller: 'ConfigurationCtrl' }) + .when('/configuration/editor', { + templateUrl: 'app/editor-configuration/editor.configuration.html', + controller: 'EditorConfigurationCtrl' + }) .when('/search/:searchTerm', { templateUrl: 'app/search/result-list.html', controller: 'SearchResultCtrl' diff --git a/zeppelin-web/src/app/editor-configuration/editor.configuration.controller.js b/zeppelin-web/src/app/editor-configuration/editor.configuration.controller.js new file mode 100644 index 00000000000..95d1c1043be --- /dev/null +++ b/zeppelin-web/src/app/editor-configuration/editor.configuration.controller.js @@ -0,0 +1,231 @@ +/* + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +'use strict'; + +angular.module('zeppelinWebApp').controller('EditorConfigurationCtrl', function($scope, $route, $routeParams, $location, + $rootScope, $http, baseUrlSrv, $timeout, + editorConfigSrv, websocketMsgSrv) { + $scope.configrations = []; + $scope._ = _; + + $scope.dumyParagraphInformation = { + config: {looknfeel: 'default'}, + id: 'previewDumyNote', + paragraphs: [{ + id: '0', + text: '%spark', + config: { + colWidth: 12, + editorMode: 'ace/mode/scala', + enabled: true, + graph: { + 'mode': 'table', + 'height': 300.0, + 'optionOpen': false, + 'keys': [], + 'values': [], + 'groups': [], + 'scatter': {} + } + } + }] + }; + + $scope.isChangedConfigurationValue = false; + + $scope.updateEditorSettings = function() { + $rootScope.$broadcast('updateEditorSettings'); + }; + + $scope.startSaveTimer = function() { + $scope.killSaveTimer(); + $scope.isNoteDirty = false; + $scope.saveTimer = $timeout(function() { + }, 10000); + }; + + $scope.killSaveTimer = function() { + if ($scope.saveTimer) { + $timeout.cancel($scope.saveTimer); + $scope.saveTimer = null; + } + }; + + $scope.codeEditorConfigurations = angular.copy(editorConfigSrv.getEditorConfigLists()); + $scope.editorThemeList = editorConfigSrv.getThemeSupportLists(); + $scope.editorLanguageList = editorConfigSrv.getLanguageSupportLists(); + + $scope.isChangedConfiguration = function(isChanged) { + $scope.isChangedConfigurationValue = isChanged; + }; + + $scope.changeLanguage = function(languageType) { + $scope.isChangedConfiguration(true); + $scope.modifyTargetEditorSetting.language = languageType; + $scope.updateEditorSettings(); + }; + + $scope.changeTheme = function(newTheme) { + $scope.isChangedConfiguration(true); + $scope.modifyTargetEditorSetting.setting.theme = newTheme.themeValue; + $scope.updateEditorSettings(); + }; + + $scope.changeTabSize = function(tabSize) { + $scope.isChangedConfiguration(true); + $scope.modifyTargetEditorSetting.setting.tabSize = tabSize; + $scope.updateEditorSettings(); + }; + + $scope.changeFontSize = function(fontSize) { + $scope.isChangedConfiguration(true); + $scope.modifyTargetEditorSetting.setting.fontSize = fontSize; + $scope.updateEditorSettings(); + }; + + $scope.changeShowLineNumber = function(isShowLineNumber) { + $scope.isChangedConfiguration(true); + $scope.modifyTargetEditorSetting.setting.showLineNumber = isShowLineNumber; + $scope.updateEditorSettings(); + }; + + $scope.changeShowFocusLine = function(isShowActiveLine) { + $scope.isChangedConfiguration(true); + $scope.modifyTargetEditorSetting.setting.activeLine = isShowActiveLine; + $scope.updateEditorSettings(); + }; + + $scope.changeLiveAutoCompletion = function(isLiveAutoCompletion) { + $scope.isChangedConfiguration(true); + $scope.modifyTargetEditorSetting.setting.liveAutoCompletion = isLiveAutoCompletion; + $scope.updateEditorSettings(); + }; + + $scope.changeShowPrintMargin = function(isShowPrintMargin) { + $scope.isChangedConfiguration(true); + $scope.modifyTargetEditorSetting.setting.showPrintMargin = isShowPrintMargin; + $scope.updateEditorSettings(); + }; + + $scope.changeShowPrintMarginColumn = function(marginColumn) { + $scope.isChangedConfiguration(true); + $scope.modifyTargetEditorSetting.setting.showPrintMarginColumn = marginColumn; + $scope.updateEditorSettings(); + }; + + $scope.saveConfiguration = function() { + $scope.isChangedConfiguration(false); + $scope.restoreEditorConfig.userConfig = angular.copy($scope.modifyTargetEditorSetting); + websocketMsgSrv.saveUserCodeEditorSetting(editorConfigSrv.getEditorConfigLists()); + }; + + $scope.restoreConfiguration = function() { + if ($scope.selectedEditorConfigIndex !== undefined && $scope.restoreEditorConfig !== undefined) { + editorConfigSrv.setEditorConfig($scope.selectedEditorConfigIndex, $scope.restoreEditorConfig); + $scope.updateEditorSettings(); + } + }; + + $scope.changeEditorTargetFromName = function(editorModeName) { + var config = editorConfigSrv.findGetEditorConfigFromName(editorModeName); + if (config !== undefined) { + $scope.restoreEditorConfig = angular.copy(config.data); + $scope.selectedEditorConfigIndex = config.index; + $scope.modifyTargetEditorSetting = config.data.getUserConfig(); + } + }; + + $scope.changeEditorTargetFromIndex = function(index) { + var config = editorConfigSrv.getEditorConfig(index); + if (config !== undefined) { + $scope.restoreEditorConfig = angular.copy(config); + $scope.selectedEditorConfigIndex = index; + $scope.modifyTargetEditorSetting = config.getUserConfig(); + } + }; + + $scope.showRemoveConfigurationDialog = function() { + BootstrapDialog.confirm({ + closable: true, + type: BootstrapDialog.TYPE_DANGER, + title: 'DELETE', + message: 'Do you want to delete this editor configuration?', + callback: function(result) { + if (result) { + $scope.isChangedConfiguration(true); + $scope.removeConfiguration($scope.selectedEditorConfigIndex); + } + } + }); + }; + + $scope.showCreateConfigurationDialog = function() { + BootstrapDialog.show({ + title: 'Please Input to New <User Editor Setting> Name', + message: jQuery('
' + + '' + + '
'), + buttons: [{ + label: 'Create', + cssClass: 'btn-primary', + action: function(itSelf) { + var configName = angular.element('#newEditorConfigName').val(); + if (configName !== undefined || configName !== '') { + $scope.createConfiguration(configName); + itSelf.close(); + } + } + }] + }); + }; + + $scope.removeConfiguration = function(index) { + editorConfigSrv.removeEditorConfig(index); + init(); + }; + + $scope.userSelectConfigTarget = function(index) { + $scope.restoreConfiguration(); + $scope.changeEditorTargetFromIndex(index); + }; + + $scope.createConfiguration = function(configName) { + var newConfigName = configName; + var newConfigRegex = '^%' + configName + '$'; + var newConfigIndex = editorConfigSrv.newEditorConfig(newConfigName, newConfigRegex); + init(newConfigIndex); + }; + + var init = function(index) { + var targetIndex = index ? index : 0; + $scope.codeEditorConfigurations = angular.copy(editorConfigSrv.getEditorConfigLists()); + $scope.editorThemeList = editorConfigSrv.getThemeSupportLists(); + $scope.editorLanguageList = editorConfigSrv.getLanguageSupportLists(); + $scope.changeEditorTargetFromIndex(targetIndex); + }; + + init(); + + $rootScope.$on('receiveEditorSettings', function() { + init(); + }); + + $scope.$on('$destroy', function() { + $scope.restoreConfiguration(); + }); + +}); diff --git a/zeppelin-web/src/app/editor-configuration/editor.configuration.css b/zeppelin-web/src/app/editor-configuration/editor.configuration.css new file mode 100644 index 00000000000..470e2b1203a --- /dev/null +++ b/zeppelin-web/src/app/editor-configuration/editor.configuration.css @@ -0,0 +1,73 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.editorConfigurationHead { + margin: -10px -10px 20px; + padding: 10px 15px 5px 11px; + background: white; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15); + border-bottom: 1px solid #E5E5E5; +} + +.editorConfigurationHead .header { + font-family: 'Roboto', sans-serif; +} + +.editorConfigurationHead .configuration-title { + font-size: 20px; + font-weight: bold; + color: #3071a9; + float: left; + margin-top: 0; +} + +.editorConfigurationHead ul { + margin: 0; + padding: 0; +} + +.editorConfigurationHead .configurationInfo { + list-style-type: none; +} + +.editorConfigurationHead table { + table-layout: fixed; + word-break: break-all; +} + +.editorConfigurationHead table tr .configurationPropertyKey { + padding : 5px 5px 5px 5px; +} + +.editorConfigurationHead table tr .configurationPropertyValue { + padding : 5px 5px 5px 5px; + display: block; + max-height: 100px; + overflow-y: auto; +} + +.editorConfigurationHead h5 { + font-weight: bold; +} + +.editorTableNameColumn { + background-color: #eee; + padding-top: 15px !important; + padding-left: 15px !important; +} + +.editorTableValueColumn { + padding-top: 15px !important; + padding-left: 15px !important; +} diff --git a/zeppelin-web/src/app/editor-configuration/editor.configuration.html b/zeppelin-web/src/app/editor-configuration/editor.configuration.html new file mode 100644 index 00000000000..51456c60bc7 --- /dev/null +++ b/zeppelin-web/src/app/editor-configuration/editor.configuration.html @@ -0,0 +1,353 @@ + +
+
+
+
+

+ Editor Configurations +

+
+
+
+
+ You can change to Code Editor setting +
+
+
+
+
+ + + + +
+ + + + + +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+

Code Editor Settings

+
+ + + + + + + + + + + + + + + + + +
+ Editor Config Name + + +
+ Active Interpreter Tag Regex + + +
+ Active Language Syntax + + + + + +
+
+
+
+
+
+ +
+
+
+
+

+ Preview Editor +   + + {{userEditorConfig.getModeName()}} + {{userEditorConfig.getLanguage()}} + {{userEditorConfig.getModeRegex()}} + +

+
+
+ +
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+

Code Style Settings

+
+ + + + + + + + + + +
+ Indent Tab Size + + + + Font Size + + +
+
+
+
+ +
+
+
+
+

Code Editor Usability Settings

+
+ + + + + + + + + + + + + + + + + + + + + + +
+
+ Editor Theme +
+
+ + + + + +
+ Show Line Number +
+
+ +
+
+ Highlight Active Line +
+
+ + +
+ Live Auto Completion +
+
+ +
+
+ Show print margin +
+
+ + +
+ Show print margin Column +
+
+ +
+
+
+
+
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js index 1d672e75cbb..76e57fda71a 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js @@ -16,12 +16,14 @@ angular.module('zeppelinWebApp').controller('ParagraphCtrl', function($scope, $rootScope, $route, $window, $routeParams, $location, $timeout, $compile, $http, websocketMsgSrv, baseUrlSrv, ngToast, - saveAsService) { + saveAsService, editorConfigSrv) { var ANGULAR_FUNCTION_OBJECT_NAME_PREFIX = '_Z_ANGULAR_FUNC_'; $scope.parentNote = null; $scope.paragraph = null; $scope.originalText = ''; $scope.editor = null; + $scope.defaultEditorConfig = editorConfigSrv.getDefaultEditorSetting(); + $scope.userEditorConfig = $scope.defaultEditorConfig; var paragraphScope = $rootScope.$new(true, $rootScope); @@ -78,13 +80,68 @@ 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/ + $rootScope.$on('updateEditorSettings', function() { + console.log('change to paragraph editor theme', $scope.defaultEditorConfig); + $scope.defaultEditorConfig = editorConfigSrv.getDefaultEditorSetting(); + $scope.userEditorConfig = $scope.defaultEditorConfig; + $scope.setParagraphMode( + $scope.editor.getSession(), + $scope.paragraph.text, + {row: 0, column: 0} + ); + }); + + $scope.setEditorConfig = function(newValue, oldValue) { + if (newValue === undefined && oldValue === undefined) { + if ($scope.editor !== null) { + $scope.editor.renderer.setShowGutter(false); + $scope.editor.setHighlightGutterLine(false); + $scope.editor.getSession().setMode('ace/mode/scala'); + $scope.editor.setHighlightActiveLine(false); + $scope.editor.getSession().setTabSize(4); + $scope.editor.setTheme('ace/theme/chrome'); + $scope.editor.setShowFoldWidgets(true); + $scope.editor.setOptions({ + enableBasicAutocompletion: true, + enableSnippets: false, + enableLiveAutocompletion: false, + fontSize: 12 + }); + + $scope.editor.setShowPrintMargin(false); + $scope.editor.setPrintMarginColumn(false); + } + $scope.paragraph.config.editorMode = 'ace/mode/scala'; + return; + } + + var targetEditorConfig = newValue !== undefined ? newValue : oldValue; + var isShowLineNumberValue = $scope.paragraph.config.lineNumbers; + + // show line number option precious order (paragraph setting > user theme) + if (isShowLineNumberValue === undefined) { + isShowLineNumberValue = targetEditorConfig.isShowNumberLine(); + } + + $scope.editor.renderer.setShowGutter(isShowLineNumberValue); + $scope.editor.setHighlightGutterLine(isShowLineNumberValue); + $scope.editor.getSession().setMode(targetEditorConfig.getMode()); + $scope.editor.setHighlightActiveLine(targetEditorConfig.isActiveLine()); + $scope.editor.getSession().setTabSize(targetEditorConfig.getTabSize()); + $scope.editor.setTheme(targetEditorConfig.getTheme()); + $scope.editor.setShowFoldWidgets(true); + $scope.editor.setOptions({ + enableBasicAutocompletion: true, + enableSnippets: false, + enableLiveAutocompletion: targetEditorConfig.isLiveAutoCompletion(), + fontSize: targetEditorConfig.getFontSizeHtmlFormat() + }); + console.log('live set ', targetEditorConfig.isLiveAutoCompletion()); + $scope.editor.setShowPrintMargin(targetEditorConfig.isShowPrintMargin()); + $scope.editor.setPrintMarginColumn(targetEditorConfig.getShowPrintMarginColumn()); + $scope.paragraph.config.editorMode = targetEditorConfig.getMode(); + + $scope.userEditorConfig = targetEditorConfig; }; // Controller init @@ -536,17 +593,19 @@ angular.module('zeppelinWebApp').controller('ParagraphCtrl', function($scope, $r _editor.$blockScrolling = Infinity; $scope.editor = _editor; 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.setHighlightGutterLine(false); - $scope.editor.getSession().setUseWrapMode(true); - $scope.editor.setTheme('ace/theme/chrome'); if ($scope.paragraphFocused) { $scope.editor.focus(); $scope.goToLineEnd(); } + if (editorConfigSrv.isServerReceived() === false) { + editorConfigSrv.setServerReceived(true); + websocketMsgSrv.getUserCodeEditorSetting(); + } + + // set default editor config + $scope.setEditorConfig($scope.userEditorConfig, null); + autoAdjustEditorHeight(_editor.container.id); angular.element(window).resize(function() { autoAdjustEditorHeight(_editor.container.id); @@ -569,23 +628,9 @@ angular.module('zeppelinWebApp').controller('ParagraphCtrl', function($scope, $r 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 tempEditorConfig = editorConfigSrv.findGetEditorConfigFromInterpreterTag(paragraphText); + $scope.setEditorConfig(tempEditorConfig, $scope.userEditorConfig); + return true; } } }; @@ -620,9 +665,10 @@ angular.module('zeppelinWebApp').controller('ParagraphCtrl', function($scope, $r langTools.textCompleter]); $scope.editor.setOptions({ - enableBasicAutocompletion: true, + enableBasicAutocompletion: false, enableSnippets: false, - enableLiveAutocompletion: false + enableLiveAutocompletion: false, + fontSize: 12 }); $scope.handleFocus = function(value) { @@ -646,7 +692,7 @@ angular.module('zeppelinWebApp').controller('ParagraphCtrl', function($scope, $r autoAdjustEditorHeight(_editor.container.id); }); - $scope.setParagraphMode($scope.editor.getSession(), $scope.editor.getSession().getValue()); + $scope.setParagraphMode($scope.editor.getSession(), $scope.editor.getSession().getValue(), {row: 0, column: 0}); // autocomplete on '.' /* diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.css b/zeppelin-web/src/app/notebook/paragraph/paragraph.css index d8b464eeed1..5f1e3dc5af8 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.css +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.css @@ -263,7 +263,6 @@ table.dataTable.table-condensed .sorting_desc:after { .paragraph .editor { width: 100%; border-left: 4px solid #DDDDDD; - background: rgba(255, 255, 255, 0.0); margin: 7px 0 2px 0; } @@ -271,10 +270,6 @@ table.dataTable.table-condensed .sorting_desc:after { border-left: 4px solid #E67E22 !important; } -.paragraph .ace_print-margin { - background: none !important; -} - .ace_marker-layer .ace_selection { z-index: 0 !important; } diff --git a/zeppelin-web/src/components/editor-config/editor.config.service.js b/zeppelin-web/src/components/editor-config/editor.config.service.js new file mode 100644 index 00000000000..192e252552b --- /dev/null +++ b/zeppelin-web/src/components/editor-config/editor.config.service.js @@ -0,0 +1,349 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +'use strict'; + +angular.module('zeppelinWebApp').service('editorConfigSrv', function($q) { + + var builtInConfig = { + editorSettings: [ + { + userConfig: + { + modeName: 'Scala Spark', + language: 'scala', + modeRegex: '^%.*(spark)$', + setting: { + theme: 'chrome', + tabSize: 4, + showLineNumber: false, + activeLine: false, + liveAutoCompletion: false, + showPrintMargin: false, + showPrintMarginColumn: 100, + fontSize: 10 + } + } + }, + { + userConfig: + { + modeName: 'Python', + language: 'python', + modeRegex: '^%.*(pyspark|python)$', + setting: { + theme: 'chrome', + tabSize: 4, + showLineNumber: false, + activeLine: false, + liveAutoCompletion: false, + showPrintMargin: false, + showPrintMarginColumn: 100, + fontSize: 10 + } + } + }, + { + userConfig: { + modeName: 'R', + language: 'r', + modeRegex: '^%(r|sparkr|knitr).*', + setting: { + theme: 'chrome', + tabSize: 4, + showLineNumber: false, + activeLine: false, + liveAutoCompletion: false, + showPrintMargin: false, + showPrintMarginColumn: 100, + fontSize: 10 + } + } + }, + { + userConfig: { + modeName: 'SQL', + language: 'sql', + modeRegex: '^%(\w*\.)?\wql', + setting: { + theme: 'chrome', + tabSize: 4, + showLineNumber: false, + activeLine: false, + liveAutoCompletion: false, + showPrintMargin: false, + showPrintMarginColumn: 100, + fontSize: 10 + } + } + }, + { + userConfig: + { + modeName: 'Markdown', + language: 'markdown', + modeRegex: '^%md', + setting: { + theme: 'chrome', + tabSize: 4, + showLineNumber: false, + activeLine: false, + liveAutoCompletion: false, + showPrintMargin: false, + showPrintMarginColumn: 100, + fontSize: 10 + } + } + }, + { + userConfig: + { + modeName: 'Sh', + language: 'sh', + modeRegex: '^%sh', + setting: { + theme: 'chrome', + tabSize: 4, + showLineNumber: false, + activeLine: false, + liveAutoCompletion: false, + showPrintMargin: false, + showPrintMarginColumn: 100, + fontSize: 10 + } + } + } + ] + }; + + var EditorConfigObject = { + userConfig: {}, + setUserConfig: function(newConfig) { + this.userConfig = newConfig; + }, + getUserConfig: function() { + return this.userConfig; + }, + getLanguage: function() { + return this.userConfig.language; + }, + getModeName: function() { + return this.userConfig.modeName; + }, + getMode: function() { + return 'ace/mode/' + this.userConfig.language; + }, + getModeRegex: function() { + return this.userConfig.modeRegex; + }, + getThemeName: function() { + return this.userConfig.setting.theme; + }, + getTheme: function() { + return 'ace/theme/' + this.userConfig.setting.theme; + }, + getTabSize: function() { + return this.userConfig.setting.tabSize; + }, + getShowPrintMarginColumn: function() { + return this.userConfig.setting.showPrintMarginColumn; + }, + getFontFamily: function() { + return this.userConfig.setting.fontFamily; + }, + getFontSize: function() { + return this.userConfig.setting.fontSize; + }, + getFontSizeHtmlFormat: function() { + return this.userConfig.setting.fontSize + 'pt'; + }, + isShowNumberLine: function() { + return this.userConfig.setting.showLineNumber; + }, + isActiveLine: function() { + return this.userConfig.setting.activeLine; + }, + isShowPrintMargin: function() { + return this.userConfig.setting.showPrintMargin; + }, + isLiveAutoCompletion: function() { + return this.userConfig.setting.liveAutoCompletion; + }, + isCorrectEditorFromInterpreterTag: function(context) { + var checkRegex = new RegExp(this.getModeRegex()); + return checkRegex.test(context); + } + }; + + var themeSupportLists = [ + {'themeValue': 'ambiance'}, + {'themeValue': 'chaos'}, + {'themeValue': 'chrome'}, + {'themeValue': 'clouds'}, + {'themeValue': 'clouds_midnight'}, + {'themeValue': 'cobalt'}, + {'themeValue': 'crimson_editor'}, + {'themeValue': 'dawn'}, + {'themeValue': 'dreamweaver'}, + {'themeValue': 'eclipse'}, + {'themeValue': 'github'}, + {'themeValue': 'idle_fingers'}, + {'themeValue': 'katzenmilch'}, + {'themeValue': 'kr_theme'}, + {'themeValue': 'kuroir'}, + {'themeValue': 'merbivore'}, + {'themeValue': 'merbivore_soft'}, + {'themeValue': 'mono_industrial'}, + {'themeValue': 'monokai'}, + {'themeValue': 'pastel_on_dark'}, + {'themeValue': 'solarized_dark'}, + {'themeValue': 'solarized_light'}, + {'themeValue': 'terminal'}, + {'themeValue': 'textmate'}, + {'themeValue': 'tomorrow'}, + {'themeValue': 'tomorrow_night'}, + {'themeValue': 'tomorrow_night_blue'}, + {'themeValue': 'tomorrow_night_bright'}, + {'themeValue': 'tomorrow_night_eighties'}, + {'themeValue': 'twilight'}, + {'themeValue': 'vibrant_ink'}, + {'themeValue': 'xcode'} + ]; + + var languageSupportLists = [ + {languageType: 'scala'}, + {languageType: 'sh'}, + {languageType: 'java'}, + {languageType: 'sql'}, + {languageType: 'r'}, + {languageType: 'markdown'}, + {languageType: 'python'}, + {languageType: 'javascript'}, + {languageType: 'mysql'}, + {languageType: 'pgsql'} + ]; + + var defaultModeValue = 'Scala Spark'; + var isServerReceivedValue = false; + var editorConfigList = []; + + function initializeEditorSettings(userEditorSettings) { + return $q(function(success, fail) { + if (userEditorSettings.length > 0) { + editorConfigList.splice(0, editorConfigList.length); + userEditorSettings.map(function(editorConfig) { + var configItem = angular.copy(EditorConfigObject); + configItem.setUserConfig(editorConfig.userConfig); + editorConfigList.push(configItem); + }); + } + if (success !== undefined) { + success(); + } + }); + } + + this.initConfig = initializeEditorSettings; + + this.initConfig(builtInConfig.editorSettings); + + this.isServerReceived = function() { + return isServerReceivedValue; + }; + + this.setServerReceived = function(status) { + isServerReceivedValue = status; + }; + + this.getEditorConfigLists = function() { + return editorConfigList; + }; + + this.setEditorConfig = function(index, data) { + editorConfigList.splice(index, 1, data); + }; + + this.getEditorConfig = function(index) { + return editorConfigList[index]; + }; + + this.newEditorConfig = function(configName, configRegex) { + console.log(configName); + var newConfig = { + modeName: configName, + language: 'scala', + modeRegex: configRegex, + setting: { + theme: 'chrome', + tabSize: 4, + showLineNumber: false, + activeLine: false, + liveAutoCompletion: false, + showPrintMargin: false, + showPrintMarginColumn: 100, + fontSize: 10 + } + }; + var configItem = angular.copy(EditorConfigObject); + configItem.setUserConfig(newConfig); + console.log(configItem); + editorConfigList.push(configItem); + return editorConfigList.length - 1; + }; + + this.removeEditorConfig = function(index) { + editorConfigList.splice(index, 1); + }; + + this.getThemeSupportLists = function() { + return angular.copy(themeSupportLists); + }; + + this.getLanguageSupportLists = function() { + return angular.copy(languageSupportLists); + }; + + this.findGetEditorConfigFromName = function(modeName) { + var index = _.findIndex(editorConfigList, function(config) { + return config.getModeName() === modeName; + }); + return index < 0 ? undefined : {index: index, data: editorConfigList[index]}; + }; + + this.findGetEditorConfigFromInterpreterTag = function(interpreterTag) { + var index = _.findIndex(editorConfigList, function(config) { + return config.isCorrectEditorFromInterpreterTag(interpreterTag); + }); + return editorConfigList[index]; + }; + + this.getDefaultModeName = function() { + return defaultModeValue; + }; + + this.getDefaultEditorSetting = function() { + var defaultConfigIndex = _.findIndex(editorConfigList, function(config) { + if (config === undefined) { + return false; + } + return config.getModeName() === defaultModeValue; + }); + + if (defaultConfigIndex >= 0) { + return editorConfigList[defaultConfigIndex]; + } else { + return editorConfigList ? editorConfigList[0] : undefined; + } + }; + +}); diff --git a/zeppelin-web/src/components/navbar/navbar.html b/zeppelin-web/src/components/navbar/navbar.html index 6b9e7860972..70f44e8f647 100644 --- a/zeppelin-web/src/components/navbar/navbar.html +++ b/zeppelin-web/src/components/navbar/navbar.html @@ -87,6 +87,7 @@
  • Interpreter
  • Credential
  • +
  • Editor Configuration
  • Configuration
  • Logout
  • diff --git a/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js b/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js index 76571374998..c408fe8b33e 100644 --- a/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js +++ b/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js @@ -14,7 +14,7 @@ 'use strict'; angular.module('zeppelinWebApp').factory('websocketEvents', - function($rootScope, $websocket, $location, baseUrlSrv) { + function($rootScope, $websocket, $location, $timeout, $window, baseUrlSrv, editorConfigSrv) { var websocketCalls = {}; websocketCalls.ws = $websocket(baseUrlSrv.getWebsocketUrl()); @@ -109,6 +109,14 @@ angular.module('zeppelinWebApp').factory('websocketEvents', $rootScope.$broadcast('appLoad', data); } else if (op === 'APP_STATUS_CHANGE') { $rootScope.$broadcast('appStatusChange', data); + } else if (op === 'GET_USER_CODE_EDITOR_SETTING') { + editorConfigSrv.initConfig(data.editorSettings).then(function() { + $timeout(function() { + $rootScope.$broadcast('receiveEditorSettings'); + $rootScope.$broadcast('updateEditorSettings'); + }, 1000); + }); + editorConfigSrv.setServerReceived(true); } else if (op === 'LIST_REVISION_HISTORY') { $rootScope.$broadcast('listRevisionHistory', data); } else if (op === 'NOTE_REVISION') { diff --git a/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js b/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js index a4f7802d348..052d8dacbec 100644 --- a/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js +++ b/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js @@ -196,6 +196,28 @@ angular.module('zeppelinWebApp').service('websocketMsgSrv', function($rootScope, unsubscribeJobManager: function() { websocketEvents.sendNewEvent({op: 'UNSUBSCRIBE_JOBMANAGER'}); + }, + + getUserCodeEditorSetting: function() { + websocketEvents.sendNewEvent( + { + op: 'GET_USER_CODE_EDITOR_SETTING', + data: {principal: $rootScope.ticket.principal} + } + ); + }, + + saveUserCodeEditorSetting: function(userEditorSettingData) { + console.log(userEditorSettingData); + websocketEvents.sendNewEvent( + { + op: 'SAVE_USER_CODE_EDITOR_SETTING', + data: { + principal: $rootScope.ticket.principal, + editorSettings: userEditorSettingData + } + } + ); } }; diff --git a/zeppelin-web/src/index.html b/zeppelin-web/src/index.html index 9fe9489501c..9a1db95eda0 100644 --- a/zeppelin-web/src/index.html +++ b/zeppelin-web/src/index.html @@ -59,6 +59,7 @@ + @@ -119,9 +120,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -157,6 +193,7 @@ + @@ -178,6 +215,7 @@ + diff --git a/zeppelin-web/test/karma.conf.js b/zeppelin-web/test/karma.conf.js index 778f0f151fd..5ada01d23bb 100644 --- a/zeppelin-web/test/karma.conf.js +++ b/zeppelin-web/test/karma.conf.js @@ -39,9 +39,44 @@ module.exports = function(config) { 'bower_components/ace-builds/src-noconflict/mode-markdown.js', 'bower_components/ace-builds/src-noconflict/mode-sh.js', 'bower_components/ace-builds/src-noconflict/mode-r.js', + 'bower_components/ace-builds/src-noconflict/mode-mysql.js', + 'bower_components/ace-builds/src-noconflict/mode-pgsql.js', + 'bower_components/ace-builds/src-noconflict/mode-javascript.js', + 'bower_components/ace-builds/src-noconflict/mode-java.js', 'bower_components/ace-builds/src-noconflict/keybinding-emacs.js', 'bower_components/ace-builds/src-noconflict/ext-language_tools.js', + 'bower_components/ace-builds/src-noconflict/theme-ambiance.js', + 'bower_components/ace-builds/src-noconflict/theme-chaos.js', 'bower_components/ace-builds/src-noconflict/theme-chrome.js', + 'bower_components/ace-builds/src-noconflict/theme-clouds.js', + 'bower_components/ace-builds/src-noconflict/theme-clouds_midnight.js', + 'bower_components/ace-builds/src-noconflict/theme-cobalt.js', + 'bower_components/ace-builds/src-noconflict/theme-crimson_editor.js', + 'bower_components/ace-builds/src-noconflict/theme-dawn.js', + 'bower_components/ace-builds/src-noconflict/theme-dreamweaver.js', + 'bower_components/ace-builds/src-noconflict/theme-eclipse.js', + 'bower_components/ace-builds/src-noconflict/theme-github.js', + 'bower_components/ace-builds/src-noconflict/theme-idle_fingers.js', + 'bower_components/ace-builds/src-noconflict/theme-katzenmilch.js', + 'bower_components/ace-builds/src-noconflict/theme-kr_theme.js', + 'bower_components/ace-builds/src-noconflict/theme-kuroir.js', + 'bower_components/ace-builds/src-noconflict/theme-merbivore.js', + 'bower_components/ace-builds/src-noconflict/theme-merbivore_soft.js', + 'bower_components/ace-builds/src-noconflict/theme-mono_industrial.js', + 'bower_components/ace-builds/src-noconflict/theme-monokai.js', + 'bower_components/ace-builds/src-noconflict/theme-pastel_on_dark.js', + 'bower_components/ace-builds/src-noconflict/theme-solarized_dark.js', + 'bower_components/ace-builds/src-noconflict/theme-solarized_light.js', + 'bower_components/ace-builds/src-noconflict/theme-terminal.js', + 'bower_components/ace-builds/src-noconflict/theme-textmate.js', + 'bower_components/ace-builds/src-noconflict/theme-tomorrow.js', + 'bower_components/ace-builds/src-noconflict/theme-tomorrow_night.js', + 'bower_components/ace-builds/src-noconflict/theme-tomorrow_night_blue.js', + 'bower_components/ace-builds/src-noconflict/theme-tomorrow_night_bright.js', + 'bower_components/ace-builds/src-noconflict/theme-tomorrow_night_eighties.js', + 'bower_components/ace-builds/src-noconflict/theme-twilight.js', + 'bower_components/ace-builds/src-noconflict/theme-vibrant_ink.js', + 'bower_components/ace-builds/src-noconflict/theme-xcode.js', 'bower_components/angular-ui-ace/ui-ace.js', 'bower_components/jquery.scrollTo/jquery.scrollTo.js', 'bower_components/d3/d3.js', diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java index 1845e6c6a9e..f5b8c8f4af9 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java @@ -362,6 +362,10 @@ public String getInterpreterSettingPath() { return getRelativeDir(String.format("%s/interpreter.json", getConfDir())); } + public String getUserCodeWebEditorSettingPath() { + return getRelativeDir(String.format("%s/code-editor.json", getConfDir())); + } + public String getHeliumConfPath() { return getRelativeDir(String.format("%s/helium.json", getConfDir())); } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/socket/Message.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/socket/Message.java index e2f5fe9186f..46affc63fed 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/socket/Message.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/socket/Message.java @@ -127,8 +127,10 @@ public static enum OP { APP_STATUS_CHANGE, // [s-c] on app status change LIST_NOTEBOOK_JOBS, // [c-s] get notebook job management infomations - LIST_UPDATE_NOTEBOOK_JOBS // [c-s] get job management informations for until unixtime + LIST_UPDATE_NOTEBOOK_JOBS, // [c-s] get job management informations for until unixtime // @param unixTime + GET_USER_CODE_EDITOR_SETTING, // [c-s] get user web editor setting + SAVE_USER_CODE_EDITOR_SETTING // [c-s] save user web editor setting } public OP op; diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/util/CodeEditorWebSettings.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/util/CodeEditorWebSettings.java new file mode 100644 index 00000000000..8b443f8fc4f --- /dev/null +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/util/CodeEditorWebSettings.java @@ -0,0 +1,312 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.zeppelin.util; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import org.apache.zeppelin.conf.ZeppelinConfiguration; +import org.apache.zeppelin.interpreter.InterpreterFactory; +import org.apache.zeppelin.interpreter.InterpreterInfoSaving; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Code editor settings for Zeppelin Web Application + */ +public class CodeEditorWebSettings { + private static Logger logger = LoggerFactory.getLogger(CodeEditorWebSettings.class); + + protected UserEditorSettingDao userEditorSettingDao; + protected final String USER_EDITOR_SETTINGS_KEY_NAME = "editorInformation"; + + private ZeppelinConfiguration conf; + private Gson gson; + + public CodeEditorWebSettings(ZeppelinConfiguration conf) { + GsonBuilder builder = new GsonBuilder(); + builder.setPrettyPrinting(); + gson = builder.create(); + + this.conf = conf; + userEditorSettingDao = new UserEditorSettingDao(); + } + + public List getUserEditorConfig(String userName) { + logger.info("clover 1 {}", userEditorSettingDao.toString()); + return userEditorSettingDao.getUserSetting(userName); + } + + public void setUserEditorConfig(String userName, List userEditorSetting ) { + userEditorSettingDao.setUserSetting(userName, userEditorSetting); + } + + public String getUserEditorSettingsKeyName() { + return USER_EDITOR_SETTINGS_KEY_NAME; + } + + public boolean deserialize() { + try { + String jsonString = loadFromFileToString(conf.getUserCodeWebEditorSettingPath()); + if (jsonString == null) { + logger.info("code editor setting context is null"); + return false; + } + + logger.info("load to User code editor setting", jsonString); + Map>> mapData = gson.fromJson(jsonString, Map.class); + if (mapData == null) { + logger.info("code editor setting is invalid json format - {}", + conf.getUserCodeWebEditorSettingPath()); + return false; + } + userEditorSettingDao = new UserEditorSettingDao(); + if (mapData.get(getUserEditorSettingsKeyName()) != null) { + userEditorSettingDao.setEditorInformation(mapData.get(getUserEditorSettingsKeyName())); + } else { + logger.info("Exists to code editor setting, but can not load to {} key", + getUserEditorSettingsKeyName()); + } + + } catch (IOException ex) { + logger.error("can not read code editor setting file - {}", + conf.getUserCodeWebEditorSettingPath()); + return false; + } + + return true; + } + + public boolean serialize() { + try { + saveToJsonFile(conf.getUserCodeWebEditorSettingPath()); + } catch (IOException ex) { + logger.error("can not write code editor setting file - {}", + conf.getUserCodeWebEditorSettingPath()); + return false; + } + return true; + } + + protected String loadFromFileToString(String configPath) throws IOException { + File settingFile = new File(configPath); + if (!settingFile.exists()) { + // nothing to read + return null; + } + FileInputStream fis = new FileInputStream(settingFile); + InputStreamReader isr = new InputStreamReader(fis); + BufferedReader bufferedReader = new BufferedReader(isr); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = bufferedReader.readLine()) != null) { + sb.append(line); + } + isr.close(); + fis.close(); + + return sb.toString(); + } + + protected void saveToJsonFile(String configPath) throws IOException { + String jsonString = gson.toJson(userEditorSettingDao, + new TypeToken() {}.getType()); + + File settingFile = new File(configPath); + if (!settingFile.exists()) { + settingFile.createNewFile(); + } + + FileOutputStream fos = new FileOutputStream(settingFile, false); + OutputStreamWriter out = new OutputStreamWriter(fos); + out.append(jsonString); + out.close(); + fos.close(); + } + /** + * Code editor settings for Zeppelin Web Application + * data access object + */ + public class UserEditorSettingDao { + public Map> editorInformation; + + public UserEditorSettingDao() { + this.editorInformation = new HashMap<>(); + } + + public Map> getEditorInformation() { + return this.editorInformation; + } + + public void setEditorInformation(Map> editorInformation) { + this.editorInformation = editorInformation; + } + + public List getUserSetting(String userName) { + return editorInformation.get(userName); + } + + public void setUserSetting(String userName, List editorConfig) { + this.editorInformation.put(userName, editorConfig); + } + + } + /** + * Code editor settings for Zeppelin Web Application + * editor config class + */ + public class EditorConfig { + private UserConfig userConfig; + + public UserConfig getUserConfig() { + return this.userConfig; + } + + public void setUserConfig(UserConfig userConfig) { + this.userConfig = userConfig; + } + }; + + /** + * Code editor settings for Zeppelin Web Application + * user config class + */ + public class UserConfig { + public String modeName; + public String language; + public String modeRegex; + public Setting setting; + + public String getModeName() { + return modeName; + } + + public void setModeName(String modeName) { + this.modeName = modeName; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + public String getModeRegex() { + return modeRegex; + } + + public void setModeRegex(String modeRegex) { + this.modeRegex = modeRegex; + } + + public Setting getSetting() { + return setting; + } + + public void setSetting(Setting setting) { + this.setting = setting; + } + + /** + * Code editor settings for Zeppelin Web Application + * setting sub class + */ + public class Setting { + public String theme; + public Integer tabSize; + public Integer showLineNumber; + public boolean activeLine; + public boolean liveAutoCompletion; + public boolean showPrintMargin; + public Integer showPrintMarginColumn; + public Integer fontSize; + + public String getTheme() { + return theme; + } + + public void setTheme(String theme) { + this.theme = theme; + } + + public int getTabSize() { + return tabSize; + } + + public void setTabSize(int tabSize) { + this.tabSize = tabSize; + } + + public int getShowLineNumber() { + return showLineNumber; + } + + public void setShowLineNumber(int showLineNumber) { + this.showLineNumber = showLineNumber; + } + + public boolean isActiveLine() { + return activeLine; + } + + public void setActiveLine(boolean activeLine) { + this.activeLine = activeLine; + } + + public boolean isLiveAutoCompletion() { + return liveAutoCompletion; + } + + public void setLiveAutoCompletion(boolean liveAutoCompletion) { + this.liveAutoCompletion = liveAutoCompletion; + } + + public boolean isShowPrintMargin() { + return showPrintMargin; + } + + public void setShowPrintMargin(boolean showPrintMargin) { + this.showPrintMargin = showPrintMargin; + } + + public int getShowPrintMarginColumn() { + return showPrintMarginColumn; + } + + public void setShowPrintMarginColumn(int showPrintMarginColumn) { + this.showPrintMarginColumn = showPrintMarginColumn; + } + + public int getFontSize() { + return fontSize; + } + + public void setFontSize(int fontSize) { + this.fontSize = fontSize; + } + } + + } +}