Cycle through tab-complete results

Allow cycling through tab-complete options sent back by the server by
pressing TAB multiple times in a row. Fix #148
This commit is contained in:
ORelio 2016-05-14 11:51:02 +02:00
parent f37bd96ff9
commit 66d57b0ce6
3 changed files with 31 additions and 25 deletions

View file

@ -16,6 +16,7 @@ namespace MinecraftClient
{ {
public static bool basicIO = false; public static bool basicIO = false;
private static IAutoComplete autocomplete_engine; private static IAutoComplete autocomplete_engine;
private static LinkedList<string> autocomplete_words = new LinkedList<string>();
private static LinkedList<string> previous = new LinkedList<string>(); private static LinkedList<string> previous = new LinkedList<string>();
private static readonly object io_lock = new object(); private static readonly object io_lock = new object();
private static bool reading = false; private static bool reading = false;
@ -168,14 +169,20 @@ namespace MinecraftClient
} }
break; break;
case ConsoleKey.Tab: case ConsoleKey.Tab:
if (autocomplete_engine != null && buffer.Length > 0) if (autocomplete_words.Count == 0 && autocomplete_engine != null && buffer.Length > 0)
foreach (string result in autocomplete_engine.AutoComplete(buffer))
autocomplete_words.AddLast(result);
string word_autocomplete = null;
if (autocomplete_words.Count > 0)
{ {
string word_autocomplete = autocomplete_engine.AutoComplete(buffer); word_autocomplete = autocomplete_words.First.Value;
if (!String.IsNullOrEmpty(word_autocomplete) && word_autocomplete != buffer) autocomplete_words.RemoveFirst();
{ autocomplete_words.AddLast(word_autocomplete);
while (buffer.Length > 0 && buffer[buffer.Length - 1] != ' ') { RemoveOneChar(); } }
foreach (char c in word_autocomplete) { AddChar(c); } if (!String.IsNullOrEmpty(word_autocomplete) && word_autocomplete != buffer)
} {
while (buffer.Length > 0 && buffer[buffer.Length - 1] != ' ') { RemoveOneChar(); }
foreach (char c in word_autocomplete) { AddChar(c); }
} }
break; break;
default: default:
@ -184,6 +191,8 @@ namespace MinecraftClient
break; break;
} }
} }
if (k.Key != ConsoleKey.Tab)
autocomplete_words.Clear();
} }
} }
@ -444,6 +453,6 @@ namespace MinecraftClient
public interface IAutoComplete public interface IAutoComplete
{ {
string AutoComplete(string BehindCursor); IEnumerable<string> AutoComplete(string BehindCursor);
} }
} }

View file

@ -656,10 +656,10 @@ namespace MinecraftClient.Protocol.Handlers
catch (System.IO.IOException) { return false; } catch (System.IO.IOException) { return false; }
} }
public string AutoComplete(string BehindCursor) IEnumerable<string> IAutoComplete.AutoComplete(string BehindCursor)
{ {
if (String.IsNullOrEmpty(BehindCursor)) if (String.IsNullOrEmpty(BehindCursor))
return ""; return new string[] { };
byte[] autocomplete = new byte[3 + (BehindCursor.Length * 2)]; byte[] autocomplete = new byte[3 + (BehindCursor.Length * 2)];
autocomplete[0] = 0xCB; autocomplete[0] = 0xCB;
@ -674,8 +674,9 @@ namespace MinecraftClient.Protocol.Handlers
int wait_left = 50; //do not wait more than 5 seconds (50 * 100 ms) int wait_left = 50; //do not wait more than 5 seconds (50 * 100 ms)
while (wait_left > 0 && !autocomplete_received) { System.Threading.Thread.Sleep(100); wait_left--; } while (wait_left > 0 && !autocomplete_received) { System.Threading.Thread.Sleep(100); wait_left--; }
string[] results = autocomplete_result.Split((char)0x00); if (!String.IsNullOrEmpty(autocomplete_result) && autocomplete_received)
return results[0]; ConsoleIO.WriteLineFormatted("§8" + autocomplete_result.Replace((char)0x00, ' '), false);
return autocomplete_result.Split((char)0x00);
} }
private static byte[] concatBytes(params byte[][] bytes) private static byte[] concatBytes(params byte[][] bytes)

View file

@ -23,7 +23,7 @@ namespace MinecraftClient.Protocol.Handlers
private int compression_treshold = 0; private int compression_treshold = 0;
private bool autocomplete_received = false; private bool autocomplete_received = false;
private string autocomplete_result = ""; private readonly List<string> autocomplete_result = new List<string>();
private bool login_phase = true; private bool login_phase = true;
private bool encrypted = false; private bool encrypted = false;
private int protocolversion; private int protocolversion;
@ -393,17 +393,10 @@ namespace MinecraftClient.Protocol.Handlers
break; break;
case PacketIncomingType.TabCompleteResult: case PacketIncomingType.TabCompleteResult:
int autocomplete_count = readNextVarInt(packetData); int autocomplete_count = readNextVarInt(packetData);
string tab_list = ""; autocomplete_result.Clear();
for (int i = 0; i < autocomplete_count; i++) for (int i = 0; i < autocomplete_count; i++)
{ autocomplete_result.Add(readNextString(packetData));
autocomplete_result = readNextString(packetData);
if (autocomplete_result != "")
tab_list = tab_list + autocomplete_result + " ";
}
autocomplete_received = true; autocomplete_received = true;
tab_list = tab_list.Trim();
if (tab_list.Length > 0)
ConsoleIO.WriteLineFormatted("§8" + tab_list, false);
break; break;
case PacketIncomingType.PluginMessage: case PacketIncomingType.PluginMessage:
String channel = readNextString(packetData); String channel = readNextString(packetData);
@ -1331,10 +1324,10 @@ namespace MinecraftClient.Protocol.Handlers
/// <param name="BehindCursor">Text behind cursor</param> /// <param name="BehindCursor">Text behind cursor</param>
/// <returns>Completed text</returns> /// <returns>Completed text</returns>
public string AutoComplete(string BehindCursor) IEnumerable<string> IAutoComplete.AutoComplete(string BehindCursor)
{ {
if (String.IsNullOrEmpty(BehindCursor)) if (String.IsNullOrEmpty(BehindCursor))
return ""; return new string[] { };
byte[] tocomplete_val = Encoding.UTF8.GetBytes(BehindCursor); byte[] tocomplete_val = Encoding.UTF8.GetBytes(BehindCursor);
byte[] tocomplete_len = getVarInt(tocomplete_val.Length); byte[] tocomplete_len = getVarInt(tocomplete_val.Length);
@ -1347,11 +1340,14 @@ namespace MinecraftClient.Protocol.Handlers
: concatBytes(tocomplete_len, tocomplete_val); : concatBytes(tocomplete_len, tocomplete_val);
autocomplete_received = false; autocomplete_received = false;
autocomplete_result = BehindCursor; autocomplete_result.Clear();
autocomplete_result.Add(BehindCursor);
SendPacket(protocolversion >= MC19Version ? 0x01 : 0x14, tabcomplete_packet); SendPacket(protocolversion >= MC19Version ? 0x01 : 0x14, tabcomplete_packet);
int wait_left = 50; //do not wait more than 5 seconds (50 * 100 ms) int wait_left = 50; //do not wait more than 5 seconds (50 * 100 ms)
while (wait_left > 0 && !autocomplete_received) { System.Threading.Thread.Sleep(100); wait_left--; } while (wait_left > 0 && !autocomplete_received) { System.Threading.Thread.Sleep(100); wait_left--; }
if (autocomplete_result.Count > 0)
ConsoleIO.WriteLineFormatted("§8" + String.Join(" ", autocomplete_result), false);
return autocomplete_result; return autocomplete_result;
} }