Indent Cucumber Step Definitions in VIM

When writing Cucumber step definition ruby files with VIM, I noticed it can’t be indent correctly. Here is an example:

Given /^login as "(.+?)",\s*"(.+)"$/ do |user, password|
When "goto /system/account/login"
When "browser type 'login' #{user}"
When "browser type 'password' #{password}"
When "browser click 'text-input-password'"
  Given "user #{user.strip}'s network"
end

After a few trying I found the root cause is the “When” keywords used by Cucumber step definitions. In Cucumber feature file, “When” is a keyword; while in step definition ruby file, “When” is a pre-defined method name. In VIM ruby indent rules (on my Mac OSX, it’s located at /usr/share/vim/vim72/indent/ruby.vim), “when” is recognized as a ruby language keyword, but the regex matching is CASE INSENSITIVE! Actually in ruby all keywords are case sensitive, so what we need to do is fix the regex in vim indent file, let the regular expressions be case sensitive.

File diff below shows the changes:

--- indent/ruby.vim	2009-12-17 14:50:02.000000000 +0800
+++ /usr/share/vim/vim72/indent/ruby.vim	2009-07-14 13:28:14.000000000 +0800
@@ -54,11 +54,11 @@
       \ '\|while\|until\|else\|elsif\|case\|when\|unless\|begin\|ensure' .
       \ '\|rescue\)\>' .
       \ '\|\%([*+/,=-]\|<<\|>>\|:\s\)\s*\zs' .
-      \    '\<\%(if\|for\|while\|until\|case\|unless\|begin\)\>\C'
+      \    '\<\%(if\|for\|while\|until\|case\|unless\|begin\)\>'
 
 " Regex used for words that, at the start of a line, remove a level of indent.
 let s:ruby_deindent_keywords =
-      \ '^\s*\zs\<\%(ensure\|else\|rescue\|elsif\|when\|end\)\>\C'
+      \ '^\s*\zs\<\%(ensure\|else\|rescue\|elsif\|when\|end\)\>'
 
 " Regex that defines the start-match for the 'end' keyword.
 "let s:end_start_regex = '\%(^\|[^.]\)\<\%(module\|class\|def\|if\|for\|while\|until\|case\|unless\|begin\|do\)\>'
@@ -70,7 +70,7 @@
       \ '\|\'
 
 " Regex that defines the middle-match for the 'end' keyword.
-let s:end_middle_regex = '\<\%(ensure\|else\|\%(\%(^\|;\)\s*\)\@<=\\|when\|elsif\)\>\C'
+let s:end_middle_regex = '\<\%(ensure\|else\|\%(\%(^\|;\)\s*\)\@<=\\|when\|elsif\)\>'
 
 " Regex that defines the end-match for the 'end' keyword.
 let s:end_end_regex = '\%(^\|[^.:@$]\)\@<=\'

Now my VIM indent the snippet correctly, yeah!

Given /^login as "(.+?)",\s*"(.+)"$/ do |user, password|
  When "goto /system/account/login"
  When "browser type 'login' #{user}"
  When "browser type 'password' #{password}"
  When "browser click 'text-input-password'"
  Given "user #{user.strip}'s network"
end

=======================
This has been fixed by Tim Pope who maintains the vim-ruby script on GitHub. He also points out this problem only happens when VIM was globally set to “ignorecase”.

Join the Conversation

2 Comments

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.