Merge pull request #708 from Carreau/fix-shift-jk

[WIP] Prototype to fix Shift-J/K selection with anchor and cursor
Brian E. Granger 10 years ago
commit feb83d6c9d

@ -1,5 +1,6 @@
# http://travis-ci.org/#!/ipython/ipython
language: python
group: edge
cache:
directories:
- ~/.cache/bower

@ -368,7 +368,7 @@ define([
**/
var action_name = this.actions.get_name(data);
if (! action_name){
throw new Error('does not know how to deal with', data);
throw new Error('does not know how to deal with : ' + data);
}
shortcut = normalize_shortcut(shortcut);
this.set_shortcut(shortcut, action_name);

@ -107,10 +107,10 @@ define(function(require){
}
},
'run-cell':{
help : 'run marked cells',
help : 'run selected cells',
help_index : 'bb',
handler : function (env) {
env.notebook.execute_marked_cells();
env.notebook.execute_selected_cells();
}
},
'run-cell-and-insert-below':{
@ -163,7 +163,7 @@ define(function(require){
handler : function (env) {
var index = env.notebook.get_selected_index();
if (index !== 0 && index !== null) {
env.notebook.select_prev();
env.notebook.select_prev(true);
env.notebook.focus_cell();
}
}
@ -174,23 +174,23 @@ define(function(require){
handler : function (env) {
var index = env.notebook.get_selected_index();
if (index !== (env.notebook.ncells()-1) && index !== null) {
env.notebook.select_next();
env.notebook.select_next(true);
env.notebook.focus_cell();
}
}
},
'extend-marked-cells-above' : {
help: 'extend marked cells above',
'extend-selection-above' : {
help: 'extend selected cells above',
help_index : 'dc',
handler : function (env) {
env.notebook.extend_marked(-1);
env.notebook.extend_selection_by(-1)
}
},
'extend-marked-cells-below' : {
help: 'extend marked cells below',
'extend-selection-below' : {
help: 'extend selected cells below',
help_index : 'dd',
handler : function (env) {
env.notebook.extend_marked(1);
env.notebook.extend_selection_by(1)
}
},
'cut-cell' : {
@ -229,7 +229,7 @@ define(function(require){
help_index : 'ec',
handler : function (env) {
env.notebook.insert_cell_above();
env.notebook.select_prev();
env.notebook.select_prev(true);
env.notebook.focus_cell();
}
},
@ -239,7 +239,7 @@ define(function(require){
help_index : 'ed',
handler : function (env) {
env.notebook.insert_cell_below();
env.notebook.select_next();
env.notebook.select_next(true);
env.notebook.focus_cell();
}
},
@ -380,16 +380,10 @@ define(function(require){
}
},
'merge-cells' : {
help : 'merge marked cells',
help : 'merge selected cells',
help_index: 'el',
handler: function(env) {
env.notebook.merge_marked_cells();
}
},
'close-pager' : {
help_index : 'gd',
handler : function (env) {
env.pager.collapse();
env.notebook.merge_selected_cells();
}
},
'show-command-palette': {
@ -400,29 +394,6 @@ define(function(require){
env.notebook.show_command_palette();
}
},
'toggle-cell-marked': {
help_index : 'cj',
help: 'toggle marks',
icon: 'fa-check',
handler : function(env){
// Use bitwise logic to toggle the marked state.
env.notebook.get_selected_cell().marked ^= true;
}
},
'unmark-all-cells': {
help_index : 'ck',
help : 'unmark all cells',
handler : function(env) {
env.notebook.unmark_all_cells();
}
},
'mark-all-cells': {
help_index : 'cl',
help : 'mark all cells',
handler : function(env) {
env.notebook.mark_all_cells();
}
},
'toggle-toolbar':{
help: 'hide/show the toolbar',
handler : function(env){
@ -438,14 +409,12 @@ define(function(require){
events.trigger('resize-header.Page');
}
},
'close-pager-or-unmark-all-cells': {
help : 'close the pager or unmark all cells',
'close-pager': {
help : 'close the pager',
handler : function(env) {
// Collapse the page if it is open, otherwise unmark all.
// Collapse the page if it is open
if (env.pager && env.pager.expanded) {
env.pager.collapse();
} else {
env.notebook.unmark_all_cells();
}
}
},
@ -481,7 +450,7 @@ define(function(require){
event.preventDefault();
}
env.notebook.command_mode();
env.notebook.select_prev();
env.notebook.select_prev(true);
env.notebook.edit_mode();
cm = env.notebook.get_selected_cell().code_mirror;
cm.setCursor(cm.lastLine(), 0);
@ -498,7 +467,7 @@ define(function(require){
event.preventDefault();
}
env.notebook.command_mode();
env.notebook.select_next();
env.notebook.select_next(true);
env.notebook.edit_mode();
var cm = env.notebook.get_selected_cell().code_mirror;
cm.setCursor(0, 0);

@ -1,4 +1,3 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
/**
@ -21,7 +20,7 @@ define([
"use strict";
var overlayHack = CodeMirror.scrollbarModel.native.prototype.overlayHack;
CodeMirror.scrollbarModel.native.prototype.overlayHack = function () {
overlayHack.apply(this, arguments);
// Reverse `min-height: 18px` scrollbar hack on OS X
@ -55,6 +54,7 @@ define([
this.placeholder = config.placeholder || '';
this.selected = false;
this.anchor = false;
this.rendered = false;
this.mode = 'command';
@ -154,30 +154,29 @@ define([
}
};
/**
* trigger on focus and on click to bubble up to the notebook and
* potentially extend the selection if shift-click, contract the selection
* if just codemirror focus (so edit mode).
* We **might** be able to move that to notebook `handle_edit_mode`.
*/
Cell.prototype._on_click = function(event){
if (!this.selected) {
this.events.trigger('select.Cell', {'cell':this, 'extendSelection':event.shiftKey});
}
}
/**
* Subclasses can implement override bind_events.
* Be carefull to call the parent method when overwriting as it fires event.
* this will be triggerd after create_element in constructor.
* Be careful to call the parent method when overwriting as it fires event.
* this will be triggered after create_element in constructor.
* @method bind_events
*/
Cell.prototype.bind_events = function () {
var that = this;
// We trigger events so that Cell doesn't have to depend on Notebook.
that.element.click(function (event) {
if (!that.selected) {
that.events.trigger('select.Cell', {'cell':that});
}
// Cmdtrl-click should mark the cell.
var isMac = navigator.platform.slice(0, 3).toLowerCase() === 'mac';
if ((!isMac && event.ctrlKey) || (isMac && event.metaKey)) {
that.marked = !that.marked;
}
});
that.element.focusin(function (event) {
if (!that.selected) {
that.events.trigger('select.Cell', {'cell':that});
}
that._on_click(event)
});
if (this.code_mirror) {
this.code_mirror.on("change", function(cm, change) {
@ -186,6 +185,9 @@ define([
}
if (this.code_mirror) {
this.code_mirror.on('focus', function(cm, change) {
if (!that.selected) {
that.events.trigger('select.Cell', {'cell':that});
}
that.events.trigger('edit_mode.Cell', {cell: that});
});
}
@ -239,7 +241,7 @@ define([
/**
* Triger typsetting of math by mathjax on current cell element
* Triger typesetting of math by mathjax on current cell element
* @method typeset
*/
Cell.prototype.typeset = function () {
@ -251,7 +253,13 @@ define([
* @method select
* @return is the action being taken
*/
Cell.prototype.select = function () {
Cell.prototype.select = function (moveanchor) {
// if anchor is true, set the move the anchor
moveanchor = (moveanchor === undefined)? true:moveanchor;
if(moveanchor){
this.anchor=true;
}
if (!this.selected) {
this.element.addClass('selected');
this.element.removeClass('unselected');
@ -265,10 +273,14 @@ define([
/**
* handle cell level logic when the cell is unselected
* @method unselect
* @param {bool} leave_selected - true to move cursor away and extend selection
* @return is the action being taken
*/
Cell.prototype.unselect = function (leave_selected) {
Cell.prototype.unselect = function (moveanchor) {
// if anchor is true, remove also the anchor
moveanchor = (moveanchor === undefined)? true:moveanchor;
if (moveanchor){
this.anchor = false
}
if (this.selected) {
this.element.addClass('unselected');
this.element.removeClass('selected');
@ -279,32 +291,9 @@ define([
}
};
/**
* Whether or not the cell is marked.
* @return {boolean}
*/
Object.defineProperty(Cell.prototype, 'marked', {
get: function() {
return this.element.hasClass('marked');
},
set: function(value) {
var isMarked = this.element.hasClass('marked');
// Use a casting comparison. Allows for the caller to assign 0 or
// 1 instead of a boolean value, which in return means the caller
// can do cell.marked ^= true to toggle the mark.
if (isMarked != value) {
if (value) {
this.element.addClass('marked');
} else {
this.element.removeClass('marked');
}
this.events.trigger('marked_changed.Cell', {cell: this, value: value});
}
}
});
/**
* should be overritten by subclass
* should be overwritten by subclass
* @method execute
*/
Cell.prototype.execute = function () {
@ -426,6 +415,7 @@ define([
*/
Cell.prototype.focus_cell = function () {
this.element.focus();
this._on_click({});
};
/**

@ -196,7 +196,7 @@ define([
/** @method bind_events */
CodeCell.prototype.bind_events = function () {
Cell.prototype.bind_events.apply(this);
Cell.prototype.bind_events.apply(this, arguments);
var that = this;
this.element.focusout(
@ -401,7 +401,7 @@ define([
// Basic cell manipulation.
CodeCell.prototype.select = function () {
var cont = Cell.prototype.select.apply(this);
var cont = Cell.prototype.select.apply(this, arguments);
if (cont) {
this.code_mirror.refresh();
this.auto_highlight();
@ -410,7 +410,7 @@ define([
};
CodeCell.prototype.render = function () {
var cont = Cell.prototype.render.apply(this);
var cont = Cell.prototype.render.apply(this, arguments);
// Always execute, even if we are already in the rendered state
return cont;
};
@ -548,7 +548,7 @@ define([
* @return is the action being taken
*/
CodeCell.prototype.unselect = function() {
var cont = Cell.prototype.unselect.call(this);
var cont = Cell.prototype.unselect.apply(this, arguments);
if (cont) {
// When a code cell is unselected, make sure that the corresponding
// tooltip and completer to that cell is closed.

@ -96,14 +96,14 @@ define([
'i,i' : 'jupyter-notebook:interrupt-kernel',
'0,0' : 'jupyter-notebook:confirm-restart-kernel',
'd,d' : 'jupyter-notebook:delete-cell',
'esc': 'jupyter-notebook:close-pager-or-unmark-all-cells',
'esc': 'jupyter-notebook:close-pager',
'up' : 'jupyter-notebook:select-previous-cell',
'k' : 'jupyter-notebook:select-previous-cell',
'j' : 'jupyter-notebook:select-next-cell',
'shift-k': 'jupyter-notebook:extend-marked-cells-above',
'shift-j': 'jupyter-notebook:extend-marked-cells-below',
'shift-up': 'jupyter-notebook:extend-marked-cells-above',
'shift-down': 'jupyter-notebook:extend-marked-cells-below',
'shift-k': 'jupyter-notebook:extend-selection-above',
'shift-j': 'jupyter-notebook:extend-selection-below',
'shift-up': 'jupyter-notebook:extend-selection-above',
'shift-down': 'jupyter-notebook:extend-selection-below',
'x' : 'jupyter-notebook:cut-cell',
'c' : 'jupyter-notebook:copy-cell',
'v' : 'jupyter-notebook:paste-cell-below',

@ -63,7 +63,7 @@ define([
// The selected cell loses focus when the menu is entered, so we
// re-select it upon selection.
var i = that.notebook.get_selected_index();
that.notebook.select(i);
that.notebook.select(i, false);
}
);
};

@ -29,6 +29,12 @@ define(function (require) {
var scrollmanager = require('notebook/js/scrollmanager');
var commandpalette = require('notebook/js/commandpalette');
var _SOFT_SELECTION_CLASS = 'jupyter-soft-selected';
function soft_selected(cell){
return cell.element.hasClass(_SOFT_SELECTION_CLASS);
}
/**
* Contains and manages cells.
* @class Notebook
@ -187,9 +193,6 @@ define(function (require) {
Notebook.prototype.bind_events = function () {
var that = this;
this.events.on('marked_changed.Cell', function() {
that.update_marked_status();
});
this.events.on('set_next_input.Notebook', function (event, data) {
if (data.replace) {
@ -221,7 +224,7 @@ define(function (require) {
this.events.on('select.Cell', function (event, data) {
var index = that.find_cell_index(data.cell);
that.select(index);
that.select(index, !data.extendSelection);
});
this.events.on('edit_mode.Cell', function (event, data) {
@ -298,9 +301,6 @@ define(function (require) {
expand_time(time);
});
this.scroll_manager.onScroll(function () {
that.update_marked_status();
}, 100);
// Firefox 22 broke $(window).on("beforeunload")
// I'm not sure why or how.
@ -584,13 +584,30 @@ define(function (require) {
return i;
};
Notebook.prototype.get_selected_cells = function () {
return this.get_cells().filter(function(cell, index){ return cell.selected || soft_selected(cell) || cell.anchor})
};
Notebook.prototype.get_selected_cells_indices = function () {
var result = [];
this.get_cells().filter(function (cell, index) {
if (cell.selected || soft_selected(cell) || cell.anchor) {
result.push(index);
}
});
return result;
};
/**
* Get the currently selected cell.
*
* @return {Cell} The selected cell
*/
Notebook.prototype.get_selected_cell = function () {
var index = this.get_selected_index();
var index = this.get_selected_cells_indices();
return this.get_cell(index);
};
@ -608,6 +625,15 @@ define(function (require) {
}
};
Notebook.prototype.get_anchor_index = function () {
var result = null;
this.get_cell_elements().filter(function (index) {
if ($(this).data("cell").anchor === true) {
result = index;
}
});
return result;
};
/**
* Get the index of the currently selected cell.
*
@ -622,144 +648,52 @@ define(function (require) {
});
return result;
};
/**
* Toggles the marks on the cells
* @param {Cell[]} [cells] - optionally specify what cells should be toggled
*/
Notebook.prototype.toggle_cells_marked = function(cells) {
cells = cells || this.get_cells();
cells.forEach(function(cell) { cell.marked = !cell.marked; });
};
/**
* Mark all of the cells
* @param {Cell[]} [cells] - optionally specify what cells should be marked
*/
Notebook.prototype.mark_all_cells = function(cells) {
cells = cells || this.get_cells();
cells.forEach(function(cell) { cell.marked = true; });
};
/**
* Unmark all of the cells
* @param {Cell[]} [cells] - optionally specify what cells should be unmarked
*/
Notebook.prototype.unmark_all_cells = function(cells) {
this.get_marked_cells(cells).forEach(function(cell) { cell.marked = false; });
};
/**
* Set the cells that should be marked, exclusively
* @param {Cell[]} cells
*/
Notebook.prototype.set_marked_cells = function(cells) {
this.unmark_all_cells();
this.mark_all_cells(cells);
};
/**
* Gets the cells that are marked
* @param {Cell[]} [cells] - optionally provide the cells to search through
* @return {Cell[]} marked cells
*/
Notebook.prototype.get_marked_cells = function(cells) {
cells = cells || this.get_cells();
return cells.filter(function(cell) { return (cell.marked || cell.selected); });
};
/**
* Sets the cells that are marked by indices
* @param {number[]} indices
* @param {Cell[]} [cells] - optionally provide the cells to search through
*/
Notebook.prototype.set_marked_indices = function(indices, cells) {
cells = cells || this.get_cells();
this.unmark_all_cells(cells);
this.mark_all_cells(cells.filter(function(cell, index) { return indices.indexOf(index) !== -1; }));
};
/**
* Gets the indices of the cells that are marked
* @param {Cell[]} [cells] - optionally provide the cells to search through
* @return {number[]} marked cell indices
*/
Notebook.prototype.get_marked_indices = function(cells) {
cells = cells || this.get_cells();
var markedCells = this.get_marked_cells(cells);
return markedCells.map(function(cell) { return cells.indexOf(cell); });
};
/**
* Checks if the marked cells are contiguous
* @param {Cell[]} [cells] - optionally provide the cells to search through
* @return {boolean}
*/
Notebook.prototype.are_marked_cells_contiguous = function(cells) {
// Get a numerically sorted list of the marked indices.
var markedIndices = this.get_marked_indices(cells).sort(
function(a,b) { return a-b; });
// Check for contiguousness
for (var i = 0; i < markedIndices.length - 1; i++) {
if (markedIndices[i+1] - markedIndices[i] !== 1) {
return false;
}
}
return true;
};
/**
* Checks if the marked cells specified by their indices are contiguous
* @param {number[]} indices - the cell indices to search through
* @param {Cell[]} [cells] - the cells to search through
* @return {boolean}
*/
Notebook.prototype.are_marked_indices_contiguous = function(indices, cells) {
cells = cells || this.get_cells();
return this.are_marked_cells_contiguous(cells.filter(function(cell, index) {
return indices.indexOf(index) !== -1;
}));
};
/**
* Extend the selected range
*
* @param {number} offset
*/
Notebook.prototype.extend_marked = function(offset) {
// Mark currently selected cell
this.get_selected_cell().marked = true;
// Cell selection.
// Select the cell in the offset direction. Bound index between 0 and
// the number of cells -1.
var selectedIndex = Math.min(Math.max(this.get_selected_index() + offset, 0), this.ncells()-1);
this.select(selectedIndex);
this.ensure_focused();
Notebook.prototype.extend_selection_by = function(delta) {
var index = this.get_selected_index();
// do not move anchor
return this.select(index+delta, false);
};
Notebook.prototype.update_marked_status = function() {
var marked_cells = this.get_marked_cells();
var num_offscreen = 0;
var i;
for (i = 0; i < marked_cells.length; i++) {
if (!this.scroll_manager.is_cell_visible(marked_cells[i])) {
num_offscreen += 1;
}
}
this.events.trigger('marked_offscreen.Cell', num_offscreen);
};
Notebook.prototype.update_soft_selection = function(){
var i1 = this.get_selected_index();
var i2 = this.get_anchor_index();
var low = Math.min(i1, i2);
var high = Math.max(i1, i2);
if (low !== high){
$('body').addClass('jupyter-multi-select');
} else {
$('body').removeClass('jupyter-multi-select');
}
this.get_cells().map(function(cell, index, all){
if( low <= index && index <= high ){
cell.element.addClass(_SOFT_SELECTION_CLASS);
} else {
cell.element.removeClass(_SOFT_SELECTION_CLASS);
}
})
}
// Cell selection.
Notebook.prototype._contract_selection = function(){
var i = this.get_selected_index();
this.select(i, true);
}
/**
* Programmatically select a cell.
*
* @param {integer} index - A cell's index
* @param {bool} moveanchor whether to move the selection
* anchor, default to true.
* @return {Notebook} This notebook
*/
Notebook.prototype.select = function (index) {
Notebook.prototype.select = function (index, moveanchor) {
moveanchor = (moveanchor===undefined)? true : moveanchor;
if (this.is_valid_cell_index(index)) {
var sindex = this.get_selected_index();
if (sindex !== null && index !== sindex) {
@ -768,11 +702,13 @@ define(function (require) {
if (this.mode !== 'command') {
this.command_mode();
}
this.get_cell(sindex).unselect();
this.get_cell(sindex).unselect(moveanchor);
}
if(moveanchor){
this.get_cell(this.get_anchor_index()).unselect(true);
}
var cell = this.get_cell(index);
cell.select();
this.update_marked_status();
cell.select(moveanchor);
if (cell.cell_type === 'heading') {
this.events.trigger('selected_cell_type_changed.Notebook',
{'cell_type':cell.cell_type,level:cell.level}
@ -783,17 +719,20 @@ define(function (require) {
);
}
}
this.update_soft_selection();
return this;
};
/**
* Programmatically select the next cell.
*
* @param {bool} moveanchor whether to move the selection
* anchor, default to true.
* @return {Notebook} This notebook
*/
Notebook.prototype.select_next = function () {
Notebook.prototype.select_next = function (moveanchor) {
var index = this.get_selected_index();
this.select(index+1);
this.select(index+1, moveanchor);
return this;
};
@ -802,9 +741,9 @@ define(function (require) {
*
* @return {Notebook} This notebook
*/
Notebook.prototype.select_prev = function () {
Notebook.prototype.select_prev = function (moveanchor) {
var index = this.get_selected_index();
this.select(index-1);
this.select(index-1, moveanchor);
return this;
};
@ -858,6 +797,7 @@ define(function (require) {
* @param {Cell} [cell] Cell to enter edit mode on.
*/
Notebook.prototype.handle_edit_mode = function (cell) {
this._contract_selection();
if (cell && this.mode !== 'edit') {
cell.edit_mode();
this.mode = 'edit';
@ -870,6 +810,7 @@ define(function (require) {
* Make a cell enter edit mode.
*/
Notebook.prototype.edit_mode = function () {
this._contract_selection();
var cell = this.get_selected_cell();
if (cell && this.mode !== 'edit') {
cell.unrender();
@ -978,7 +919,7 @@ define(function (require) {
*/
Notebook.prototype.delete_cells = function(indices) {
if (indices === undefined) {
indices = this.get_marked_indices();
indices = this.get_selected_cells_indices();
}
this.undelete_backup = [];
@ -1418,7 +1359,7 @@ define(function (require) {
* Copy cells.
*/
Notebook.prototype.copy_cell = function () {
var cells = this.get_marked_cells();
var cells = this.get_selected_cells();
if (cells.length === 0) {
cells = [this.get_selected_cell()];
}
@ -1507,7 +1448,6 @@ define(function (require) {
// Unrender the new cell so we can call set_text.
new_cell.unrender();
new_cell.set_text(texta);
new_cell.marked = cell.marked;
}
};
@ -1564,14 +1504,13 @@ define(function (require) {
this.delete_cells(indices);
this.select(this.find_cell_index(target));
this.unmark_all_cells();
};
/**
* Merge the selected range of cells
*/
Notebook.prototype.merge_marked_cells = function() {
this.merge_cells(this.get_marked_indices());
Notebook.prototype.merge_selected_cells = function() {
this.merge_cells(this.get_selected_cells_indices());
};
/**
@ -2009,27 +1948,27 @@ define(function (require) {
/**
* Execute or render cell outputs and go into command mode.
*/
Notebook.prototype.execute_marked_cells = function () {
this.execute_cells(this.get_marked_indices());
Notebook.prototype.execute_selected_cells = function () {
this.execute_cells(this.get_selected_cells_indices());
};
/**
* Alias for execute_marked_cells, for backwards compatibility --
* Alias for execute_selected_cells, for backwards compatibility --
* previously, doing "Run Cell" would only ever run a single cell (hence
* `execute_cell`), but now it runs all marked cells, so that's the
* preferable function to use. But it is good to keep this function to avoid
* breaking existing extensions, etc.
*/
Notebook.prototype.execute_cell = function () {
this.execute_marked_cells();
this.execute_selected_cells();
};
/**
* Execute or render cell outputs and insert a new cell below.
*/
Notebook.prototype.execute_cell_and_insert_below = function () {
// execute the marked cells, and don't insert anything
var indices = this.get_marked_indices();
var indices = this.get_selected_cells_indices();
if (indices.length > 1) {
this.execute_cells(indices);
return;
@ -2061,8 +2000,7 @@ define(function (require) {
* Execute or render cell outputs and select the next cell.
*/
Notebook.prototype.execute_cell_and_select_below = function () {
// execute the marked cells, and don't select anything
var indices = this.get_marked_indices();
var indices = this.get_selected_cells_indices();
if (indices.length > 1) {
this.execute_cells(indices);
return;

@ -118,7 +118,7 @@ define([
// Cell level actions
TextCell.prototype.select = function () {
var cont = Cell.prototype.select.apply(this);
var cont = Cell.prototype.select.apply(this, arguments);
if (cont) {
if (this.mode === 'edit') {
this.code_mirror.refresh();

@ -1,59 +1,65 @@
@_cell_padding_minus_border: @cell_padding - @cell_border_width;
._marked_style(@n) {
border-left-width: @n;
padding-left: @cell_padding - @n;
._selected_style(@c1, @c2, @sep:0, @border_width:@cell_border_width) {
border-left-width: @border_width;
padding-left: @cell_padding - @border_width;
background: linear-gradient(to right, @c1 -40px,@c1 @sep,@c2 @sep,@c2 100%);
}
div.cell {
border: @cell_border_width solid transparent;
.vbox();
.corner-all();
.border-box-sizing();
border-width: @cell_border_width;
border-style: solid;
border-color: transparent;
&.marked {
._marked_style(3px);
border-left-color: @marked_border_color_light;
/* Don't border the cells when printing */
width: 100%;
padding: @_cell_padding_minus_border;
/* This acts as a spacer between cells, that is outside the border */
margin: 0px;
outline: none;
._selected_style(transparent, transparent, @cell_border_width);
.jupyter-multi-select &.jupyter-soft-selected {
border-left-color: @selected_border_color_light;
border-left-color: @soft_select_color;
._selected_style(@selected_border_color_light, @soft_select_color, 5px, 0px);
@media print {
border-color: transparent;
}
}
&.selected {
&.selected {
border-color: @border_color;
border-left-color: @marked_border_color;
._marked_style(2px);
&.marked {
._marked_style(4px);
}
/* Don't border the cells when printing */
._selected_style(@selected_border_color, transparent, 5px, 0px);
@media print {
border-color: transparent;
}
}
.jupyter-multi-select &.selected.jupyter-soft-selected {
._selected_style(@selected_border_color, @soft_select_color, 7px, 0);
}
.edit_mode &.selected {
border-color: @edit_mode_border_color;
._selected_style(@edit_mode_border_color, transparent, 5px, 0px);
/* Don't border the cells when printing */
@media print {
border-color: transparent;
}
}
width: 100%;
padding: @_cell_padding_minus_border;
/* This acts as a spacer between cells, that is outside the border */
margin: 0px;
outline: none;
}
.prompt {
/* This needs to be wide enough for 3 digit prompt numbers: In[100]: */
min-width: 14ex;

@ -11,12 +11,17 @@
@code_line_height: 1.21429em; // changed from 1.231 to get 17px even
@code_padding: 0.4em; // 5.6 px
@rendered_html_border_color: black;
@input_prompt_color: navy;
@output_prompt_color: darkred;
@input_prompt_color: #303F9F;
@output_prompt_color: #D84315;
@output_pre_color: black;
@notification_widget_bg: rgba(240, 240, 240, 0.5);
@marked_border_color: #009AF5;
@marked_border_color_light: #7AC7F5;
@edit_mode_border_color: green;
@selected_border_color: #42A5F5;
@selected_border_color_light: #90CAF9;
@soft_select_color: #E3F2FD;
@edit_mode_border_color: #66BB6A;
@cell_padding: 6px;
@cell_border_width: 1px;
@cell_border_width: 1px;

@ -41,7 +41,7 @@ casper.notebook_test(function () {
this.test.assertEquals(this.get_cell_text(1), 'cd', 'split; Verify that cell 1 has the second half.');
this.validate_notebook_state('split', 'edit', 1);
this.select_cell(0); // Move up to cell 0
this.evaluate(function() { IPython.notebook.extend_marked(1);});
this.evaluate(function() { IPython.notebook.extend_selection_by(1);});
this.trigger_keydown('shift-m'); // Merge
this.validate_notebook_state('merge', 'command', 0);
this.test.assertEquals(this.get_cell_text(0), a, 'merge; Verify that cell 0 has the merged contents.');

@ -3,16 +3,18 @@
//
casper.notebook_test(function () {
var that = this;
var assert_outputs = function (expected) {
var assert_outputs = function (expected, msg_prefix) {
var msg, i;
msg_prefix = "(assert_outputs) "+(msg_prefix || 'no prefix')+": ";
for (i = 0; i < that.get_cells_length(); i++) {
if (expected[i] === undefined) {
msg = 'cell ' + i + ' not executed';
msg = msg_prefix + 'cell ' + i + ' not executed';
that.test.assertFalse(that.cell_has_outputs(i), msg);
} else {
msg = 'cell ' + i + ' executed';
that.test.assertEquals(that.get_output_cell(i).text, expected[i], msg);
msg = msg_prefix + 'cell ' + i + ' executed';
var out = that.get_output_cell(i, undefined, msg_prefix).text
that.test.assertEquals(out, expected[i], msg + 'out is: '+out);
}
}
};
@ -23,69 +25,54 @@ casper.notebook_test(function () {
this.append_cell('print("c")');
this.append_cell('print("d")');
this.test.assertEquals(this.get_cells_length(), 4, "correct number of cells");
this.evaluate(function () {
IPython.notebook.unmark_all_cells();
IPython.notebook.set_marked_indices([1, 2]);
});
});
this.then(function () {
this.evaluate(function () {
IPython.notebook.clear_all_output();
});
this.select_cell(1);
this.validate_notebook_state('before execute', 'command', 1);
this.trigger_keydown('ctrl-enter');
});
this.wait_for_output(1);
this.wait_for_output(2);
this.then(function () {
assert_outputs([undefined, 'b\n', 'c\n', undefined]);
this.validate_notebook_state('run marked cells', 'command', 2);
this.select_cell(2, false);
});
// execute cells in place when there are marked cells
this.then(function () {
this.evaluate(function () {
IPython.notebook.clear_all_output();
});
})
this.then(function(){
this.select_cell(1);
this.validate_notebook_state('before execute', 'command', 1);
this.trigger_keydown('shift-enter');
this.validate_notebook_state('before execute 1', 'command', 1);
this.select_cell(1);
this.select_cell(2, false);
this.trigger_keydown('ctrl-enter');
});
this.wait_for_output(1);
this.wait_for_output(2);
this.then(function () {
assert_outputs([undefined, 'b\n', 'c\n', undefined]);
this.validate_notebook_state('run marked cells', 'command', 2);
assert_outputs([undefined, 'b\n', 'c\n', undefined], 'run selected 1');
this.validate_notebook_state('run selected cells 1', 'command', 2);
});
// execute and insert below when there are marked cells
// execute and insert below when there are selected cells
this.then(function () {
this.evaluate(function () {
IPython.notebook.clear_all_output();
});
this.select_cell(1);
this.validate_notebook_state('before execute', 'command', 1);
this.validate_notebook_state('before execute 2', 'command', 1);
this.evaluate(function () {
$("#run_cell_insert_below").click();
});
});
this.wait_for_output(1);
this.wait_for_output(2);
this.then(function () {
assert_outputs([undefined, 'b\n', 'c\n', undefined]);
this.validate_notebook_state('run marked cells', 'command', 2);
assert_outputs([undefined, 'b\n', undefined, undefined , undefined],'run selected cells 2');
this.validate_notebook_state('run selected cells 2', 'edit', 2);
});
// check that it doesn't affect run all above
@ -95,7 +82,7 @@ casper.notebook_test(function () {
});
this.select_cell(1);
this.validate_notebook_state('before execute', 'command', 1);
this.validate_notebook_state('before execute 3', 'command', 1);
this.evaluate(function () {
$("#run_all_cells_above").click();
});
@ -104,7 +91,7 @@ casper.notebook_test(function () {
this.wait_for_output(0);
this.then(function () {
assert_outputs(['a\n', undefined, undefined, undefined]);
assert_outputs(['a\n', undefined, undefined, undefined],'run cells above');
this.validate_notebook_state('run cells above', 'command', 0);
});
@ -115,7 +102,7 @@ casper.notebook_test(function () {
});
this.select_cell(1);
this.validate_notebook_state('before execute', 'command', 1);
this.validate_notebook_state('before execute 4', 'command', 1);
this.evaluate(function () {
$("#run_all_cells_below").click();
});
@ -126,8 +113,8 @@ casper.notebook_test(function () {
this.wait_for_output(3);
this.then(function () {
assert_outputs([undefined, 'b\n', 'c\n', 'd\n']);
this.validate_notebook_state('run cells below', 'command', 3);
assert_outputs([undefined, 'b\n', undefined, 'c\n', 'd\n'],'run cells below');
this.validate_notebook_state('run cells below', 'command', 4);
});
// check that it doesn't affect run all
@ -137,7 +124,7 @@ casper.notebook_test(function () {
});
this.select_cell(1);
this.validate_notebook_state('before execute', 'command', 1);
this.validate_notebook_state('before execute 5', 'command', 1);
this.evaluate(function () {
$("#run_all_cells").click();
});
@ -149,7 +136,7 @@ casper.notebook_test(function () {
this.wait_for_output(3);
this.then(function () {
assert_outputs(['a\n', 'b\n', 'c\n', 'd\n']);
this.validate_notebook_state('run all cells', 'command', 3);
assert_outputs(['a\n', 'b\n', undefined, 'c\n', 'd\n'],'run all cells');
this.validate_notebook_state('run all cells', 'command', 4);
});
});

@ -1,73 +0,0 @@
// Test
casper.notebook_test(function () {
var that = this;
var a = 'print("a")';
var index = this.append_cell(a);
var b = 'print("b")';
index = this.append_cell(b);
var c = 'print("c")';
index = this.append_cell(c);
this.then(function () {
var selectedIndex = this.evaluate(function () {
Jupyter.notebook.select(0);
return Jupyter.notebook.get_selected_index();
});
this.test.assertEquals(this.evaluate(function() {
return Jupyter.notebook.get_marked_cells().length;
}), 1, 'only one cell is marked programmatically');
this.test.assertEquals(this.evaluate(function() {
return Jupyter.notebook.get_marked_indices()[0];
}), selectedIndex, 'marked cell is selected cell');
this.test.assertEquals(this.evaluate(function() {
return $('.cell.marked').length;
}), 0, 'no cells are marked visibily');
this.evaluate(function() {
Jupyter.notebook.mark_all_cells();
});
var cellCount = this.evaluate(function() {
return Jupyter.notebook.ncells();
});
this.test.assertEquals(this.evaluate(function() {
return Jupyter.notebook.get_marked_cells().length;
}), cellCount, 'mark_all');
this.test.assertEquals(this.evaluate(function() {
return $('.cell.marked').length;
}), cellCount, 'marked cells are marked visibily');
this.evaluate(function() {
Jupyter.notebook.unmark_all_cells();
});
this.test.assertEquals(this.evaluate(function() {
return Jupyter.notebook.get_marked_cells().length;
}), 1, 'unmark_all');
this.test.assertEquals(this.evaluate(function() {
return Jupyter.notebook.get_marked_indices()[0];
}), selectedIndex, 'marked cell is selected cell');
this.evaluate(function() {
Jupyter.notebook.set_marked_indices([1]);
});
this.test.assertEquals(this.evaluate(function() {
return Jupyter.notebook.get_marked_cells().length;
}), 2, 'two cells are marked');
this.test.assertEquals(this.evaluate(function() {
return Jupyter.notebook.get_marked_indices();
}), [selectedIndex, 1], 'get/set_marked_indices');
});
});

@ -0,0 +1,49 @@
// Test
casper.notebook_test(function () {
var that = this;
var a = 'print("a")';
var index = this.append_cell(a);
var b = 'print("b")';
index = this.append_cell(b);
var c = 'print("c")';
index = this.append_cell(c);
this.then(function () {
var selectedIndex = this.evaluate(function () {
Jupyter.notebook.select(0);
return Jupyter.notebook.get_selected_index();
});
this.test.assertEquals(this.evaluate(function() {
return Jupyter.notebook.get_selected_cells().length;
}), 1, 'only one cell is selected programmatically');
this.test.assertEquals(this.evaluate(function() {
return $('.cell.jupyter-soft-selected').length;
}), 1, 'one cell is selected');
this.test.assertEquals(this.evaluate(function() {
Jupyter.notebook.extend_selection_by(1);
return Jupyter.notebook.get_selected_cells().length;
}), 2, 'extend selection by one');
this.test.assertEquals(this.evaluate(function() {
Jupyter.notebook.extend_selection_by(-1);
return Jupyter.notebook.get_selected_cells().length;
}), 1, 'contract selection by one');
this.test.assertEquals(this.evaluate(function() {
Jupyter.notebook.select(1);
Jupyter.notebook.extend_selection_by(-1);
return Jupyter.notebook.get_selected_cells().length;
}), 2, 'extend selection by one up');
});
});

@ -4,11 +4,11 @@
casper.notebook_test(function () {
var that = this;
var assert_marked_cells = function (action, indices) {
var marked = that.evaluate(function () {
return IPython.notebook.get_marked_indices();
var assert_selected_cells = function (action, indices) {
var selected = that.evaluate(function () {
return IPython.notebook.get_selected_cells_indices();
});
that.test.assertEquals(marked, indices, action + "; verify marked cells");
that.test.assertEquals( selected, indices, action + "; verify selected cells");
};
var assert_cells = function (action, cells, index) {
@ -22,7 +22,7 @@ casper.notebook_test(function () {
}
that.validate_notebook_state(action, 'command', index);
assert_marked_cells(action, [index]);
assert_selected_cells(action, [index]);
};
var a = 'print("a")';
@ -59,7 +59,7 @@ casper.notebook_test(function () {
this.select_cell(1);
this.trigger_keydown('esc');
this.trigger_keydown('shift-j');
assert_marked_cells("select cells 1-2", [1, 2]);
assert_selected_cells("select cells 1-2", [1, 2]);
this.trigger_keydown('shift-m');
this.trigger_keydown('esc');
assert_cells("merge cells 1-2", [a, bc, d], 1);
@ -75,7 +75,7 @@ casper.notebook_test(function () {
this.select_cell(3);
this.trigger_keydown('esc');
this.trigger_keydown('shift-k');
assert_marked_cells("select cells 3-2", [2, 3]);
assert_selected_cells("select cells 3-2", [2, 3]);
this.trigger_keydown('shift-m');
this.trigger_keydown('esc');
assert_cells("merge cells 3-2", [a, bc, cd], 2);

@ -169,11 +169,11 @@ casper.wait_for_output = function (cell_num, out_num) {
},
function then() { },
function timeout() {
this.echo("wait_for_output timed out on cell "+cell_num+", waiting for "+out_num+"outputs .");
this.echo("wait_for_output timed out on cell "+cell_num+", waiting for "+out_num+" outputs .");
var pn = this.evaluate(function get_prompt(c) {
return IPython.notebook.get_cell(c).input_prompt_number;
return (IPython.notebook.get_cell(c)|| {'input_prompt_number':'no cell'}).input_prompt_number;
});
this.echo("cell prompt was :'"+pn+"'.")
this.echo("cell prompt was :'"+pn+"'.");
});
});
};
@ -229,7 +229,8 @@ casper.cell_has_outputs = function (cell_num) {
return result > 0;
};
casper.get_output_cell = function (cell_num, out_num) {
casper.get_output_cell = function (cell_num, out_num, message) {
messsge = message+': ' ||'no category :'
// return an output of a given cell
out_num = out_num || 0;
var result = casper.evaluate(function (c, o) {
@ -244,7 +245,7 @@ casper.get_output_cell = function (cell_num, out_num) {
},
{c : cell_num});
this.test.assertTrue(false,
"Cell " + cell_num + " has no output #" + out_num + " (" + num_outputs + " total)"
message+"Cell " + cell_num + " has no output #" + out_num + " (" + num_outputs + " total)"
);
} else {
return result;
@ -394,14 +395,19 @@ casper.cell_element_function = function(index, selector, function_name, function
casper.validate_notebook_state = function(message, mode, cell_index) {
// Validate the entire dual mode state of the notebook. Make sure no more than
// one cell is selected, focused, in edit mode, etc...
// General tests.
this.test.assertEquals(this.get_keyboard_mode(), this.get_notebook_mode(),
message + '; keyboard and notebook modes match');
// Is the selected cell the only cell that is selected?
if (cell_index!==undefined) {
this.test.assert(this.is_only_cell_selected(cell_index),
message + '; cell ' + cell_index + ' is the only cell selected');
message + '; expecting cell ' + cell_index + ' to be the only cell selected. Got selected cell(s):'+
(function(){
return casper.evaluate(function(){
return IPython.notebook.get_selected_cells_indices();
})
})()
);
}
// Mode specific tests.
@ -433,11 +439,11 @@ casper.validate_notebook_state = function(message, mode, cell_index) {
}
};
casper.select_cell = function(index) {
casper.select_cell = function(index, moveanchor) {
// Select a cell in the notebook.
this.evaluate(function (i) {
IPython.notebook.select(i);
}, {i: index});
this.evaluate(function (i, moveanchor) {
IPython.notebook.select(i, moveanchor);
}, {i: index, moveanchor: moveanchor});
};
casper.click_cell_editor = function(index) {

Loading…
Cancel
Save