geansai gorm

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 Responses to “using javascript folds in vim”

  1. PaulG says:

    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. » neatly indented folds in vim « klog says:

    [...] If, like me, you use tabs for indenting your code, then you may have been annoyed to find that the amazing folds capability in vim indents its collapsed lines by default with spaces. [...]

  3. Holy Shmoly! :: Vim folding redux says:

    [...] Vim folding is so handy as Kae found out ages ago. I finally tried it again this morning.. Related Stories (vim, folding, redux) [...]

  4. quatroparedes » Blog Archive » Vim goodies says:

    [...] Comfortable PHP editing with VIM Using javascript folds in vim [...]

  5. Ray Burgemeestre says:

    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