I discovered a fantastic tool in vim recently – folding.
Now that I’ve learned to use it, I find that I’m addicted! I’ve installed Linux on a few machines in the last few weeks, and each time I do it, I don’t feel complete until I activate it.
To turn on folding for JavaScript, place the following line in your ~/.vimrc
.
let javaScript_fold=1
To show you an example of how it appears, I’m working on the following page: borders.
The JavaScript for that page is getting pretty massive, so folding lets me navigate the file easily. Here’s how the script looks upon opening it with folding inactive:
/* coded by Kae - kae@verens.com */ {
/*
I'd appreciate any feedback.
You have the right to include this in your sites.
Please retain this notice.
This source has elements coded by Dean Edwards - please read his notices before copying
*/
}
function getCss(){
var els,i;
getCss_done=1;
// get external stylesheets
els=document.getElementsByTagName('link');
for(i=0;i<els.length;++i){
if(els[i].type=='text/css' && els[i].href){
getCss_done=0;
getCss_leftToDo++;
var x=new XMLHttpRequest();
x.open('GET',els[i].href,true);
x.onreadystatechange=function(){
if(x.readyState==4 && (x.status==200 || x.status==304) ){
getCss_text+=x.responseText;
getCss_leftToDo--;
getCss_parseCssText();
if(!getCss_leftToDo)getCss_done=1;
}
}
x.send(null);
}
}
}
Reasonable, you probably think. But, what if you’re looking for a section of that file and it’s 970 lines long? That’s how long that particular script is so far. The default folding macros supplied by vim turn the above into this:
/* coded by Kae - kae@verens.com */ {
/*
I'd appreciate any feedback.
You have the right to include this in your sites.
Please retain this notice.
This source has elements coded by Dean Edwards - please read his notices before copying
*/
}
function getCss(){--------------------------------------------------------------------------------------------------------------------------------------------------------
function getCss_parseCssText(){-------------------------------------------------------------------------------------------------------------------------------------------
function borderRadius(){--------------------------------------------------------------------------------------------------------------------------------------------------
function borderRadius_waitForCss(){---------------------------------------------------------------------------------------------------------------------------------------
function borderRadius_generateCurves(){-----------------------------------------------------------------------------------------------------------------------------------
function sqrt(num){-------------------------------------------------------------------------------------------------------------------------------------------------------
function drawRect(el,x,y,w,h,bc,bi,op){-----------------------------------------------------------------------------------------------------------------------------------
Isn’t that great!? Now, to view any particular function, I don’t need to go searching for it – I can just close the one I’m working on (zc
), use my arrow keys to go to the function I want, then open that with zo
.
But, that’s not good enough… The borderRadius_generateCurves()
function is 471 lines long, for example.
So, I took a look inside the vim macro file for JavaScript (/usr/share/vim/vim63/syntax/javascript.vim
).
The language used by the syntax manager is damned ugly looking, so it was a real trial trying to do what I wanted, without screwing everything up. Eventually, I found it. Here’s the code that does the default folding:
if exists("javaScript_fold")
syn match javaScriptFunction "\<function\>"
syn region javaScriptFunctionFold start="\<function\>.*[^};]$" end="^\z1}.*$" transparent fold keepend
syn sync match javaScriptSync grouphere javaScriptFunctionFold "\<function\>"
syn sync match javaScriptSync grouphere NONE "^}"
setlocal foldmethod=syntax
setlocal foldtext=getline(v:foldstart)
else
syn keyword javaScriptFunction function
syn match javaScriptBraces "[{}]"
endif
syn sync fromstart
syn sync maxlines=100
What I wanted, was to fold at every loop, comment, conditional, and function. It was actually quite simple in the end – just replace the above with this:
syn region myFold start="{" end="}" transparent fold
syn sync fromstart
set foldmethod=syntax
set foldtext=getline(v:foldstart)
syn sync maxlines=100
Now, the entire file was visible when I opened it:
/* coded by Kae - kae@verens.com */ {-------------------------------------------------------------------------------------------------------------------------------------
function getCss(){--------------------------------------------------------------------------------------------------------------------------------------------------------
function getCss_parseCssText(){-------------------------------------------------------------------------------------------------------------------------------------------
function borderRadius(){--------------------------------------------------------------------------------------------------------------------------------------------------
function borderRadius_waitForCss(){---------------------------------------------------------------------------------------------------------------------------------------
function borderRadius_generateCurves(){-----------------------------------------------------------------------------------------------------------------------------------
function sqrt(num){-------------------------------------------------------------------------------------------------------------------------------------------------------
function drawRect(el,x,y,w,h,bc,bi,op){-----------------------------------------------------------------------------------------------------------------------------------
/* global variables */ {--------------------------------------------------------------------------------------------------------------------------------------------------
/* load xmlhttprequest compatibility layer, if required */ {--------------------------------------------------------------------------------------------------------------
/* attach the onload event */ {-------------------------------------------------------------------------------------------------------------------------------------------
/* the following code was not written by Kae Verens. licenses have been kept */ {-----------------------------------------------------------------------------------------
When I open a function, it doesn’t automatically expand the whole thing now, making it a bit more readable. I can expand it out as I need, and keep the bits I’m not interested in hidden:
function borderRadius_generateCurves(){
/* common variables */ {-------------------------------------------------------------------------------------------------------------------------------------------------
for(;i<getCss_rules.length;++i){
rules=getCss_rules[i][1];
/* fix undefined css */ {-----------------------------------------------------------------------------------------------------------------------------------------------
/* common variables */ {------------------------------------------------------------------------------------------------------------------------------------------------
if(parseInt(tr[0]) || parseInt(br[0]) || parseInt(bl[0]) || parseInt(tl[0])){
/* common variables */ {-----------------------------------------------------------------------------------------------------------------------------------------------
for(;j<els.length;++j){ /* draw border and background */
/* setup border array and common variables */ {-----------------------------------------------------------------------------------------------------------------------
/* create background element */ {-------------------------------------------------------------------------------------------------------------------------------------
/* generate curved borders */ {
if(tr0){ // top right corner-----------------------------------------------------------------------------------------------------------------------------------------
if(br0){ // bottom right corner
var irx=tppBR0-tppWR,iry=tppBR1-tppWB;
var irx2=irx*irx,tmp2=tppHeight-tppBR1;
for(var cx=tppBR0-1;cx>-1;--cx){
var cx2=cx*cx,ind=tppWidth-tppBR0+cx;
var h=(cx<irx)?sqrt(iry*iry*(1-cx2/irx2)):0;
b_arr[ind][2]=tmp2+h;
b_arr[ind][3]=tmp2+sqrt(tppBR1*tppBR1*(1-cx2/(tppBR0*tppBR0)));
}
}
if(bl0){ // bottom left corner---------------------------------------------------------------------------------------------------------------------------------------
if(tl0){ // top left corner------------------------------------------------------------------------------------------------------------------------------------------
}
/* fill in the background */ {----------------------------------------------------------------------------------------------------------------------------------------
/* draw borders */ {--------------------------------------------------------------------------------------------------------------------------------------------------
theEl.parentNode.insertBefore(bgw,theEl);
}
for(j=0;j<els.length;j++){ // remove the original border and adjust the padding accordingly ---------------------------------------------------------------------------
}
}
/* show timer */ {-------------------------------------------------------------------------------------------------------------------------------------------------------
}
Note that I’ve managed to contract comments as well. You do that by using the {
and }
symbols:
/* coded by Kae - kae@verens.com */ {
/*
I'd appreciate any feedback.
You have the right to include this in your sites.
Please retain this notice.
This source has elements coded by Dean Edwards - please read his notices before copying
*/
}
That trick can also be used to close switch
es as well:
switch(parts2[0]){
case 'background': {--------------------------------------------------------------------------------------------------------------------------------------------------
case 'border': {------------------------------------------------------------------------------------------------------------------------------------------------------
case 'border-color': {------------------------------------------------------------------------------------------------------------------------------------------------
case 'border-radius': {-----------------------------------------------------------------------------------------------------------------------------------------------
case 'border-top-right-radius': {
m=(parts3L>1)?parts3[0]+' '+parts3[1]:parts3[0]+' '+parts3[0];
getCss_rules[a][1]['border-top-right-radius']=m;
break; }
case 'border-top-left-radius': {--------------------------------------------------------------------------------------------------------------------------------------
case 'border-bottom-right-radius': {----------------------------------------------------------------------------------------------------------------------------------
case 'border-bottom-left-radius': {-----------------------------------------------------------------------------------------------------------------------------------
case 'border-style': {------------------------------------------------------------------------------------------------------------------------------------------------
case 'border-width': {------------------------------------------------------------------------------------------------------------------------------------------------
default: {------------------------------------------------------------------------------------------------------------------------------------------------------------
}
Well, that’s the longest thing I’ve written in a while… I hope it’s useful to someone!