jline / jline3

JLine is a Java library for handling console input.
Other
1.49k stars 218 forks source link

Something odd with backslashes and completion #173

Closed tomq42 closed 6 years ago

tomq42 commented 7 years ago

I'm trying to write a simple jline completer that will help me enter a filename, so it will take the current line as a path, list the files, and so on. I can write the completer I want, I'm just getting very odd behaviour with backslashes. I'm running on Windows, and actually doing this through karaf (I haven't actually been able to create a simple standalone example of running jline of ANY kind, a simple working hello world example would be really good).

If I type "c:/" (note forward slash) at the prompt and tab, my completer gets called, and I correctly get a displayed list of all files in c:. If however I type "c:\" instead (backslash) then I get a listing, but what I've typed has been replaced by "cc:\". All completion then fails. If I type in, say, "c:\work" (a valid path), my completer gets called, but nothing is displayed. So backslashes appear to make it all go wrong.

My completer is:

    @Override
    public void complete(LineReader reader, ParsedLine commandLine, final List<Candidate> candidates) {
        String line = commandLine.line();

        File[] filenames;
        if (line.endsWith("/") || line.endsWith("\\")) {
            filenames = new File(line).listFiles();
        }
        else {
            File f = new File(line);
            String partialUpper = f.getName().toUpperCase();
            f = f.getParentFile();
            filenames = Arrays.stream(f.listFiles()).
                    filter(fileName->fileName.getName().toUpperCase().startsWith(partialUpper)).
                    toArray(File[]::new);
        }

        String sep = System.getProperty("file.separator");
        Arrays.stream(filenames).forEach(fileName->{
            candidates.add(new Candidate(
                    fileName.toString().replace('\\', '/'), 
                    fileName.getName(), 
                    null, null, 
                    fileName.isDirectory() ? sep : null,
                    null,
                    fileName.isDirectory() ? false : true));
        });
    }

I'm setting lineReader.setOpt(LineReader.Option.DISABLE_EVENT_EXPANSION); in an attempt to avoid backslashes being replaced.

gnodet commented 7 years ago

I think you need to configure a parser to suit your needs. The default parser can be configured using

DefaultParser parser = new DefaultParser();
parser.setEscapeChars(null);
...
lineReaderBuilder.parser(parser);
gnodet commented 7 years ago

Please reopen if needed

tomq42 commented 7 years ago

Yes, sure I can work round the issue. But I can't believe that the behaviour of replacing "c:\" with "cc:\" (I type "c colon backslash", and it gets changed to "c c colon backslash") and hence rendering the filename incorrect is what's supposed to happen.

gnodet commented 7 years ago

I think the problem is in your completer. First, the completer should only complete the last word of the parsed command line. Then, the candidates should be full words for the currently being completed word. So if you are completing C:\, the candidates should be C:\Users, C:\Program Files, not Users, etc.. Could you set up a unit test with a fake completer that would demonstrate the problem ? See https://github.com/jline/jline3/blob/master/reader/src/test/java/org/jline/reader/completer/StringsCompleterTest.java for a simple completion test. You can add a test to that class with a custom completer.

Victor9401 commented 6 years ago

Hello. Can confirm this behavior

It caused by two things:

DefaultParser in reader lists \ as escape char (normal for Unix terminals, but not for windows), so when parser splits string "c:\" into ParsedLine, it has just one word "c:" without trailing \. So when completer tries to rewrite string, it clears last word (2 chars), and writes candidate (1st word) and it becomes "cc:". It could be fixed with clearing escape chars in parser object and then passing it to LineReader, but you must be sure that you understand what you do. It can cause strange behavior on Unix like terminals.

Second one is caused by default FileNameCompleter. It uses File.separator property just once. On the other lines "\" is hard coded. That's a bug for now.

Victor9401 commented 6 years ago

Ok, so just haven't read this issue properly. I think I should create new one for the second part, because this issue is not about it...

Locke commented 6 years ago

Second one is caused by default FileNameCompleter. It uses File.separator property just once. On the other lines "\" is hard coded. That's a bug for now.

I can confirm this. I think a new issue would be better to track that. As parsing "\" from the input requires parser.setEscapeChars(null); I'd suggest to use the unix style "/" also on windows, just to make it easier to use.

gnodet commented 6 years ago

This should have been affected by #245