Merge branch 'master' into ZEPPELIN-2813

This commit is contained in:
tinkoff-dwh 2017-10-31 08:46:24 +03:00
commit efa7ce23a6
11 changed files with 163 additions and 31 deletions

View file

@ -116,7 +116,8 @@ Here is an example of `interpreter-setting.json` on your own interpreter.
},
"editor": {
"language": "your-syntax-highlight-language",
"editOnDblClick": false
"editOnDblClick": false,
"completionKey": "TAB"
}
},
{
@ -170,6 +171,19 @@ If your interpreter uses mark-up language such as markdown or HTML, set `editOnD
"editOnDblClick": false
}
```
### Completion key (Optional)
By default, `Ctrl+dot(.)` brings autocompletion list in the editor.
Through `completionKey`, each interpreter can configure autocompletion key.
Currently `TAB` is only available option.
```
"editor": {
"completionKey": "TAB"
}
```
## Install your interpreter binary
Once you have built your interpreter, you can place it under the interpreter directory with all its dependencies.

View file

@ -48,6 +48,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.security.kerberos.client.KerberosRestTemplate;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;
@ -290,6 +291,7 @@ public abstract class BaseLivyInterpreter extends Interpreter {
}
stmtInfo = executeStatement(new ExecuteRequest(code));
}
// pull the statement status
while (!stmtInfo.isAvailable()) {
if (paragraphId != null && paragraphsToCancel.contains(paragraphId)) {
@ -358,7 +360,7 @@ public abstract class BaseLivyInterpreter extends Interpreter {
InterpreterResult result2 = new InterpreterResult(result.code());
result2.add(InterpreterResult.Type.HTML,
"<font color=\"red\">Previous livy session is expired, new livy session is created. " +
"Paragraphs that depend on this paragraph need to be re-executed!" + "</font>");
"Paragraphs that depend on this paragraph need to be re-executed!</font>");
for (InterpreterResultMessage message : result.message()) {
result2.add(message.getType(), message.getData());
}
@ -582,6 +584,15 @@ public abstract class BaseLivyInterpreter extends Interpreter {
throw new LivyException(cause.getResponseBodyAsString() + "\n"
+ ExceptionUtils.getFullStackTrace(ExceptionUtils.getRootCause(e)));
}
if (e instanceof HttpServerErrorException) {
HttpServerErrorException errorException = (HttpServerErrorException) e;
String errorResponse = errorException.getResponseBodyAsString();
if (errorResponse.contains("Session is in state dead")) {
throw new LivyException("%html <font color=\"red\">Livy session is dead somehow, " +
"please check log to see why it is dead, and then restart livy interpreter</font>");
}
throw new LivyException(errorResponse, e);
}
throw new LivyException(e);
}
if (response == null) {

View file

@ -44,7 +44,8 @@
},
"editor": {
"language": "python",
"editOnDblClick": false
"editOnDblClick": false,
"completionKey": "TAB"
}
},
{
@ -55,7 +56,8 @@
},
"editor":{
"language": "sql",
"editOnDblClick": false
"editOnDblClick": false,
"completionKey": "TAB"
}
},
{

View file

@ -24,6 +24,11 @@
"defaultValue": "60",
"type": "number"
}
},
"editor": {
"language": "r",
"editOnDblClick": false,
"completionKey": "TAB"
}
},
{

View file

@ -71,7 +71,8 @@
},
"editor": {
"language": "scala",
"editOnDblClick": false
"editOnDblClick": false,
"completionKey": "TAB"
}
},
{
@ -110,7 +111,8 @@
},
"editor": {
"language": "sql",
"editOnDblClick": false
"editOnDblClick": false,
"completionKey": "TAB"
}
},
{
@ -135,7 +137,8 @@
},
"editor": {
"language": "scala",
"editOnDblClick": false
"editOnDblClick": false,
"completionKey": "TAB"
}
},
{
@ -160,7 +163,8 @@
},
"editor": {
"language": "python",
"editOnDblClick": false
"editOnDblClick": false,
"completionKey": "TAB"
}
},
{

View file

@ -19,10 +19,10 @@ limitations under the License.
<input type="text" pu-elastic-input class="form-control2" placeholder="New name" style="min-width: 0px; max-width: 95%;"
ng-if="input.showEditor" ng-model="input.value" ng-escape="input.showEditor = false" focus-if="input.showEditor"
ng-blur="updateNoteName(input.value);input.showEditor = false;" ng-enter="updateNoteName(input.value);input.showEditor = false;" />
<p class="form-control-static2"
tooltip-placement="bottom"
<p class="form-control-static2"
tooltip-placement="bottom"
uib-tooltip={{noteName(note)}}
ng-click="input.showEditor = !revisionView; input.value = note.name"
ng-click="input.showEditor = !revisionView; input.value = note.name"
ng-show="!input.showEditor">{{noteName(note)}}</p>
</div>
<div style="float: left; padding-bottom: 10px">
@ -306,7 +306,7 @@ limitations under the License.
type="button"
data-toggle="modal"
data-target="#shortcutModal"
tooltip-placement="bottom" uib-tooltip="List of shortcut">
tooltip-placement="bottom" uib-tooltip="List of shortcuts">
<i class="fa fa-keyboard-o"></i>
</span>
<span class="setting-btn"

View file

@ -185,6 +185,11 @@ function ParagraphCtrl ($scope, $rootScope, $route, $window, $routeParams, $loca
}
}
const isTabCompletion = function() {
const completionKey = $scope.paragraph.config.editorSetting.completionKey
return completionKey === 'TAB'
}
$scope.$on('updateParagraphOutput', function (event, data) {
if ($scope.paragraph.id === data.paragraphId) {
if (!$scope.paragraph.results) {
@ -851,6 +856,33 @@ function ParagraphCtrl ($scope, $rootScope, $route, $window, $routeParams, $loca
// 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.insertSnippet('\t')
})
}
}
})
let keyBindingEditorFocusAction = function (scrollValue) {
let numRows = $scope.editor.getSession().getLength()
let currentRow = $scope.editor.getCursorPosition().row

View file

@ -205,10 +205,10 @@ public class InterpreterSetting {
return this;
}
// public Builder setInterpreterRunner(InterpreterRunner runner) {
// interpreterSetting.interpreterRunner = runner;
// return this;
// }
public Builder setInterpreterRunner(InterpreterRunner runner) {
interpreterSetting.interpreterRunner = runner;
return this;
}
public Builder setIntepreterSettingManager(
InterpreterSettingManager interpreterSettingManager) {
@ -248,7 +248,6 @@ public class InterpreterSetting {
}
void postProcessing() {
// createLauncher();
this.status = Status.READY;
}
@ -370,7 +369,7 @@ public class InterpreterSetting {
try {
interpreterGroupWriteLock.lock();
if (!interpreterGroups.containsKey(groupId)) {
LOGGER.info("Create InterpreterGroup with groupId {} for user {} and note {}",
LOGGER.info("Create InterpreterGroup with groupId: {} for user: {} and note: {}",
groupId, user, noteId);
ManagedInterpreterGroup intpGroup = createInterpreterGroup(groupId);
interpreterGroups.put(groupId, intpGroup);
@ -653,7 +652,7 @@ public class InterpreterSetting {
return process;
}
private List<Interpreter> getOrCreateSession(String user, String noteId) {
List<Interpreter> getOrCreateSession(String user, String noteId) {
ManagedInterpreterGroup interpreterGroup = getOrCreateInterpreterGroup(user, noteId);
Preconditions.checkNotNull(interpreterGroup, "No InterpreterGroup existed for user {}, " +
"noteId {}", user, noteId);

View file

@ -782,13 +782,7 @@ public class InterpreterSettingManager {
//clean up metaInfos
intpSetting.setInfos(null);
copyDependenciesFromLocalPath(intpSetting);
if (user.equals("anonymous")) {
intpSetting.close();
} else {
intpSetting.closeInterpreters(user, noteId);
}
intpSetting.closeInterpreters(user, noteId);
} else {
throw new InterpreterException("Interpreter setting id " + settingId + " not found");
}

View file

@ -85,7 +85,7 @@ public class ManagedInterpreterGroup extends InterpreterGroup {
close(sessions.remove(sessionId));
//TODO(zjffdu) whether close InterpreterGroup if there's no session left in Zeppelin Server
if (sessions.isEmpty() && interpreterSetting != null) {
LOGGER.info("Remove this InterpreterGroup {} as all the sessions are closed", id);
LOGGER.info("Remove this InterpreterGroup: {} as all the sessions are closed", id);
interpreterSetting.removeInterpreterGroup(id);
if (remoteInterpreterProcess != null) {
LOGGER.info("Kill RemoteIntetrpreterProcess");
@ -133,7 +133,7 @@ public class ManagedInterpreterGroup extends InterpreterGroup {
for (Interpreter interpreter : interpreters) {
interpreter.setInterpreterGroup(this);
}
LOGGER.info("Create Session {} in InterpreterGroup {} for user {}", sessionId, id, user);
LOGGER.info("Create Session: {} in InterpreterGroup: {} for user: {}", sessionId, id, user);
sessions.put(sessionId, interpreters);
return interpreters;
}

View file

@ -34,6 +34,7 @@ import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@ -181,9 +182,6 @@ public class InterpreterSettingManagerTest extends AbstractInterpreterTest {
// only close user1's session
interpreterSettingManager.restart(interpreterSetting.getId(), "note1", "user1");
assertEquals(2, interpreterGroup.getSessionNum());
// close all the sessions
interpreterSettingManager.restart(interpreterSetting.getId(), "note1", "anonymous");
assertEquals(0, interpreterGroup.getSessionNum());
// remove interpreter setting
interpreterSettingManager.remove(interpreterSetting.getId());
@ -281,6 +279,79 @@ public class InterpreterSettingManagerTest extends AbstractInterpreterTest {
Interpreter mock1Interpreter = interpreterFactory.getInterpreter("user1", "note1", "mock1");
editor = interpreterSettingManager.getEditorSetting(mock1Interpreter,"user1", "note1", "mock1");
assertEquals("text", editor.get("language"));
}
@Test
public void testRestartShared() throws InterpreterException {
InterpreterSetting interpreterSetting = interpreterSettingManager.getByName("test");
interpreterSetting.getOption().setPerUser("shared");
interpreterSetting.getOption().setPerNote("shared");
interpreterSetting.getOrCreateSession("user1", "note1");
interpreterSetting.getOrCreateInterpreterGroup("user2", "note2");
assertEquals(1, interpreterSetting.getAllInterpreterGroups().size());
interpreterSettingManager.restart(interpreterSetting.getId(), "user1", "note1");
assertEquals(0, interpreterSetting.getAllInterpreterGroups().size());
}
@Test
public void testRestartPerUserIsolated() throws InterpreterException {
InterpreterSetting interpreterSetting = interpreterSettingManager.getByName("test");
interpreterSetting.getOption().setPerUser("isolated");
interpreterSetting.getOption().setPerNote("shared");
interpreterSetting.getOrCreateSession("user1", "note1");
interpreterSetting.getOrCreateSession("user2", "note2");
assertEquals(2, interpreterSetting.getAllInterpreterGroups().size());
interpreterSettingManager.restart(interpreterSetting.getId(), "note1", "user1");
assertEquals(1, interpreterSetting.getAllInterpreterGroups().size());
}
@Test
public void testRestartPerNoteIsolated() throws InterpreterException {
InterpreterSetting interpreterSetting = interpreterSettingManager.getByName("test");
interpreterSetting.getOption().setPerUser("shared");
interpreterSetting.getOption().setPerNote("isolated");
interpreterSetting.getOrCreateSession("user1", "note1");
interpreterSetting.getOrCreateSession("user2", "note2");
assertEquals(2, interpreterSetting.getAllInterpreterGroups().size());
interpreterSettingManager.restart(interpreterSetting.getId(), "note1", "user1");
assertEquals(1, interpreterSetting.getAllInterpreterGroups().size());
}
@Test
public void testRestartPerUserScoped() throws InterpreterException {
InterpreterSetting interpreterSetting = interpreterSettingManager.getByName("test");
interpreterSetting.getOption().setPerUser("scoped");
interpreterSetting.getOption().setPerNote("shared");
interpreterSetting.getOrCreateSession("user1", "note1");
interpreterSetting.getOrCreateSession("user2", "note2");
assertEquals(1, interpreterSetting.getAllInterpreterGroups().size());
assertEquals(2, interpreterSetting.getAllInterpreterGroups().get(0).getSessionNum());
interpreterSettingManager.restart(interpreterSetting.getId(), "note1", "user1");
assertEquals(1, interpreterSetting.getAllInterpreterGroups().size());
assertEquals(1, interpreterSetting.getAllInterpreterGroups().get(0).getSessionNum());
}
@Test
public void testRestartPerNoteScoped() throws InterpreterException {
InterpreterSetting interpreterSetting = interpreterSettingManager.getByName("test");
interpreterSetting.getOption().setPerUser("shared");
interpreterSetting.getOption().setPerNote("scoped");
interpreterSetting.getOrCreateSession("user1", "note1");
interpreterSetting.getOrCreateSession("user2", "note2");
assertEquals(1, interpreterSetting.getAllInterpreterGroups().size());
assertEquals(2, interpreterSetting.getAllInterpreterGroups().get(0).getSessionNum());
interpreterSettingManager.restart(interpreterSetting.getId(), "note1", "user1");
assertEquals(1, interpreterSetting.getAllInterpreterGroups().size());
assertEquals(1, interpreterSetting.getAllInterpreterGroups().get(0).getSessionNum());
}
}