indenting with tabs

This post was written as an IPython Notebook. You can view it on nbviewer, or download it.

Greg Wilson asked on the IPython mailing list:

Subject: easiest way to insert a literal tab character in a code
cell? 
Greg Wilson, on 2014-04-03 18:37,  wrote:
> Hi,
> I'd like to put literal tab characters in cells, but of course tab means 
> "indent" to the editor.  What's the easiest way to do this?
> Thanks,
> Greg
> p.s. because I'm going to write Makefiles in the notebook...

The easiest way to do this is to just get a tab character somewhere that you can copy, and then paste it in.

In [1]:
print("\t")


In [2]:
    # I copy pasted the output of the cell above here

An alternative solution is to make a string with tabs and insert it into another cell, using IPython machinery.

In [3]:
ip = get_ipython()
In [4]:
ip.set_next_input("\tMakefiles are awesome")
In []:
    Makefiles are awesome

If you have a file on disk or on the web, you can also just use the %load magic to do this.

In [5]:
%load /home/pi/file_with_tabs
In []:
default:
    cat /etc/issue
    whoami

Such files can be written with the %%writefile cell magic... but of course you need to have inserted tabs there in some manner.

In [6]:
%%writefile ~/file_with_tabs
default:
    cat /etc/issue
    whoami
Overwriting /home/pi/file_with_tabs

In [7]:
!make -f /home/pi/file_with_tabs
cat /etc/issue
Debian GNU/Linux jessie/sid \n \l

whoami
pi

The more involved, but more usable way

We can set up CodeMirror to insert tabs instead of spaces.

In [8]:
%%javascript

IPython.tab_as_tab_everywhere = function(use_tabs) {
    if (use_tabs === undefined) {
        use_tabs = true; 
    }

    // apply setting to all current CodeMirror instances
    IPython.notebook.get_cells().map(
        function(c) {  return c.code_mirror.options.indentWithTabs=use_tabs;  }
    );
    // make sure new CodeMirror instances created in the future also use this setting
    CodeMirror.defaults.indentWithTabs=use_tabs;

    };

The reason we attach tab_as_tab_everywhere to IPython is because when we use the %%javascript magic, any variables we define there must be called in the same cell that defined it - they get their own closure. The reason we do this is to allow the notebook javascript to not get screwed up when there are javascript errors. We could have attached it to window or CodeMirror or anything else that's already in javascript-land.

I covered how to add functions like this to the custom.js file in your profile in my post about disabling blinking in the notebook. That way these little functions are available in every notebook, without you having to insert a cell defining them.

Now we've got code that allows us to apply the change to all current and future cells. We leave it as an exercise for the interested reader to modify that code and make a little button in the toolbar, to toggle it on a per-cell basis.

Hints:

You can get to the code mirror instance via IPython.notebook.get_selected_cell().code_mirror

This post was written as an IPython Notebook. You can view it on nbviewer, or download it.