AndrewRadev / sideways.vim

A Vim plugin to move function arguments (and other delimited-by-something items) left and right.
http://www.vim.org/scripts/script.php?script_id=4171
MIT License
481 stars 9 forks source link

Unmatched single quotes make SidewaysArgumentTextobjI jump to unrelated line #34

Open idbrii opened 5 years ago

idbrii commented 5 years ago

Similar to #24.

If I type some text and use Sideways to add quotes, it fails when the text has an unmatched quote (single or double) and later in the file there's a matching quote.

With file test.cs:

public class Modifier
{
    public static bool Test()
    {
        Debug.Log(this unmatched quote ' breaks surrounding text with quotes, this);
        return true;
    }

    // because of this unmatched quote '

}

omap i, <Plug>SidewaysArgumentTextobjI
0
/this unmatched
normal nsi,"

I guess it's interpreting ' breaks surrounding text with quotes, this); as a string that continues past the end of the line. My syntax highlighting (nickspoons/vim-cs) doesn't, but maybe sideways has its own way to determine what's a string?

Initially found on 01bcf0f, but also reproduced on 9dd871ee10e0e553214d94cab246ad5107eeafc3.

idbrii commented 5 years ago

I suppose an argument against fixing this would be even more egregious cases of invalid code:

        Debug.Log(this unmatched quote ' is bad, but commas are worse, this);

But as a user, it makes sense that the only up to the first comma is quoted. Jumping several lines down when ' is not a multi-line string delimiter doesn't make sense.

Regardless, I accept this is an edge case. I understand if you want to close as wontfix : )

AndrewRadev commented 5 years ago

As you've guessed, sideways generally expects the code to be valid in order for it to work. There's some permissiveness, like def foo(one, two will work regardless of the lack of a closing bracket, because it's an end of the line, and there's no delimiter after two. But there's no way I can see to get it to work with invalid code.

The "parser" is not particularly smart. I figure the best I can do is rely on Vim to jump from opening to closing bracket, and consider delimiters -- that works well with nested lists as well. But figuring out what to do in your example case seems impossible to me. I assume even Vim would syntax-highlight the rest of the line as a string?

The annoying thing in this case is probably the jumping -- if it didn't do anything, it'd probably be better than doing the wrong thing. I've had similar annoyances with other situations. But I can't think of a way to avoid it -- the multiline functionality is quite useful, and there are valid cases where quoted strings can be multiline (like in Rust):

call_function(
  one,
  r#"
    two
    three
  "#,
  four
)

I wonder if it would make sense to maintain a pair of "multiline_brackets" and one for "single_line_brackets" -- brackets like ([{ would be of the first type, and '" of the second, for most languages. In that case, I guess it would at least not do the jump, and simply terminate if it can't find a matching ' or " on the current line (except for a select few languages like rust). There's probably a lot of language where multiline strings are possible, so I'll probably miss quite a few of them, but it might be a reasonable general case. What do you think?

idbrii commented 5 years ago

There's probably a lot of language where multiline strings are possible, so I'll probably miss quite a few of them, but it might be a reasonable general case

In my experience, multiline strings are usually not delimited with ' or ", but with additional characters: rust's #","#; python's """, lua's [[,]], C#'s @",". So if multiline_brackets allows for each "bracket" to have multiple characters, then that sounds like a good solution. We'd also need to know the method of escaping the end of string character (C# uses "" but I'd guess most others use \ before the close character).