|
@@ -16,273 +16,372 @@ using System.Windows.Media;
|
|
|
using System.Windows.Media.Imaging;
|
|
|
using System.Windows.Shapes;
|
|
|
|
|
|
-namespace InABox.DynamicGrid
|
|
|
+namespace InABox.DynamicGrid;
|
|
|
+
|
|
|
+public class ExpressionEditorVariable(string variableName, string display, List<ExpressionEditorVariable> children)
|
|
|
+{
|
|
|
+ public string VariableName { get; } = variableName;
|
|
|
+
|
|
|
+ public string Display { get; } = display;
|
|
|
+
|
|
|
+ public List<ExpressionEditorVariable> ChildVariables { get; } = children;
|
|
|
+
|
|
|
+ public bool HasChildren => ChildVariables.Count > 0;
|
|
|
+}
|
|
|
+
|
|
|
+public class ExpressionEditorVariableTemplateItemStyleSelector : StyleSelector
|
|
|
+{
|
|
|
+ public override Style? SelectStyle(object item, DependencyObject container)
|
|
|
+ {
|
|
|
+ if(item is ExpressionEditorVariable variable && container is FrameworkElement element && variable.HasChildren)
|
|
|
+ {
|
|
|
+ return element.FindResource("variableTemplateItemStyle") as Style;
|
|
|
+ }
|
|
|
+ return base.SelectStyle(item, container);
|
|
|
+ }
|
|
|
+}
|
|
|
+public class ExpressionEditorVariableTemplateSelector : DataTemplateSelector
|
|
|
{
|
|
|
- /// <summary>
|
|
|
- /// Interaction logic for ExpressionEditorWindow.xaml
|
|
|
- /// </summary>
|
|
|
- public partial class ExpressionEditorWindow : Window
|
|
|
+ public override DataTemplate? SelectTemplate(object item, DependencyObject container)
|
|
|
{
|
|
|
- public static List<Tuple<string, List<FunctionTemplate>>> FunctionTemplates = new();
|
|
|
+ if(item is ExpressionEditorVariable variable && container is FrameworkElement element)
|
|
|
+ {
|
|
|
+ if (variable.HasChildren)
|
|
|
+ {
|
|
|
+ return element.FindResource("VariableExpanderTemplate") as DataTemplate;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return element.FindResource("VariableTemplate") as DataTemplate;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return base.SelectTemplate(item, container);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// <summary>
|
|
|
+/// Interaction logic for ExpressionEditorWindow.xaml
|
|
|
+/// </summary>
|
|
|
+public partial class ExpressionEditorWindow : Window
|
|
|
+{
|
|
|
+ public static List<Tuple<string, List<FunctionTemplate>>> FunctionTemplates = new();
|
|
|
|
|
|
- public IEnumerable<string> Variables { get; private set; }
|
|
|
+ public IEnumerable<ExpressionEditorVariable> Variables { get; private set; }
|
|
|
|
|
|
- public string Expression
|
|
|
+ public string Expression
|
|
|
+ {
|
|
|
+ get => Editor.Text;
|
|
|
+ set => Editor.Text = value;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void RegisterFunctionTemplate(string group, string name, string? description, List<IFunctionParameter> parameters)
|
|
|
+ {
|
|
|
+ var groupList = FunctionTemplates.FirstOrDefault(x => x.Item1 == group)?.Item2;
|
|
|
+ if(groupList is null)
|
|
|
{
|
|
|
- get => Editor.Text;
|
|
|
- set => Editor.Text = value;
|
|
|
+ groupList = new List<FunctionTemplate>();
|
|
|
+ FunctionTemplates.Add(new(group, groupList));
|
|
|
}
|
|
|
|
|
|
- private static void RegisterFunctionTemplate(string group, string name, string? description, List<IFunctionParameter> parameters)
|
|
|
+ groupList.Add(new FunctionTemplate
|
|
|
{
|
|
|
- var groupList = FunctionTemplates.FirstOrDefault(x => x.Item1 == group)?.Item2;
|
|
|
- if(groupList is null)
|
|
|
+ Name = name,
|
|
|
+ Parameters = parameters,
|
|
|
+ Description = description ?? ""
|
|
|
+ });
|
|
|
+ }
|
|
|
+ private static void RegisterFunctionTemplate(string group, string name, params string[] parameters)
|
|
|
+ => RegisterFunctionTemplate(group, name, null, parameters.Select(x =>
|
|
|
+ {
|
|
|
+ var parts = x.Split(' ');
|
|
|
+ if (parts.Length == 1)
|
|
|
{
|
|
|
- groupList = new List<FunctionTemplate>();
|
|
|
- FunctionTemplates.Add(new(group, groupList));
|
|
|
+ return new FunctionParameter(parts[0], "");
|
|
|
}
|
|
|
-
|
|
|
- groupList.Add(new FunctionTemplate
|
|
|
+ return new FunctionParameter(parts[1], parts[0]);
|
|
|
+ }).ToList<IFunctionParameter>());
|
|
|
+
|
|
|
+ private static void RegisterFunctionTemplateDesc(string group, string name, string description, params string[] parameters)
|
|
|
+ => RegisterFunctionTemplate(group, name, description, parameters.Select(x =>
|
|
|
+ {
|
|
|
+ var parts = x.Split(' ');
|
|
|
+ if (parts.Length == 1)
|
|
|
{
|
|
|
- Name = name,
|
|
|
- Parameters = parameters,
|
|
|
- Description = description ?? ""
|
|
|
- });
|
|
|
+ return new FunctionParameter(parts[0], "");
|
|
|
+ }
|
|
|
+ return new FunctionParameter(parts[1], parts[0]);
|
|
|
+ }).ToList<IFunctionParameter>());
|
|
|
+
|
|
|
+ static ExpressionEditorWindow()
|
|
|
+ {
|
|
|
+ RegisterFunctionTemplate("Math", "Abs", "double x");
|
|
|
+ RegisterFunctionTemplate("Math", "Acos", "double x");
|
|
|
+ RegisterFunctionTemplate("Math", "Asin", "double x");
|
|
|
+ RegisterFunctionTemplate("Math", "Atan", "double x");
|
|
|
+ RegisterFunctionTemplate("Math", "Ceiling", "double x");
|
|
|
+ RegisterFunctionTemplate("Math", "Cos", "double x");
|
|
|
+ RegisterFunctionTemplate("Math", "Count", "...");
|
|
|
+ RegisterFunctionTemplate("Math", "Exp", "double exp");
|
|
|
+ RegisterFunctionTemplate("Math", "Floor", "double x");
|
|
|
+ RegisterFunctionTemplate("Math", "IEEERemainder", "double x", "double y");
|
|
|
+ RegisterFunctionTemplate("Math", "Log10", "double x");
|
|
|
+ RegisterFunctionTemplate("Math", "Log", "double x", "double base");
|
|
|
+ RegisterFunctionTemplate("Math", "Max", "double x", "double y");
|
|
|
+ RegisterFunctionTemplate("Math", "Min", "double x", "double y");
|
|
|
+ RegisterFunctionTemplateDesc("Math", "Pow", "Returns {base} ^ {exp}.", "double base", "double exp");
|
|
|
+ RegisterFunctionTemplate("Math", "Random");
|
|
|
+ RegisterFunctionTemplateDesc("Math", "Round", "Rounds a number to a particular number of decimal places, given by {precision}.", "double x", "double precision");
|
|
|
+ RegisterFunctionTemplate("Math", "Sign", "double x");
|
|
|
+ RegisterFunctionTemplate("Math", "Sin", "double x");
|
|
|
+ RegisterFunctionTemplate("Math", "Sqrt", "double x");
|
|
|
+ RegisterFunctionTemplate("Math", "Tan", "double x");
|
|
|
+ RegisterFunctionTemplate("Math", "Truncate", "double x");
|
|
|
+
|
|
|
+ RegisterFunctionTemplateDesc("Logical", "If", "If {condition} is true or non-zero, returns {exp1}; otherwise, returns {exp2}.", "bool condition", "exp1", "exp2");
|
|
|
+ RegisterFunctionTemplateDesc("Logical", "In", "Returns true if {value} is in the list of values.", "value", "...");
|
|
|
+
|
|
|
+ RegisterFunctionTemplateDesc("Statistical", "Average", "Takes the average of a list of numbers.", "double ...");
|
|
|
+ RegisterFunctionTemplateDesc("Statistical", "Mean", "Calculates the mean for a list of numbers.", "double ...");
|
|
|
+ RegisterFunctionTemplateDesc("Statistical", "Median", "Calculates the median for a list of numbers.", "double ...");
|
|
|
+ RegisterFunctionTemplateDesc("Statistical", "Mode", "Calculates the mode for a list of numbers.", "double ...");
|
|
|
+ RegisterFunctionTemplateDesc("Statistical", "Sum", "Calculates the sum of a list of numbers.", "double ...");
|
|
|
+
|
|
|
+ RegisterFunctionTemplate("String", "Contains", "string text", "str value");
|
|
|
+ RegisterFunctionTemplate("String", "EndsWith", "string text", "str value");
|
|
|
+ RegisterFunctionTemplate("String", "Length", "string value");
|
|
|
+ RegisterFunctionTemplate("String", "PadLeft", "string value", "int length", "char character");
|
|
|
+ RegisterFunctionTemplate("String", "PadRight", "string value", "int length", "char character");
|
|
|
+ RegisterFunctionTemplate("String", "StartsWith", "string text", "string value");
|
|
|
+ RegisterFunctionTemplate("String", "Substring", "string text", "int startIndex", "int length");
|
|
|
+
|
|
|
+ RegisterFunctionTemplate("Date", "AddYears", "date date", "int years");
|
|
|
+ RegisterFunctionTemplate("Date", "AddMonths", "date date", "int months");
|
|
|
+ RegisterFunctionTemplate("Date", "AddDays", "date date", "int days");
|
|
|
+ RegisterFunctionTemplate("Date", "AddHours", "date date", "int hrs");
|
|
|
+ RegisterFunctionTemplate("Date", "AddMinutes", "date date", "int mins");
|
|
|
+ RegisterFunctionTemplate("Date", "AddSeconds", "date date", "int secs");
|
|
|
+ RegisterFunctionTemplate("Date", "AddMilliseconds", "date date", "int ms");
|
|
|
+ RegisterFunctionTemplate("Date", "YearOf", "date date");
|
|
|
+ RegisterFunctionTemplate("Date", "MonthOf", "date date");
|
|
|
+ RegisterFunctionTemplate("Date", "DayOf", "date date");
|
|
|
+ RegisterFunctionTemplate("Date", "HourOf", "date date");
|
|
|
+ RegisterFunctionTemplate("Date", "MinuteOf", "date date");
|
|
|
+ RegisterFunctionTemplate("Date", "SecondOf", "date date");
|
|
|
+ RegisterFunctionTemplate("Date", "MillisecondOf", "date date");
|
|
|
+ RegisterFunctionTemplate("Date", "DaysBetween", "date date", "date date");
|
|
|
+ RegisterFunctionTemplate("Date", "HoursBetween", "date date", "date date");
|
|
|
+ RegisterFunctionTemplate("Date", "MinutesBetween", "date date", "date date");
|
|
|
+ RegisterFunctionTemplate("Date", "SecondsBetween", "date date", "date date");
|
|
|
+ RegisterFunctionTemplate("Date", "MillisecondsBetween", "date date", "date date");
|
|
|
+
|
|
|
+ RegisterFunctionTemplate("Conversion", "Date", "value");
|
|
|
+ RegisterFunctionTemplate("Conversion", "Date", "value", "string format");
|
|
|
+ RegisterFunctionTemplate("Conversion", "Decimal", "value");
|
|
|
+ RegisterFunctionTemplate("Conversion", "Double", "value");
|
|
|
+ RegisterFunctionTemplate("Conversion", "Integer", "value");
|
|
|
+ RegisterFunctionTemplate("Conversion", "Long", "value");
|
|
|
+ RegisterFunctionTemplate("Conversion", "String", "value");
|
|
|
+
|
|
|
+ foreach(var function in CoreExpression.Functions)
|
|
|
+ {
|
|
|
+ RegisterFunctionTemplateDesc(
|
|
|
+ function.Group,
|
|
|
+ function.Name,
|
|
|
+ function.Description,
|
|
|
+ function.Parameters);
|
|
|
}
|
|
|
- private static void RegisterFunctionTemplate(string group, string name, params string[] parameters)
|
|
|
- => RegisterFunctionTemplate(group, name, null, parameters.Select(x =>
|
|
|
+ }
|
|
|
+
|
|
|
+ public ExpressionEditorWindow(IEnumerable<string> variables)
|
|
|
+ {
|
|
|
+ var parentColumns = new Dictionary<string, ExpressionEditorVariable>();
|
|
|
+ var items = new List<ExpressionEditorVariable>();
|
|
|
+ foreach (var variable in variables)
|
|
|
+ {
|
|
|
+ var display = variable;
|
|
|
+ var lastDot = variable.LastIndexOf('.');
|
|
|
+ if(lastDot != -1)
|
|
|
{
|
|
|
- var parts = x.Split(' ');
|
|
|
- if (parts.Length == 1)
|
|
|
+ display = variable[(lastDot + 1)..];
|
|
|
+ }
|
|
|
+ var item = new ExpressionEditorVariable(variable, display, []);
|
|
|
+
|
|
|
+ var props = variable.Split('.');
|
|
|
+
|
|
|
+ var vars = items;
|
|
|
+ string? parent = null;
|
|
|
+ for (int i = 0; i < props.Length - 1; ++i)
|
|
|
+ {
|
|
|
+ if (parent is null)
|
|
|
{
|
|
|
- return new FunctionParameter(parts[0], "");
|
|
|
+ parent = props[i];
|
|
|
}
|
|
|
- return new FunctionParameter(parts[1], parts[0]);
|
|
|
- }).ToList<IFunctionParameter>());
|
|
|
-
|
|
|
- private static void RegisterFunctionTemplateDesc(string group, string name, string description, params string[] parameters)
|
|
|
- => RegisterFunctionTemplate(group, name, description, parameters.Select(x =>
|
|
|
- {
|
|
|
- var parts = x.Split(' ');
|
|
|
- if (parts.Length == 1)
|
|
|
+ else
|
|
|
{
|
|
|
- return new FunctionParameter(parts[0], "");
|
|
|
+ parent = $"{parent}.{props[i]}";
|
|
|
}
|
|
|
- return new FunctionParameter(parts[1], parts[0]);
|
|
|
- }).ToList<IFunctionParameter>());
|
|
|
-
|
|
|
- static ExpressionEditorWindow()
|
|
|
- {
|
|
|
- RegisterFunctionTemplate("Math", "Abs", "double x");
|
|
|
- RegisterFunctionTemplate("Math", "Acos", "double x");
|
|
|
- RegisterFunctionTemplate("Math", "Asin", "double x");
|
|
|
- RegisterFunctionTemplate("Math", "Atan", "double x");
|
|
|
- RegisterFunctionTemplate("Math", "Ceiling", "double x");
|
|
|
- RegisterFunctionTemplate("Math", "Cos", "double x");
|
|
|
- RegisterFunctionTemplate("Math", "Count", "...");
|
|
|
- RegisterFunctionTemplate("Math", "Exp", "double exp");
|
|
|
- RegisterFunctionTemplate("Math", "Floor", "double x");
|
|
|
- RegisterFunctionTemplate("Math", "IEEERemainder", "double x", "double y");
|
|
|
- RegisterFunctionTemplate("Math", "Log10", "double x");
|
|
|
- RegisterFunctionTemplate("Math", "Log", "double x", "double base");
|
|
|
- RegisterFunctionTemplate("Math", "Max", "double x", "double y");
|
|
|
- RegisterFunctionTemplate("Math", "Min", "double x", "double y");
|
|
|
- RegisterFunctionTemplateDesc("Math", "Pow", "Returns {base} ^ {exp}.", "double base", "double exp");
|
|
|
- RegisterFunctionTemplate("Math", "Random");
|
|
|
- RegisterFunctionTemplateDesc("Math", "Round", "Rounds a number to a particular number of decimal places, given by {precision}.", "double x", "double precision");
|
|
|
- RegisterFunctionTemplate("Math", "Sign", "double x");
|
|
|
- RegisterFunctionTemplate("Math", "Sin", "double x");
|
|
|
- RegisterFunctionTemplate("Math", "Sqrt", "double x");
|
|
|
- RegisterFunctionTemplate("Math", "Tan", "double x");
|
|
|
- RegisterFunctionTemplate("Math", "Truncate", "double x");
|
|
|
-
|
|
|
- RegisterFunctionTemplateDesc("Logical", "If", "If {condition} is true or non-zero, returns {exp1}; otherwise, returns {exp2}.", "bool condition", "exp1", "exp2");
|
|
|
- RegisterFunctionTemplateDesc("Logical", "In", "Returns true if {value} is in the list of values.", "value", "...");
|
|
|
-
|
|
|
- RegisterFunctionTemplateDesc("Statistical", "Average", "Takes the average of a list of numbers.", "double ...");
|
|
|
- RegisterFunctionTemplateDesc("Statistical", "Mean", "Calculates the mean for a list of numbers.", "double ...");
|
|
|
- RegisterFunctionTemplateDesc("Statistical", "Median", "Calculates the median for a list of numbers.", "double ...");
|
|
|
- RegisterFunctionTemplateDesc("Statistical", "Mode", "Calculates the mode for a list of numbers.", "double ...");
|
|
|
- RegisterFunctionTemplateDesc("Statistical", "Sum", "Calculates the sum of a list of numbers.", "double ...");
|
|
|
-
|
|
|
- RegisterFunctionTemplate("String", "Contains", "string text", "str value");
|
|
|
- RegisterFunctionTemplate("String", "EndsWith", "string text", "str value");
|
|
|
- RegisterFunctionTemplate("String", "Length", "string value");
|
|
|
- RegisterFunctionTemplate("String", "PadLeft", "string value", "int length", "char character");
|
|
|
- RegisterFunctionTemplate("String", "PadRight", "string value", "int length", "char character");
|
|
|
- RegisterFunctionTemplate("String", "StartsWith", "string text", "string value");
|
|
|
- RegisterFunctionTemplate("String", "Substring", "string text", "int startIndex", "int length");
|
|
|
-
|
|
|
- RegisterFunctionTemplate("Date", "AddYears", "date date", "int years");
|
|
|
- RegisterFunctionTemplate("Date", "AddMonths", "date date", "int months");
|
|
|
- RegisterFunctionTemplate("Date", "AddDays", "date date", "int days");
|
|
|
- RegisterFunctionTemplate("Date", "AddHours", "date date", "int hrs");
|
|
|
- RegisterFunctionTemplate("Date", "AddMinutes", "date date", "int mins");
|
|
|
- RegisterFunctionTemplate("Date", "AddSeconds", "date date", "int secs");
|
|
|
- RegisterFunctionTemplate("Date", "AddMilliseconds", "date date", "int ms");
|
|
|
- RegisterFunctionTemplate("Date", "YearOf", "date date");
|
|
|
- RegisterFunctionTemplate("Date", "MonthOf", "date date");
|
|
|
- RegisterFunctionTemplate("Date", "DayOf", "date date");
|
|
|
- RegisterFunctionTemplate("Date", "HourOf", "date date");
|
|
|
- RegisterFunctionTemplate("Date", "MinuteOf", "date date");
|
|
|
- RegisterFunctionTemplate("Date", "SecondOf", "date date");
|
|
|
- RegisterFunctionTemplate("Date", "MillisecondOf", "date date");
|
|
|
- RegisterFunctionTemplate("Date", "DaysBetween", "date date", "date date");
|
|
|
- RegisterFunctionTemplate("Date", "HoursBetween", "date date", "date date");
|
|
|
- RegisterFunctionTemplate("Date", "MinutesBetween", "date date", "date date");
|
|
|
- RegisterFunctionTemplate("Date", "SecondsBetween", "date date", "date date");
|
|
|
- RegisterFunctionTemplate("Date", "MillisecondsBetween", "date date", "date date");
|
|
|
-
|
|
|
- RegisterFunctionTemplate("Conversion", "Date", "value");
|
|
|
- RegisterFunctionTemplate("Conversion", "Date", "value", "string format");
|
|
|
- RegisterFunctionTemplate("Conversion", "Decimal", "value");
|
|
|
- RegisterFunctionTemplate("Conversion", "Double", "value");
|
|
|
- RegisterFunctionTemplate("Conversion", "Integer", "value");
|
|
|
- RegisterFunctionTemplate("Conversion", "Long", "value");
|
|
|
- RegisterFunctionTemplate("Conversion", "String", "value");
|
|
|
-
|
|
|
- foreach(var function in CoreExpression.Functions)
|
|
|
- {
|
|
|
- RegisterFunctionTemplateDesc(
|
|
|
- function.Group,
|
|
|
- function.Name,
|
|
|
- function.Description,
|
|
|
- function.Parameters);
|
|
|
+ if(!parentColumns.TryGetValue(parent, out var parentVar))
|
|
|
+ {
|
|
|
+ parentVar = new ExpressionEditorVariable(parent, props[i], []);
|
|
|
+ parentColumns.Add(parent, parentVar);
|
|
|
+ vars.Add(parentVar);
|
|
|
+ }
|
|
|
+ vars = parentVar.ChildVariables;
|
|
|
}
|
|
|
+ vars.Add(item);
|
|
|
}
|
|
|
+ SortVariables(items);
|
|
|
|
|
|
- public ExpressionEditorWindow(IEnumerable<string> variables)
|
|
|
- {
|
|
|
- Variables = variables;
|
|
|
-
|
|
|
- InitializeComponent();
|
|
|
- }
|
|
|
+ Variables = items;
|
|
|
|
|
|
- private void ExpressionWindow_Loaded(object sender, RoutedEventArgs e)
|
|
|
- {
|
|
|
- Editor.Focus();
|
|
|
- }
|
|
|
+ InitializeComponent();
|
|
|
+ }
|
|
|
|
|
|
- private void InsertText(string text)
|
|
|
- {
|
|
|
- var newCaret = Editor.CaretIndex + text.Length;
|
|
|
- Editor.Text = Editor.Text.Insert(Editor.CaretIndex, text);
|
|
|
- Editor.CaretIndex = newCaret;
|
|
|
- }
|
|
|
- private void AppendText(string text)
|
|
|
- {
|
|
|
- var oldCaret = Editor.CaretIndex;
|
|
|
- Editor.Text = Editor.Text.Insert(Editor.CaretIndex, text);
|
|
|
- Editor.CaretIndex = oldCaret;
|
|
|
- }
|
|
|
- private void DeleteText(int start, int length)
|
|
|
+ private void SortVariables(List<ExpressionEditorVariable> vars)
|
|
|
+ {
|
|
|
+ vars.Sort((a, b) =>
|
|
|
{
|
|
|
- var oldCaret = Editor.CaretIndex;
|
|
|
- Editor.Text = Editor.Text.Remove(start, length);
|
|
|
- if(oldCaret >= start && oldCaret < start + length)
|
|
|
+ if (a.HasChildren && !b.HasChildren)
|
|
|
{
|
|
|
- Editor.CaretIndex = start;
|
|
|
+ return -1;
|
|
|
}
|
|
|
- else
|
|
|
+ else if (!a.HasChildren && b.HasChildren)
|
|
|
{
|
|
|
- Editor.CaretIndex = oldCaret - length;
|
|
|
+ return 1;
|
|
|
}
|
|
|
+ return a.Display.CompareTo(b.Display);
|
|
|
+ });
|
|
|
+ foreach(var v in vars)
|
|
|
+ {
|
|
|
+ SortVariables(v.ChildVariables);
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- private static Regex tagRegex = new("\\{\\S+\\}");
|
|
|
- private static Regex backTagRegex = new("\\{\\S+\\}", RegexOptions.RightToLeft);
|
|
|
+ private void ExpressionWindow_Loaded(object sender, RoutedEventArgs e)
|
|
|
+ {
|
|
|
+ Editor.Focus();
|
|
|
+ }
|
|
|
|
|
|
- private bool DeletePlaceholder()
|
|
|
+ private void InsertText(string text)
|
|
|
+ {
|
|
|
+ var newCaret = Editor.CaretIndex + text.Length;
|
|
|
+ Editor.Text = Editor.Text.Insert(Editor.CaretIndex, text);
|
|
|
+ Editor.CaretIndex = newCaret;
|
|
|
+ }
|
|
|
+ private void AppendText(string text)
|
|
|
+ {
|
|
|
+ var oldCaret = Editor.CaretIndex;
|
|
|
+ Editor.Text = Editor.Text.Insert(Editor.CaretIndex, text);
|
|
|
+ Editor.CaretIndex = oldCaret;
|
|
|
+ }
|
|
|
+ private void DeleteText(int start, int length)
|
|
|
+ {
|
|
|
+ var oldCaret = Editor.CaretIndex;
|
|
|
+ Editor.Text = Editor.Text.Remove(start, length);
|
|
|
+ if(oldCaret >= start && oldCaret < start + length)
|
|
|
{
|
|
|
- if (Editor.CaretIndex >= Editor.Text.Length) return false;
|
|
|
+ Editor.CaretIndex = start;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Editor.CaretIndex = oldCaret - length;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- var tagStart = Editor.Text.LastIndexOf('{', Editor.CaretIndex);
|
|
|
- if (tagStart == -1) return false;
|
|
|
+ private static Regex tagRegex = new("\\{\\S+\\}");
|
|
|
+ private static Regex backTagRegex = new("\\{\\S+\\}", RegexOptions.RightToLeft);
|
|
|
|
|
|
- var tagMatch = tagRegex.Match(Editor.Text, tagStart);
|
|
|
- if (!tagMatch.Success) return false;
|
|
|
+ private bool DeletePlaceholder()
|
|
|
+ {
|
|
|
+ if (Editor.CaretIndex >= Editor.Text.Length) return false;
|
|
|
|
|
|
- var startIndex = tagMatch.Index;
|
|
|
+ var tagStart = Editor.Text.LastIndexOf('{', Editor.CaretIndex);
|
|
|
+ if (tagStart == -1) return false;
|
|
|
|
|
|
- if (startIndex != tagStart) return false;
|
|
|
+ var tagMatch = tagRegex.Match(Editor.Text, tagStart);
|
|
|
+ if (!tagMatch.Success) return false;
|
|
|
|
|
|
- var length = tagMatch.Length;
|
|
|
+ var startIndex = tagMatch.Index;
|
|
|
|
|
|
- if (Editor.CaretIndex >= startIndex + length) return false;
|
|
|
+ if (startIndex != tagStart) return false;
|
|
|
|
|
|
- DeleteText(startIndex, length);
|
|
|
+ var length = tagMatch.Length;
|
|
|
|
|
|
- return true;
|
|
|
- }
|
|
|
- private void JumpPlaceholderBackwards()
|
|
|
- {
|
|
|
- if (Editor.CaretIndex == 0) return;
|
|
|
+ if (Editor.CaretIndex >= startIndex + length) return false;
|
|
|
|
|
|
- var tagMatch = backTagRegex.Match(Editor.Text, Editor.CaretIndex - 1);
|
|
|
- if (!tagMatch.Success) return;
|
|
|
+ DeleteText(startIndex, length);
|
|
|
|
|
|
- Editor.CaretIndex = tagMatch.Index;
|
|
|
- }
|
|
|
- private void JumpPlaceholder(bool allowStart = false)
|
|
|
- {
|
|
|
- if (Editor.CaretIndex >= Editor.Text.Length) return;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ private void JumpPlaceholderBackwards()
|
|
|
+ {
|
|
|
+ if (Editor.CaretIndex == 0) return;
|
|
|
|
|
|
- var start = allowStart ? Editor.CaretIndex : Editor.CaretIndex + 1;
|
|
|
+ var tagMatch = backTagRegex.Match(Editor.Text, Editor.CaretIndex - 1);
|
|
|
+ if (!tagMatch.Success) return;
|
|
|
|
|
|
- var tagMatch = tagRegex.Match(Editor.Text, start);
|
|
|
- if (!tagMatch.Success) return;
|
|
|
+ Editor.CaretIndex = tagMatch.Index;
|
|
|
+ }
|
|
|
+ private void JumpPlaceholder(bool allowStart = false)
|
|
|
+ {
|
|
|
+ if (Editor.CaretIndex >= Editor.Text.Length) return;
|
|
|
|
|
|
- Editor.CaretIndex = tagMatch.Index;
|
|
|
- }
|
|
|
+ var start = allowStart ? Editor.CaretIndex : Editor.CaretIndex + 1;
|
|
|
|
|
|
- private void FunctionTemplate_Click(object sender, MouseButtonEventArgs e)
|
|
|
- {
|
|
|
- if ((sender as FrameworkElement)!.Tag is not FunctionTemplate template) return;
|
|
|
+ var tagMatch = tagRegex.Match(Editor.Text, start);
|
|
|
+ if (!tagMatch.Success) return;
|
|
|
|
|
|
- var placeholder = DeletePlaceholder();
|
|
|
- InsertText($"{template.Name}(");
|
|
|
- AppendText(string.Join(", ", template.Parameters.Select(x => $"{{{x.Name}}}")) + ")");
|
|
|
- if (placeholder)
|
|
|
- JumpPlaceholder(true);
|
|
|
- }
|
|
|
+ Editor.CaretIndex = tagMatch.Index;
|
|
|
+ }
|
|
|
|
|
|
- private void Variable_Click(object sender, MouseButtonEventArgs e)
|
|
|
- {
|
|
|
- if ((sender as FrameworkElement)!.Tag is not string str) return;
|
|
|
+ private void FunctionTemplate_Click(object sender, MouseButtonEventArgs e)
|
|
|
+ {
|
|
|
+ if ((sender as FrameworkElement)!.Tag is not FunctionTemplate template) return;
|
|
|
|
|
|
- DeletePlaceholder();
|
|
|
- InsertText($"[{str}]");
|
|
|
- }
|
|
|
+ var placeholder = DeletePlaceholder();
|
|
|
+ InsertText($"{template.Name}(");
|
|
|
+ AppendText(string.Join(", ", template.Parameters.Select(x => $"{{{x.Name}}}")) + ")");
|
|
|
+ if (placeholder)
|
|
|
+ JumpPlaceholder(true);
|
|
|
+ }
|
|
|
|
|
|
- private void Editor_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
|
|
|
- {
|
|
|
- e.Handled = true;
|
|
|
- }
|
|
|
+ private void Variable_Click(object sender, MouseButtonEventArgs e)
|
|
|
+ {
|
|
|
+ if ((sender as FrameworkElement)!.Tag is not string str) return;
|
|
|
|
|
|
- private void Editor_PreviewTextInput(object sender, TextCompositionEventArgs e)
|
|
|
- {
|
|
|
- DeletePlaceholder();
|
|
|
- }
|
|
|
+ DeletePlaceholder();
|
|
|
+ InsertText($"[{str}]");
|
|
|
+ }
|
|
|
+
|
|
|
+ private void Editor_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
|
|
|
+ {
|
|
|
+ e.Handled = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void Editor_PreviewTextInput(object sender, TextCompositionEventArgs e)
|
|
|
+ {
|
|
|
+ DeletePlaceholder();
|
|
|
+ }
|
|
|
|
|
|
- private void Editor_KeyDown(object sender, KeyEventArgs e)
|
|
|
+ private void Editor_KeyDown(object sender, KeyEventArgs e)
|
|
|
+ {
|
|
|
+ if(e.Key == Key.Tab)
|
|
|
{
|
|
|
- if(e.Key == Key.Tab)
|
|
|
+ if((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
|
|
|
{
|
|
|
- if((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
|
|
|
- {
|
|
|
- JumpPlaceholderBackwards();
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- JumpPlaceholder();
|
|
|
- }
|
|
|
- e.Handled = true;
|
|
|
+ JumpPlaceholderBackwards();
|
|
|
}
|
|
|
+ else
|
|
|
+ {
|
|
|
+ JumpPlaceholder();
|
|
|
+ }
|
|
|
+ e.Handled = true;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- private void OK_Click(object sender, RoutedEventArgs e)
|
|
|
- {
|
|
|
- DialogResult = true;
|
|
|
- }
|
|
|
+ private void OK_Click(object sender, RoutedEventArgs e)
|
|
|
+ {
|
|
|
+ DialogResult = true;
|
|
|
}
|
|
|
+
|
|
|
}
|