1773a97c2SBram Moolenaar" Vim indent file 2773a97c2SBram Moolenaar" Language: TypeScript 3773a97c2SBram Moolenaar" Maintainer: See https://github.com/HerringtonDarkholme/yats.vim 496f45c0bSBram Moolenaar" Last Change: 2019 Oct 18 5773a97c2SBram Moolenaar" Acknowledgement: Based off of vim-ruby maintained by Nikolai Weibull http://vim-ruby.rubyforge.org 6773a97c2SBram Moolenaar 7773a97c2SBram Moolenaar" 0. Initialization {{{1 8773a97c2SBram Moolenaar" ================= 9773a97c2SBram Moolenaar 10773a97c2SBram Moolenaar" Only load this indent file when no other was loaded. 11773a97c2SBram Moolenaarif exists("b:did_indent") 12773a97c2SBram Moolenaar finish 13773a97c2SBram Moolenaarendif 14773a97c2SBram Moolenaarlet b:did_indent = 1 15773a97c2SBram Moolenaar 16773a97c2SBram Moolenaarsetlocal nosmartindent 17773a97c2SBram Moolenaar 18773a97c2SBram Moolenaar" Now, set up our indentation expression and keys that trigger it. 19773a97c2SBram Moolenaarsetlocal indentexpr=GetTypescriptIndent() 20773a97c2SBram Moolenaarsetlocal formatexpr=Fixedgq(v:lnum,v:count) 21773a97c2SBram Moolenaarsetlocal indentkeys=0{,0},0),0],0\,,!^F,o,O,e 22773a97c2SBram Moolenaar 23773a97c2SBram Moolenaar" Only define the function once. 24773a97c2SBram Moolenaarif exists("*GetTypescriptIndent") 25773a97c2SBram Moolenaar finish 26773a97c2SBram Moolenaarendif 27773a97c2SBram Moolenaar 28773a97c2SBram Moolenaarlet s:cpo_save = &cpo 29773a97c2SBram Moolenaarset cpo&vim 30773a97c2SBram Moolenaar 31773a97c2SBram Moolenaar" 1. Variables {{{1 32773a97c2SBram Moolenaar" ============ 33773a97c2SBram Moolenaar 34773a97c2SBram Moolenaarlet s:js_keywords = '^\s*\(break\|case\|catch\|continue\|debugger\|default\|delete\|do\|else\|finally\|for\|function\|if\|in\|instanceof\|new\|return\|switch\|this\|throw\|try\|typeof\|var\|void\|while\|with\)' 35773a97c2SBram Moolenaar 36773a97c2SBram Moolenaar" Regex of syntax group names that are or delimit string or are comments. 37773a97c2SBram Moolenaarlet s:syng_strcom = 'string\|regex\|comment\c' 38773a97c2SBram Moolenaar 39773a97c2SBram Moolenaar" Regex of syntax group names that are strings. 40773a97c2SBram Moolenaarlet s:syng_string = 'regex\c' 41773a97c2SBram Moolenaar 42773a97c2SBram Moolenaar" Regex of syntax group names that are strings or documentation. 43773a97c2SBram Moolenaarlet s:syng_multiline = 'comment\c' 44773a97c2SBram Moolenaar 45773a97c2SBram Moolenaar" Regex of syntax group names that are line comment. 46773a97c2SBram Moolenaarlet s:syng_linecom = 'linecomment\c' 47773a97c2SBram Moolenaar 48773a97c2SBram Moolenaar" Expression used to check whether we should skip a match with searchpair(). 49773a97c2SBram Moolenaarlet s:skip_expr = "synIDattr(synID(line('.'),col('.'),1),'name') =~ '".s:syng_strcom."'" 50773a97c2SBram Moolenaar 51773a97c2SBram Moolenaarlet s:line_term = '\s*\%(\%(\/\/\).*\)\=$' 52773a97c2SBram Moolenaar 53773a97c2SBram Moolenaar" Regex that defines continuation lines, not including (, {, or [. 54773a97c2SBram Moolenaarlet s:continuation_regex = '\%([\\*+/.:]\|\%(<%\)\@<![=-]\|\W[|&?]\|||\|&&\|[^=]=[^=].*,\)' . s:line_term 55773a97c2SBram Moolenaar 56773a97c2SBram Moolenaar" Regex that defines continuation lines. 57773a97c2SBram Moolenaar" TODO: this needs to deal with if ...: and so on 58773a97c2SBram Moolenaarlet s:msl_regex = s:continuation_regex 59773a97c2SBram Moolenaar 60773a97c2SBram Moolenaarlet s:one_line_scope_regex = '\<\%(if\|else\|for\|while\)\>[^{;]*' . s:line_term 61773a97c2SBram Moolenaar 62773a97c2SBram Moolenaar" Regex that defines blocks. 63773a97c2SBram Moolenaarlet s:block_regex = '\%([{[]\)\s*\%(|\%([*@]\=\h\w*,\=\s*\)\%(,\s*[*@]\=\h\w*\)*|\)\=' . s:line_term 64773a97c2SBram Moolenaar 65773a97c2SBram Moolenaarlet s:var_stmt = '^\s*var' 66773a97c2SBram Moolenaar 67773a97c2SBram Moolenaarlet s:comma_first = '^\s*,' 68773a97c2SBram Moolenaarlet s:comma_last = ',\s*$' 69773a97c2SBram Moolenaar 70773a97c2SBram Moolenaarlet s:ternary = '^\s\+[?|:]' 71773a97c2SBram Moolenaarlet s:ternary_q = '^\s\+?' 72773a97c2SBram Moolenaar 73773a97c2SBram Moolenaar" 2. Auxiliary Functions {{{1 74773a97c2SBram Moolenaar" ====================== 75773a97c2SBram Moolenaar 76773a97c2SBram Moolenaar" Check if the character at lnum:col is inside a string, comment, or is ascii. 77773a97c2SBram Moolenaarfunction s:IsInStringOrComment(lnum, col) 78773a97c2SBram Moolenaar return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_strcom 79773a97c2SBram Moolenaarendfunction 80773a97c2SBram Moolenaar 81773a97c2SBram Moolenaar" Check if the character at lnum:col is inside a string. 82773a97c2SBram Moolenaarfunction s:IsInString(lnum, col) 83773a97c2SBram Moolenaar return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_string 84773a97c2SBram Moolenaarendfunction 85773a97c2SBram Moolenaar 86773a97c2SBram Moolenaar" Check if the character at lnum:col is inside a multi-line comment. 87773a97c2SBram Moolenaarfunction s:IsInMultilineComment(lnum, col) 88773a97c2SBram Moolenaar return !s:IsLineComment(a:lnum, a:col) && synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_multiline 89773a97c2SBram Moolenaarendfunction 90773a97c2SBram Moolenaar 91773a97c2SBram Moolenaar" Check if the character at lnum:col is a line comment. 92773a97c2SBram Moolenaarfunction s:IsLineComment(lnum, col) 93773a97c2SBram Moolenaar return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_linecom 94773a97c2SBram Moolenaarendfunction 95773a97c2SBram Moolenaar 96773a97c2SBram Moolenaar" Find line above 'lnum' that isn't empty, in a comment, or in a string. 97773a97c2SBram Moolenaarfunction s:PrevNonBlankNonString(lnum) 98773a97c2SBram Moolenaar let in_block = 0 99773a97c2SBram Moolenaar let lnum = prevnonblank(a:lnum) 100773a97c2SBram Moolenaar while lnum > 0 101773a97c2SBram Moolenaar " Go in and out of blocks comments as necessary. 102773a97c2SBram Moolenaar " If the line isn't empty (with opt. comment) or in a string, end search. 103773a97c2SBram Moolenaar let line = getline(lnum) 104773a97c2SBram Moolenaar if line =~ '/\*' 105773a97c2SBram Moolenaar if in_block 106773a97c2SBram Moolenaar let in_block = 0 107773a97c2SBram Moolenaar else 108773a97c2SBram Moolenaar break 109773a97c2SBram Moolenaar endif 110773a97c2SBram Moolenaar elseif !in_block && line =~ '\*/' 111773a97c2SBram Moolenaar let in_block = 1 112773a97c2SBram Moolenaar elseif !in_block && line !~ '^\s*\%(//\).*$' && !(s:IsInStringOrComment(lnum, 1) && s:IsInStringOrComment(lnum, strlen(line))) 113773a97c2SBram Moolenaar break 114773a97c2SBram Moolenaar endif 115773a97c2SBram Moolenaar let lnum = prevnonblank(lnum - 1) 116773a97c2SBram Moolenaar endwhile 117773a97c2SBram Moolenaar return lnum 118773a97c2SBram Moolenaarendfunction 119773a97c2SBram Moolenaar 120773a97c2SBram Moolenaar" Find line above 'lnum' that started the continuation 'lnum' may be part of. 121773a97c2SBram Moolenaarfunction s:GetMSL(lnum, in_one_line_scope) 122773a97c2SBram Moolenaar " Start on the line we're at and use its indent. 123773a97c2SBram Moolenaar let msl = a:lnum 124773a97c2SBram Moolenaar let lnum = s:PrevNonBlankNonString(a:lnum - 1) 125773a97c2SBram Moolenaar while lnum > 0 126773a97c2SBram Moolenaar " If we have a continuation line, or we're in a string, use line as MSL. 127773a97c2SBram Moolenaar " Otherwise, terminate search as we have found our MSL already. 128773a97c2SBram Moolenaar let line = getline(lnum) 129773a97c2SBram Moolenaar let col = match(line, s:msl_regex) + 1 130773a97c2SBram Moolenaar if (col > 0 && !s:IsInStringOrComment(lnum, col)) || s:IsInString(lnum, strlen(line)) 131773a97c2SBram Moolenaar let msl = lnum 132773a97c2SBram Moolenaar else 133773a97c2SBram Moolenaar " Don't use lines that are part of a one line scope as msl unless the 134773a97c2SBram Moolenaar " flag in_one_line_scope is set to 1 135773a97c2SBram Moolenaar " 136773a97c2SBram Moolenaar if a:in_one_line_scope 137773a97c2SBram Moolenaar break 138773a97c2SBram Moolenaar end 139773a97c2SBram Moolenaar let msl_one_line = s:Match(lnum, s:one_line_scope_regex) 140773a97c2SBram Moolenaar if msl_one_line == 0 141773a97c2SBram Moolenaar break 142773a97c2SBram Moolenaar endif 143773a97c2SBram Moolenaar endif 144773a97c2SBram Moolenaar let lnum = s:PrevNonBlankNonString(lnum - 1) 145773a97c2SBram Moolenaar endwhile 146773a97c2SBram Moolenaar return msl 147773a97c2SBram Moolenaarendfunction 148773a97c2SBram Moolenaar 149773a97c2SBram Moolenaarfunction s:RemoveTrailingComments(content) 150773a97c2SBram Moolenaar let single = '\/\/\(.*\)\s*$' 151773a97c2SBram Moolenaar let multi = '\/\*\(.*\)\*\/\s*$' 152773a97c2SBram Moolenaar return substitute(substitute(a:content, single, '', ''), multi, '', '') 153773a97c2SBram Moolenaarendfunction 154773a97c2SBram Moolenaar 155773a97c2SBram Moolenaar" Find if the string is inside var statement (but not the first string) 156773a97c2SBram Moolenaarfunction s:InMultiVarStatement(lnum) 157773a97c2SBram Moolenaar let lnum = s:PrevNonBlankNonString(a:lnum - 1) 158773a97c2SBram Moolenaar 159773a97c2SBram Moolenaar" let type = synIDattr(synID(lnum, indent(lnum) + 1, 0), 'name') 160773a97c2SBram Moolenaar 161773a97c2SBram Moolenaar " loop through previous expressions to find a var statement 162773a97c2SBram Moolenaar while lnum > 0 163773a97c2SBram Moolenaar let line = getline(lnum) 164773a97c2SBram Moolenaar 165773a97c2SBram Moolenaar " if the line is a js keyword 166773a97c2SBram Moolenaar if (line =~ s:js_keywords) 167773a97c2SBram Moolenaar " check if the line is a var stmt 168773a97c2SBram Moolenaar " if the line has a comma first or comma last then we can assume that we 169773a97c2SBram Moolenaar " are in a multiple var statement 170773a97c2SBram Moolenaar if (line =~ s:var_stmt) 171773a97c2SBram Moolenaar return lnum 172773a97c2SBram Moolenaar endif 173773a97c2SBram Moolenaar 174773a97c2SBram Moolenaar " other js keywords, not a var 175773a97c2SBram Moolenaar return 0 176773a97c2SBram Moolenaar endif 177773a97c2SBram Moolenaar 178773a97c2SBram Moolenaar let lnum = s:PrevNonBlankNonString(lnum - 1) 179773a97c2SBram Moolenaar endwhile 180773a97c2SBram Moolenaar 181773a97c2SBram Moolenaar " beginning of program, not a var 182773a97c2SBram Moolenaar return 0 183773a97c2SBram Moolenaarendfunction 184773a97c2SBram Moolenaar 185773a97c2SBram Moolenaar" Find line above with beginning of the var statement or returns 0 if it's not 186773a97c2SBram Moolenaar" this statement 187773a97c2SBram Moolenaarfunction s:GetVarIndent(lnum) 188773a97c2SBram Moolenaar let lvar = s:InMultiVarStatement(a:lnum) 189773a97c2SBram Moolenaar let prev_lnum = s:PrevNonBlankNonString(a:lnum - 1) 190773a97c2SBram Moolenaar 191773a97c2SBram Moolenaar if lvar 192773a97c2SBram Moolenaar let line = s:RemoveTrailingComments(getline(prev_lnum)) 193773a97c2SBram Moolenaar 194773a97c2SBram Moolenaar " if the previous line doesn't end in a comma, return to regular indent 195773a97c2SBram Moolenaar if (line !~ s:comma_last) 196773a97c2SBram Moolenaar return indent(prev_lnum) - shiftwidth() 197773a97c2SBram Moolenaar else 198773a97c2SBram Moolenaar return indent(lvar) + shiftwidth() 199773a97c2SBram Moolenaar endif 200773a97c2SBram Moolenaar endif 201773a97c2SBram Moolenaar 202773a97c2SBram Moolenaar return -1 203773a97c2SBram Moolenaarendfunction 204773a97c2SBram Moolenaar 205773a97c2SBram Moolenaar 206773a97c2SBram Moolenaar" Check if line 'lnum' has more opening brackets than closing ones. 207773a97c2SBram Moolenaarfunction s:LineHasOpeningBrackets(lnum) 208773a97c2SBram Moolenaar let open_0 = 0 209773a97c2SBram Moolenaar let open_2 = 0 210773a97c2SBram Moolenaar let open_4 = 0 211773a97c2SBram Moolenaar let line = getline(a:lnum) 212773a97c2SBram Moolenaar let pos = match(line, '[][(){}]', 0) 213773a97c2SBram Moolenaar while pos != -1 214773a97c2SBram Moolenaar if !s:IsInStringOrComment(a:lnum, pos + 1) 215773a97c2SBram Moolenaar let idx = stridx('(){}[]', line[pos]) 216773a97c2SBram Moolenaar if idx % 2 == 0 217773a97c2SBram Moolenaar let open_{idx} = open_{idx} + 1 218773a97c2SBram Moolenaar else 219773a97c2SBram Moolenaar let open_{idx - 1} = open_{idx - 1} - 1 220773a97c2SBram Moolenaar endif 221773a97c2SBram Moolenaar endif 222773a97c2SBram Moolenaar let pos = match(line, '[][(){}]', pos + 1) 223773a97c2SBram Moolenaar endwhile 224773a97c2SBram Moolenaar return (open_0 > 0) . (open_2 > 0) . (open_4 > 0) 225773a97c2SBram Moolenaarendfunction 226773a97c2SBram Moolenaar 227773a97c2SBram Moolenaarfunction s:Match(lnum, regex) 228773a97c2SBram Moolenaar let col = match(getline(a:lnum), a:regex) + 1 229773a97c2SBram Moolenaar return col > 0 && !s:IsInStringOrComment(a:lnum, col) ? col : 0 230773a97c2SBram Moolenaarendfunction 231773a97c2SBram Moolenaar 232773a97c2SBram Moolenaarfunction s:IndentWithContinuation(lnum, ind, width) 233773a97c2SBram Moolenaar " Set up variables to use and search for MSL to the previous line. 234773a97c2SBram Moolenaar let p_lnum = a:lnum 235773a97c2SBram Moolenaar let lnum = s:GetMSL(a:lnum, 1) 236773a97c2SBram Moolenaar let line = getline(lnum) 237773a97c2SBram Moolenaar 238773a97c2SBram Moolenaar " If the previous line wasn't a MSL and is continuation return its indent. 239773a97c2SBram Moolenaar " TODO: the || s:IsInString() thing worries me a bit. 240773a97c2SBram Moolenaar if p_lnum != lnum 241773a97c2SBram Moolenaar if s:Match(p_lnum,s:continuation_regex)||s:IsInString(p_lnum,strlen(line)) 242773a97c2SBram Moolenaar return a:ind 243773a97c2SBram Moolenaar endif 244773a97c2SBram Moolenaar endif 245773a97c2SBram Moolenaar 246773a97c2SBram Moolenaar " Set up more variables now that we know we aren't continuation bound. 247773a97c2SBram Moolenaar let msl_ind = indent(lnum) 248773a97c2SBram Moolenaar 249773a97c2SBram Moolenaar " If the previous line ended with [*+/.-=], start a continuation that 250773a97c2SBram Moolenaar " indents an extra level. 251773a97c2SBram Moolenaar if s:Match(lnum, s:continuation_regex) 252773a97c2SBram Moolenaar if lnum == p_lnum 253773a97c2SBram Moolenaar return msl_ind + a:width 254773a97c2SBram Moolenaar else 255773a97c2SBram Moolenaar return msl_ind 256773a97c2SBram Moolenaar endif 257773a97c2SBram Moolenaar endif 258773a97c2SBram Moolenaar 259773a97c2SBram Moolenaar return a:ind 260773a97c2SBram Moolenaarendfunction 261773a97c2SBram Moolenaar 262773a97c2SBram Moolenaarfunction s:InOneLineScope(lnum) 263773a97c2SBram Moolenaar let msl = s:GetMSL(a:lnum, 1) 264773a97c2SBram Moolenaar if msl > 0 && s:Match(msl, s:one_line_scope_regex) 265773a97c2SBram Moolenaar return msl 266773a97c2SBram Moolenaar endif 267773a97c2SBram Moolenaar return 0 268773a97c2SBram Moolenaarendfunction 269773a97c2SBram Moolenaar 270773a97c2SBram Moolenaarfunction s:ExitingOneLineScope(lnum) 271773a97c2SBram Moolenaar let msl = s:GetMSL(a:lnum, 1) 272773a97c2SBram Moolenaar if msl > 0 273773a97c2SBram Moolenaar " if the current line is in a one line scope .. 274773a97c2SBram Moolenaar if s:Match(msl, s:one_line_scope_regex) 275773a97c2SBram Moolenaar return 0 276773a97c2SBram Moolenaar else 277773a97c2SBram Moolenaar let prev_msl = s:GetMSL(msl - 1, 1) 278773a97c2SBram Moolenaar if s:Match(prev_msl, s:one_line_scope_regex) 279773a97c2SBram Moolenaar return prev_msl 280773a97c2SBram Moolenaar endif 281773a97c2SBram Moolenaar endif 282773a97c2SBram Moolenaar endif 283773a97c2SBram Moolenaar return 0 284773a97c2SBram Moolenaarendfunction 285773a97c2SBram Moolenaar 286773a97c2SBram Moolenaar" 3. GetTypescriptIndent Function {{{1 287773a97c2SBram Moolenaar" ========================= 288773a97c2SBram Moolenaar 289773a97c2SBram Moolenaarfunction GetTypescriptIndent() 290773a97c2SBram Moolenaar " 3.1. Setup {{{2 291773a97c2SBram Moolenaar " ---------- 292773a97c2SBram Moolenaar 293773a97c2SBram Moolenaar " Set up variables for restoring position in file. Could use v:lnum here. 294773a97c2SBram Moolenaar let vcol = col('.') 295773a97c2SBram Moolenaar 296773a97c2SBram Moolenaar " 3.2. Work on the current line {{{2 297773a97c2SBram Moolenaar " ----------------------------- 298773a97c2SBram Moolenaar 299773a97c2SBram Moolenaar let ind = -1 300773a97c2SBram Moolenaar " Get the current line. 301773a97c2SBram Moolenaar let line = getline(v:lnum) 302773a97c2SBram Moolenaar " previous nonblank line number 303773a97c2SBram Moolenaar let prevline = prevnonblank(v:lnum - 1) 304773a97c2SBram Moolenaar 305773a97c2SBram Moolenaar " If we got a closing bracket on an empty line, find its match and indent 306773a97c2SBram Moolenaar " according to it. For parentheses we indent to its column - 1, for the 307773a97c2SBram Moolenaar " others we indent to the containing line's MSL's level. Return -1 if fail. 308773a97c2SBram Moolenaar let col = matchend(line, '^\s*[],})]') 309773a97c2SBram Moolenaar if col > 0 && !s:IsInStringOrComment(v:lnum, col) 310773a97c2SBram Moolenaar call cursor(v:lnum, col) 311773a97c2SBram Moolenaar 312773a97c2SBram Moolenaar let lvar = s:InMultiVarStatement(v:lnum) 313773a97c2SBram Moolenaar if lvar 314773a97c2SBram Moolenaar let prevline_contents = s:RemoveTrailingComments(getline(prevline)) 315773a97c2SBram Moolenaar 316773a97c2SBram Moolenaar " check for comma first 317773a97c2SBram Moolenaar if (line[col - 1] =~ ',') 318773a97c2SBram Moolenaar " if the previous line ends in comma or semicolon don't indent 319773a97c2SBram Moolenaar if (prevline_contents =~ '[;,]\s*$') 320773a97c2SBram Moolenaar return indent(s:GetMSL(line('.'), 0)) 321773a97c2SBram Moolenaar " get previous line indent, if it's comma first return prevline indent 322773a97c2SBram Moolenaar elseif (prevline_contents =~ s:comma_first) 323773a97c2SBram Moolenaar return indent(prevline) 324773a97c2SBram Moolenaar " otherwise we indent 1 level 325773a97c2SBram Moolenaar else 326773a97c2SBram Moolenaar return indent(lvar) + shiftwidth() 327773a97c2SBram Moolenaar endif 328773a97c2SBram Moolenaar endif 329773a97c2SBram Moolenaar endif 330773a97c2SBram Moolenaar 331773a97c2SBram Moolenaar 332773a97c2SBram Moolenaar let bs = strpart('(){}[]', stridx(')}]', line[col - 1]) * 2, 2) 333773a97c2SBram Moolenaar if searchpair(escape(bs[0], '\['), '', bs[1], 'bW', s:skip_expr) > 0 334773a97c2SBram Moolenaar if line[col-1]==')' && col('.') != col('$') - 1 335773a97c2SBram Moolenaar let ind = virtcol('.')-1 336773a97c2SBram Moolenaar else 337773a97c2SBram Moolenaar let ind = indent(s:GetMSL(line('.'), 0)) 338773a97c2SBram Moolenaar endif 339773a97c2SBram Moolenaar endif 340773a97c2SBram Moolenaar return ind 341773a97c2SBram Moolenaar endif 342773a97c2SBram Moolenaar 343773a97c2SBram Moolenaar " If the line is comma first, dedent 1 level 344773a97c2SBram Moolenaar if (getline(prevline) =~ s:comma_first) 345773a97c2SBram Moolenaar return indent(prevline) - shiftwidth() 346773a97c2SBram Moolenaar endif 347773a97c2SBram Moolenaar 348773a97c2SBram Moolenaar if (line =~ s:ternary) 349773a97c2SBram Moolenaar if (getline(prevline) =~ s:ternary_q) 350773a97c2SBram Moolenaar return indent(prevline) 351773a97c2SBram Moolenaar else 352773a97c2SBram Moolenaar return indent(prevline) + shiftwidth() 353773a97c2SBram Moolenaar endif 354773a97c2SBram Moolenaar endif 355773a97c2SBram Moolenaar 356773a97c2SBram Moolenaar " If we are in a multi-line comment, cindent does the right thing. 357773a97c2SBram Moolenaar if s:IsInMultilineComment(v:lnum, 1) && !s:IsLineComment(v:lnum, 1) 358773a97c2SBram Moolenaar return cindent(v:lnum) 359773a97c2SBram Moolenaar endif 360773a97c2SBram Moolenaar 361773a97c2SBram Moolenaar " Check for multiple var assignments 362773a97c2SBram Moolenaar" let var_indent = s:GetVarIndent(v:lnum) 363773a97c2SBram Moolenaar" if var_indent >= 0 364773a97c2SBram Moolenaar" return var_indent 365773a97c2SBram Moolenaar" endif 366773a97c2SBram Moolenaar 367773a97c2SBram Moolenaar " 3.3. Work on the previous line. {{{2 368773a97c2SBram Moolenaar " ------------------------------- 369773a97c2SBram Moolenaar 370773a97c2SBram Moolenaar " If the line is empty and the previous nonblank line was a multi-line 371773a97c2SBram Moolenaar " comment, use that comment's indent. Deduct one char to account for the 372773a97c2SBram Moolenaar " space in ' */'. 373773a97c2SBram Moolenaar if line =~ '^\s*$' && s:IsInMultilineComment(prevline, 1) 374773a97c2SBram Moolenaar return indent(prevline) - 1 375773a97c2SBram Moolenaar endif 376773a97c2SBram Moolenaar 377773a97c2SBram Moolenaar " Find a non-blank, non-multi-line string line above the current line. 378773a97c2SBram Moolenaar let lnum = s:PrevNonBlankNonString(v:lnum - 1) 379773a97c2SBram Moolenaar 380773a97c2SBram Moolenaar " If the line is empty and inside a string, use the previous line. 381773a97c2SBram Moolenaar if line =~ '^\s*$' && lnum != prevline 382773a97c2SBram Moolenaar return indent(prevnonblank(v:lnum)) 383773a97c2SBram Moolenaar endif 384773a97c2SBram Moolenaar 385773a97c2SBram Moolenaar " At the start of the file use zero indent. 386773a97c2SBram Moolenaar if lnum == 0 387773a97c2SBram Moolenaar return 0 388773a97c2SBram Moolenaar endif 389773a97c2SBram Moolenaar 390773a97c2SBram Moolenaar " Set up variables for current line. 391773a97c2SBram Moolenaar let line = getline(lnum) 392773a97c2SBram Moolenaar let ind = indent(lnum) 393773a97c2SBram Moolenaar 394773a97c2SBram Moolenaar " If the previous line ended with a block opening, add a level of indent. 395773a97c2SBram Moolenaar if s:Match(lnum, s:block_regex) 396773a97c2SBram Moolenaar return indent(s:GetMSL(lnum, 0)) + shiftwidth() 397773a97c2SBram Moolenaar endif 398773a97c2SBram Moolenaar 399773a97c2SBram Moolenaar " If the previous line contained an opening bracket, and we are still in it, 400773a97c2SBram Moolenaar " add indent depending on the bracket type. 401773a97c2SBram Moolenaar if line =~ '[[({]' 402773a97c2SBram Moolenaar let counts = s:LineHasOpeningBrackets(lnum) 403773a97c2SBram Moolenaar if counts[0] == '1' && searchpair('(', '', ')', 'bW', s:skip_expr) > 0 404773a97c2SBram Moolenaar if col('.') + 1 == col('$') 405773a97c2SBram Moolenaar return ind + shiftwidth() 406773a97c2SBram Moolenaar else 407773a97c2SBram Moolenaar return virtcol('.') 408773a97c2SBram Moolenaar endif 409773a97c2SBram Moolenaar elseif counts[1] == '1' || counts[2] == '1' 410773a97c2SBram Moolenaar return ind + shiftwidth() 411773a97c2SBram Moolenaar else 412773a97c2SBram Moolenaar call cursor(v:lnum, vcol) 413773a97c2SBram Moolenaar end 414773a97c2SBram Moolenaar endif 415773a97c2SBram Moolenaar 416773a97c2SBram Moolenaar " 3.4. Work on the MSL line. {{{2 417773a97c2SBram Moolenaar " -------------------------- 418773a97c2SBram Moolenaar 419773a97c2SBram Moolenaar let ind_con = ind 420773a97c2SBram Moolenaar let ind = s:IndentWithContinuation(lnum, ind_con, shiftwidth()) 421773a97c2SBram Moolenaar 422773a97c2SBram Moolenaar " }}}2 423773a97c2SBram Moolenaar " 424773a97c2SBram Moolenaar " 425773a97c2SBram Moolenaar let ols = s:InOneLineScope(lnum) 426773a97c2SBram Moolenaar if ols > 0 427773a97c2SBram Moolenaar let ind = ind + shiftwidth() 428773a97c2SBram Moolenaar else 429773a97c2SBram Moolenaar let ols = s:ExitingOneLineScope(lnum) 430773a97c2SBram Moolenaar while ols > 0 && ind > 0 431773a97c2SBram Moolenaar let ind = ind - shiftwidth() 432773a97c2SBram Moolenaar let ols = s:InOneLineScope(ols - 1) 433773a97c2SBram Moolenaar endwhile 434773a97c2SBram Moolenaar endif 435773a97c2SBram Moolenaar 436773a97c2SBram Moolenaar return ind 437773a97c2SBram Moolenaarendfunction 438773a97c2SBram Moolenaar 439773a97c2SBram Moolenaar" }}}1 440773a97c2SBram Moolenaar 441773a97c2SBram Moolenaarlet &cpo = s:cpo_save 442773a97c2SBram Moolenaarunlet s:cpo_save 443773a97c2SBram Moolenaar 444773a97c2SBram Moolenaarfunction! Fixedgq(lnum, count) 44596f45c0bSBram Moolenaar let l:tw = &tw ? &tw : 80 446773a97c2SBram Moolenaar 447773a97c2SBram Moolenaar let l:count = a:count 448773a97c2SBram Moolenaar let l:first_char = indent(a:lnum) + 1 449773a97c2SBram Moolenaar 450773a97c2SBram Moolenaar if mode() == 'i' " gq was not pressed, but tw was set 451773a97c2SBram Moolenaar return 1 452773a97c2SBram Moolenaar endif 453773a97c2SBram Moolenaar 454773a97c2SBram Moolenaar " This gq is only meant to do code with strings, not comments 455773a97c2SBram Moolenaar if s:IsLineComment(a:lnum, l:first_char) || s:IsInMultilineComment(a:lnum, l:first_char) 456773a97c2SBram Moolenaar return 1 457773a97c2SBram Moolenaar endif 458773a97c2SBram Moolenaar 459773a97c2SBram Moolenaar if len(getline(a:lnum)) < l:tw && l:count == 1 " No need for gq 460773a97c2SBram Moolenaar return 1 461773a97c2SBram Moolenaar endif 462773a97c2SBram Moolenaar 463*6c391a74SBram Moolenaar " Put all the lines on one line and do normal splitting after that 464773a97c2SBram Moolenaar if l:count > 1 465773a97c2SBram Moolenaar while l:count > 1 466773a97c2SBram Moolenaar let l:count -= 1 467773a97c2SBram Moolenaar normal J 468773a97c2SBram Moolenaar endwhile 469773a97c2SBram Moolenaar endif 470773a97c2SBram Moolenaar 471773a97c2SBram Moolenaar let l:winview = winsaveview() 472773a97c2SBram Moolenaar 473773a97c2SBram Moolenaar call cursor(a:lnum, l:tw + 1) 474773a97c2SBram Moolenaar let orig_breakpoint = searchpairpos(' ', '', '\.', 'bcW', '', a:lnum) 475773a97c2SBram Moolenaar call cursor(a:lnum, l:tw + 1) 476773a97c2SBram Moolenaar let breakpoint = searchpairpos(' ', '', '\.', 'bcW', s:skip_expr, a:lnum) 477773a97c2SBram Moolenaar 478773a97c2SBram Moolenaar " No need for special treatment, normal gq handles edgecases better 479773a97c2SBram Moolenaar if breakpoint[1] == orig_breakpoint[1] 480773a97c2SBram Moolenaar call winrestview(l:winview) 481773a97c2SBram Moolenaar return 1 482773a97c2SBram Moolenaar endif 483773a97c2SBram Moolenaar 484773a97c2SBram Moolenaar " Try breaking after string 485773a97c2SBram Moolenaar if breakpoint[1] <= indent(a:lnum) 486773a97c2SBram Moolenaar call cursor(a:lnum, l:tw + 1) 487773a97c2SBram Moolenaar let breakpoint = searchpairpos('\.', '', ' ', 'cW', s:skip_expr, a:lnum) 488773a97c2SBram Moolenaar endif 489773a97c2SBram Moolenaar 490773a97c2SBram Moolenaar 491773a97c2SBram Moolenaar if breakpoint[1] != 0 492773a97c2SBram Moolenaar call feedkeys("r\<CR>") 493773a97c2SBram Moolenaar else 494773a97c2SBram Moolenaar let l:count = l:count - 1 495773a97c2SBram Moolenaar endif 496773a97c2SBram Moolenaar 497773a97c2SBram Moolenaar " run gq on new lines 498773a97c2SBram Moolenaar if l:count == 1 499773a97c2SBram Moolenaar call feedkeys("gqq") 500773a97c2SBram Moolenaar endif 501773a97c2SBram Moolenaar 502773a97c2SBram Moolenaar return 0 503773a97c2SBram Moolenaarendfunction 504