##// END OF EJS Templates
Fix #4777 and #7887...
Fix #4777 and #7887 The function in charge of actually converting cursor offset to CodeMirror line number and character number was actually crashing when the cursor was at the last character (loop until undefined, then access length of variable, which is undefined). This was hiding a bug in which when you would completer to a single completion pressing tab after as-you-type filtering, the completion would be completed twice. The logic that was supposed to detect whether or not all completions had a common prefix was actually faulty as the common prefix used to be a string but was then changed to an object. Hence the logic to check whether or not there was actually a common prefix was always true, even for empty string, leading to the deletion of the line (replace by '') in some cases.

File last commit:

r20374:1755c4ac
r20538:ae7f6d6a
Show More
notificationarea.js
347 lines | 13.7 KiB | application/javascript | JavascriptLexer
define([
'base/js/namespace',
'jquery',
'base/js/utils',
'base/js/dialog',
'base/js/notificationarea',
'moment'
], function(IPython, $, utils, dialog, notificationarea, moment) {
"use strict";
var NotificationArea = notificationarea.NotificationArea;
var NotebookNotificationArea = function(selector, options) {
NotificationArea.apply(this, [selector, options]);
this.save_widget = options.save_widget;
this.notebook = options.notebook;
this.keyboard_manager = options.keyboard_manager;
};
NotebookNotificationArea.prototype = Object.create(NotificationArea.prototype);
/**
* Initialize the default set of notification widgets.
*
* @method init_notification_widgets
*/
NotebookNotificationArea.prototype.init_notification_widgets = function () {
this.init_kernel_notification_widget();
this.init_notebook_notification_widget();
};
/**
* Initialize the notification widget for kernel status messages.
*
* @method init_kernel_notification_widget
*/
NotebookNotificationArea.prototype.init_kernel_notification_widget = function () {
var that = this;
var knw = this.new_notification_widget('kernel');
var $kernel_ind_icon = $("#kernel_indicator_icon");
var $modal_ind_icon = $("#modal_indicator");
var $readonly_ind_icon = $('#readonly-indicator');
var $body = $('body');
// Listen for the notebook loaded event. Set readonly indicator.
this.events.on('notebook_loaded.Notebook', function() {
if (that.notebook.writable) {
$readonly_ind_icon.hide();
} else {
$readonly_ind_icon.show();
}
});
// Command/Edit mode
this.events.on('edit_mode.Notebook', function () {
that.save_widget.update_document_title();
$body.addClass('edit_mode');
$body.removeClass('command_mode');
$modal_ind_icon.attr('title','Edit Mode');
});
this.events.on('command_mode.Notebook', function () {
that.save_widget.update_document_title();
$body.removeClass('edit_mode');
$body.addClass('command_mode');
$modal_ind_icon.attr('title','Command Mode');
});
// Implicitly start off in Command mode, switching to Edit mode will trigger event
$modal_ind_icon.addClass('modal_indicator').attr('title','Command Mode');
$body.addClass('command_mode');
// Kernel events
// this can be either kernel_created.Kernel or kernel_created.Session
this.events.on('kernel_created.Kernel kernel_created.Session', function () {
knw.info("Kernel Created", 500);
});
this.events.on('kernel_reconnecting.Kernel', function () {
knw.warning("Connecting to kernel");
});
this.events.on('kernel_connection_dead.Kernel', function (evt, info) {
knw.danger("Not Connected", undefined, function () {
// schedule reconnect a short time in the future, don't reconnect immediately
setTimeout($.proxy(info.kernel.reconnect, info.kernel), 500);
}, {title: 'click to reconnect'});
});
this.events.on('kernel_connected.Kernel', function () {
knw.info("Connected", 500);
});
this.events.on('kernel_restarting.Kernel', function () {
that.save_widget.update_document_title();
knw.set_message("Restarting kernel", 2000);
});
this.events.on('kernel_autorestarting.Kernel', function (evt, info) {
// Only show the dialog on the first restart attempt. This
// number gets tracked by the `Kernel` object and passed
// along here, because we don't want to show the user 5
// dialogs saying the same thing (which is the number of
// times it tries restarting).
if (info.attempt === 1) {
dialog.kernel_modal({
notebook: that.notebook,
keyboard_manager: that.keyboard_manager,
title: "Kernel Restarting",
body: "The kernel appears to have died. It will restart automatically.",
buttons: {
OK : {
class : "btn-primary"
}
}
});
}
that.save_widget.update_document_title();
knw.danger("Dead kernel");
$kernel_ind_icon.attr('class','kernel_dead_icon').attr('title','Kernel Dead');
});
this.events.on('kernel_interrupting.Kernel', function () {
knw.set_message("Interrupting kernel", 2000);
});
this.events.on('kernel_disconnected.Kernel', function () {
$kernel_ind_icon
.attr('class', 'kernel_disconnected_icon')
.attr('title', 'No Connection to Kernel');
});
this.events.on('kernel_connection_failed.Kernel', function (evt, info) {
// only show the dialog if this is the first failed
// connect attempt, because the kernel will continue
// trying to reconnect and we don't want to spam the user
// with messages
if (info.attempt === 1) {
var msg = "A connection to the notebook server could not be established." +
" The notebook will continue trying to reconnect, but" +
" until it does, you will NOT be able to run code. Check your" +
" network connection or notebook server configuration.";
dialog.kernel_modal({
title: "Connection failed",
body: msg,
keyboard_manager: that.keyboard_manager,
notebook: that.notebook,
buttons : {
"OK": {}
}
});
}
});
this.events.on('kernel_killed.Kernel kernel_killed.Session', function () {
that.save_widget.update_document_title();
knw.warning("No kernel");
$kernel_ind_icon.attr('class','kernel_busy_icon').attr('title','Kernel is not running');
});
this.events.on('kernel_dead.Kernel', function () {
var showMsg = function () {
var msg = 'The kernel has died, and the automatic restart has failed.' +
' It is possible the kernel cannot be restarted.' +
' If you are not able to restart the kernel, you will still be able to save' +
' the notebook, but running code will no longer work until the notebook' +
' is reopened.';
dialog.kernel_modal({
title: "Dead kernel",
body : msg,
keyboard_manager: that.keyboard_manager,
notebook: that.notebook,
buttons : {
"Manual Restart": {
class: "btn-danger",
click: function () {
that.notebook.start_session();
}
},
"Don't restart": {}
}
});
return false;
};
that.save_widget.update_document_title();
knw.danger("Dead kernel", undefined, showMsg);
$kernel_ind_icon.attr('class','kernel_dead_icon').attr('title','Kernel Dead');
showMsg();
});
this.events.on("no_kernel.Kernel", function (evt, data) {
$("#kernel_indicator").find('.kernel_indicator_name').text("No Kernel");
});
this.events.on('kernel_dead.Session', function (evt, info) {
var full = info.xhr.responseJSON.message;
var short = info.xhr.responseJSON.short_message || 'Kernel error';
var traceback = info.xhr.responseJSON.traceback;
var showMsg = function () {
var msg = $('<div/>').append($('<p/>').text(full));
var cm, cm_elem, cm_open;
if (traceback) {
cm_elem = $('<div/>')
.css('margin-top', '1em')
.css('padding', '1em')
.addClass('output_scroll');
msg.append(cm_elem);
cm = CodeMirror(cm_elem.get(0), {
mode: "python",
readOnly : true
});
cm.setValue(traceback);
cm_open = $.proxy(cm.refresh, cm);
}
dialog.kernel_modal({
title: "Failed to start the kernel",
body : msg,
keyboard_manager: that.keyboard_manager,
notebook: that.notebook,
open: cm_open,
buttons : {
"Ok": { class: 'btn-primary' }
}
});
return false;
};
that.save_widget.update_document_title();
$kernel_ind_icon.attr('class','kernel_dead_icon').attr('title','Kernel Dead');
knw.danger(short, undefined, showMsg);
});
this.events.on('kernel_starting.Kernel kernel_created.Session', function () {
window.document.title='(Starting) '+window.document.title;
$kernel_ind_icon.attr('class','kernel_busy_icon').attr('title','Kernel Busy');
knw.set_message("Kernel starting, please wait...");
});
this.events.on('kernel_ready.Kernel', function () {
that.save_widget.update_document_title();
$kernel_ind_icon.attr('class','kernel_idle_icon').attr('title','Kernel Idle');
knw.info("Kernel ready", 500);
});
this.events.on('kernel_idle.Kernel', function () {
that.save_widget.update_document_title();
$kernel_ind_icon.attr('class','kernel_idle_icon').attr('title','Kernel Idle');
});
this.events.on('kernel_busy.Kernel', function () {
window.document.title='(Busy) '+window.document.title;
$kernel_ind_icon.attr('class','kernel_busy_icon').attr('title','Kernel Busy');
});
this.events.on('spec_match_found.Kernel', function (evt, data) {
that.widget('kernelspec').info("Using kernel: " + data.found.spec.display_name, 3000, undefined, {
title: "Only candidate for language: " + data.selected.language + " was " + data.found.spec.display_name
});
});
// Start the kernel indicator in the busy state, and send a kernel_info request.
// When the kernel_info reply arrives, the kernel is idle.
$kernel_ind_icon.attr('class','kernel_busy_icon').attr('title','Kernel Busy');
};
/**
* Initialize the notification widget for notebook status messages.
*
* @method init_notebook_notification_widget
*/
NotebookNotificationArea.prototype.init_notebook_notification_widget = function () {
var nnw = this.new_notification_widget('notebook');
// Notebook events
this.events.on('notebook_loading.Notebook', function () {
nnw.set_message("Loading notebook",500);
});
this.events.on('notebook_loaded.Notebook', function () {
nnw.set_message("Notebook loaded",500);
});
this.events.on('notebook_saving.Notebook', function () {
nnw.set_message("Saving notebook",500);
});
this.events.on('notebook_saved.Notebook', function () {
nnw.set_message("Notebook saved",2000);
});
this.events.on('notebook_save_failed.Notebook', function (evt, error) {
nnw.warning(error.message || "Notebook save failed");
});
this.events.on('notebook_copy_failed.Notebook', function (evt, error) {
nnw.warning(error.message || "Notebook copy failed");
});
// Checkpoint events
this.events.on('checkpoint_created.Notebook', function (evt, data) {
var msg = "Checkpoint created";
if (data.last_modified) {
var d = new Date(data.last_modified);
msg = msg + ": " + moment(d).format("HH:mm:ss");
}
nnw.set_message(msg, 2000);
});
this.events.on('checkpoint_failed.Notebook', function () {
nnw.warning("Checkpoint failed");
});
this.events.on('checkpoint_deleted.Notebook', function () {
nnw.set_message("Checkpoint deleted", 500);
});
this.events.on('checkpoint_delete_failed.Notebook', function () {
nnw.warning("Checkpoint delete failed");
});
this.events.on('checkpoint_restoring.Notebook', function () {
nnw.set_message("Restoring to checkpoint...", 500);
});
this.events.on('checkpoint_restore_failed.Notebook', function () {
nnw.warning("Checkpoint restore failed");
});
// Autosave events
this.events.on('autosave_disabled.Notebook', function () {
nnw.set_message("Autosave disabled", 2000);
});
this.events.on('autosave_enabled.Notebook', function (evt, interval) {
nnw.set_message("Saving every " + interval / 1000 + "s", 1000);
});
};
// Backwards compatibility.
IPython.NotificationArea = NotebookNotificationArea;
return {'NotebookNotificationArea': NotebookNotificationArea};
});