using javascript folds in vim

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 switches 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!

5 Replies to “using javascript folds in vim”

  1. Great post.

    Don’t think I got all of it, but went running off to find out about folding. Isn’t that the smartest thing you ever saw! Gobsmacking.

    I even got the “let php_folding=1” line (Note let not “set php_…”) added to my .vimrc file so all existing scripts fold up automatically without me doing a damn thing.

    Thks for the heads up!

  2. I created a VIM script called phpfolding.vim on vim.org and it folds php functions and classes. It supports PHPDoc API comments too, like the following function:

    /**
    * This does foo and bar
    * @param bar some description
    */
    function foo($bar)
    {
    // …
    }

    Is folded into this:

    +– 9 lines function foo($bar)—————————————

    As a PHP developer I use this alot and thought people might find this useful. You can find it here: http://www.vim.org/scripts/script.php?script_id=1623

Comments are closed.