From d83cd7bbd4efc5be57c92501eb3780c339700933 Mon Sep 17 00:00:00 2001 From: andre Date: Wed, 29 Feb 2012 09:30:19 +0000 Subject: [PATCH] Added features for javascript JSBeautify (,ff) added and modified to retain position Slightly changed vimrc for different filetypes --- vim/indent/javascript.vim | 330 ++++++++++++++++++++ vim/plugin/jsbeautify.vim | 629 ++++++++++++++++++++++++++++++++++++++ vim/syntax/javascript.vim | 246 +++++++++++++++ vim/vimrc | 55 +++- 4 files changed, 1253 insertions(+), 7 deletions(-) create mode 100644 vim/indent/javascript.vim create mode 100644 vim/plugin/jsbeautify.vim create mode 100644 vim/syntax/javascript.vim diff --git a/vim/indent/javascript.vim b/vim/indent/javascript.vim new file mode 100644 index 0000000..7551777 --- /dev/null +++ b/vim/indent/javascript.vim @@ -0,0 +1,330 @@ +" Vim indent file +" Language: Javascript +" Maintainer: Darrick Wiebe +" URL: http://github.com/pangloss/vim-javascript +" Version: 1.0.0 +" Last Change: August 31, 2009 +" Acknowledgement: Based off of vim-ruby maintained by Nikolai Weibull http://vim-ruby.rubyforge.org + +" 0. Initialization {{{1 +" ================= + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal nosmartindent + +" Now, set up our indentation expression and keys that trigger it. +setlocal indentexpr=GetJavascriptIndent() +setlocal indentkeys=0{,0},0),0],!^F,o,O,e + +" Only define the function once. +if exists("*GetJavascriptIndent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +" 1. Variables {{{1 +" ============ + +" Regex of syntax group names that are or delimit string or are comments. +let s:syng_strcom = '\' + +" Regex of syntax group names that are strings. +let s:syng_string = + \ '\' + +" Regex of syntax group names that are strings or documentation. +let s:syng_stringdoc = + \'\' + +" Expression used to check whether we should skip a match with searchpair(). +let s:skip_expr = "synIDattr(synID(line('.'),col('.'),1),'name') =~ '".s:syng_strcom."'" + +let s:line_term = '\s*\%(\%(\/\/\).*\)\=$' + +" Regex that defines continuation lines, not including (, {, or [. +let s:continuation_regex = '\%([\\*+/.:]\|\%(<%\)\@[^{]*' . s:line_term + +" Regex that defines blocks. +let s:block_regex = '\%({\)\s*\%(|\%([*@]\=\h\w*,\=\s*\)\%(,\s*[*@]\=\h\w*\)*|\)\=' . s:line_term + +" 2. Auxiliary Functions {{{1 +" ====================== + +" Check if the character at lnum:col is inside a string, comment, or is ascii. +function s:IsInStringOrComment(lnum, col) + return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_strcom +endfunction + +" Check if the character at lnum:col is inside a string. +function s:IsInString(lnum, col) + return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_string +endfunction + +" Check if the character at lnum:col is inside a string or documentation. +function s:IsInStringOrDocumentation(lnum, col) + return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_stringdoc +endfunction + +" Find line above 'lnum' that isn't empty, in a comment, or in a string. +function s:PrevNonBlankNonString(lnum) + let in_block = 0 + let lnum = prevnonblank(a:lnum) + while lnum > 0 + " Go in and out of blocks comments as necessary. + " If the line isn't empty (with opt. comment) or in a string, end search. + let line = getline(lnum) + if line =~ '/\*' + if in_block + let in_block = 0 + else + break + endif + elseif !in_block && line =~ '\*/' + let in_block = 1 + elseif !in_block && line !~ '^\s*\%(//\).*$' && !(s:IsInStringOrComment(lnum, 1) && s:IsInStringOrComment(lnum, strlen(line))) + break + endif + let lnum = prevnonblank(lnum - 1) + endwhile + return lnum +endfunction + +" Find line above 'lnum' that started the continuation 'lnum' may be part of. +function s:GetMSL(lnum, in_one_line_scope) + " Start on the line we're at and use its indent. + let msl = a:lnum + let lnum = s:PrevNonBlankNonString(a:lnum - 1) + while lnum > 0 + " If we have a continuation line, or we're in a string, use line as MSL. + " Otherwise, terminate search as we have found our MSL already. + let line = getline(lnum) + let col = match(line, s:msl_regex) + 1 + if (col > 0 && !s:IsInStringOrComment(lnum, col)) || s:IsInString(lnum, strlen(line)) + let msl = lnum + else + " Don't use lines that are part of a one line scope as msl unless the + " flag in_one_line_scope is set to 1 + " + if a:in_one_line_scope + break + end + let msl_one_line = s:Match(lnum, s:one_line_scope_regex) + if msl_one_line == 0 + break + endif + endif + let lnum = s:PrevNonBlankNonString(lnum - 1) + endwhile + return msl +endfunction + +" Check if line 'lnum' has more opening brackets than closing ones. +function s:LineHasOpeningBrackets(lnum) + let open_0 = 0 + let open_2 = 0 + let open_4 = 0 + let line = getline(a:lnum) + let pos = match(line, '[][(){}]', 0) + while pos != -1 + if !s:IsInStringOrComment(a:lnum, pos + 1) + let idx = stridx('(){}[]', line[pos]) + if idx % 2 == 0 + let open_{idx} = open_{idx} + 1 + else + let open_{idx - 1} = open_{idx - 1} - 1 + endif + endif + let pos = match(line, '[][(){}]', pos + 1) + endwhile + return (open_0 > 0) . (open_2 > 0) . (open_4 > 0) +endfunction + +function s:Match(lnum, regex) + let col = match(getline(a:lnum), a:regex) + 1 + return col > 0 && !s:IsInStringOrComment(a:lnum, col) ? col : 0 +endfunction + +function s:IndentWithContinuation(lnum, ind, width) + " Set up variables to use and search for MSL to the previous line. + let p_lnum = a:lnum + let lnum = s:GetMSL(a:lnum, 1) + let line = getline(line) + + " If the previous line wasn't a MSL and is continuation return its indent. + " TODO: the || s:IsInString() thing worries me a bit. + if p_lnum != lnum + if s:Match(p_lnum,s:continuation_regex)||s:IsInString(p_lnum,strlen(line)) + return a:ind + a:width + endif + endif + + " Set up more variables now that we know we aren't continuation bound. + let msl_ind = indent(lnum) + + " If the previous line ended with [*+/.-=], start a continuation that + " indents an extra level. + if s:Match(lnum, s:continuation_regex) + if lnum == p_lnum + return msl_ind + a:width + else + return msl_ind + endif + endif + + return a:ind +endfunction + +function s:InOneLineScope(lnum) + let msl = s:GetMSL(a:lnum, 1) + if msl > 0 && s:Match(msl, s:one_line_scope_regex) + return msl + endif + return 0 +endfunction + +function s:ExitingOneLineScope(lnum) + let msl = s:GetMSL(a:lnum, 1) + if msl > 0 + " if the current line is in a one line scope .. + if s:Match(msl, s:one_line_scope_regex) + return 0 + else + let prev_msl = s:GetMSL(msl - 1, 1) + if s:Match(prev_msl, s:one_line_scope_regex) + return prev_msl + endif + endif + endif + return 0 +endfunction + +" 3. GetJavascriptIndent Function {{{1 +" ========================= + +function GetJavascriptIndent() + " 3.1. Setup {{{2 + " ---------- + + " Set up variables for restoring position in file. Could use v:lnum here. + let vcol = col('.') + + " 3.2. Work on the current line {{{2 + " ----------------------------- + + " Get the current line. + let line = getline(v:lnum) + let ind = -1 + + + " If we got a closing bracket on an empty line, find its match and indent + " according to it. For parentheses we indent to its column - 1, for the + " others we indent to the containing line's MSL's level. Return -1 if fail. + let col = matchend(line, '^\s*[]})]') + if col > 0 && !s:IsInStringOrComment(v:lnum, col) + call cursor(v:lnum, col) + let bs = strpart('(){}[]', stridx(')}]', line[col - 1]) * 2, 2) + if searchpair(escape(bs[0], '\['), '', bs[1], 'bW', s:skip_expr) > 0 + if line[col-1]==')' && col('.') != col('$') - 1 + let ind = virtcol('.')-1 + else + let ind = indent(s:GetMSL(line('.'), 0)) + endif + endif + return ind + endif + + " If we have a /* or */ set indent to first column. + if match(line, '^\s*\%(/\*\|\*/\)$') != -1 + return 0 + endif + + " If we are in a multi-line string or line-comment, don't do anything to it. + if s:IsInStringOrDocumentation(v:lnum, matchend(line, '^\s*') + 1) + return indent('.') + endif + + " 3.3. Work on the previous line. {{{2 + " ------------------------------- + + " Find a non-blank, non-multi-line string line above the current line. + let lnum = s:PrevNonBlankNonString(v:lnum - 1) + + " If the line is empty and inside a string, use the previous line. + if line =~ '^\s*$' && lnum != prevnonblank(v:lnum - 1) + return indent(prevnonblank(v:lnum)) + endif + + " At the start of the file use zero indent. + if lnum == 0 + return 0 + endif + + " Set up variables for current line. + let line = getline(lnum) + let ind = indent(lnum) + + " If the previous line ended with a block opening, add a level of indent. + if s:Match(lnum, s:block_regex) + return indent(s:GetMSL(lnum, 0)) + &sw + endif + + " If the previous line contained an opening bracket, and we are still in it, + " add indent depending on the bracket type. + if line =~ '[[({]' + let counts = s:LineHasOpeningBrackets(lnum) + if counts[0] == '1' && searchpair('(', '', ')', 'bW', s:skip_expr) > 0 + if col('.') + 1 == col('$') + return ind + &sw + else + return virtcol('.') + endif + elseif counts[1] == '1' || counts[2] == '1' + return ind + &sw + else + call cursor(v:lnum, vcol) + end + endif + + " 3.4. Work on the MSL line. {{{2 + " -------------------------- + + let ind_con = ind + let ind = s:IndentWithContinuation(lnum, ind_con, &sw) + + " }}}2 + " + " + let ols = s:InOneLineScope(lnum) + if ols > 0 + let ind = ind + &sw + else + let ols = s:ExitingOneLineScope(lnum) + while ols > 0 && ind > 0 + let ind = ind - &sw + let ols = s:InOneLineScope(ols - 1) + endwhile + endif + + return ind +endfunction + +" }}}1 + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim:set sw=2 sts=2 ts=8 noet: + diff --git a/vim/plugin/jsbeautify.vim b/vim/plugin/jsbeautify.vim new file mode 100644 index 0000000..913d792 --- /dev/null +++ b/vim/plugin/jsbeautify.vim @@ -0,0 +1,629 @@ +if &cp || exists("loaded_jsbeautify") + finish +endif +let loaded_jsbeautify = 3 + + + +function! s:trim_output() + while len(s:output) > 0 && (s:output[len(s:output)-1] == " " || s:output[len(s:output)-1] == s:indent_string) + call remove(s:output, -1) + endwhile +endfunction + +function! s:print_newline(ignore_repeated) + let s:if_line_flag = 0 + call s:trim_output() + if len(s:output)==0 + return + endif + if s:output[len(s:output)-1] != "\n" || !a:ignore_repeated + call add(s:output, "\n") + endif + let index = 0 + while index < s:indent_level + call add(s:output, s:indent_string) + let index += 1 + endwhile +endfunction + +function! s:print_space() + let last_output = " " + if len(s:output) > 0 + let last_output = s:output[len(s:output) - 1] + endif + if last_output != " " && last_output != "\n" && last_output != s:indent_string + call add(s:output, " ") + endif +endfunction + +function! s:print_token() + call add(s:output, s:token_text) +endfunctio + +function! s:indent() + let s:indent_level += 1 +endfunction + +function! s:unindent() + if s:indent_level + let s:indent_level -= 1 + endif +endfunction + +function! s:remove_indent() + if len(s:output)>0 && s:output[len(s:output) -1] == s:indent_string + call remove(s:output, -1) + endif +endfunction + +function! s:set_mode(mode) + call add(s:modes, s:current_mode) + let s:current_mode = a:mode +endfunction + +function! s:restore_mode() + if s:current_mode == "DO_BLOCK" + let s:do_block_just_closed = 1 + else + let s:do_block_just_closed = 0 + endif + let s:current_mode = remove(s:modes, -1) +endfunction + +function! s:in_array(what, arr) + return index(a:arr, a:what) != -1 +endfunction + +function! s:get_next_token() + let n_newlines = 0 + + if s:parser_pos >= len(s:input) + return ["", "TK_EOF"] + endif + + let c = s:input[s:parser_pos] + let s:parser_pos += 1 + + while s:in_array(c, s:whitespace) + if s:parser_pos >= len(s:input) + return ["", "TK_EOF"] + endif + + if c == "\n" + let n_newlines += 1 + endif + + let c = s:input[s:parser_pos] + let s:parser_pos += 1 + endwhile + + let wanted_newline = 0 + + if s:opt_preserve_newlines + if n_newlines > 1 + for i in [0, 1] + call s:print_newline(i==0) + endfor + endif + let wanted_newline = n_newlines == 1 + endif + + if s:in_array(c, s:wordchar) + if s:parser_pos < len(s:input) + while s:in_array(s:input[s:parser_pos], s:wordchar) + let c .= s:input[s:parser_pos] + let s:parser_pos += 1 + if s:parser_pos == len(s:input) + break + endif + endwhile + endif + + "if s:parser_pos != len(s:input) && c =~ /^[0-9]+[Ee]$/ && (s:input[s:parser_pos] == "-" || s:input[s:parser_pos] == "+") + "let sign = s:input[s:parser_pos] + "let s:parser_pos += 1 + + "let t = get_next_token(s:parser_pos) + "let c .= sign . t[0] + "return [c, "TK_WORD"] + " endif + + if c == "in" + return [c, "TK_OPERATOR"] + endif + if wanted_newline && s:last_type != "TK_OPERATOR" && !s:if_line_flag + call s:print_newline(1) + endif + return [c, "TK_WORD"] + endif + if c == "(" || c == "[" + return [c, "TK_START_EXPR"] + endif + + if c == ")" || c == "]" + return [c, "TK_END_EXPR"] + endif + + if c == "{" + return [c, "TK_START_BLOCK"] + endif + + if c == "}" + return [c, "TK_END_BLOCK"] + endif + + if c == ";" + return [c, "TK_SEMICOLON"] + endif + + if c == "/" + let comment = "" + if s:input[s:parser_pos] == "*" + let s:parser_pos += 1 + if s:parser_pos < len(s:input) + while !(s:input[s:parser_pos] == "*" && s:parser_pos + 1 < len(s:input) && s:input[s:parser_pos + 1] == "/" && s:parser_pos < len(s:input)) + let comment .= s:input[s:parser_pos] + let s:parser_pos += 1 + if s:parser_pos >= len(s:input) + break + endif + endwhile + endif + let s:parser_pos += 2 + return ['/*' . comment . '*/', 'TK_BLOCK_COMMENT'] + endif + + " peek for comment // ... + if s:input[s:parser_pos] == "/" + let comment = c + while s:input[s:parser_pos] != "\r" && s:input[s:parser_pos] != "\n" + let comment .= s:input[s:parser_pos] + let s:parser_pos += 1 + if s:parser_pos >= len(s:input) + break + endif + endwhile + let s:parser_pos += 1 + if wanted_newline + call s:print_newline(1) + endif + return [comment, "TK_COMMENT"] + endif + endif + + if c == "'" || c =='"' || (c == "/" && ((s:last_type == "TK_WORD" && s:last_text == "return") || (s:last_type == "TK_START_EXPR" || s:last_type == "TK_START_BLOCK" || s:last_type == "TK_END_BLOCK" || s:last_type == "TK_OPERATOR" || s:last_type == "TK_EOF" || s:last_type == "TK_SEMICOLON"))) + let sep = c + let esc = 0 + let resulting_string = c + + if s:parser_pos < len(s:input) + while esc || s:input[s:parser_pos] != sep + let resulting_string .= s:input[s:parser_pos] + if !esc + let esc = s:input[s:parser_pos] == "\\" + else + let esc = 0 + endif + let s:parser_pos += 1 + if s:parser_pos >= len(s:input) + return [resulting_string, "TK_STRING"] + endif + endwhile + endif + + let s:parser_pos += 1 + + let resulting_string .= sep + + if sep == "/" + + while s:parser_pos < len(s:input) && s:in_array(s:input[s:parser_pos], s:wordchar) + let resulting_string .= s:input[s:parser_pos] + let s:parser_pos += 1 + endwhile + endif + return [resulting_string, "TK_STRING"] + endif + + if c == "#" + let sharp = "#" + if s:parser_pos < len(s:input) && s:in_array(s:input[s:parser_pos], s:digits) + let c = s:input[s:parser_pos] + let sharp .= c + let s:parser_pos += 1 + + while s:parser_pos < len(s:input) && c != "#" && c !="=" + let c = s:input[s:parser_pos] + let sharp .= c + let s:parser_pos += 1 + endwhile + + if c == "#" + return [sharp, "TK_WORD"] + else + return [sharp, "TK_OPERATOR"] + endif + endif + endif + + if c == "<" && s:input[s:parser_pos-1 : s:parser_pos+3] == "" + let s:parser_pos += 2 + if wanted_newline + call s:print_newline(1) + endif + return ["-->", "TK_COMMENT"] + endif + + if s:in_array(c, s:punct) + while s:parser_pos < len(s:input) && s:in_array(c . s:input[s:parser_pos], s:punct) + let c .= s:input[s:parser_pos] + let s:parser_pos += 1 + if s:parser_pos >= len(s:input) + break + endif + endwhile + + return [c, "TK_OPERATOR"] + endif + + return [c, "TK_UNKNOWN"] + endif + + + +endfunction + +function! s:is_js() + return expand("%:e") == "js" +endfunction + +"function! g:Jsbeautify(js_source_text, options) +function! g:Jsbeautify() + if !s:is_js() + echo "Not a JS file." + return + endif + + "let a:options = {} + let s:opt_indent_size = 1 + let s:opt_indent_char = "\t" + let s:opt_preserve_newlines = 1 + let s:opt_indent_level = 0 + + let s:if_line_flag = 0 + "-------------------------------- + + let s:indent_string = "" + while s:opt_indent_size > 0 + let s:indent_string .= s:opt_indent_char + let s:opt_indent_size -= 1 + endwhile + + let s:indent_level = s:opt_indent_level + + let lines = getline(1, "$") + let s:input = join(lines, "\n") + "let s:input = a:js_source_text + + let s:last_word = "" "last 'TK_WORD' passed + let s:last_type = "TK_START_EXPR" "last token type + let s:last_text = "" "last token text + let s:output = [] + + let s:do_block_just_closed = 0 + let s:var_line = 0 + let s:var_line_tainted = 0 + + let s:whitespace = ["\n", "\r", "\t", " "] + let s:wordchar = split("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$", '\zs') + let s:digits = split("0123456789", '\zs') + + "