Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Vim, how can I make CSS rules into one liners?

I would like to come up with a Vim substitution command to turn multi-line CSS rules, like this one:

#main {
      padding: 0;
      margin: 10px auto;
}

into compacted single-line rules, like so:

#main {padding:0;margin:10px auto;}

I have a ton of CSS rules that are taking up too many lines, and I cannot figure out the :%s/ commands to use.

like image 336
mager Avatar asked Sep 14 '09 17:09

mager


5 Answers

Here's a one-liner:

:%s/{\_.\{-}}/\=substitute(submatch(0), '\n', '', 'g')/

\_. matches any character, including a newline, and \{-} is the non-greedy version of *, so {\_.\{-}} matches everything between a matching pair of curly braces, inclusive.

The \= allows you to substitute the result of a vim expression, which we here use to strip out all the newlines '\n' from the matched text (in submatch(0)) using the substitute() function.

The inverse (converting the one-line version to multi-line) can also be done as a one liner:

:%s/{\_.\{-}}/\=substitute(submatch(0), '[{;]', '\0\r', 'g')/
like image 76
rampion Avatar answered Oct 16 '22 06:10

rampion


If you are at the beginning or end of the rule, V%J will join it into a single line:

  • Go to the opening (or closing) brace
  • Hit V to enter visual mode
  • Hit % to match the other brace, selecting the whole rule
  • Hit J to join the lines
like image 32
sth Avatar answered Oct 16 '22 05:10

sth


Try something like this:

:%s/{\n/{/g
:%s/;\n/;/g
:%s/{\s+/{/g
:%s/;\s+/;/g

This removes the newlines after opening braces and semicolons ('{' and ';') and then removes the extra whitespace between the concatenated lines.

like image 29
Lucas Oman Avatar answered Oct 16 '22 06:10

Lucas Oman


If you want to change the file, go for rampion's solution.

If you don't want (or can't) change the file, you can play with a custom folding as it permits to choose what and how to display the folded text. For instance:

" {rtp}/fold/css-fold.vim
" [-- local settings --]               {{{1
setlocal foldexpr=CssFold(v:lnum)
setlocal foldtext=CssFoldText()

let b:width1 = 20
let b:width2 = 15

nnoremap <buffer> + :let b:width2+=1<cr><c-l>
nnoremap <buffer> - :let b:width2-=1<cr><c-l>

" [-- global definitions --]           {{{1
if exists('*CssFold')
  setlocal foldmethod=expr
  " finish
endif

function! CssFold(lnum)
  let cline = getline(a:lnum)
  if     cline =~ '{\s*$'
      return 'a1'
  elseif cline =~ '}\s*$'
      return 's1'
  else
      return '='
  endif
endfunction

function! s:Complete(txt, width)
  let length = strlen(a:txt)
  if length > a:width
      return a:txt
  endif
  return a:txt . repeat(' ', a:width - length)
endfunction

function! CssFoldText()
  let lnum = v:foldstart
  let txt = s:Complete(getline(lnum), b:width1)
  let lnum += 1
  while lnum < v:foldend
      let add = s:Complete(substitute(getline(lnum), '^\s*\(\S\+\)\s*:\s*\(.\{-}\)\s*;\s*$', '\1: \2;', ''), b:width2)
      if add !~ '^\s*$'
          let txt .= ' ' . add
      endif

      let lnum += 1
  endwhile
  return txt. '}'
endfunction

I leave the sorting of the fields as exercise. Hint: get all the lines between v:foldstart+1 and v:voldend in a List, sort the list, build the string, and that's all.

like image 4
Luc Hermitte Avatar answered Oct 16 '22 04:10

Luc Hermitte


I won’t answer the question directly, but instead I suggest you to reconsider your needs. I think that your “bad” example is in fact the better one. It is more readable, easier to modify and reason about. Good indentation is very important not only when it comes to programming languages, but also in CSS and HTML.

You mention that CSS rules are “taking up too many lines”. If you are worried about file size, you should consider using CSS and JS minifiers like YUI Compressor instead of making the code less readable.

like image 3
Adam Byrtek Avatar answered Oct 16 '22 06:10

Adam Byrtek