Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[hinterland] make all regexes configurable #815

Merged
merged 3 commits into from
Dec 9, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
define(function (require, exports, module) {
'use strict';

var $ = require('jquery');
var utils = require('base/js/utils');
var keyboard = require('base/js/keyboard');
var Cell = require('notebook/js/cell').Cell;
var CodeCell = require('notebook/js/codecell').CodeCell;
var Completer = require('notebook/js/completer').Completer;
var ConfigSection = require('services/config').ConfigSection;

var log_prefix = '[' + module.id + ']';

// default config (updated on nbextension load)
var config = {
enable_at_start: true,
exclude_regexp: ':',
include_regexp: '',
tooltip_regexp: '\\(',
};
// flag denoting whether hinting is enabled
var do_hinting;

// ignore most specially-named keys
Expand Down Expand Up @@ -63,14 +74,6 @@ define(function (require, exports, module) {
);
}

/**
* having autocomplete trigger on a colon is actually really
* frustrating, as it means typing the usual newline after the colon
* results in inserting the first autocomplete suggestion.
* As such, don't show completions if preceding char is a colon
*/
var ignore_re = /[:]/i;

function patch_cell_keyevent () {
console.log(log_prefix, 'patching Cell.prototype.handle_codemirror_keyevent');
var orig_handle_codemirror_keyevent = Cell.prototype.handle_codemirror_keyevent;
Expand All @@ -79,12 +82,11 @@ define(function (require, exports, module) {
// Tab completion.
this.tooltip.remove_and_cancel_tooltip();
// don't attempt completion when selecting, or when using multicursor
if ( !editor.somethingSelected() &&
if ( !editor.somethingSelected() &&
editor.getSelections().length <= 1 &&
!this.completer.visible &&
specials.indexOf(event.keyCode) == -1) {
var cell = this;
var completer = this.completer;
// set a timeout to try to ensure that CodeMirror inserts
// the new key *before* the completion request happens
setTimeout(function () {
Expand All @@ -93,14 +95,15 @@ define(function (require, exports, module) {
line: cur.line,
ch: cur.ch - 1
}, cur);

if (pre_cursor !== '' && !ignore_re.test(pre_cursor)) {
if (pre_cursor === '(') {
if ( pre_cursor !== '' &&
(config.include_regexp.test(pre_cursor) || config.tooltip_regexp.test(pre_cursor)) &&
!config.exclude_regexp.test(pre_cursor) ) {
if (config.tooltip_regexp.test(pre_cursor)) {
cell.tooltip.request(cell);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tooltip_regexp character(s) must be included in the include_regexp characters, otherwise this is not fired. Perhaps that the the two regexp have to be merged (or more simply, the test modified perhaps (config.include_regexp.test(pre_cursor) || config.tooltip_regexp.test(pre_cursor) ) && .. (untested)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, good point. Probably simplest/best to alter the test

}
else {
completer.startCompletion();
completer.autopick = false;
cell.completer.startCompletion();
cell.completer.autopick = false;
}
}
}, 200);
Expand Down Expand Up @@ -139,9 +142,36 @@ define(function (require, exports, module) {
}

function load_notebook_extension () {
patch_cell_keyevent();
add_menu_item();
set_hinterland_state(true);
var base_url = utils.get_body_data('baseUrl');
var conf_sect = new ConfigSection('notebook', {base_url: base_url});
conf_sect.load().then(function on_success (new_conf_data) {
$.extend(true, config, new_conf_data.hinterland);
// special defaults:
// default include is taken from Completer, rather than the blank
if (config.include_regexp === '') {
config.include_regexp = Completer.reinvoke_re;
}
// now turn regexps loaded from config (which will be strings) into
// actual RegExp objects.
var regexp_names = ['exclude_regexp', 'include_regexp', 'tooltip_regexp'];
for (var ii=0; ii < regexp_names.length; ii++) {
if (config[regexp_names[ii]] === '') {
continue;
}
try {
config[regexp_names[ii]] = new RegExp(config[regexp_names[ii]]);
}
catch (err) {
console.warn(log_prefix, 'error parsing', regexp_names[ii] + ':', err);
}
}
}, function on_error (err) {
console.warn(log_prefix, 'error loading config:', err);
}).then(function on_success () {
patch_cell_keyevent();
add_menu_item();
set_hinterland_state(config.enable_at_start);
});
}

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,56 @@ Type: Jupyter Notebook Extension
Main: hinterland.js
Name: Hinterland
Link: README.md
Description: Enable code autocompletion menu for every keypress in a code cell, instead of only enabling it with tab
Description: |
Enable code autocompletion menu for every keypress in a code cell, instead of
only enabling it with tab
Compatibility: 4.x
Parameters:
- name: hinterland.enable_at_start
description: |
Enable hinterland's continuous hinting when notebook is first opened
input_type: checkbox
default: true
- name: hinterland.exclude_regexp
description: |
exclude_regexp:
A regular expression tested against the character before the cursor, which,
if a match occurs, prevents autocompletion from being triggered.
This is useful, for example, to prevent triggering autocomplete on a colon,
which is included by the default Completer.reinvoke pattern.
If blank, no test is performed.
Note that the regex will be created without any flags, making it case
sensitive.
input_type: text
# note that the YAML single-quoted string allows us to use the \ character
# without escaping it, similar to python's raw string syntax
default: ':'
- name: hinterland.include_regexp
description: |
include_regexp:
A regular expression tested against the character before the cursor, which
must match in order for autocompletion to be triggered.
If left blank, the value of the notebook's Completer.reinvoke_re parameter
is used, which can be modified by kernels, but defaults to
/[%0-9a-z._/\\:~-]/i.
Note that although the Completer.reinvoke_re default is case insensitive by
virtue of its /i flag, any regex specified by the user will be created
without any flags, making it case sensitive.
input_type: text
# note that the YAML single-quoted string allows us to use the \ character
# without escaping it, similar to python's raw string syntax
default: ''
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex entered here is case sensitive, because the /i flag is not added for now when creating the regex object. Perhaps warn the user on that and provide an example regex, eg [%0-9A-Za-z._/\:~-]

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, worth adding

- name: hinterland.tooltip_regexp
description: |
tooltip_regexp:
A regular expression tested against the character before the cursor, which
if it matches, causes a tooltip to be triggered, instead of regular
autocompletion.
For python, this is useful for example for function calls, so the default
regex matches opening parentheses.
Note that the regex will be created without any flags, making it case
sensitive.
input_type: text
# note that the YAML single-quoted string allows us to use the \ character
# without escaping it, similar to python's raw string syntax
default: '\('