mirror of
https://github.com/apache/zeppelin
synced 2026-05-24 09:38:26 +00:00
Merge branch 'master' into ZEPPELIN-2813
This commit is contained in:
commit
efa7ce23a6
11 changed files with 163 additions and 31 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@
|
|||
"defaultValue": "60",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"language": "r",
|
||||
"editOnDblClick": false,
|
||||
"completionKey": "TAB"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue