Add basic tab completion

master
d4vid 3 months ago
parent 1878d31454
commit 70684d140e

@ -44,5 +44,8 @@
<Reference Include="UnityEngine.UI">
<HintPath>$(LogicWorldGameLocation)\Logic_World_Data\Managed\UnityEngine.UI.dll</HintPath>
</Reference>
<Reference Include="LICC">
<HintPath>$(LogicWorldGameLocation)\Logic_World_Data\Managed\LICC.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

@ -1,8 +1,8 @@
ID: D4VID_ConsoleImprovements
Name: ConsoleImprovements
Author: D4VID
Version: 1.1.2
Priority: 0
Version: 1.2.0
Priority: -90
ClientOnly: true
Dependencies:
- HarmonyForLogicWorld

@ -1,9 +1,11 @@
using System.Collections;
using System.Reflection;
using System.Collections.Generic;
using EccsLogicWorldAPI.Shared.AccessHelper;
using FancyInput;
using HarmonyLib;
using JimmysUnityUtilities;
using LICC;
using LogicLog;
using TMPro;
using Console = FancyPantsConsole.Console;
@ -17,6 +19,7 @@ namespace ConsoleImprovements.Client {
private static LinkedList<string> _history = new LinkedList<string>();
private static LinkedListNode<string>? _command;
private static Trie _commandRegistry = new Trie();
public static bool Prepare(ILogicLogger logger) {
_logger = logger;
@ -24,6 +27,21 @@ namespace ConsoleImprovements.Client {
// Get access to the rich text string inside the MessageData class
_commandInputFieldField = Fields.getPrivate(typeof(Console), "CommandInputField");
CommandConsole commandConsole = CommandConsole.Current;
// ICommandRegistryInternal
var registry = Fields.getPrivate(typeof(CommandConsole), "CommandRegistry").GetValue(commandConsole);
// CommandRegistry.CommandCollectionByName
var commandCollection = Properties.get(registry, "AllRegisteredCommands").GetValue(registry);
// IEnumerable<Command>
var commands = (IEnumerable) Methods.getPublic(commandCollection, "EnumerateAllCommands").Invoke(commandCollection, null)!;
foreach (var command in commands) {
var nameProperty = Properties.getPublic(command, "Name");
var hiddenProperty = Properties.getPublic(command, "Hidden");
if ((bool) hiddenProperty.GetValue(command)!) continue;
_commandRegistry.AddEntry((string) nameProperty.GetValue(command)!);
}
return true;
}
@ -65,6 +83,27 @@ namespace ConsoleImprovements.Client {
}
}
if (RawInput.Tab.DownThisFrame()) {
TMP_InputField? commandInputField = _commandInputFieldField!.GetValue(__instance) as TMP_InputField;
if (commandInputField == null) {
_logger.Error("Cannot get CommandInputField value");
return true;
}
string current = commandInputField.text;
string? completed = _commandRegistry.LookupNext(current, out Trie.Node? node);
if (completed != null) {
if (completed == current) {
List<string> commands = node?.CollectChildren(completed)!;
if (commands.Count > 0) {
_logger.Info($"Available commands: {string.Join(", ", commands)}");
}
} else {
UpdateCommandTextField(__instance, completed);
}
}
}
return true; // Resume original functionality
}

@ -0,0 +1,87 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleImprovements.Client {
/// <summary>
/// A crude implementation of the trie data structure for fast lookups
/// </summary>
public class Trie {
private readonly Node _root = new Node() {
Children = new Dictionary<char, Node>()
};
/// <summary>
/// Add a new string entry to the structure
/// </summary>
public void AddEntry(string str) {
Node current = _root;
foreach (char ch in str.ToLower()) {
if (!current.Children.TryGetValue(ch, out Node? next)) {
next = new Node();
current.Children[ch] = next;
}
current = next;
}
current.End = true;
}
/// <summary>
/// Look up how the given string may continue
/// </summary>
/// <param name="prefix">The string to base the search on</param>
/// <param name="node">outputs the reached node which is either an end node or has multiple children</param>
/// <returns>Continuation of the prefix (including the prefix)</returns>
public string? LookupNext(string prefix, out Node? node) {
string lowercase = prefix.ToLower();
Node current = _root;
foreach (char ch in lowercase) {
if (!current.Children.TryGetValue(ch, out Node? next)) {
node = null;
return null;
}
current = next;
}
var commonBuilder = new StringBuilder(lowercase);
while (current.Children.Count == 1) {
if (current.End) {
break;
}
KeyValuePair<char, Node> single = current.Children.First();
commonBuilder.Append(single.Key);
current = single.Value;
}
node = current;
return commonBuilder.ToString();
}
public class Node {
public bool End;
public Dictionary<char, Node> Children = new Dictionary<char, Node>();
/// <summary>
/// Get all possible strings that can be made from the children of this node
/// </summary>
/// <param name="prefix">Pass in the previously obtained string path to this node</param>
public List<string> CollectChildren(string prefix) {
var collected = new List<string>();
if (End) {
collected.Add(prefix.ToLower());
}
foreach ((char ch, Node node) in Children) {
collected.AddRange(node.CollectChildren(prefix + ch));
}
return collected;
}
}
}
}

@ -1,8 +1,9 @@
# Console Improvements Mod
This mod does 2 things:
This mod does 3 things:
1. It removes the `> ` prefix from copied commands
2. Adds a command history that can be cycled using up and down arrow keys
3. Adds tab completion
## Dependencies

Loading…
Cancel
Save