Skip to content
This repository

Add Tooltip to notebook. #987

Merged
merged 15 commits into from over 2 years ago

5 participants

Matthias Bussonnier Min RK Fernando Perez Thomas Kluyver Stefan van der Walt
Matthias Bussonnier
Collaborator
When user press '(' and nothing for 1200ms, 'object_info_request' sent to
the kernel. Then tooltip with 'definition' and beggining of 'docstring'
shown to the user if non empty response.

Unlike Completion <select> , <div> is not focusable so event handled in main
cell event handeling function

Add some CSS3 that most browser with websocket should support.

I wasn't able to replicate the 'object oriented' way of dismissing the completer with <div/> because they are not 'focusable' but it works.

I've also add some CSS3, for tooltip round-corner and fade in, if you agree...

Min RK
Owner

Seems like a useful feature. Why 1200ms, though? That's an extremely long time, and if I knew I wanted the info, I would do foo? which would be faster. It should probably be as short as possible, while minimizing the likelihood of coming up while actively typing. I would expect that to be less than 500ms.

Fernando Perez
Owner

I actually would perhaps prefer it to be handled differently: instead of being time-triggered, I think it would be better if it were tab-activated. I actually don't like that the tooltips in the qtconsole come up always. I don't need to see the range( tooltip all the time, it's just an annoyance. What I'd like to have instead is showing the tooltip if I hit tab, so that as I'm coding, if I think I need the call reference, I can just hit tab after (, and instead of attempting to complete a parenthesis (which makes no sense), tab does the useful thing and gives me the tooltip.

Do you think that's doable, @Carreau?

Thomas Kluyver
Collaborator
Matthias Bussonnier
Collaborator

@minrk,
1200 ms was to be not to quick to avoid having the tooltip poping up all the time and sending request to the kernel at every (. By trying a little, for me, 1200 ms is the right time to not having it appear when I know what I want to type and small enough to have the docstring if my memory is lacking, 500 was too short.

I see foo? like a more extensive help, which is intrusive (you have to validate the cell, and then come back to the cell) that actually stay when you start writing. if you have foo( arg1 , bar=bar , ..., nthkwarg=nthkwarg) the tooltip will disapear while you are entering the argument while foo? will stay.

I can try to do it configurable...

@fperez
I can try to do it this way, and event do it configurable I think of something that might please you and @minrk.
I propse to be able to set the time before the tooltip appear:

  • time >= 0 current behaviour
  • time =-1 never show tooltip
  • time <-1 show only on tab press

Or something like that.

Matthias Bussonnier
Collaborator

Almost done. It works both ways, with pressing tab, waiting ... etc.
I think about using 2 configurable variable, for example tooltip_wait_time and tooltip_on_tab but I need a few advice to how to make them configurable ? In notebook.js ? Should the configuration be in ipython_notebook_config.py ? Or Notebook dependant ?

@takluyver
I have enable tooltip with and without <tab> only just after ( so even if you press tab with the cursor after a opening bracket, you dont have anything to complete... for now of couse, a list of kwarg might be welcome.

@fperez
Your tab suggestion is really great, you can now have completion even when not on the last command, so you can navigate in your line and get the tooltip again ! I'll push as it is for now , with variable hard coded.

Thomas Kluyver
Collaborator

I thought we already had completions for kwargs. Am I misunderstanding what you mean?

Should it only be just after a (? There are other situations, e.g. s.decode('utf-8', ...now I want to know what the options are for the second argument. Similarly for something like compile(). Is it possible to bring up a tooltip any time we're between brackets for a function call, or if there's an unmatched open-bracket before the cursor position?

Matthias Bussonnier
Collaborator

@takluyver
About completion on kwarg, I didn't knew there was some ... but you are right, if I try :

>>> rang<tab> # complete to range

but

>>> hist( x , rang<tab> # propose `range` and `range=`

I have done tooltip which activate only when the cursor is just after an opening bracket , matched or unmatched, with characters after the cursor or not.
Tooltip can be triggerd by :

  • Pressing ( , they apear after XXX ms if no keys are pressed in between. (my first implementation cc48f13 )
  • Pressing and the character just befor the cursor is (, (otherwise clasical completion) ( added in 20d764f )

We could try to improve when tooltip can be called, by playing with regular expression. I'm for now matching to something like /[a-zA-Z_.]\+\(?$/ and also have hard coded a if( pre_cursor.substr(-1) === '(' ).
Ways of improving I see might be :

  • Fallback to tooltip if kernel response on completion is empty (too complicated I think)
  • Don't autocomplete if caracter before cursor is whitespace fallback on tooltip (My favorite choice)
  • Promote completion ending with = (kwarg completion) to the top of completer

We can think of a lot of other option, like keeping the tooltip open as long as the user hasn't press) or other, but i don't think the tooltip can really replace something like foo? without becoming over complicated for a not so obvious advantage as you most of the time don't use kwargs you don't know about...

For now, I'm refactoring the current completer, by adding an "as you type" filter, I can try to "merge" it with the tooltip, with something which look like a completer but with the docstring and definition of the 'best match'

Matthias Bussonnier
Collaborator

e999f09 improve tooltip triggering
@takluyver
you can now do somthing like

s.decode('utf-8',[space][tab] #to get the tooltip (on s.decode) while
s.decode('utf-8',[tab] # will get you the completer with the whole namespace and
s.decode('utf-8',[spaceOrNot][anyletter][tab] # will also get you a completer

commit message give some example. If someone is better than me at RegExp we can shorten up a few line of code with a while loop in codecell.js

Thomas Kluyver
Collaborator
Matthias Bussonnier
Collaborator

That should be possible, but as for now, trying to complete after a white space always give the same list :

  • %alias
  • %autocall
  • %automagic
  • ... Which I don't think is verry usefull. Prompting the user with both the tooltip and the completion as long as the completion is not smarter is IMHO useless. I'll try to get a completion that give you only the kwargs and then maybe merge it in the tooltip.
Thomas Kluyver
Collaborator
Matthias Bussonnier
Collaborator

For most of the cases it is not that hard, just have to find the previous non-matched opening bracket and request info on what is before, all done inside the request_tooltip function.
Of course if you start to have some kwarg=')' inside function call it start to be difficult, but that' an edge case...

Having a completer that gives kwargs completion first is really easy, just 10 line of js in the right place... merging it with tooltip start to be annoying as you have to handle both completion_request and object_info_request to merge the result before showing smth to the user...

Thomas Kluyver
Collaborator
Matthias Bussonnier
Collaborator

no problem. Issues are that both completion's and tooltip's divs are positionned absolutly, so will overlap, and the completer's select take focus which will prevent the tooltip from beeing dissmissed when the completer give focus back to code mirror.
In the end it might be possible, but the code will be too complicated (at least I think so...). If we really want something that handle a little of completion and a little of tooltip the resonable way is to create a uniq object which deal with all the logic of completion_request/reply and object_info_request/reply

That my opinion,but it's only my 3rd real day of implementing smth in JS which is not just opennig firebug and messing with live code in the debugger...

BTW, do you have any idea of how I can make the all things configurable (time for tooltip and so on ) or to who I need to ask. I have looked at the documentation, but I dont find how to add a configurable for the NotebookWebApp, I did came across c.NotebookApp.password but I wasn't able to follow the track well enough to understand ...

Thomas Kluyver
Collaborator
Matthias Bussonnier
Collaborator

No problem, I spend time also because I feel the utility for me also to have such a feature.

@minrk , could you help on how to have a variable going from ipython_notebook_config.py to somwhere in the notebook ?
I intend to add configuration like c.NotebookApp.time_before_tooltip , c.NotebookApp.activate_tooltip_on_tab_press and maybe some other.

Min RK
Owner

Currently no config is forwarded to the browser, yet. Our config is JSON-able, so we should be able to send the whole thing (or any subset) just fine.

Perhaps what makes the most sense, is to create a new Configurable, for purely frontend options, and send just that subset of config.

Matthias Bussonnier
Collaborator

Hum, that seem a little to much for now. I have a working copie of a configurable thing with a "configuration" panel below the "help" panel on the left side .. one big commit though, with "tooltip when pressing tabs","time before tooltip appear" and an option to get the kwags first with the completer.
I'll try to give a look at interactive rebasing to get cleaner commits and push soon.
Exchanging the configuration with the web server can come later.

thank you

Matthias Bussonnier
Collaborator

Hum, just had the chance to test my code on debian/firefox.
it seem to me that some keycode differs from my mac laptop "(" = 53 on chrome mac, "("=57 on debian firefox ... is it normal or is it because i have a french keyboard ?
How do I get around that? Can it interfear in other cases ?

Matthias Bussonnier
Collaborator

Hi,
I've reworked the order of the comits and merge some together then done a force pushed.
I have added a pannel on the Left pannel to change the setting (we can hide it by default)
make the 3 following things configurable :

  • Time before tooltip apear when '(' is pressed.
  • Wether to try or not showing tooltip when is pressed
  • Wether trying to put the kwargs first with the completer

I changed the logic for trigering on '(' from keydown to keypress because with keydown the value might change depending on keyboard layout/system
Could you please test it and told me if the tooltip is triggering after some time when pressing ( on different platform/browser/keyboard layout ?

Matthias Bussonnier
Collaborator

Ah, sorry I accidentally pushed a commit from another branch (trying to autotest the copyright date in every .py files)
so I force pushed a branch rebased on a slightly more recent master...
By the way I had once to tooltip at the same time, but wasn't able to reproduce it since then... Did anybody saw it also ?
(It might have been because it was on the last no-so-stable dev of Chrome)

Fernando Perez
Owner

I love it!! Great job, many thanks.

Some feedback before we merge it (but rest assured, this is definitely going in, it's awesome :)

  • could you add a little 'close' button on the top-right of the tooltip so it can be closed with the mouse?

  • can the tooltip be made resizeable?

I think the ideal toolbar would be both resizeable and with a scroll bar. This way, people can leave it alone when reading, but could resize it if they need to see a little more, or scroll down to copy an example.

Matthias Bussonnier
Collaborator

-close button I can take a look
-resizable that start to be out of my reach as I have no Idea how I can do that. It certainly can be made resizable through some jquerry that look like magic to me.

What I can try for the time beeing are more.. and close href link with more... bringing the pager up filled with the help, and closeclosing it

Fernando Perez
Owner

If a scroll bar is possible that would help, even if it's not resizeable. And if resizing is hard, then the more... button leading to the help pager is definitely the way to go. Especially because it gives users a way to get the help pager open when they're in the middle of editing a cell (right now they have to go up/down, make a new cell, type foo? and go back to where they were editing).

Thanks! This is great work, much appreciated.

Matthias Bussonnier
Collaborator

@fperez
Done, 2 buttons just after where the docstring is cut.
not really happy with the fact that I have to reexecute foo? which increse the prompt number.
the other way around is to totally re-parse the object_info_request to display it in the pager... that will be annoying to maintain two way of displaying the same info in the same place depending on how you get there

Will take a look for the scrollbar.

Matthias Bussonnier
Collaborator

@fperez ,
Improve the things a little, ie button on the upper right corner, made some concessions for the scrollbar appearance
can easily be changed

Cherry on the cake, if the completer get only one result, and the same, 3 time in a row, (meaning the completion is done) it shows a tooltip

Fernando Perez
Owner

A few cosmetic comments and then a more serious issue:

  1. Could the buttons be put so that the tooltip doesn't need to be made so wide? I imagine you did this b/c they'd overlap with the text...

So here's a suggestion: for expand and pager, put them at the bottom in a separate div, so the tooltip box can stay as it was. And the close one could go on the top-right, alone, and be an X in a square box instead of a full button. There are lots of little 'x' icons on the net that can be used, here's one for example: close icon (could be made dark gray instead of blue to match the rest of our colors).

  1. Serious problem: I can trivially lock up the notebook by requesting the same tooltip twice. Try this:
some_function( <tab> <esc> <tab>

The second tab doesn't produce a tooltip, and now the kernel won't accept any further input.

I don't know if this is a kernel or frontend issue, but we need to sort it out before merging.

Matthias Bussonnier
Collaborator
 `some_function( <tab> <esc> <tab>`

That works for me on Chrome and Safari even 10 times in a row ... I really don't understand why it would make the connexion crash, but i'll try to find a debian to test it. But true, it doen't work on FF, i'll investigate.

(could be made dark gray instead of blue to match the rest of our colors).

They should be (like that)... could you send me a screenshot ?

Could the buttons be put so that the tooltip doesn't need to be made so wide.

Yes they are floating-right, no problem, but I choose the tooltip "max-width" so that the text of the doc would generally no be wraped and if it touch the right border of the screen it should shrink

[...] put them at the bottom in a separate div, so the tooltip box can stay as it was

They will be in the css "overflow area" ant might not always be visible but i'll try to find a way.

Matthias Bussonnier
Collaborator

@fperez
That's actually an issues on master for me (ddb3a0f):
open a notebook, press <esc>

La connexion avec ws://127.0.0.1:8888/kernels/13ca3b5b-efb6-4055-890b-61ebec1b015e/iopub a été interrompue pendant le chargement de la page.
this.iopub_channel = new this.WebSocket(ws_url + "/iopub");
kernel.js (ligne 125)
La connexion avec ws://127.0.0.1:8888/kernels/13ca3b5b-efb6-4055-890b-61ebec1b015e/shell a été interrompue pendant le chargement de la page.
this.shell_channel = new this.WebSocket(ws_url + "/shell"); 

i'll open an issue,only on FF

IPython/frontend/html/notebook/static/js/notebook.js
((23 lines not shown))
701 732
     };
702 733
 
703 734
 
704 735
     Notebook.prototype.handle_payload = function (cell, payload) {
705 736
         var l = payload.length;
  737
+        console.log(payload);
2
Stefan van der Walt
stefanv added a note November 23, 2011

Forgotten debug?

Matthias Bussonnier Collaborator
Carreau added a note November 23, 2011

Yes, missed that

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
IPython/frontend/html/notebook/static/js/kernel.js
... ...
@@ -170,6 +170,15 @@ var IPython = (function (IPython) {
170 170
         };
171 171
     };
172 172
 
  173
+    Kernel.prototype.object_info_request = function (objname) {
  174
+        var content = {
  175
+            oname : objname.toString(),
2
Stefan van der Walt
stefanv added a note November 23, 2011

Careful here; objname can be none. E.g., tab-complete on

foo((
Matthias Bussonnier Collaborator
Carreau added a note November 23, 2011

hum... to I just stay silent in that case, or shall I try to match on foo ?

I'll also add a test to be sure objname is not null

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
IPython/frontend/html/notebook/static/css/notebook.css
((19 lines not shown))
  367
+/*"close" "expand" and "Open in pager button" of
  368
+/* the tooltip*/
  369
+.tooltip a{
  370
+    float:right;
  371
+}
  372
+
  373
+/*properties of tooltip after "expand"*/
  374
+.bigtooltip{
  375
+    height:420px;
  376
+}
  377
+
  378
+/*properties of tooltip before "expand"*/
  379
+.smalltooltip{
  380
+    text-overflow: ellipsis;
  381
+    overflow: hidden;
  382
+    height:100px;
2
Stefan van der Walt
stefanv added a note November 23, 2011

The height, by default, seems very small on most systems?

Maybe set it relative to the height of the main editor?

Matthias Bussonnier Collaborator
Carreau added a note November 23, 2011

That's a good idea, I'm still wondering what we get on really huge screen...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Stefan van der Walt stefanv commented on the diff November 23, 2011
IPython/frontend/html/notebook/static/js/codecell.js
((7 lines not shown))
  150
+        // as in the completer, because it is not focusable, so won't
  151
+        // get the event.
  152
+        clearTimeout(timeout);
  153
+        $('#tooltip').remove();
  154
+    }
  155
+
  156
+    CodeCell.prototype.finish_tooltip = function (reply) {
  157
+        defstring=reply.definition;
  158
+        docstring=reply.docstring;
  159
+        name=reply.name;
  160
+
  161
+        var that = this;
  162
+        var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
  163
+        // remove to have the tooltip not Limited in X and Y
  164
+        tooltip.addClass('smalltooltip');
  165
+        var pre=$('<pre/>').html(utils.fixConsole(docstring));
2
Stefan van der Walt
stefanv added a note November 23, 2011

Careful: docstring may be empty, e.g.

def foo():
    ""
    return 1
Matthias Bussonnier Collaborator
Carreau added a note November 23, 2011

right, missed the empty docstring... I was thinking it would reply <no docstring>...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
IPython/frontend/html/notebook/static/js/codecell.js
((103 lines not shown))
114 246
         if (!this.is_completing || matches.length === 0) {return;}
115 247
 
  248
+        //try to check if the user is typing tab at least twice after a word
  249
+        // and completion is "done"
  250
+        fallback_on_tooltip_after=2
  251
+        if(matches.length==1 && matched_text === matches[0])
  252
+        {
  253
+            if(this.npressed >fallback_on_tooltip_after  && this.prevmatch==matched_text)
  254
+            {
  255
+                console.log('Ok, you really want to complete after pressing tab '+this.npressed+' times !');
  256
+                console.log('You should undersand that there is no (more) completion for that !');
1
Stefan van der Walt
stefanv added a note November 23, 2011

"understand"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
IPython/frontend/html/notebook/static/js/codecell.js
((103 lines not shown))
114 246
         if (!this.is_completing || matches.length === 0) {return;}
115 247
 
  248
+        //try to check if the user is typing tab at least twice after a word
  249
+        // and completion is "done"
  250
+        fallback_on_tooltip_after=2
  251
+        if(matches.length==1 && matched_text === matches[0])
  252
+        {
  253
+            if(this.npressed >fallback_on_tooltip_after  && this.prevmatch==matched_text)
  254
+            {
  255
+                console.log('Ok, you really want to complete after pressing tab '+this.npressed+' times !');
  256
+                console.log('You should undersand that there is no (more) completion for that !');
  257
+                console.log("I'll show you the tooltip, will you stop bothering me ?");
1
Stefan van der Walt
stefanv added a note November 23, 2011

:)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
IPython/frontend/html/notebook/static/js/panelsection.js
... ...
@@ -121,13 +121,52 @@ var IPython = (function (IPython) {
121 121
         });
122 122
     };
123 123
 
  124
+    // ConfigSection
  125
+
  126
+    var ConfigSection = function () {
  127
+        PanelSection.apply(this, arguments);
  128
+    };
  129
+
  130
+    ConfigSection.prototype = new PanelSection();
  131
+
  132
+    ConfigSection.prototype.style = function () {
  133
+        PanelSection.prototype.style.apply(this);
  134
+        this.content.addClass('ui-helper-clearfix');
  135
+        this.content.find('div.section_row').addClass('ui-helper-clearfix');
  136
+
  137
+        this.content.find('#tooltipontab').attr('title', 'Show tooltip if yuo press <Tab> after "(" or a white space');
2
Stefan van der Walt
stefanv added a note November 23, 2011

"you"

Do we want to have this panel at all? The defaults seem pretty reasonable, and this space is very limited. This topic probably warrants a bigger discussion on client configuration. If we do want configuration, it may be better to have a little gear and a pop-up box like Google does.

Matthias Bussonnier Collaborator
Carreau added a note November 23, 2011

We can just add a visibility : hidden in css, the thing is that the configuration logic is there if we want to bind it later to save the user preferences. And for example Minrk and fperez have 2 totally different view on when a tooltip should appear.

We can also have "hidden preferences“ with one-liner js command that change the notebook behaviour, and put a cookbook somewhere in the doc otherwise

Will try to correct the you but I was certain to already have done this one...

Thanks for all the feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Stefan van der Walt

This is a great addition, thanks!

added some commits November 07, 2011
Matthias Bussonnier Add Tootip to notebook.
	When user press '(' and nothing for 1200ms, 'object_info_request' sent to
	the kernel. Then tooltip with 'definition' and beggining of 'docstring'
	 shown to the user if non empty response.

	Unlike Completion <select> , <div> is not focusable so event handled in main
	cell event handeling function

	Add some CSS3 that most browser with websocket should support.
7155489
Matthias Bussonnier tooltip on <tab> c06eaa6
Matthias Bussonnier Improve tooltip tringgering,make it configurable
	As until now, when pressing tab and a white space was preceding the cursor
	The completion was triggerd with the whole namespace in it. Now if a
	whitespace or an opening bracket is just befor the cursor it will try to
	display a tooltip. The logic to find what object_info_request is send have
	been sightly changed to try to match the expression just before the last
	unmached openig bracket before the cursor (without considering what is
	after the cursor).

	example (_|_ represent the cursor):
	>>> his_|_<tab> # completion
	>>> hist(_|_<tab> # tooltip on hist
	>>> hist(rand(20),bins=range(_|_ <tab> #tooltip on range
	>>> hist(rand(20),bins=range(10), _|_ <tab> # tooltip on hist (whitespace before cursor)
	>>> hist(rand(20),bins=range(10),_|_ <tab> # completion

	as we dont care of what is after the cursor:

	>>> hist(rand(5000), bins=50, _|_orientaion='horizontal') # and tab, equivalent to
	>>> hist(rand(5000), bins=50, _|_<tab> # onte the space again
	>>> hist(_|_rand(5000), bins=50, orientaion='horizontal') # and tab, equivalent to
	>>> hist(_|_

	the 4 give tooltip on hist

	note that you can get tooltip on things that aren't function by appending a
	'(' like

	>>> matplotlib(<tab>

	Which is kinda weird... so we might want to bound another shortcut for
	tooltip, but which matches without bracket...

	additionnaly I have added a "Config" pannel in the left pannel with a checkbox
	bind to wether or not activate this functionnality

	Note, (rebase and edited commit, might not work perfetly xwithout the following ones)
f73c6ce
Matthias Bussonnier Make the time before activating a tooltip configurable
	add a section in the left pannel of the notebook to make the time before
	triggering a tooltip when pressign "(" configurable.  Negative values will
	disable the tooltip (comparaison at each keypress for now, but can be
	improved) The syle of the <input> field should be a little improve, why not
	a slider with jquerry
d388be1
Matthias Bussonnier fix timebeforetooltip span and css d31d3cf
Matthias Bussonnier smart kwarg completion
	change order of completion element so that the ones ending with '='
	will be at the beginning of the list. When you complete inside a
	fonction call, you then have kwargs first add configuration in the left
	pannel
5c61534
Matthias Bussonnier Replace trigering tooltip for cross platform indep
	For opening bracket '(' keyCode/keydown is not sufficient because it depends
	on keyboard layout so rely on charcode/keypress ...
cc20def
Matthias Bussonnier add 'more...' and 'close' button to the pager
	'more...' button execute 'foo?' **On the behalf of the current cell**
	which increase it's prompt number to the curent value, and give focus back
bf70f08
Matthias Bussonnier improve tooltip
	put "close" and "more..." button in upper right corner,
	rename "more..." to "open in pager", add expand button.

	tooltip don't have scroll bar an are 'small' until pressing "Expand"
	they then change their height and add scrollbars.
	Horizontal scrollbar behaviour seem to depend on the browser
	>>> plot(<tab> and then click on "expand" :
	Firefox : no horizontal scrollbar (wrap line ?)
	Chrome  : horizontal scrollbar

	beware that don't limitting tooltip height or width through css will sometime give
	tooltip that are too heigh and will overflow below the bottom of the screen

	animation are broken with max-height and/or min-height
643700b
Matthias Bussonnier show tooltip if completer 'fail' afer n attempt
	if completer get only one match back and the user press tab n time,
	it fallbacks on showing a tooltip

	n=2 , hard coded for know, and completer verbose on the command line
296d02b
Matthias Bussonnier Change tooltip button to use jquery css 9ecce42
Matthias Bussonnier handle null objectname on tooltip 11f0863
Matthias Bussonnier tooltip height in % ed10bd9
Matthias Bussonnier handle empty docstring baf40ab
Matthias Bussonnier fix 2 typos 0bdcdf1
Matthias Bussonnier
Collaborator

@stefanv,
Thank you again for the feedback. I'm still quite not happy with the tooltip height, but having them too high cut the lower part when you are editing things on a cell near the bottom of the screen. I don't think there is a good way to handle that in css, somethink like 'until 200px from the bottom of the screen' or else. It should be do-able in js but tooltip are no replacement for real (foo?) help, and I don't want to put too much effort 'just' for this.

@fperez
Are the top right icons (^/+/x , for pager/expand/close) ok with you ? They should match JQuery theming as I uses jquery css classes to get them.

rebased and pushed, as usual.

Fernando Perez
Owner

This is looking great. In the interest of moving forward, I'm going to merge it now; I think we've mostly converged on something pretty good, and it can (and will) always be fine-tuned further in use. But I don't want to hold things forever on very small details anymore.

@Carreau, thanks for the great work, this is an excellent feature. And thanks everyone for the careful reviewing!

Fernando Perez
Owner

In fact, the positioning of the tooltip when expanded at the bottom of the screen isn't perfect, but hopefully later someone who knows lots of js/css tricks can help us with that.

Fernando Perez fperez merged commit a8ed50a into from November 24, 2011
Fernando Perez fperez closed this November 24, 2011
Fernando Perez fperez referenced this pull request from a commit January 10, 2012
Commit has since been removed from the repository and is no longer available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 15 unique commits by 1 author.

Nov 24, 2011
Matthias Bussonnier Add Tootip to notebook.
	When user press '(' and nothing for 1200ms, 'object_info_request' sent to
	the kernel. Then tooltip with 'definition' and beggining of 'docstring'
	 shown to the user if non empty response.

	Unlike Completion <select> , <div> is not focusable so event handled in main
	cell event handeling function

	Add some CSS3 that most browser with websocket should support.
7155489
Matthias Bussonnier tooltip on <tab> c06eaa6
Matthias Bussonnier Improve tooltip tringgering,make it configurable
	As until now, when pressing tab and a white space was preceding the cursor
	The completion was triggerd with the whole namespace in it. Now if a
	whitespace or an opening bracket is just befor the cursor it will try to
	display a tooltip. The logic to find what object_info_request is send have
	been sightly changed to try to match the expression just before the last
	unmached openig bracket before the cursor (without considering what is
	after the cursor).

	example (_|_ represent the cursor):
	>>> his_|_<tab> # completion
	>>> hist(_|_<tab> # tooltip on hist
	>>> hist(rand(20),bins=range(_|_ <tab> #tooltip on range
	>>> hist(rand(20),bins=range(10), _|_ <tab> # tooltip on hist (whitespace before cursor)
	>>> hist(rand(20),bins=range(10),_|_ <tab> # completion

	as we dont care of what is after the cursor:

	>>> hist(rand(5000), bins=50, _|_orientaion='horizontal') # and tab, equivalent to
	>>> hist(rand(5000), bins=50, _|_<tab> # onte the space again
	>>> hist(_|_rand(5000), bins=50, orientaion='horizontal') # and tab, equivalent to
	>>> hist(_|_

	the 4 give tooltip on hist

	note that you can get tooltip on things that aren't function by appending a
	'(' like

	>>> matplotlib(<tab>

	Which is kinda weird... so we might want to bound another shortcut for
	tooltip, but which matches without bracket...

	additionnaly I have added a "Config" pannel in the left pannel with a checkbox
	bind to wether or not activate this functionnality

	Note, (rebase and edited commit, might not work perfetly xwithout the following ones)
f73c6ce
Matthias Bussonnier Make the time before activating a tooltip configurable
	add a section in the left pannel of the notebook to make the time before
	triggering a tooltip when pressign "(" configurable.  Negative values will
	disable the tooltip (comparaison at each keypress for now, but can be
	improved) The syle of the <input> field should be a little improve, why not
	a slider with jquerry
d388be1
Matthias Bussonnier fix timebeforetooltip span and css d31d3cf
Matthias Bussonnier smart kwarg completion
	change order of completion element so that the ones ending with '='
	will be at the beginning of the list. When you complete inside a
	fonction call, you then have kwargs first add configuration in the left
	pannel
5c61534
Matthias Bussonnier Replace trigering tooltip for cross platform indep
	For opening bracket '(' keyCode/keydown is not sufficient because it depends
	on keyboard layout so rely on charcode/keypress ...
cc20def
Matthias Bussonnier add 'more...' and 'close' button to the pager
	'more...' button execute 'foo?' **On the behalf of the current cell**
	which increase it's prompt number to the curent value, and give focus back
bf70f08
Matthias Bussonnier improve tooltip
	put "close" and "more..." button in upper right corner,
	rename "more..." to "open in pager", add expand button.

	tooltip don't have scroll bar an are 'small' until pressing "Expand"
	they then change their height and add scrollbars.
	Horizontal scrollbar behaviour seem to depend on the browser
	>>> plot(<tab> and then click on "expand" :
	Firefox : no horizontal scrollbar (wrap line ?)
	Chrome  : horizontal scrollbar

	beware that don't limitting tooltip height or width through css will sometime give
	tooltip that are too heigh and will overflow below the bottom of the screen

	animation are broken with max-height and/or min-height
643700b
Matthias Bussonnier show tooltip if completer 'fail' afer n attempt
	if completer get only one match back and the user press tab n time,
	it fallbacks on showing a tooltip

	n=2 , hard coded for know, and completer verbose on the command line
296d02b
Matthias Bussonnier Change tooltip button to use jquery css 9ecce42
Matthias Bussonnier handle null objectname on tooltip 11f0863
Matthias Bussonnier tooltip height in % ed10bd9
Matthias Bussonnier handle empty docstring baf40ab
Matthias Bussonnier fix 2 typos 0bdcdf1
This page is out of date. Refresh to see the latest.
71  IPython/frontend/html/notebook/static/css/notebook.css
@@ -115,6 +115,18 @@ span.section_row_buttons a {
115 115
     float: right;
116 116
 }
117 117
 
  118
+#timebeforetooltip_span {
  119
+    float: right;
  120
+}
  121
+
  122
+#tooltipontab_span {
  123
+    float: right;
  124
+}
  125
+
  126
+#smartcompleter_span {
  127
+    float: right;
  128
+}
  129
+
118 130
 .checkbox_label {
119 131
     font-size: 85%;
120 132
     float: right;
@@ -321,7 +333,7 @@ div.text_cell_render {
321 333
 .ansigrey {color: grey;}
322 334
 .ansibold {font-weight: bold;}
323 335
 
324  
-.completions {
  336
+.completions , .tooltip{
325 337
     position: absolute;
326 338
     z-index: 10;
327 339
     overflow: auto;
@@ -337,6 +349,63 @@ div.text_cell_render {
337 349
     font-family: monospace;
338 350
 }
339 351
 
  352
+@-moz-keyframes fadeIn {
  353
+    from {opacity:0;}
  354
+    to {opacity:1;}
  355
+}
  356
+
  357
+@-webkit-keyframes fadeIn {
  358
+    from {opacity:0;}
  359
+    to {opacity:1;}
  360
+}
  361
+
  362
+@keyframes fadeIn {
  363
+    from {opacity:0;}
  364
+    to {opacity:1;}
  365
+}
  366
+
  367
+/*"close" "expand" and "Open in pager button" of
  368
+/* the tooltip*/
  369
+.tooltip a{
  370
+    float:right;
  371
+}
  372
+
  373
+/*properties of tooltip after "expand"*/
  374
+.bigtooltip{
  375
+    height:60%;
  376
+}
  377
+
  378
+/*properties of tooltip before "expand"*/
  379
+.smalltooltip{
  380
+    text-overflow: ellipsis;
  381
+    overflow: hidden;
  382
+    height:15%;
  383
+}
  384
+
  385
+.tooltip{
  386
+    /*transition when "expand"ing tooltip */
  387
+    -webkit-transition-property: height;
  388
+    -webkit-transition-duration: 1s;
  389
+    -moz-transition-property: height;
  390
+    -moz-transition-duration: 1s;
  391
+    transition-property: height;
  392
+    transition-duration: 1s;
  393
+    max-width:700px;
  394
+    border-radius: 0px 10px 10px 10px;
  395
+    box-shadow: 3px 3px 5px #999;
  396
+    /*fade-in animation when inserted*/
  397
+    -webkit-animation: fadeIn 200ms;
  398
+    -moz-animation: fadeIn 200ms;
  399
+    animation: fadeIn 200ms;
  400
+    vertical-align: middle;
  401
+    background: #FDFDD8;
  402
+    outline: none;
  403
+    padding: 3px;
  404
+    margin: 0px;
  405
+    font-family: monospace;
  406
+    min-height:50px;
  407
+}
  408
+
340 409
 @media print {
341 410
     body { overflow: visible !important; }
342 411
     .ui-widget-content { border: 0px; }
1  IPython/frontend/html/notebook/static/js/cell.js
@@ -85,7 +85,6 @@ var IPython = (function (IPython) {
85 85
         }
86 86
     };
87 87
 
88  
-
89 88
     // Subclasses must implement create_element.
90 89
     Cell.prototype.create_element = function () {};
91 90
 
159  IPython/frontend/html/notebook/static/js/codecell.js
@@ -47,24 +47,60 @@ var IPython = (function (IPython) {
47 47
         this.collapse()
48 48
     };
49 49
 
  50
+    //TODO, try to diminish the number of parameters.
  51
+    CodeCell.prototype.request_tooltip_after_time = function (pre_cursor,time,that){
  52
+        if (pre_cursor === "" || pre_cursor === "(" ) {
  53
+            // don't do anything if line beggin with '(' or is empty
  54
+        } else {
  55
+            // Will set a timer to request tooltip in `time`
  56
+            that.tooltip_timeout = setTimeout(function(){
  57
+                    IPython.notebook.request_tool_tip(that, pre_cursor)
  58
+                },time);
  59
+        }
  60
+    };
50 61
 
51 62
     CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
52 63
         // This method gets called in CodeMirror's onKeyDown/onKeyPress
53 64
         // handlers and is used to provide custom key handling. Its return
54 65
         // value is used to determine if CodeMirror should ignore the event:
55 66
         // true = ignore, false = don't ignore.
  67
+
  68
+        // note that we are comparing and setting the time to wait at each key press.
  69
+        // a better wqy might be to generate a new function on each time change and
  70
+        // assign it to CodeCell.prototype.request_tooltip_after_time
  71
+        tooltip_wait_time = this.notebook.time_before_tooltip;
  72
+        tooltip_on_tab    = this.notebook.tooltip_on_tab;
  73
+        var that = this;
  74
+        // whatever key is pressed, first, cancel the tooltip request before
  75
+        // they are sent, and remove tooltip if any
  76
+        if(event.type === 'keydown' && this.tooltip_timeout != null){
  77
+            CodeCell.prototype.remove_and_cancell_tooltip(that.tooltip_timeout);
  78
+            that.tooltip_timeout=null;
  79
+        }
  80
+
56 81
         if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
57 82
             // Always ignore shift-enter in CodeMirror as we handle it.
58 83
             return true;
  84
+        }else if (event.which === 40 && event.type === 'keypress' && tooltip_wait_time >= 0) {
  85
+            // triger aon keypress (!) otherwise inconsistent event.which depending on plateform
  86
+            // browser and keyboard layout !
  87
+            // Pressing '(' , request tooltip, don't forget to reappend it
  88
+            var cursor = editor.getCursor();
  89
+            var pre_cursor = editor.getRange({line:cursor.line,ch:0},cursor).trim()+'(';
  90
+            CodeCell.prototype.request_tooltip_after_time(pre_cursor,tooltip_wait_time,that);
59 91
         } else if (event.keyCode === 9 && event.type == 'keydown') {
60 92
             // Tab completion.
61 93
             var cur = editor.getCursor();
62  
-            var pre_cursor = editor.getRange({line:cur.line,ch:0},cur).trim();
63  
-            if (pre_cursor === "") {
  94
+            //Do not trim here because of tooltip
  95
+            var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
  96
+            if (pre_cursor.trim() === "") {
64 97
                 // Don't autocomplete if the part of the line before the cursor
65 98
                 // is empty.  In this case, let CodeMirror handle indentation.
66 99
                 return false;
  100
+            } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && tooltip_on_tab ) {
  101
+                CodeCell.prototype.request_tooltip_after_time(pre_cursor,0,that);
67 102
             } else {
  103
+                pre_cursor.trim();
68 104
                 // Autocomplete the current line.
69 105
                 event.stop();
70 106
                 var line = editor.getLine(cur.line);
@@ -108,11 +144,130 @@ var IPython = (function (IPython) {
108 144
         };
109 145
     };
110 146
 
  147
+    CodeCell.prototype.remove_and_cancell_tooltip = function(timeout)
  148
+    {
  149
+        // note that we don't handle closing directly inside the calltip
  150
+        // as in the completer, because it is not focusable, so won't
  151
+        // get the event.
  152
+        clearTimeout(timeout);
  153
+        $('#tooltip').remove();
  154
+    }
  155
+
  156
+    CodeCell.prototype.finish_tooltip = function (reply) {
  157
+        defstring=reply.definition;
  158
+        docstring=reply.docstring;
  159
+        if(docstring == null){docstring="<empty docstring>"};
  160
+        name=reply.name;
  161
+
  162
+        var that = this;
  163
+        var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
  164
+        // remove to have the tooltip not Limited in X and Y
  165
+        tooltip.addClass('smalltooltip');
  166
+        var pre=$('<pre/>').html(utils.fixConsole(docstring));
  167
+        var expandlink=$('<a/>').attr('href',"#");
  168
+            expandlink.addClass("ui-corner-all"); //rounded corner
  169
+            expandlink.attr('role',"button");
  170
+            //expandlink.addClass('ui-button');
  171
+            //expandlink.addClass('ui-state-default');
  172
+        var expandspan=$('<span/>').text('Expand');
  173
+            expandspan.addClass('ui-icon');
  174
+            expandspan.addClass('ui-icon-plus');
  175
+        expandlink.append(expandspan);
  176
+        expandlink.attr('id','expanbutton');
  177
+        expandlink.click(function(){
  178
+            tooltip.removeClass('smalltooltip');
  179
+            tooltip.addClass('bigtooltip');
  180
+            $('#expanbutton').remove();
  181
+            setTimeout(function(){that.code_mirror.focus();}, 50);
  182
+        });
  183
+        var morelink=$('<a/>').attr('href',"#");
  184
+            morelink.attr('role',"button");
  185
+            morelink.addClass('ui-button');
  186
+            //morelink.addClass("ui-corner-all"); //rounded corner
  187
+            //morelink.addClass('ui-state-default');
  188
+        var morespan=$('<span/>').text('Open in Pager');
  189
+            morespan.addClass('ui-icon');
  190
+            morespan.addClass('ui-icon-arrowstop-l-n');
  191
+        morelink.append(morespan);
  192
+        morelink.click(function(){
  193
+            var msg_id = IPython.notebook.kernel.execute(name+"?");
  194
+            IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.selected_cell().cell_id;
  195
+            CodeCell.prototype.remove_and_cancell_tooltip(that.tooltip_timeout);
  196
+            setTimeout(function(){that.code_mirror.focus();}, 50);
  197
+        });
  198
+
  199
+        var closelink=$('<a/>').attr('href',"#");
  200
+            closelink.attr('role',"button");
  201
+            closelink.addClass('ui-button');
  202
+            //closelink.addClass("ui-corner-all"); //rounded corner
  203
+            //closelink.adClass('ui-state-default'); // grey background and blue cross
  204
+        var closespan=$('<span/>').text('Close');
  205
+            closespan.addClass('ui-icon');
  206
+            closespan.addClass('ui-icon-close');
  207
+        closelink.append(closespan);
  208
+        closelink.click(function(){
  209
+            CodeCell.prototype.remove_and_cancell_tooltip(that.tooltip_timeout);
  210
+            setTimeout(function(){that.code_mirror.focus();}, 50);
  211
+            });
  212
+        //construct the tooltip
  213
+        tooltip.append(closelink);
  214
+        tooltip.append(expandlink);
  215
+        tooltip.append(morelink);
  216
+        if(defstring){
  217
+            defstring_html= $('<pre/>').html(utils.fixConsole(defstring));
  218
+            tooltip.append(defstring_html);
  219
+        }
  220
+        tooltip.append(pre);
  221
+        var pos = this.code_mirror.cursorCoords();
  222
+        tooltip.css('left',pos.x+'px');
  223
+        tooltip.css('top',pos.yBot+'px');
  224
+        $('body').append(tooltip);
  225
+
  226
+        // issues with cross-closing if multiple tooltip in less than 5sec
  227
+        // keep it comented for now
  228
+        // setTimeout(CodeCell.prototype.remove_and_cancell_tooltip, 5000);
  229
+    };
  230
+
111 231
 
112 232
     CodeCell.prototype.finish_completing = function (matched_text, matches) {
113 233
         // console.log("Got matches", matched_text, matches);
  234
+        var newm = new Array();
  235
+        if(this.notebook.smart_completer)
  236
+        {
  237
+            kwargs = new Array();
  238
+            other = new Array();
  239
+            for(var i=0;i<matches.length; ++i){
  240
+                if(matches[i].substr(-1) === '='){
  241
+                    kwargs.push(matches[i]);
  242
+                }else{other.push(matches[i]);}
  243
+            }
  244
+            newm = kwargs.concat(other);
  245
+            matches=newm;
  246
+        }
114 247
         if (!this.is_completing || matches.length === 0) {return;}
115 248
 
  249
+        //try to check if the user is typing tab at least twice after a word
  250
+        // and completion is "done"
  251
+        fallback_on_tooltip_after=2
  252
+        if(matches.length==1 && matched_text === matches[0])
  253
+        {
  254
+            if(this.npressed >fallback_on_tooltip_after  && this.prevmatch==matched_text)
  255
+            {
  256
+                console.log('Ok, you really want to complete after pressing tab '+this.npressed+' times !');
  257
+                console.log('You should understand that there is no (more) completion for that !');
  258
+                console.log("I'll show you the tooltip, will you stop bothering me ?");
  259
+                this.request_tooltip_after_time(matched_text+'(',0,this);
  260
+                return;
  261
+            }
  262
+            this.prevmatch=matched_text
  263
+            this.npressed=this.npressed+1;
  264
+        }
  265
+        else
  266
+        {
  267
+            this.prevmatch="";
  268
+            this.npressed=0;
  269
+        }
  270
+
116 271
         var that = this;
117 272
         var cur = this.completion_cursor;
118 273
 
13  IPython/frontend/html/notebook/static/js/kernel.js
@@ -170,6 +170,19 @@ var IPython = (function (IPython) {
170 170
         };
171 171
     };
172 172
 
  173
+    Kernel.prototype.object_info_request = function (objname) {
  174
+        if(typeof(objname)!=null)
  175
+        {
  176
+            var content = {
  177
+                oname : objname.toString(),
  178
+            };
  179
+            var msg = this.get_msg("object_info_request", content);
  180
+            this.shell_channel.send(JSON.stringify(msg));
  181
+            return msg.header.msg_id;
  182
+        }
  183
+        return;
  184
+    }
  185
+
173 186
     Kernel.prototype.execute = function (code) {
174 187
         var content = {
175 188
             code : code,
1  IPython/frontend/html/notebook/static/js/leftpanel.js
@@ -67,6 +67,7 @@ var IPython = (function (IPython) {
67 67
         this.notebook_section = new IPython.NotebookSection('div#notebook_section');
68 68
         if (! IPython.read_only){
69 69
             this.cell_section = new IPython.CellSection('div#cell_section');
  70
+            this.config_section = new IPython.ConfigSection('div#config_section');
70 71
             this.kernel_section = new IPython.KernelSection('div#kernel_section');
71 72
         }
72 73
         this.help_section = new IPython.HelpSection('div#help_section');
61  IPython/frontend/html/notebook/static/js/notebook.js
@@ -27,6 +27,9 @@ var IPython = (function (IPython) {
27 27
         this.style();
28 28
         this.create_elements();
29 29
         this.bind_events();
  30
+        this.set_tooltipontab(true);
  31
+        this.set_smartcompleter(true);
  32
+        this.set_timebeforetooltip(1200);
30 33
     };
31 34
 
32 35
 
@@ -621,6 +624,21 @@ var IPython = (function (IPython) {
621 624
     };
622 625
 
623 626
 
  627
+    Notebook.prototype.set_timebeforetooltip = function (time) {
  628
+        console.log("change time before tooltip to : "+time);
  629
+        this.time_before_tooltip = time;
  630
+    };
  631
+
  632
+    Notebook.prototype.set_tooltipontab = function (state) {
  633
+        console.log("change tooltip on tab to : "+state);
  634
+        this.tooltip_on_tab = state;
  635
+    };
  636
+
  637
+    Notebook.prototype.set_smartcompleter = function (state) {
  638
+        console.log("Smart completion (kwargs first) changed to  to : "+state);
  639
+        this.smart_completer = state;
  640
+    };
  641
+
624 642
     Notebook.prototype.set_autoindent = function (state) {
625 643
         var cells = this.cells();
626 644
         len = cells.length;
@@ -700,9 +718,22 @@ var IPython = (function (IPython) {
700 718
             this.dirty = true;
701 719
         } else if (msg_type === "complete_reply") {
702 720
             cell.finish_completing(content.matched_text, content.matches);
703  
-        };
704  
-        var payload = content.payload || [];
705  
-        this.handle_payload(cell, payload);
  721
+        } else if (msg_type === "object_info_reply"){
  722
+            //console.log('back from object_info_request : ')
  723
+            rep = reply.content;
  724
+            if(rep.found)
  725
+            {
  726
+                cell.finish_tooltip(rep);
  727
+            }
  728
+        } else {
  729
+          //console.log("unknown reply:"+msg_type);
  730
+        }
  731
+        // when having a rely from object_info_reply,
  732
+        // no payload so no nned to handle it
  733
+        if(typeof(content.payload)!='undefined') {
  734
+            var payload = content.payload || [];
  735
+            this.handle_payload(cell, payload);
  736
+        }
706 737
     };
707 738
 
708 739
 
@@ -868,6 +899,30 @@ var IPython = (function (IPython) {
868 899
     };
869 900
 
870 901
 
  902
+    Notebook.prototype.request_tool_tip = function (cell,func) {
  903
+        // Feel free to shorten this logic if you are better
  904
+        // than me in regEx
  905
+        // basicaly you shoul be able to get xxx.xxx.xxx from 
  906
+        // something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2, 
  907
+        // remove everything between matchin bracket (need to iterate)
  908
+        matchBracket = /\([^\(\)]+\)/g;
  909
+        oldfunc = func;
  910
+        func = func.replace(matchBracket,"");
  911
+        while( oldfunc != func )
  912
+        {
  913
+        oldfunc = func;
  914
+        func = func.replace(matchBracket,"");
  915
+        }
  916
+        // remove everythin after last open bracket
  917
+        endBracket = /\([^\(]*$/g;
  918
+        func = func.replace(endBracket,"");
  919
+        var re = /[a-zA-Z._]+$/g;
  920
+        var msg_id = this.kernel.object_info_request(re.exec(func));
  921
+        if(typeof(msg_id)!='undefined'){
  922
+            this.msg_cell_map[msg_id] = cell.cell_id;
  923
+            }
  924
+    };
  925
+
871 926
     Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
872 927
         var msg_id = this.kernel.complete(line, cursor_pos);
873 928
         this.msg_cell_map[msg_id] = cell.cell_id;
1  IPython/frontend/html/notebook/static/js/notebookmain.js
@@ -51,6 +51,7 @@ $(document).ready(function () {
51 51
         IPython.quick_help.element.addClass('hidden'); // shortcuts are disabled in read_only
52 52
         $('button#new_notebook').addClass('hidden');
53 53
         $('div#cell_section').addClass('hidden');
  54
+        $('div#config_section').addClass('hidden');
54 55
         $('div#kernel_section').addClass('hidden');
55 56
         $('span#login_widget').removeClass('hidden');
56 57
         // left panel starts collapsed, but the collapse must happen after
46  IPython/frontend/html/notebook/static/js/panelsection.js
@@ -121,13 +121,52 @@ var IPython = (function (IPython) {
121 121
         });
122 122
     };
123 123
 
  124
+    // ConfigSection
  125
+
  126
+    var ConfigSection = function () {
  127
+        PanelSection.apply(this, arguments);
  128
+    };
  129
+
  130
+    ConfigSection.prototype = new PanelSection();
  131
+
  132
+    ConfigSection.prototype.style = function () {
  133
+        PanelSection.prototype.style.apply(this);
  134
+        this.content.addClass('ui-helper-clearfix');
  135
+        this.content.find('div.section_row').addClass('ui-helper-clearfix');
  136
+
  137
+        this.content.find('#tooltipontab').attr('title', 'Show tooltip if you press <Tab> after "(" or a white space');
  138
+        this.content.find('#tooltipontab_label').attr('title', 'Show Tooltip when pressing Tab');
  139
+
  140
+        this.content.find('#timebeforetooltip').attr('title', 'Time before a tooltip auto-appear when "(" is pressed (negative value supress tooltip)');
  141
+        this.content.find('#timebeforetooltip_label').attr('title', 'Time before a tooltip auto-appear when "(" is pressed (negative value supress tooltip)');
  142
+
  143
+        this.content.find('#smartcompleter').attr('title', 'When inside function call, completer try to propose kwargs first');
  144
+        this.content.find('#smartcompleter_label').attr('title', 'When inside function call, completer try to propose kwargs first');
  145
+    };
  146
+
  147
+
  148
+    ConfigSection.prototype.bind_events = function () {
  149
+        PanelSection.prototype.bind_events.apply(this);
  150
+        this.content.find('#tooltipontab').change(function () {
  151
+            var state = $('#tooltipontab').prop('checked');
  152
+            IPython.notebook.set_tooltipontab(state);
  153
+        });
  154
+        this.content.find('#timebeforetooltip').change(function () {
  155
+            var state = $('#timebeforetooltip').prop('value');
  156
+            IPython.notebook.set_timebeforetooltip(state);
  157
+        });
  158
+        this.content.find('#smartcompleter').change(function () {
  159
+            var state = $('#smartcompleter').prop('checked');
  160
+            IPython.notebook.set_smartcompleter(state);
  161
+        });
  162
+    };
  163
+
124 164
     // CellSection
125 165
 
126 166
     var CellSection = function () {
127 167
         PanelSection.apply(this, arguments);
128 168
     };
129 169
 
130  
-
131 170
     CellSection.prototype = new PanelSection();
132 171
 
133 172
 
@@ -201,6 +240,10 @@ var IPython = (function (IPython) {
201 240
             var state = $('#autoindent').prop('checked');
202 241
             IPython.notebook.set_autoindent(state);
203 242
         });
  243
+        this.content.find('#tooltipontab').change(function () {
  244
+            var state = $('#tooltipontab').prop('checked');
  245
+            IPython.notebook.set_tooltipontab(state);
  246
+        });
204 247
     };
205 248
 
206 249
 
@@ -280,6 +323,7 @@ var IPython = (function (IPython) {
280 323
     IPython.PanelSection = PanelSection;
281 324
     IPython.NotebookSection = NotebookSection;
282 325
     IPython.CellSection = CellSection;
  326
+    IPython.ConfigSection = ConfigSection;
283 327
     IPython.KernelSection = KernelSection;
284 328
     IPython.HelpSection = HelpSection;
285 329
 
26  IPython/frontend/html/notebook/templates/notebook.html
@@ -251,6 +251,32 @@
251 251
             </div>
252 252
         </div>
253 253
 
  254
+        <div id="config_section">
  255
+            <div class="section_header">
  256
+                <h3>Config</h3>
  257
+            </div>
  258
+            <div class="section_content">
  259
+                <div class="section_row">
  260
+                    <span id="tooltipontab_span">
  261
+                        <input type="checkbox" id="tooltipontab" checked="true"></input>
  262
+                    </span>
  263
+                    <span class="checkbox_label" id="tooltipontab_label">Tooltip on tab:</span>
  264
+                </div>
  265
+                <div class="section_row">
  266
+                    <span id="smartcompleter_span">
  267
+                        <input type="checkbox" id="smartcompleter" checked="true"></input>
  268
+                    </span>
  269
+                    <span class="checkbox_label" id="smartcompleter_label">Smart completer:</span>
  270
+                </div>
  271
+                <div class="section_row">
  272
+                    <span id="timebeforetooltip_span">
  273
+                        <input type="text" id="timebeforetooltip" value="1200"></input>
  274
+                    </span>
  275
+                    <span class="numeric_input_label" id="timebeforetooltip_label">Time before tooltip : </span>
  276
+                </div>
  277
+            </div>
  278
+        </div>
  279
+
254 280
     </div>
255 281
     <div id="left_panel_splitter"></div>
256 282
     <div id="notebook_panel">
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.