3

I am accepting a user input file path right now:

Console.WriteLine("Input file path");
string path = Console.ReadLine();

try
{
    data = System.IO.File.ReadAllBytes(path);
}
catch
{
    Console.WriteLine("Invalid file path entered");
    System.Console.ReadKey();
    return 1;
}

But if the user enters the path incorrectly, they'll have to enter the entire thing again. I realize that currently my app will just exit when the user enters something wrong and I could ask again, but I would still like to make this a bit easier for the user.

Instead, I would like to have the Windows command line functionality of autocomplete for a path when a user tabs while entering it. For example, if I open cmd and type cd C:\win and hit TAB, cmd will find C:\Windows.

Is it possible to add that ability to a console app for user input?

Blorgbeard
  • 97,316
  • 47
  • 222
  • 267
hwustrack
  • 47
  • 3
  • 8
  • You could do it, but you'd have to basically reimplement `Console.ReadLine()` using `ReadKey()` etc. – Blorgbeard Aug 19 '16 at 01:47
  • This is a lot of work to make it pretty & usable. See my suggestion on [Code Review](http://codereview.stackexchange.com/a/139203/59161) – t3chb0t Aug 20 '16 at 06:59

3 Answers3

7

At first I thought clearing a specific console line was not feasible, but a quick search showed me that nothing is impossible.

So I created a new console application and started thinking of how I'd get something like that to work. Below is the "first working draft" - I'm about to [heavily] refactor it on my own and then put the resulting code up on Code Review, but this should be good enough to get you started.

The program makes the Tab key autocomplete the current input using an array of strings as data, matching the first item it finds; you'll have to tweak it a bit if you want something smarter (like having the current folder's child paths as data, and/or iterating through matches at each consecutive press of the Tab key):

class Program
{
    static void Main(string[] args)
    {
        var data = new[]
        {
            "Bar",
            "Barbec",
            "Barbecue",
            "Batman",
        };

        var builder = new StringBuilder();
        var input = Console.ReadKey(intercept:true);

        while (input.Key != ConsoleKey.Enter)
        {
            var currentInput = builder.ToString();
            if (input.Key == ConsoleKey.Tab)
            {
                var match = data.FirstOrDefault(item => item != currentInput && item.StartsWith(currentInput, true, CultureInfo.InvariantCulture));
                if (string.IsNullOrEmpty(match))
                {
                    input = Console.ReadKey(intercept: true);
                    continue;
                }

                ClearCurrentLine();
                builder.Clear();

                Console.Write(match);
                builder.Append(match);
            }
            else
            {
                if (input.Key == ConsoleKey.Backspace && currentInput.Length > 0)
                {
                    builder.Remove(builder.Length - 1, 1);
                    ClearCurrentLine();

                    currentInput = currentInput.Remove(currentInput.Length - 1);
                    Console.Write(currentInput);
                }
                else
                {
                    var key = input.KeyChar;
                    builder.Append(key);
                    Console.Write(key);
                }
            }

            input = Console.ReadKey(intercept:true);
        }
        Console.Write(input.KeyChar);
    }

    /// <remarks>
    /// https://stackoverflow.com/a/8946847/1188513
    /// </remarks>>
    private static void ClearCurrentLine()
    {
        var currentLine = Console.CursorTop;
        Console.SetCursorPosition(0, Console.CursorTop);
        Console.Write(new string(' ', Console.WindowWidth));
        Console.SetCursorPosition(0, currentLine);
    }
}

Thanks for this question, that was fun!

Community
  • 1
  • 1
Mathieu Guindon
  • 67,201
  • 8
  • 101
  • 218
  • Note: a roughly refactored version of this code is up for review [here](http://codereview.stackexchange.com/q/139172/23788) if the reader wants to help improve this code. – Mathieu Guindon Aug 19 '16 at 18:32
1

I'm adding to Mathieu Guindon's excellent answer in case someone else has this use case.

If you want to have a prompt inviting the user to make their choice, and want to be able to keep the prompt and just cycle through the options replacing the name of each one, all you have to do is change the ClearCurrentLine method so that it looks like this:

private static void ClearCurrentLine(int cursorLeft)
{
    var currentLine = Console.CursorTop;
    Console.SetCursorPosition(cursorLeft, Console.CursorTop);
    Console.Write(new string(' ', Console.WindowWidth - cursorLeft));
    Console.SetCursorPosition(cursorLeft, currentLine);
}

Then change the caller to pass in the appropriate value for cursorLeft. Dumb example:

var prompt = "Make your choice: ";
Console.Write(prompt);

// ...

ClearCurrentLine(prompt.Length)

This way 'Make your choice: ' won't be deleted by the method.

You could just print the prompt again, but this feels a bit cleaner.

s.m.
  • 7,705
  • 2
  • 36
  • 44
0

You should be able to achieve it. Although it is not straight forward and there may be no ready-made libraries, you can use something like below:

 var userInputString = "";
    while (true){

    var char = Console.Read();
    // append to userInputString
    // search your directory and suggest the path using a combination of SetCursorPosition and Console.Write and bring the cursor back to user's current typing position. Hint: store Console.CursorLeft in another variable before you use SetCursorPosition so that you can set the cursor position back. 
    // If user presses <TAB> to accept the suggestion or of the file path that user keyed in exists "break" the loop.
    // So the loop never exits until the user either keys in correct path or accepts the suggestion
    }

Some links that may help you:

C# Console - set cursor position to the last visible line

https://msdn.microsoft.com/en-us/library/system.console.setcursorposition(v=vs.110).aspx

Community
  • 1
  • 1
Raghu
  • 659
  • 6
  • 14
  • May be useful: http://stackoverflow.com/questions/29201697/hide-replace-when-typing-a-password-c – Raghu Aug 19 '16 at 02:03