From 380738609b830897d6395ecb4f2f85bb30c2aedb Mon Sep 17 00:00:00 2001 From: haven1433 Date: Sat, 3 Dec 2022 19:09:31 -0600 Subject: [PATCH] draft of auto-complete for script tool --- .../Models/Code/ScriptParser.cs | 58 ++++++++++++++++++- .../ViewModels/Tools/CodeTool.cs | 18 +++--- .../Before_Baseclass/ScriptTests.cs | 2 +- 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/src/HexManiac.Core/Models/Code/ScriptParser.cs b/src/HexManiac.Core/Models/Code/ScriptParser.cs index 7c4a985a..be911dad 100644 --- a/src/HexManiac.Core/Models/Code/ScriptParser.cs +++ b/src/HexManiac.Core/Models/Code/ScriptParser.cs @@ -386,21 +386,65 @@ namespace HavenSoft.HexManiac.Core.Models.Code { return new LabelLibrary(model, labels); } - public string GetHelp(string currentLine) { + public string GetHelp(IDataModel model, HelpContext context) { + var currentLine = context.Line; if (string.IsNullOrWhiteSpace(currentLine)) return null; var tokens = ScriptLine.Tokenize(currentLine.Trim()); var candidates = engine.Where(line => line.LineCommand.Contains(tokens[0])).ToList(); + + var isAfterToken = context.Index > 0 && + (context.Line.Length == context.Index || context.Line[context.Index] == ' ') && + char.IsLetterOrDigit(context.Line[context.Index - 1]); + if (isAfterToken) { + tokens = ScriptLine.Tokenize(currentLine.Substring(0, context.Index).Trim()); + // try to auto-complete whatever token is left of the cursor + + // need autocomplete for command? + if (tokens.Length == 1) { + candidates.Where(line => line.LineCommand.StartsWith(tokens[0])).ToList(); + return Environment.NewLine.Join(candidates.Take(10).Select(line => line.Usage)); + } + + // filter down to just perfect matches. There could be several (trainerbattle) + candidates = candidates.Where(line => line.LineCommand == tokens[0]).ToList(); + var checkToken = 1; + while (candidates.Count > 1 && checkToken < tokens.Length) { + if (!tokens[checkToken].TryParseHex(out var codeValue)) break; + candidates = candidates.Where(line => line.LineCode[checkToken] == codeValue).ToList(); + } + var syntax = candidates.FirstOrDefault(); + if (syntax != null) { + var args = syntax.Args.Where(arg => arg is ScriptArg).ToList(); + if (args.Count + 1 > tokens.Length) { + var arg = args[tokens.Length - 2]; + if (!string.IsNullOrEmpty(arg.EnumTableName)) { + var options = model.GetOptions(arg.EnumTableName).Where(option => option.MatchesPartial(tokens[tokens.Length - 1])).ToList(); + if (options.Count > 10) { + while (options.Count > 9) options.RemoveAt(options.Count - 1); + options.Add("..."); + } + return Environment.NewLine.Join(options); + } + } + } + } + if (candidates.Count > 10) return null; if (candidates.Count == 0) return null; if (candidates.Count == 1) { - if (candidates[0].Args.Count == tokens.Length - 1) return null; + if (candidates[0].CountShowArgs() == tokens.Length - 1) return null; return candidates[0].Usage + Environment.NewLine + string.Join(Environment.NewLine, candidates[0].Documentation); } var perfectMatch = candidates.FirstOrDefault(candidate => (currentLine + " ").StartsWith(candidate.LineCommand + " ")); if (perfectMatch != null) { - if (perfectMatch.Args.Count == tokens.Length - 1) return null; + if (perfectMatch.CountShowArgs() == tokens.Length - 1) return null; return perfectMatch.Usage + Environment.NewLine + string.Join(Environment.NewLine, perfectMatch.Documentation); } + var bestMatch = candidates.FirstOrDefault(candidate => tokens[0].Contains(candidate.LineCommand)); + if (bestMatch != null) { + if (bestMatch.CountShowArgs() == tokens.Length - 1) return null; + return bestMatch.Usage + Environment.NewLine + Environment.NewLine.Join(bestMatch.Documentation); + } return string.Join(Environment.NewLine, candidates.Select(line => line.Usage)); } @@ -450,6 +494,14 @@ namespace HavenSoft.HexManiac.Core.Models.Code { string Compile(IDataModel model, int start, string scriptLine, LabelLibrary labels, out byte[] result); void AddDocumentation(string content); + + public int CountShowArgs() { + return Args.Sum(arg => { + if (arg is ScriptArg) return 1; + return 0; + // something with array args? + }); + } } public class MacroScriptLine : IScriptLine { diff --git a/src/HexManiac.Core/ViewModels/Tools/CodeTool.cs b/src/HexManiac.Core/ViewModels/Tools/CodeTool.cs index c6094853..3f6103a6 100644 --- a/src/HexManiac.Core/ViewModels/Tools/CodeTool.cs +++ b/src/HexManiac.Core/ViewModels/Tools/CodeTool.cs @@ -300,13 +300,13 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { } } - private void UpdateScriptHelpFromLine(object sender, string line) { + private void UpdateScriptHelpFromLine(object sender, HelpContext context) { var codeBody = (CodeBody)sender; - string help = null; - if (mode == CodeMode.Script) help = ScriptParser.GetHelp(line); - else if (mode == CodeMode.BattleScript) BattleScriptParser.GetHelp(line); - else if (mode == CodeMode.AnimationScript) AnimationScriptParser.GetHelp(line); - else if (mode == CodeMode.TrainerAiScript) BattleAIScriptParser.GetHelp(line); + string help; + if (mode == CodeMode.Script) help = ScriptParser.GetHelp(model, context); + else if (mode == CodeMode.BattleScript) help = BattleScriptParser.GetHelp(model, context); + else if (mode == CodeMode.AnimationScript) help = AnimationScriptParser.GetHelp(model, context); + else if (mode == CodeMode.TrainerAiScript) help = BattleAIScriptParser.GetHelp(model, context); else throw new NotImplementedException(); codeBody.HelpContent = help; } @@ -505,7 +505,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { public class CodeBody : ViewModelCore { public event EventHandler ContentChanged; - public event EventHandler HelpSourceChanged; + public event EventHandler HelpSourceChanged; private string label; public string Label { @@ -541,7 +541,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { // only show help if we're not within content curlies. if (contentBoundaryCount != 0) HelpContent = string.Empty; - else HelpSourceChanged?.Invoke(this, lines[0]); + else HelpSourceChanged?.Invoke(this, new(lines[0], caretPosition)); } } @@ -568,4 +568,6 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { }); } } + + public record HelpContext(string Line, int Index); } diff --git a/src/HexManiac.Tests/Before_Baseclass/ScriptTests.cs b/src/HexManiac.Tests/Before_Baseclass/ScriptTests.cs index 58a24d9b..3a237ecf 100644 --- a/src/HexManiac.Tests/Before_Baseclass/ScriptTests.cs +++ b/src/HexManiac.Tests/Before_Baseclass/ScriptTests.cs @@ -200,7 +200,7 @@ end [Fact] public void LoadPointerCommand_StartsWithWhitespace_StillGetHelp() { - Assert.NotEmpty(ViewPort.Tools.CodeTool.ScriptParser.GetHelp(" loadpointer")); + Assert.NotEmpty(ViewPort.Tools.CodeTool.ScriptParser.GetHelp(Model, new(" loadpointer", 13))); } [Fact]