Repository: zeppelin Updated Branches: refs/heads/master cc0839efd -> ecb688f45
[ZEPPELIN-2460] Highlight active line in editor ### What is this PR for? Highlight active line. ### What type of PR is it? [Improvement] ### Todos * [x] - Added ui-ace option * [x] - Fix css for paragraph control not to be overrided by active line ### What is the Jira issue? [ZEPPELIN-2460](https://issues.apache.org/jira/browse/ZEPPELIN-2460) ### How should this be tested? 1. Build: `mvn clean package -DskipTests; ./bin/zeppelin-daemon.sh restart` 2. Open a note and write some text. ### Screenshots (if appropriate)  ### Questions: * Does the licenses files need update? - NO * Is there breaking changes for older versions? - NO * Does this needs documentation? - NO Author: 1ambda <[email protected]> Closes #2356 from 1ambda/ZEPPELIN-2460/highlight-active-line and squashes the following commits: 2809093 [1ambda] fix: xpath for select, checkbox DOM 087942b [1ambda] fix: Use ng-if for title e60f9a2 [1ambda] feat: Display title, control in the same line bfa2fed [1ambda] fix: Set active line when note is created ade9179 [1ambda] fix: Use blue-light color for active line 0366948 [1ambda] fix: Show single active line in a note dfcb8aa [1ambda] fix: Control setting CSS not to overwrite active line 6424db5 [1ambda] feat: Highlight active line in editor Project: http://git-wip-us.apache.org/repos/asf/zeppelin/repo Commit: http://git-wip-us.apache.org/repos/asf/zeppelin/commit/ecb688f4 Tree: http://git-wip-us.apache.org/repos/asf/zeppelin/tree/ecb688f4 Diff: http://git-wip-us.apache.org/repos/asf/zeppelin/diff/ecb688f4 Branch: refs/heads/master Commit: ecb688f45397be8c2e879663908b95c8a35964ec Parents: cc0839e Author: 1ambda <[email protected]> Authored: Mon May 22 08:14:25 2017 +0900 Committer: Lee moon soo <[email protected]> Committed: Sun May 28 09:34:12 2017 +0900 ---------------------------------------------------------------------- .../integration/ParagraphActionsIT.java | 16 ++++---- .../src/app/notebook/notebook.controller.js | 6 ++- .../notebook/paragraph/paragraph-control.html | 7 ++-- .../notebook/paragraph/paragraph.controller.js | 9 +++-- .../src/app/notebook/paragraph/paragraph.css | 35 +++++++++-------- .../src/app/notebook/paragraph/paragraph.html | 41 +++++++++++--------- .../components/editor/ace.editor.directive.html | 5 +-- 7 files changed, 64 insertions(+), 55 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/zeppelin/blob/ecb688f4/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java ---------------------------------------------------------------------- diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java index add23ac..414460b 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java @@ -253,7 +253,6 @@ public class ParagraphActionsIT extends AbstractZeppelinIT { } catch (Exception e) { handleException("Exception in ParagraphActionsIT while testDisableParagraphRunButton ", e); } - } @Test @@ -263,7 +262,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT { } try { String xpathToRunOnSelectionChangeCheckbox = getParagraphXPath(1) + "//ul/li/form/input[contains(@ng-checked, 'true')]"; - String xpathToDropdownMenu = getParagraphXPath(1) + "//select"; + String xpathToDropdownMenu = "(" + (getParagraphXPath(1) + "//select)[2]"); String xpathToResultText = getParagraphXPath(1) + "//div[contains(@id,\"_html\")]"; createNewNote(); @@ -593,7 +592,8 @@ public class ParagraphActionsIT extends AbstractZeppelinIT { driver.findElement(By.xpath(getParagraphXPath(1) + "//div[contains(@class, 'text plainTextContent')]")).getText(), CoreMatchers.equalTo("Howdy ")); - Select dropDownMenu = new Select(driver.findElement(By.xpath("(" + (getParagraphXPath(1) + "//select)[1]")))); + Select dropDownMenu = new Select(driver.findElement(By.xpath("(" + (getParagraphXPath(1) + "//select)[2]")))); + dropDownMenu.selectByVisibleText("Alice"); collector.checkThat("After selection in drop down menu, output should display the newly selected option", driver.findElement(By.xpath(getParagraphXPath(1) + "//div[contains(@class, 'text plainTextContent')]")).getText(), @@ -602,7 +602,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT { driver.findElement(By.xpath(getParagraphXPath(1) + "//span[@class='icon-settings']")).click(); clickAndWait(By.xpath(getParagraphXPath(1) + "//ul/li/form/input[contains(@ng-checked, 'true')]")); - Select sameDropDownMenu = new Select(driver.findElement(By.xpath("(" + (getParagraphXPath(1) + "//select)[1]")))); + Select sameDropDownMenu = new Select(driver.findElement(By.xpath("(" + (getParagraphXPath(1) + "//select)[2]")))); sameDropDownMenu.selectByVisibleText("Bob"); collector.checkThat("After 'Run on selection change' checkbox is unchecked, the paragraph should not run if selecting a different option", driver.findElement(By.xpath(getParagraphXPath(1) + "//div[contains(@class, 'text plainTextContent')]")).getText(), @@ -632,7 +632,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT { driver.findElement(By.xpath(getParagraphXPath(1) + "//div[contains(@class, 'text plainTextContent')]")).getText(), CoreMatchers.containsString("Greetings han and leia and luke")); - WebElement firstCheckbox = driver.findElement(By.xpath("(" + getParagraphXPath(1) + "//input[@type='checkbox'])[1]")); + WebElement firstCheckbox = driver.findElement(By.xpath("(" + getParagraphXPath(1) + "//input[@type='checkbox'])[2]")); firstCheckbox.click(); collector.checkThat("After unchecking one of the boxes, we can see the newly updated output without the option we unchecked", driver.findElement(By.xpath(getParagraphXPath(1) + "//div[contains(@class, 'text plainTextContent')]")).getText(), @@ -641,7 +641,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT { driver.findElement(By.xpath(getParagraphXPath(1) + "//span[@class='icon-settings']")).click(); clickAndWait(By.xpath(getParagraphXPath(1) + "//ul/li/form/input[contains(@ng-checked, 'true')]")); - WebElement secondCheckbox = driver.findElement(By.xpath("(" + getParagraphXPath(1) + "//input[@type='checkbox'])[2]")); + WebElement secondCheckbox = driver.findElement(By.xpath("(" + getParagraphXPath(1) + "//input[@type='checkbox'])[3]")); secondCheckbox.click(); collector.checkThat("After 'Run on selection change' checkbox is unchecked, the paragraph should not run if check box state is modified", driver.findElement(By.xpath(getParagraphXPath(1) + "//div[contains(@class, 'text plainTextContent')]")).getText(), @@ -676,7 +676,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT { driver.findElement(By.xpath(getParagraphXPath(1) + "//div[contains(@class, 'text plainTextContent')]")).getText(), CoreMatchers.equalTo("Howdy \nHowdy ")); - Select dropDownMenu = new Select(driver.findElement(By.xpath("(" + (getParagraphXPath(1) + "//select)[1]")))); + Select dropDownMenu = new Select(driver.findElement(By.xpath("(" + (getParagraphXPath(1) + "//select)[2]")))); dropDownMenu.selectByVisibleText("Apple"); collector.checkThat("After selection in drop down menu, output should display the new option we selected", driver.findElement(By.xpath(getParagraphXPath(1) + "//div[contains(@class, 'text plainTextContent')]")).getText(), @@ -685,7 +685,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT { driver.findElement(By.xpath(getParagraphXPath(1) + "//span[@class='icon-settings']")).click(); clickAndWait(By.xpath(getParagraphXPath(1) + "//ul/li/form/input[contains(@ng-checked, 'true')]")); - Select sameDropDownMenu = new Select(driver.findElement(By.xpath("(" + (getParagraphXPath(1) + "//select)[2]")))); + Select sameDropDownMenu = new Select(driver.findElement(By.xpath("(" + (getParagraphXPath(1) + "//select)[3]")))); sameDropDownMenu.selectByVisibleText("Earth"); waitForParagraph(1, "FINISHED"); collector.checkThat("After 'Run on selection change' checkbox is unchecked, the paragraph should not run if selecting a different option", http://git-wip-us.apache.org/repos/asf/zeppelin/blob/ecb688f4/zeppelin-web/src/app/notebook/notebook.controller.js ---------------------------------------------------------------------- diff --git a/zeppelin-web/src/app/notebook/notebook.controller.js b/zeppelin-web/src/app/notebook/notebook.controller.js index 3944326..a512664 100644 --- a/zeppelin-web/src/app/notebook/notebook.controller.js +++ b/zeppelin-web/src/app/notebook/notebook.controller.js @@ -469,10 +469,12 @@ function NotebookCtrl ($scope, $route, $routeParams, $location, $rootScope, let addPara = function (paragraph, index) { $scope.note.paragraphs.splice(index, 0, paragraph) - _.each($scope.note.paragraphs, function (para) { + $scope.note.paragraphs.map(para => { if (para.id === paragraph.id) { para.focus = true - $scope.$broadcast('focusParagraph', para.id, 0, false) + + // we need `$timeout` since angular DOM might not be initialized + $timeout(() => { $scope.$broadcast('focusParagraph', para.id, 0, false) }) } }) } http://git-wip-us.apache.org/repos/asf/zeppelin/blob/ecb688f4/zeppelin-web/src/app/notebook/paragraph/paragraph-control.html ---------------------------------------------------------------------- diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph-control.html b/zeppelin-web/src/app/notebook/paragraph/paragraph-control.html index 222b3b3..5523609 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph-control.html +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph-control.html @@ -44,11 +44,12 @@ limitations under the License. <!-- Run / Cancel button --> <span ng-if="!revisionView"> - <span class="icon-control-play" style="cursor:pointer;color:#3071A9" tooltip-placement="top" uib-tooltip="Run this paragraph (Shift+Enter)" + <span class="icon-control-play" style="cursor:pointer;color:#3071A9" + tooltip-placement="top" uib-tooltip="Run this paragraph (Shift+Enter)" ng-click="runParagraphFromButton(getEditorValue())" ng-show="paragraph.status!='RUNNING' && paragraph.status!='PENDING' && paragraph.config.enabled"></span> - <span class="icon-control-pause" style="cursor:pointer;color:#CD5C5C" tooltip-placement="top" - uib-tooltip="Cancel (Ctrl+{{ (isMac ? 'Option' : 'Alt') }}+C)" + <span class="icon-control-pause" style="cursor:pointer;color:#CD5C5C" + tooltip-placement="top" uib-tooltip="Cancel (Ctrl+{{ (isMac ? 'Option' : 'Alt') }}+C)" ng-click="cancelParagraph(paragraph)" ng-show="paragraph.status=='RUNNING' || paragraph.status=='PENDING'"></span> <span ng-show="paragraph.runtimeInfos.jobUrl.length == 1"> http://git-wip-us.apache.org/repos/asf/zeppelin/blob/ecb688f4/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js ---------------------------------------------------------------------- diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js index e333084..00be23e 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js @@ -647,10 +647,10 @@ function ParagraphCtrl ($scope, $rootScope, $route, $window, $routeParams, $loca $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') $scope.editor.setReadOnly($scope.isRunning($scope.paragraph)) + $scope.editor.setHighlightActiveLine($scope.paragraphFocused) if ($scope.paragraphFocused) { let prefix = '%' + getInterpreterName($scope.paragraph.text) @@ -853,8 +853,11 @@ function ParagraphCtrl ($scope, $rootScope, $route, $window, $routeParams, $loca } } - const handleFocus = function (value, isDigestPass) { - $scope.paragraphFocused = value + const handleFocus = function (focused, isDigestPass) { + $scope.paragraphFocused = focused + + if ($scope.editor) { $scope.editor.setHighlightActiveLine(focused) } + if (isDigestPass === false || isDigestPass === undefined) { // Protect against error in case digest is already running $timeout(function () { http://git-wip-us.apache.org/repos/asf/zeppelin/blob/ecb688f4/zeppelin-web/src/app/notebook/paragraph/paragraph.css ---------------------------------------------------------------------- diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.css b/zeppelin-web/src/app/notebook/paragraph/paragraph.css index b17acf7..1a5e992 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.css +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.css @@ -113,7 +113,6 @@ display: none; float: right; color: #999; - margin-top: -9px; margin-right: 0; position: absolute; clear: both; @@ -175,18 +174,12 @@ } .paragraph .control { - background: rgba(255,255,255,0.85); + display: inline-block; float: right; + background: rgba(255,255,255,0.85); color: #999; - margin-top: 1px; - margin-right: 5px; - position: absolute; - clear: both; - right: 15px; - top: 16px; - text-align: right; font-size: 12px; - padding: 4px; + margin-top: 5px; } .paragraph .control li { @@ -275,8 +268,11 @@ table.table-shortcut { */ .paragraph .title { - margin: 0; + display: inline-block; + float: left; font-size: 12px; + margin-bottom: 7px; + min-height: 24px; } .paragraph .title div { @@ -370,10 +366,22 @@ table.table-shortcut { direction: ltr; } +/** set highlight line color */ +#main .ace-chrome .ace_marker-layer .ace_active-line { + background: rgba(114, 127, 222, 0.08); +} + +/** hide not focused cursors */ .ace_hidden-cursors { opacity: 0; } +/** set cursor color */ +#main .emacs-mode .ace_cursor { + background: #C0C0C0!important; + border: none !important; +} + .ace_text-input, .ace_gutter, .ace_layer, .emacs-mode, .ace_text-layer, .ace_cursor-layer, .ace_cursor, .ace_scrollbar { @@ -384,11 +392,6 @@ table.table-shortcut { opacity: 0 !important; } -#main .emacs-mode .ace_cursor { - background: #C0C0C0!important; - border: none !important; -} - /* Table Display CSS */ http://git-wip-us.apache.org/repos/asf/zeppelin/blob/ecb688f4/zeppelin-web/src/app/notebook/paragraph/paragraph.html ---------------------------------------------------------------------- diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.html b/zeppelin-web/src/app/notebook/paragraph/paragraph.html index 0de5e64..367d3e7 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.html +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.html @@ -15,24 +15,29 @@ limitations under the License. <div id="{{paragraph.id}}_container" ng-class="{'paragraph': !asIframe, 'paragraphAsIframe': asIframe}"> - <div ng-if="paragraph.config.title" - id="{{paragraph.id}}_title" - ng-controller="ElasticInputCtrl as input" - class="title"> - <input type="text" - pu-elastic-input - style="min-width: 400px; max-width: 80%;" - placeholder="Untitled" - ng-model="paragraph.title" - ng-if="input.showEditor" - ng-escape="input.showEditor = false; paragraph.title = oldTitle;" - ng-blur="setTitle(paragraph); input.showEditor = false" - ng-enter="setTitle(paragraph); input.showEditor = false" - focus-if="input.showEditor" /> - <div ng-click="input.showEditor = !asIframe && !viewOnly && !revisionView; oldTitle = paragraph.title;" - ng-show="!input.showEditor" - ng-bind-html="paragraph.title || 'Untitled'"> + <div> + <div ng-if="paragraph.config.title" + id="{{paragraph.id}}_title" + ng-controller="ElasticInputCtrl as input" + class="title"> + <input type="text" + pu-elastic-input + style="min-width: 400px; max-width: 80%;" + placeholder="Untitled" + ng-model="paragraph.title" + ng-if="input.showEditor" + ng-escape="input.showEditor = false; paragraph.title = oldTitle;" + ng-blur="setTitle(paragraph); input.showEditor = false" + ng-enter="setTitle(paragraph); input.showEditor = false" + focus-if="input.showEditor" /> + <div ng-click="input.showEditor = !asIframe && !viewOnly && !revisionView; oldTitle = paragraph.title;" + ng-show="!input.showEditor" + ng-bind-html="paragraph.title || 'Untitled'"> + </div> </div> + + <div ng-include src="'app/notebook/paragraph/paragraph-control.html'"></div> + <div style="display: inline-block; clear: both;"></div> </div> <div> @@ -63,8 +68,6 @@ limitations under the License. </div> </div> - <div ng-include src="'app/notebook/paragraph/paragraph-control.html'"></div> - <div ng-if="!asIframe" class="paragraphFooter"> <div ng-show="!paragraph.config.tableHide && !viewOnly" id="{{paragraph.id}}_executionTime" http://git-wip-us.apache.org/repos/asf/zeppelin/blob/ecb688f4/zeppelin-web/src/components/editor/ace.editor.directive.html ---------------------------------------------------------------------- diff --git a/zeppelin-web/src/components/editor/ace.editor.directive.html b/zeppelin-web/src/components/editor/ace.editor.directive.html index 4729e4b..1c0a1f0 100644 --- a/zeppelin-web/src/components/editor/ace.editor.directive.html +++ b/zeppelin-web/src/components/editor/ace.editor.directive.html @@ -13,10 +13,7 @@ limitations under the License. --> <div class="editor" - ui-ace="{ - onLoad : onLoad, - require : ['ace/ext/language_tools'] - }" + ui-ace="{ onLoad : onLoad, require : ['ace/ext/language_tools'] }" ng-model="paragraph.text" ng-class="{'paragraph-disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' || revisionView === true, 'paragraph-text--dirty' : dirtyText !== originalText && dirtyText !== undefined}">
