Skip to content

Commit

Permalink
Merge pull request #954 from juhasch/pr/952
Browse files Browse the repository at this point in the history
Add icon for codefolding editor mode
  • Loading branch information
jcb91 authored Apr 9, 2017
2 parents c32639e + a6fee13 commit d25e7d4
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 101 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Type: Jupyter Notebook Extension
Name: Codefolding in Editor
Description: |
Enables the CodeMirror feature to allow codefolding in the Jupyter file
editor view.
Note that this also uses the codefolding hotkey from the codefolding
nbextension for the notebook view.
Link: readme.md
Icon: codefolding_editor.png
Main: edit.js
Compatibility: 4.x
Section: edit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
define(['./main'], function (codefolding) {
"use strict";
return codefolding;
});
224 changes: 123 additions & 101 deletions src/jupyter_contrib_nbextensions/nbextensions/codefolding/main.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Allow codefolding in code cells
//
// This extension enables the CodeMirror feature
// It works by adding a gutter area to each code cell.
// It works by adding a gutter area to each code cell.
// Fold-able code is marked using small triangles in the gutter.
//
// The current folding state is saved in the cell metadata as an array
Expand All @@ -15,139 +15,135 @@ define([
'require',
'base/js/events',
'services/config',
'base/js/utils',
'notebook/js/codecell',
'codemirror/lib/codemirror',
'codemirror/addon/fold/foldcode',
'codemirror/addon/fold/foldgutter',
'codemirror/addon/fold/brace-fold',
'codemirror/addon/fold/indent-fold'
], function(IPython, $, require, events, configmod, utils, codecell, codemirror) {
], function (Jupyter, $, require, events, configmod, codecell, CodeMirror) {
"use strict";

var base_url = utils.get_body_data("baseUrl");
var config = new configmod.ConfigSection('notebook', {base_url: base_url});

// define default config parameter values
var params = {
codefolding_hotkey : 'Alt-f',
};

// updates default params with any specified in the server's config
var update_params = function() {
for (var key in params){
if (config.data.hasOwnProperty(key) ){
params[key] = config.data[key];
// updates default params with any specified in the provided config data
var update_params = function (config_data) {
for (var key in params) {
if (config_data.hasOwnProperty(key)) {
params[key] = config_data[key];
}
}
};

config.loaded.then(function() {
update_params();

// register actions with ActionHandler instance
var prefix = 'auto';
var name = 'toggle-codefolding';
var action = {
icon: 'fa-comment-o',
help : 'Toggle codefolding',
help_index : 'ec',
id : 'toggle_codefolding',
handler : toggleFolding
};
var action_full_name = IPython.keyboard_manager.actions.register(action, name, prefix);

// define keyboard shortcuts
var edit_mode_shortcuts = {};
edit_mode_shortcuts[params.codefolding_hotkey] = action_full_name;

// register keyboard shortcuts with keyboard_manager
IPython.notebook.keyboard_manager.edit_shortcuts.add_shortcuts(edit_mode_shortcuts);
IPython.notebook.keyboard_manager.command_shortcuts.add_shortcuts(edit_mode_shortcuts);
});
var on_config_loaded = function () {
if (Jupyter.notebook !== undefined) {
// register actions with ActionHandler instance
var prefix = 'auto';
var name = 'toggle-codefolding';
var action = {
icon: 'fa-comment-o',
help : 'Toggle codefolding',
help_index : 'ec',
id : 'toggle_codefolding',
handler : toggleFolding
};
var action_full_name = Jupyter.keyboard_manager.actions.register(action, name, prefix);

// define keyboard shortcuts
var edit_mode_shortcuts = {};
edit_mode_shortcuts[params.codefolding_hotkey] = action_full_name;

// register keyboard shortcuts with keyboard_manager
Jupyter.notebook.keyboard_manager.edit_shortcuts.add_shortcuts(edit_mode_shortcuts);
Jupyter.notebook.keyboard_manager.command_shortcuts.add_shortcuts(edit_mode_shortcuts);
}
else {
// we're in edit view
var extraKeys = Jupyter.editor.codemirror.getOption('extraKeys');
extraKeys[params.codefolding_hotkey] = toggleFolding;
CodeMirror.normalizeKeyMap(extraKeys);
console.log('[codefolding] binding hotkey', params.codefolding_hotkey);
Jupyter.editor.codemirror.setOption('extraKeys', extraKeys);
}
};

/*
* Toggle folding on/off at current line
*
* @param cm CodeMirror instance
*
*/
function toggleFolding() {
var cm = IPython.notebook.get_selected_cell().code_mirror;
function toggleFolding () {
var cm;
var pos = {line: 0, ch: 0, xRel: 0};
if (IPython.notebook.mode === 'edit') {
if (Jupyter.notebook !== undefined) {
cm = Jupyter.notebook.get_selected_cell().code_mirror;
if (Jupyter.notebook.mode === 'edit') {
pos = cm.getCursor();
}
}
else {
cm = Jupyter.editor.codemirror;
pos = cm.getCursor();
}
var opts = cm.state.foldGutter.options;
cm.foldCode(pos, opts.rangeFinder);
}


/**
* Concatenate associative array objects
*
* Source: http://stackoverflow.com/questions/2454295/javascript-concatenate-properties-from-multiple-objects-associative-array
*/
function collect() {
var ret = {};
var len = arguments.length;
for (var i=0; i<len; i++) {
for (var p in arguments[i]) {
if (arguments[i].hasOwnProperty(p)) {
ret[p] = arguments[i][p];
}
}
}
return ret;
}

/**
* Update cell metadata with folding info, so folding state can be restored after reloading notebook
*
* @param cm CodeMirror instance
*/
function updateMetadata(cm) {
function updateMetadata (cm) {
var list = cm.getAllMarks();
var lines = [];
for (var i = 0; i < list.length; i++) {
if (list[i].__isFold == true) {
if (list[i].__isFold) {
var range = list[i].find();
lines.push(range.from.line);
}
}
/* User can click on gutter of unselected cells, so make sure we store metadata in the correct cell */
var cell = IPython.notebook.get_selected_cell();
var cell = Jupyter.notebook.get_selected_cell();
if (cell.code_mirror != cm) {
var cells = IPython.notebook.get_cells();
var ncells = IPython.notebook.ncells();
for(var k=0; k < ncells; k++){
var cells = Jupyter.notebook.get_cells();
var ncells = Jupyter.notebook.ncells();
for (var k = 0; k < ncells; k++) {
var _cell = cells[k];
if (_cell.code_mirror == cm ) { cell = _cell; break; }
}
}
cell.metadata.code_folding = lines;
}

/**
* Activate codefolding in CodeMirror options, don't overwrite other settings
*
* @param cell {codecell.CodeCell} code cell to activate folding gutter
*/
function cellFolding(cell) {
if (CodeMirror.fold != undefined) {
var mode = cell.code_mirror.getMode();
/* set indent or brace folding */
if (mode.fold === 'indent' ) {
cell.code_mirror.setOption('foldGutter',{rangeFinder: new CodeMirror.fold.combine(CodeMirror.fold.firstline, CodeMirror.fold.magic, CodeMirror.fold.indent) });
} else {
cell.code_mirror.setOption('foldGutter',{rangeFinder: new CodeMirror.fold.combine(CodeMirror.fold.firstline, CodeMirror.fold.magic, CodeMirror.fold.brace) });
}
var gutters = cell.code_mirror.getOption('gutters');
var found = jQuery.inArray("CodeMirror-foldgutter", gutters);
if ( found == -1) {
cell.code_mirror.setOption('gutters', [ gutters , "CodeMirror-foldgutter"]);
}
function activate_cm_folding (cm) {
var gutters = cm.getOption('gutters');
if ($.inArray("CodeMirror-foldgutter", gutters) < 0) {
gutters.push('CodeMirror-foldgutter');
cm.setOption('gutters', gutters);
}

/* set indent or brace folding */
var opts = true;
if (Jupyter.notebook) {
opts = {
rangeFinder: new CodeMirror.fold.combine(
CodeMirror.fold.firstline,
CodeMirror.fold.magic,
cm.getMode().fold === 'indent' ? CodeMirror.fold.indent : CodeMirror.fold.brace
)
};
}
cm.setOption('foldGutter', opts);
}

/**
Expand All @@ -157,29 +153,29 @@ define([
* @param nbcell
*
*/
var createCell = function (event,nbcell) {
var createCell = function (event, nbcell) {
var cell = nbcell.cell;
if ((cell instanceof codecell.CodeCell)) {
cellFolding(cell)
cell.code_mirror.on('fold',updateMetadata);
cell.code_mirror.on('unfold',updateMetadata);
activate_cm_folding(cell.code_mirror);
cell.code_mirror.on('fold', updateMetadata);
cell.code_mirror.on('unfold', updateMetadata);
}
};

/*
* Initialize gutter in existing cells
*
*/
var initGutter = function() {
var cells = IPython.notebook.get_cells();
var ncells = IPython.notebook.ncells();
for (var i=0; i<ncells; i++) {
var initExistingCells = function () {
var cells = Jupyter.notebook.get_cells();
var ncells = Jupyter.notebook.ncells();
for (var i = 0; i < ncells; i++) {
var cell = cells[i];
if ((cell instanceof codecell.CodeCell)) {
cellFolding(cell);
activate_cm_folding(cell.code_mirror);
/* restore folding state if previously saved */
if ( cell.metadata.code_folding != undefined) {
for (var idx=0; idx < cell.metadata.code_folding.length; idx++) {
if (cell.metadata.code_folding !== undefined) {
for (var idx = 0; idx < cell.metadata.code_folding.length; idx++) {
var line = cell.metadata.code_folding[idx];
var opts = cell.code_mirror.state.foldGutter.options;
var linetext = cell.code_mirror.getLine(line);
Expand All @@ -192,11 +188,11 @@ define([
cell.code_mirror.refresh();
}
}
cell.code_mirror.on('fold',updateMetadata);
cell.code_mirror.on('unfold',updateMetadata);
cell.code_mirror.on('fold', updateMetadata);
cell.code_mirror.on('unfold', updateMetadata);
}
}
events.on('create.Cell',createCell);
events.on('create.Cell', createCell);
};

/**
Expand All @@ -217,19 +213,45 @@ define([
* Initialize extension
*
*/
var load_extension = function() {
var load_extension = function () {
// first, check which view we're in, in order to decide whether to load
var conf_sect;
if (Jupyter.notebook) {
// we're in notebook view
conf_sect = Jupyter.notebook.config;
}
else if (Jupyter.editor) {
// we're in file-editor view
conf_sect = new configmod.ConfigSection('notebook', {base_url: Jupyter.editor.base_url});
conf_sect.load();
}
else {
// we're some other view like dashboard, terminal, etc, so bail now
return;
}

load_css('codemirror/addon/fold/foldgutter.css');
/* change default gutter width */
load_css( './foldgutter.css');
/* require our additional custom codefolding modes before initialising fully */
if (Jupyter.notebook !== undefined && Jupyter.notebook._fully_loaded) {
require(['./firstline-fold', './magic-fold'], initGutter);
} else {
events.on("notebook_loaded.Notebook", function () {
require(['./firstline-fold', './magic-fold'], initGutter);
})
}
config.load()

conf_sect.loaded
.then(function () { update_params(conf_sect.data); })
.then(on_config_loaded);

if (Jupyter.notebook) {
/* require our additional custom codefolding modes before initialising fully */
require(['./firstline-fold', './magic-fold'], function () {
if (Jupyter.notebook._fully_loaded) {
initExistingCells();
}
else {
events.one('notebook_loaded.Notebook', initExistingCells);
}
});
}
else {
activate_cm_folding(Jupyter.editor.codemirror);
}
};

return {load_ipython_extension : load_extension};
Expand Down

0 comments on commit d25e7d4

Please sign in to comment.