diff --git a/autoload/vimlparser.vim b/autoload/vimlparser.vim index ab17030..25db0a4 100644 --- a/autoload/vimlparser.vim +++ b/autoload/vimlparser.vim @@ -1509,13 +1509,15 @@ function! s:VimLParser.parse_cmd_call() abort call self.add_node(node) endfunction -function! s:VimLParser.parse_heredoc() abort +function! s:VimLParser.parse_heredoc(prefix) abort let node = s:Node(s:NODE_HEREDOC) let node.pos = self.ea.cmdpos let node.op = '' let node.rlist = [] let node.body = [] + " allow prefix to precede heredoc end marker if true + let is_trim = s:FALSE while s:TRUE call self.reader.skip_white() let pos = self.reader.getpos() @@ -1531,6 +1533,9 @@ function! s:VimLParser.parse_heredoc() abort let keynode.pos = pos let keynode.value = key call add(node.rlist, keynode) + if key ==# 'trim' + let is_trim = s:TRUE + endif endif endwhile if node.op ==# '' @@ -1543,7 +1548,7 @@ function! s:VimLParser.parse_heredoc() abort endif let pos = self.reader.getpos() let line = self.reader.getn(-1) - if line ==# node.op + if line ==# node.op || is_trim && line ==# a:prefix . node.op return node endif let linenode = s:Node(s:NODE_STRING) @@ -1600,7 +1605,31 @@ function! s:VimLParser.parse_cmd_let() abort call self.reader.getn(len(s2)) call self.reader.skip_white() let node.op = s2 - let node.right = self.parse_heredoc() + + " compute allowed prefix for heredoc end marker (e.g. EOF) + let pos = self.reader.tell() + while self.reader.tell() > 0 + if self.reader.peek() ==# '' + call self.reader.seek_cur(1) + break + endif + call self.reader.seek_cur(-1) + endwhile + let prefix = '' + while s:TRUE + let c = self.reader.getn(1) + if c ==# ':' + " any presence of leading ':' disables prefix for heredoc end marker + let prefix = '' + break + elseif !s:iswhite(c) + break + endif + let prefix .= c + endwhile + call self.reader.seek_set(pos) + + let node.right = self.parse_heredoc(prefix) call self.add_node(node) return elseif s1 ==# '=' diff --git a/js/vimlparser.js b/js/vimlparser.js index 1efd4f3..91fc9c0 100644 --- a/js/vimlparser.js +++ b/js/vimlparser.js @@ -1805,12 +1805,14 @@ VimLParser.prototype.parse_cmd_call = function() { this.add_node(node); } -VimLParser.prototype.parse_heredoc = function() { +VimLParser.prototype.parse_heredoc = function(prefix) { var node = Node(NODE_HEREDOC); node.pos = this.ea.cmdpos; node.op = ""; node.rlist = []; node.body = []; + // allow prefix to precede heredoc end marker if true + var is_trim = FALSE; while (TRUE) { this.reader.skip_white(); var pos = this.reader.getpos(); @@ -1827,6 +1829,9 @@ VimLParser.prototype.parse_heredoc = function() { keynode.pos = pos; keynode.value = key; viml_add(node.rlist, keynode); + if (key == "trim") { + var is_trim = TRUE; + } } } if (node.op == "") { @@ -1839,7 +1844,7 @@ VimLParser.prototype.parse_heredoc = function() { } var pos = this.reader.getpos(); var line = this.reader.getn(-1); - if (line == node.op) { + if (line == node.op || is_trim && line == prefix + node.op) { return node; } var linenode = Node(NODE_STRING); @@ -1894,7 +1899,30 @@ VimLParser.prototype.parse_cmd_let = function() { this.reader.getn(viml_len(s2)); this.reader.skip_white(); node.op = s2; - node.right = this.parse_heredoc(); + // compute allowed prefix for heredoc end marker (e.g. EOF) + var pos = this.reader.tell(); + while (this.reader.tell() > 0) { + if (this.reader.peek() == "") { + this.reader.seek_cur(1); + break; + } + this.reader.seek_cur(-1); + } + var prefix = ""; + while (TRUE) { + var c = this.reader.getn(1); + if (c == ":") { + // any presence of leading ':' disables prefix for heredoc end marker + var prefix = ""; + break; + } + else if (!iswhite(c)) { + break; + } + prefix += c; + } + this.reader.seek_set(pos); + node.right = this.parse_heredoc(prefix); this.add_node(node); return; } diff --git a/py/vimlparser.py b/py/vimlparser.py index 74011da..f0562b6 100644 --- a/py/vimlparser.py +++ b/py/vimlparser.py @@ -1483,12 +1483,14 @@ def parse_cmd_call(self): raise VimLParserException(Err("Not an function call", node.left.pos)) self.add_node(node) - def parse_heredoc(self): + def parse_heredoc(self, prefix): node = Node(NODE_HEREDOC) node.pos = self.ea.cmdpos node.op = "" node.rlist = [] node.body = [] + # allow prefix to precede heredoc end marker if true + is_trim = FALSE while TRUE: self.reader.skip_white() pos = self.reader.getpos() @@ -1503,6 +1505,8 @@ def parse_heredoc(self): keynode.pos = pos keynode.value = key viml_add(node.rlist, keynode) + if key == "trim": + is_trim = TRUE if node.op == "": raise VimLParserException(Err("E172: Missing marker", self.reader.getpos())) self.parse_trail() @@ -1511,7 +1515,7 @@ def parse_heredoc(self): break pos = self.reader.getpos() line = self.reader.getn(-1) - if line == node.op: + if line == node.op or is_trim and line == prefix + node.op: return node linenode = Node(NODE_STRING) linenode.pos = pos @@ -1558,7 +1562,25 @@ def parse_cmd_let(self): self.reader.getn(viml_len(s2)) self.reader.skip_white() node.op = s2 - node.right = self.parse_heredoc() + # compute allowed prefix for heredoc end marker (e.g. EOF) + pos = self.reader.tell() + while self.reader.tell() > 0: + if self.reader.peek() == "": + self.reader.seek_cur(1) + break + self.reader.seek_cur(-1) + prefix = "" + while TRUE: + c = self.reader.getn(1) + if c == ":": + # any presence of leading ':' disables prefix for heredoc end marker + prefix = "" + break + elif not iswhite(c): + break + prefix += c + self.reader.seek_set(pos) + node.right = self.parse_heredoc(prefix) self.add_node(node) return elif s1 == "=": diff --git a/test/test_heredoc.ok b/test/test_heredoc.ok index 0de3f6b..b995189 100644 --- a/test/test_heredoc.ok +++ b/test/test_heredoc.ok @@ -1,3 +1,12 @@ (let =<< a (heredoc (list) "EOS" (list "hello" " world"))) (let =<< a (heredoc (list "trim") "EOS" (list "\thello" "world"))) (let =<< a (heredoc (list) "EOS" (list))) +(if v:true + ; matching leading indentation is accepted + (let =<< a (heredoc (list "trim") "EOS" (list "\t\thello" "\tworld"))) + (let =<< a (heredoc (list "trim") "EOS" (list " hello" " world"))) + ; but isn't required + (let =<< a (heredoc (list "trim") "EOS" (list " hello" " world")))) +; we don't actually do the trimming for trim heredocs +(let =<< a (heredoc (list "trim") "EOS" (list " hello" " world"))) +(let =<< a (heredoc (list "trim") "EOS" (list "\t hello" "\t\tworld"))) diff --git a/test/test_heredoc.vim b/test/test_heredoc.vim index e92322b..2bb06f9 100644 --- a/test/test_heredoc.vim +++ b/test/test_heredoc.vim @@ -8,3 +8,28 @@ world EOS let a =<< EOS EOS +if v:true + " matching leading indentation is accepted + let a =<< trim EOS + hello + world + EOS + let a =<< trim EOS + hello + world + EOS + " but isn't required + let a =<< trim EOS + hello + world +EOS +endif +" we don't actually do the trimming for trim heredocs +let a =<< trim EOS + hello + world +EOS + : let a =<< trim EOS + hello + world +EOS