1
0
mirror of https://github.com/chylex/Query.git synced 2025-05-23 19:34:06 +02:00

Fix Rider inspections

This commit is contained in:
chylex 2024-07-29 18:09:24 +02:00
parent 356d66121b
commit 4e950b73a3
Signed by: chylex
GPG Key ID: 4DE42C8F19A80548
35 changed files with 1429 additions and 1450 deletions

View File

@ -6,236 +6,243 @@ using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Base; using Base;
namespace AppCalc { namespace AppCalc;
public sealed class App : IApp {
private static readonly Regex RegexValidCharacters = new Regex(@"^[\s\d\.\-+*/%^]+$", RegexOptions.Compiled);
private static readonly Regex RegexTokenSeparator = new Regex(@"((?<!\d)-?(((\d+)?\.\d+(\.\.\.)?)|\d+))|[^\d\s]", RegexOptions.ExplicitCapture | RegexOptions.Compiled);
private static readonly Regex RegexRecurringDecimal = new Regex(@"\b(?:(\d+?\.\d{0,}?)(\d+?)\2+|([\d+?\.]*))\b", RegexOptions.Compiled);
private static readonly char[] SplitSpace = { ' ' }; public sealed partial class App : IApp {
private static readonly Regex RegexValidCharacters = GetRegexValidCharacters();
private static readonly Regex RegexTokenSeparator = GetRegexTokenSeparator();
private static readonly Regex RegexRecurringDecimal = GetRegexRecurringDecimal();
public string[] RecognizedNames => new string[] { [GeneratedRegex(@"^[\s\d\.\-+*/%^]+$", RegexOptions.Compiled)]
"calc", private static partial Regex GetRegexValidCharacters();
"calculate",
"calculator"
};
public MatchConfidence GetConfidence(Command cmd) { [GeneratedRegex(@"((?<!\d)-?(((\d+)?\.\d+(\.\.\.)?)|\d+))|[^\d\s]", RegexOptions.ExplicitCapture | RegexOptions.Compiled)]
return RegexValidCharacters.IsMatch(cmd.Text) ? MatchConfidence.Possible : MatchConfidence.None; private static partial Regex GetRegexTokenSeparator();
[GeneratedRegex(@"\b(?:(\d+?\.\d{0,}?)(\d+?)\2+|([\d+?\.]*))\b", RegexOptions.Compiled)]
private static partial Regex GetRegexRecurringDecimal();
private static readonly char[] SplitSpace = [ ' ' ];
public string[] RecognizedNames => [
"calc",
"calculate",
"calculator"
];
public MatchConfidence GetConfidence(Command cmd) {
return RegexValidCharacters.IsMatch(cmd.Text) ? MatchConfidence.Possible : MatchConfidence.None;
}
string IApp.ProcessCommand(Command cmd) {
return ParseAndProcessExpression(cmd.Text);
}
private static string ParseAndProcessExpression(string text) {
// text = RegexBalancedParentheses.Replace(text, match => " "+ParseAndProcessExpression(match.Groups[1].Value)+" "); // parens are handled as apps
string expression = RegexTokenSeparator.Replace(text, static match => " " + match.Value + " ");
string[] tokens = expression.Split(SplitSpace, StringSplitOptions.RemoveEmptyEntries);
decimal result = ProcessExpression(tokens);
if (Math.Abs(result - decimal.Round(result)) < 0.0000000000000000000000000010M) {
return decimal.Round(result).ToString(CultureInfo.InvariantCulture);
} }
string IApp.ProcessCommand(Command cmd) { string res = result.ToString(CultureInfo.InvariantCulture);
return ParseAndProcessExpression(cmd.Text); bool hasDecimalPoint = decimal.Round(result) != result;
if (res.Length == 30 && hasDecimalPoint && res.IndexOf('.') < 15) { // Length 30 uses all available bytes
res = res[..^1];
Match match = RegexRecurringDecimal.Match(res);
if (match.Groups[2].Success) {
string repeating = match.Groups[2].Value;
StringBuilder build = new StringBuilder(34);
build.Append(match.Groups[1].Value);
do {
build.Append(repeating);
} while (build.Length + repeating.Length <= res.Length);
return build.Append(repeating[..(1 + build.Length - res.Length)]).Append("...").ToString();
}
}
else if (hasDecimalPoint) {
res = res.TrimEnd('0');
} }
private static string ParseAndProcessExpression(string text) { return res;
// text = RegexBalancedParentheses.Replace(text, match => " "+ParseAndProcessExpression(match.Groups[1].Value)+" "); // parens are handled as apps }
string expression = RegexTokenSeparator.Replace(text, match => " " + match.Value + " "); private static decimal ProcessExpression(string[] tokens) {
string[] tokens = expression.Split(SplitSpace, StringSplitOptions.RemoveEmptyEntries); bool isPostfix;
decimal result = ProcessExpression(tokens); if (tokens.Length < 3) {
isPostfix = false;
if (Math.Abs(result - decimal.Round(result)) < 0.0000000000000000000000000010M) {
return decimal.Round(result).ToString(CultureInfo.InvariantCulture);
}
string res = result.ToString(CultureInfo.InvariantCulture);
bool hasDecimalPoint = decimal.Round(result) != result;
if (res.Length == 30 && hasDecimalPoint && res.IndexOf('.') < 15) { // Length 30 uses all available bytes
res = res.Substring(0, res.Length - 1);
Match match = RegexRecurringDecimal.Match(res);
if (match.Groups[2].Success) {
string repeating = match.Groups[2].Value;
StringBuilder build = new StringBuilder(34);
build.Append(match.Groups[1].Value);
do {
build.Append(repeating);
} while (build.Length + repeating.Length <= res.Length);
return build.Append(repeating.Substring(0, 1 + build.Length - res.Length)).Append("...").ToString();
}
}
else if (hasDecimalPoint) {
res = res.TrimEnd('0');
}
return res;
} }
else {
try {
ParseNumberToken(tokens[0]);
} catch (CommandException) {
throw new CommandException("Prefix notation is not supported.");
}
private static decimal ProcessExpression(string[] tokens) { try {
bool isPostfix; ParseNumberToken(tokens[1]);
isPostfix = true;
if (tokens.Length < 3) { } catch (CommandException) {
isPostfix = false; isPostfix = false;
} }
else { }
try {
ParseNumberToken(tokens[0]); if (isPostfix) {
} catch (CommandException) { return ProcessPostfixExpression(tokens);
throw new CommandException("Prefix notation is not supported."); }
else {
return ProcessPostfixExpression(ConvertInfixToPostfix(tokens));
}
}
private static IEnumerable<string> ConvertInfixToPostfix(IEnumerable<string> tokens) {
Stack<string> operators = new Stack<string>();
foreach (string token in tokens) {
if (Operators.With2Operands.Contains(token)) {
int currentPrecedence = Operators.GetPrecedence(token);
bool currentRightAssociative = Operators.IsRightAssociative(token);
while (operators.Count > 0) {
int topPrecedence = Operators.GetPrecedence(operators.Peek());
if ((currentRightAssociative && currentPrecedence < topPrecedence) || (!currentRightAssociative && currentPrecedence <= topPrecedence)) {
yield return operators.Pop();
}
else {
break;
}
} }
try { operators.Push(token);
ParseNumberToken(tokens[1]);
isPostfix = true;
} catch (CommandException) {
isPostfix = false;
}
}
if (isPostfix) {
return ProcessPostfixExpression(tokens);
} }
else { else {
return ProcessPostfixExpression(ConvertInfixToPostfix(tokens)); yield return ParseNumberToken(token).ToString(CultureInfo.InvariantCulture);
} }
} }
private static IEnumerable<string> ConvertInfixToPostfix(IEnumerable<string> tokens) { while (operators.Count > 0) {
Stack<string> operators = new Stack<string>(); yield return operators.Pop();
}
}
foreach (string token in tokens) { private static decimal ProcessPostfixExpression(IEnumerable<string> tokens) {
if (Operators.With2Operands.Contains(token)) { Stack<decimal> stack = new Stack<decimal>();
int currentPrecedence = Operators.GetPrecedence(token);
bool currentRightAssociative = Operators.IsRightAssociative(token);
while (operators.Count > 0) { foreach (string token in tokens) {
int topPrecedence = Operators.GetPrecedence(operators.Peek()); decimal operand1, operand2;
if ((currentRightAssociative && currentPrecedence < topPrecedence) || (!currentRightAssociative && currentPrecedence <= topPrecedence)) { if (token == "-" && stack.Count == 1) {
yield return operators.Pop(); operand2 = stack.Pop();
} operand1 = 0M;
else { }
break; else if (Operators.With2Operands.Contains(token)) {
} if (stack.Count < 2) {
throw new CommandException("Incorrect syntax, not enough numbers in stack.");
}
operand2 = stack.Pop();
operand1 = stack.Pop();
}
else {
operand1 = operand2 = 0M;
}
switch (token) {
case "+":
stack.Push(operand1 + operand2);
break;
case "-":
stack.Push(operand1 - operand2);
break;
case "*":
stack.Push(operand1 * operand2);
break;
case "/":
if (operand2 == 0M) {
throw new CommandException("Cannot divide by zero.");
} }
operators.Push(token); stack.Push(operand1 / operand2);
} break;
else {
yield return ParseNumberToken(token).ToString(CultureInfo.InvariantCulture);
}
}
while (operators.Count > 0) { case "%":
yield return operators.Pop(); if (operand2 == 0M) {
} throw new CommandException("Cannot divide by zero.");
}
private static decimal ProcessPostfixExpression(IEnumerable<string> tokens) {
Stack<decimal> stack = new Stack<decimal>();
foreach (string token in tokens) {
decimal operand1, operand2;
if (token == "-" && stack.Count == 1) {
operand2 = stack.Pop();
operand1 = 0M;
}
else if (Operators.With2Operands.Contains(token)) {
if (stack.Count < 2) {
throw new CommandException("Incorrect syntax, not enough numbers in stack.");
} }
operand2 = stack.Pop(); stack.Push(operand1 % operand2);
operand1 = stack.Pop(); break;
}
else {
operand1 = operand2 = 0M;
}
switch (token) { case "^":
case "+": if (operand1 == 0M && operand2 == 0M) {
stack.Push(operand1 + operand2); throw new CommandException("Cannot evaluate 0 to the power of 0.");
break; }
case "-": else if (operand1 < 0M && Math.Abs(operand2) < 1M) {
stack.Push(operand1 - operand2); throw new CommandException("Cannot evaluate a root of a negative number.");
break; }
case "*":
stack.Push(operand1 * operand2);
break;
case "/": try {
if (operand2 == 0M) { stack.Push((decimal) Math.Pow((double) operand1, (double) operand2));
throw new CommandException("Cannot divide by zero."); } catch (OverflowException ex) {
} throw new CommandException("Number overflow.", ex);
}
stack.Push(operand1 / operand2); break;
break;
case "%": default:
if (operand2 == 0M) { stack.Push(ParseNumberToken(token));
throw new CommandException("Cannot divide by zero."); break;
}
stack.Push(operand1 % operand2);
break;
case "^":
if (operand1 == 0M && operand2 == 0M) {
throw new CommandException("Cannot evaluate 0 to the power of 0.");
}
else if (operand1 < 0M && Math.Abs(operand2) < 1M) {
throw new CommandException("Cannot evaluate a root of a negative number.");
}
try {
stack.Push((decimal) Math.Pow((double) operand1, (double) operand2));
} catch (OverflowException ex) {
throw new CommandException("Number overflow.", ex);
}
break;
default:
stack.Push(ParseNumberToken(token));
break;
}
} }
if (stack.Count != 1) {
throw new CommandException("Incorrect syntax, too many numbers in stack.");
}
return stack.Pop();
} }
private static decimal ParseNumberToken(string token) { if (stack.Count != 1) {
string str = token; throw new CommandException("Incorrect syntax, too many numbers in stack.");
}
if (str.StartsWith("-.")) { return stack.Pop();
str = "-0" + str.Substring(1); }
private static decimal ParseNumberToken(string token) {
string str = token;
if (str.StartsWith("-.")) {
str = "-0" + str[1..];
}
else if (str[0] == '.') {
str = "0" + str;
}
if (str.EndsWith("...")) {
string truncated = str[..^3];
if (truncated.Contains('.')) {
str = truncated;
} }
else if (str[0] == '.') { }
str = "0" + str;
if (decimal.TryParse(str, NumberStyles.Float, CultureInfo.InvariantCulture, out decimal value)) {
if (value.ToString(CultureInfo.InvariantCulture) != str) {
throw new CommandException("Provided number is outside of decimal range: " + token);
} }
if (str.EndsWith("...")) { return value;
string truncated = str.Substring(0, str.Length - 3); }
else {
if (truncated.IndexOf('.') != -1) { throw new CommandException("Invalid token, expected a number: " + token);
str = truncated;
}
}
decimal value;
if (decimal.TryParse(str, NumberStyles.Float, CultureInfo.InvariantCulture, out value)) {
if (value.ToString(CultureInfo.InvariantCulture) != str) {
throw new CommandException("Provided number is outside of decimal range: " + token);
}
return value;
}
else {
throw new CommandException("Invalid token, expected a number: " + token);
}
} }
} }
} }

View File

@ -1,28 +1,18 @@
namespace AppCalc { namespace AppCalc;
static class Operators {
internal static readonly string[] With2Operands = { "+", "-", "*", "/", "%", "^" };
internal static int GetPrecedence(string token) { static class Operators {
switch (token) { internal static readonly string[] With2Operands = [ "+", "-", "*", "/", "%", "^" ];
case "^":
return 4;
case "*": internal static int GetPrecedence(string token) {
case "/": return token switch {
case "%": "^" => 4,
return 3; "*" or "/" or "%" => 3,
"+" or "-" => 2,
_ => 1
};
}
case "+": internal static bool IsRightAssociative(string token) {
case "-": return token == "^";
return 2;
default:
return 1;
}
}
internal static bool IsRightAssociative(string token) {
return token == "^";
}
} }
} }

View File

@ -4,46 +4,46 @@ using AppConv.General;
using AppConv.Units; using AppConv.Units;
using Base; using Base;
namespace AppConv { namespace AppConv;
public sealed class App : IApp {
private static readonly IUnitType[] Processors = {
new Temperature(),
new Weight(),
new Length(),
new Area(),
new Volume(),
new Angle(),
new Storage(),
new Radix()
};
public string[] RecognizedNames => new string[] { public sealed class App : IApp {
"conv", private static readonly IUnitType[] Processors = [
"convert" new Temperature(),
}; new Weight(),
new Length(),
new Area(),
new Volume(),
new Angle(),
new Storage(),
new Radix()
];
public MatchConfidence GetConfidence(Command cmd) { public string[] RecognizedNames => [
return cmd.Text.IndexOf(" to ", StringComparison.InvariantCultureIgnoreCase) != -1 ? MatchConfidence.Possible : MatchConfidence.None; "conv",
"convert"
];
public MatchConfidence GetConfidence(Command cmd) {
return cmd.Text.Contains(" to ", StringComparison.InvariantCultureIgnoreCase) ? MatchConfidence.Possible : MatchConfidence.None;
}
public string ProcessCommand(Command cmd) {
string[] data = cmd.Text.Split([ " to " ], 2, StringSplitOptions.None);
string src = data[0].Trim();
string dst = data[1].Trim();
if (src.Length == 0 || dst.Length == 0) {
throw new CommandException("Unrecognized conversion app syntax.");
} }
public string ProcessCommand(Command cmd) { string result = string.Empty;
string[] data = cmd.Text.Split(new string[] { " to " }, 2, StringSplitOptions.None); IUnitType used = Processors.FirstOrDefault(processor => processor.TryProcess(src, dst, out result));
string src = data[0].Trim(); if (used == null) {
string dst = data[1].Trim(); throw new CommandException("Could not recognize conversion units.");
if (src.Length == 0 || dst.Length == 0) {
throw new CommandException("Unrecognized conversion app syntax.");
}
string result = string.Empty;
IUnitType used = Processors.FirstOrDefault(processor => processor.TryProcess(src, dst, out result));
if (used == null) {
throw new CommandException("Could not recognize conversion units.");
}
return result;
} }
return result;
} }
} }

View File

@ -3,112 +3,110 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
namespace AppConv.General { namespace AppConv.General;
abstract class DecimalUnitConverterBase<T> : IUnitType where T : struct {
internal sealed class DecimalFuncMap : Dictionary<T, Func<decimal, decimal>> {
public DecimalFuncMap() {}
public DecimalFuncMap(int capacity) : base(capacity) {}
}
internal sealed class NameMap : Dictionary<string, T> { abstract class DecimalUnitConverterBase<T> : IUnitType where T : struct {
public NameMap() {} internal sealed class DecimalFuncMap : Dictionary<T, Func<decimal, decimal>> {
public NameMap(int capacity) : base(capacity) {} public DecimalFuncMap() {}
} public DecimalFuncMap(int capacity) : base(capacity) {}
}
protected abstract NameMap Names { get; } internal sealed class NameMap : Dictionary<string, T> {
protected abstract DecimalFuncMap ConvertTo { get; } public NameMap() {}
protected abstract DecimalFuncMap ConvertFrom { get; } public NameMap(int capacity) : base(capacity) {}
}
protected virtual int Precision => 0; protected abstract NameMap Names { get; }
protected abstract DecimalFuncMap ConvertTo { get; }
protected abstract DecimalFuncMap ConvertFrom { get; }
protected virtual bool CaseCheck => false; protected virtual int Precision => 0;
protected virtual NumberStyles NumberStyle => NumberStyles.Float & ~NumberStyles.AllowLeadingSign; protected virtual bool CaseCheck => false;
protected virtual string ProcessSrc(string src) { protected virtual NumberStyles NumberStyle => NumberStyles.Float & ~NumberStyles.AllowLeadingSign;
return src;
}
protected virtual string ProcessDst(string dst) { protected virtual string ProcessSrc(string src) {
return dst; return src;
} }
protected abstract bool IsValueInvalid(T value); protected virtual string ProcessDst(string dst) {
return dst;
}
protected virtual decimal Convert(decimal value, T from, T to) { protected abstract bool IsValueInvalid(T value);
return ConvertFrom[to](ConvertTo[from](value));
}
protected virtual string Format(decimal value) { protected virtual decimal Convert(decimal value, T from, T to) {
if (Precision > 0) { return ConvertFrom[to](ConvertTo[from](value));
decimal truncated = decimal.Truncate(value); }
if (value == truncated) { protected virtual string Format(decimal value) {
return truncated.ToString(CultureInfo.InvariantCulture); if (Precision > 0) {
} decimal truncated = decimal.Truncate(value);
if (value == truncated) {
return truncated.ToString(CultureInfo.InvariantCulture);
} }
int decimalPlaces = Precision;
if (Math.Abs(value) < 1M) {
double fractionalPart = (double) Math.Abs(value % 1M);
int fractionalZeroCount = -(int) Math.Ceiling(Math.Log(fractionalPart, 10D));
decimalPlaces = Math.Min(28, fractionalZeroCount + Precision);
}
string result = decimal.Round(value, decimalPlaces, MidpointRounding.ToEven).ToString(CultureInfo.InvariantCulture);
if (decimalPlaces > 0) {
result = result.TrimEnd('0').TrimEnd('.');
}
return result;
} }
public bool TryProcess(string src, string dst, out string result) { int decimalPlaces = Precision;
src = ProcessSrc(src);
dst = ProcessDst(dst);
var pairs = new KeyValuePair<string, T>[2]; if (Math.Abs(value) < 1M) {
double fractionalPart = (double) Math.Abs(value % 1M);
int fractionalZeroCount = -(int) Math.Ceiling(Math.Log(fractionalPart, 10D));
for (int index = 0; index < 2; index++) { decimalPlaces = Math.Min(28, fractionalZeroCount + Precision);
string str = index == 0 ? src : dst; }
if (CaseCheck) { string result = decimal.Round(value, decimalPlaces, MidpointRounding.ToEven).ToString(CultureInfo.InvariantCulture);
List<KeyValuePair<string, T>> list = Names.Where(kvp => str.EndsWith(kvp.Key, StringComparison.InvariantCultureIgnoreCase) && (str.Length == kvp.Key.Length || !char.IsLetter(str[str.Length - kvp.Key.Length - 1]))).ToList();
if (list.Count == 1) { if (decimalPlaces > 0) {
pairs[index] = list[0]; result = result.TrimEnd('0').TrimEnd('.');
} }
else {
pairs[index] = list.FirstOrDefault(kvp => str.EndsWith(kvp.Key, StringComparison.InvariantCulture)); return result;
} }
public bool TryProcess(string src, string dst, out string result) {
src = ProcessSrc(src);
dst = ProcessDst(dst);
var pairs = new KeyValuePair<string, T>[2];
for (int index = 0; index < 2; index++) {
string str = index == 0 ? src : dst;
if (CaseCheck) {
List<KeyValuePair<string, T>> list = Names.Where(kvp => str.EndsWith(kvp.Key, StringComparison.InvariantCultureIgnoreCase) && (str.Length == kvp.Key.Length || !char.IsLetter(str[str.Length - kvp.Key.Length - 1]))).ToList();
if (list.Count == 1) {
pairs[index] = list[0];
} }
else { else {
pairs[index] = Names.FirstOrDefault(kvp => str.EndsWith(kvp.Key, StringComparison.InvariantCultureIgnoreCase) && (str.Length == kvp.Key.Length || !char.IsLetter(str[str.Length - kvp.Key.Length - 1]))); pairs[index] = list.FirstOrDefault(kvp => str.EndsWith(kvp.Key, StringComparison.InvariantCulture));
} }
if (IsValueInvalid(pairs[index].Value)) {
result = string.Empty;
return false;
}
if (index == 0) {
src = src.Substring(0, src.Length - pairs[index].Key.Length).TrimEnd();
}
}
decimal value;
if (decimal.TryParse(src, NumberStyle, CultureInfo.InvariantCulture, out value)) {
result = Format(Convert(value, pairs[0].Value, pairs[1].Value));
return true;
} }
else { else {
pairs[index] = Names.FirstOrDefault(kvp => str.EndsWith(kvp.Key, StringComparison.InvariantCultureIgnoreCase) && (str.Length == kvp.Key.Length || !char.IsLetter(str[str.Length - kvp.Key.Length - 1])));
}
if (IsValueInvalid(pairs[index].Value)) {
result = string.Empty; result = string.Empty;
return false; return false;
} }
if (index == 0) {
src = src[..^pairs[index].Key.Length].TrimEnd();
}
}
if (decimal.TryParse(src, NumberStyle, CultureInfo.InvariantCulture, out decimal value)) {
result = Format(Convert(value, pairs[0].Value, pairs[1].Value));
return true;
}
else {
result = string.Empty;
return false;
} }
} }
} }

View File

@ -1,46 +1,45 @@
using System; using System;
namespace AppConv.General { namespace AppConv.General;
abstract class DecimalUnitConverterSimple<T> : DecimalUnitConverterBase<T> where T : struct {
// ReSharper disable once StaticMemberInGenericType
private static readonly Func<decimal, decimal> FuncNoChange = val => val;
private readonly NameMap UnitNames = new NameMap(); abstract class DecimalUnitConverterSimple<T> : DecimalUnitConverterBase<T> where T : struct {
private readonly DecimalFuncMap MapFrom = new DecimalFuncMap(); // ReSharper disable once StaticMemberInGenericType
private readonly DecimalFuncMap MapTo = new DecimalFuncMap(); private static readonly Func<decimal, decimal> FuncNoChange = static val => val;
private int invalidUnitObject = -1; private readonly DecimalFuncMap MapFrom = new ();
private readonly DecimalFuncMap MapTo = new ();
protected sealed override NameMap Names => UnitNames; private int invalidUnitObject = -1;
protected sealed override DecimalFuncMap ConvertFrom => MapFrom; protected sealed override NameMap Names { get ; } = new ();
protected sealed override DecimalFuncMap ConvertTo => MapTo; protected sealed override DecimalFuncMap ConvertFrom => MapFrom;
protected override int Precision => 3; protected sealed override DecimalFuncMap ConvertTo => MapTo;
protected override bool CaseCheck => true; protected override int Precision => 3;
protected void AddUnit(T unitObject, params string[] names) { protected override bool CaseCheck => true;
foreach (string name in names) {
UnitNames.Add(name, unitObject);
}
ConvertFrom.Add(unitObject, FuncNoChange); protected void AddUnit(T unitObject, params string[] names) {
ConvertTo.Add(unitObject, FuncNoChange); foreach (string name in names) {
Names.Add(name, unitObject);
} }
protected void SetUnitFactor(T unitObject, decimal factor) { ConvertFrom.Add(unitObject, FuncNoChange);
ConvertFrom[unitObject] = val => val * factor; ConvertTo.Add(unitObject, FuncNoChange);
ConvertTo[unitObject] = val => val / factor; }
}
protected void SetInvalidUnitObject(T unitObject) { protected void SetUnitFactor(T unitObject, decimal factor) {
invalidUnitObject = (int) (object) unitObject; ConvertFrom[unitObject] = val => val * factor;
} ConvertTo[unitObject] = val => val / factor;
}
protected sealed override bool IsValueInvalid(T value) { protected void SetInvalidUnitObject(T unitObject) {
return (int) (object) value == invalidUnitObject; invalidUnitObject = (int) (object) unitObject;
} }
protected sealed override bool IsValueInvalid(T value) {
return (int) (object) value == invalidUnitObject;
} }
} }

View File

@ -1,5 +1,5 @@
namespace AppConv.General { namespace AppConv.General;
interface IUnitType {
bool TryProcess(string src, string dst, out string result); interface IUnitType {
} bool TryProcess(string src, string dst, out string result);
} }

View File

@ -1,28 +1,28 @@
using System; using System;
using AppConv.General; using AppConv.General;
namespace AppConv.Units { namespace AppConv.Units;
class Angle : DecimalUnitConverterSimple<Angle.Units> {
internal enum Units {
Invalid = 0,
Degree,
Radian,
Gradian
}
protected override int Precision => 4; sealed class Angle : DecimalUnitConverterSimple<Angle.Units> {
internal enum Units {
public Angle() { Invalid = 0,
AddUnit(Units.Degree, "deg", "degree", "degrees", "arc degree", "arc degrees", "arcdegree", "arcdegrees", "°"); Degree,
AddUnit(Units.Radian, "rad", "radian", "radians"); Radian,
AddUnit(Units.Gradian, "grad", "grade", "gon", "gradian", "gradians"); Gradian
SetUnitFactor(Units.Radian, (decimal) Math.PI / 180M);
SetUnitFactor(Units.Gradian, 10M / 9M);
SetInvalidUnitObject(Units.Invalid);
}
// TODO convert degree notation 15°24'9"
} }
protected override int Precision => 4;
public Angle() {
AddUnit(Units.Degree, "deg", "degree", "degrees", "arc degree", "arc degrees", "arcdegree", "arcdegrees", "°");
AddUnit(Units.Radian, "rad", "radian", "radians");
AddUnit(Units.Gradian, "grad", "grade", "gon", "gradian", "gradians");
SetUnitFactor(Units.Radian, (decimal) Math.PI / 180M);
SetUnitFactor(Units.Gradian, 10M / 9M);
SetInvalidUnitObject(Units.Invalid);
}
// TODO convert degree notation 15°24'9"
} }

View File

@ -1,58 +1,58 @@
using AppConv.General; using AppConv.General;
namespace AppConv.Units { namespace AppConv.Units;
class Area : DecimalUnitConverterSimple<Area.Units> {
internal enum Units {
Invalid = 0,
SquareMM,
SquareCM,
SquareDM,
SquareM,
SquareKM,
SquareMile,
SquareYard,
SquareFoot,
SquareInch,
Acre,
Centiare,
Deciare,
Are,
Decare,
Hectare
}
public Area() { sealed class Area : DecimalUnitConverterSimple<Area.Units> {
AddUnit(Units.SquareMM, "mm2", "square mm", "square millimeter", "square millimeters", "square millimetre", "square millimetres"); internal enum Units {
AddUnit(Units.SquareCM, "cm2", "square cm", "square centimeter", "square centimeters", "square centimetre", "square centimetres"); Invalid = 0,
AddUnit(Units.SquareDM, "dm2", "square dm", "square decimeter", "square decimeters", "square decimetre", "square decimetres"); SquareMM,
AddUnit(Units.SquareM, "m2", "square m", "square meter", "square meters", "square metre", "square metres"); SquareCM,
AddUnit(Units.SquareKM, "km2", "square km", "square kilometer", "square kilometers", "square kilometre", "square kilometres"); SquareDM,
AddUnit(Units.SquareMile, "mi2", "sq mi", "sq mile", "sq miles", "square mi", "square mile", "square miles"); SquareM,
AddUnit(Units.SquareYard, "yd2", "sq yd", "sq yard", "sq yards", "square yd", "square yard", "square yards"); SquareKM,
AddUnit(Units.SquareFoot, "ft2", "sq ft", "sq foot", "sq feet", "square ft", "square foot", "square feet"); SquareMile,
AddUnit(Units.SquareInch, "in2", "sq in", "sq inch", "sq inches", "square in", "square inch", "square inches"); SquareYard,
AddUnit(Units.Acre, "ac", "acre", "acres"); SquareFoot,
AddUnit(Units.Centiare, "ca", "centiare", "centiares"); SquareInch,
AddUnit(Units.Deciare, "da", "deciare", "deciares"); // da is not canon but w/e Acre,
AddUnit(Units.Are, "a", "are", "ares"); Centiare,
AddUnit(Units.Decare, "daa", "decare", "decares"); Deciare,
AddUnit(Units.Hectare, "ha", "hectare", "hectares"); Are,
Decare,
Hectare
}
SetUnitFactor(Units.SquareMM, 1E+6M); public Area() {
SetUnitFactor(Units.SquareCM, 1E+4M); AddUnit(Units.SquareMM, "mm2", "square mm", "square millimeter", "square millimeters", "square millimetre", "square millimetres");
SetUnitFactor(Units.SquareDM, 1E+2M); AddUnit(Units.SquareCM, "cm2", "square cm", "square centimeter", "square centimeters", "square centimetre", "square centimetres");
SetUnitFactor(Units.SquareKM, 1E-6M); AddUnit(Units.SquareDM, "dm2", "square dm", "square decimeter", "square decimeters", "square decimetre", "square decimetres");
SetUnitFactor(Units.SquareMile, 3.8610215854245E-7M); AddUnit(Units.SquareM, "m2", "square m", "square meter", "square meters", "square metre", "square metres");
SetUnitFactor(Units.SquareYard, 1.1959900463011M); AddUnit(Units.SquareKM, "km2", "square km", "square kilometer", "square kilometers", "square kilometre", "square kilometres");
SetUnitFactor(Units.SquareFoot, 10.76391041671M); AddUnit(Units.SquareMile, "mi2", "sq mi", "sq mile", "sq miles", "square mi", "square mile", "square miles");
SetUnitFactor(Units.SquareInch, 1550.0031000062M); AddUnit(Units.SquareYard, "yd2", "sq yd", "sq yard", "sq yards", "square yd", "square yard", "square yards");
SetUnitFactor(Units.Acre, 2.4710538146717E-4M); AddUnit(Units.SquareFoot, "ft2", "sq ft", "sq foot", "sq feet", "square ft", "square foot", "square feet");
SetUnitFactor(Units.Deciare, 1E-1M); AddUnit(Units.SquareInch, "in2", "sq in", "sq inch", "sq inches", "square in", "square inch", "square inches");
SetUnitFactor(Units.Are, 1E-2M); AddUnit(Units.Acre, "ac", "acre", "acres");
SetUnitFactor(Units.Decare, 1E-3M); AddUnit(Units.Centiare, "ca", "centiare", "centiares");
SetUnitFactor(Units.Hectare, 1E-4M); AddUnit(Units.Deciare, "da", "deciare", "deciares"); // da is not canon but w/e
AddUnit(Units.Are, "a", "are", "ares");
AddUnit(Units.Decare, "daa", "decare", "decares");
AddUnit(Units.Hectare, "ha", "hectare", "hectares");
SetInvalidUnitObject(Units.Invalid); SetUnitFactor(Units.SquareMM, 1E+6M);
} SetUnitFactor(Units.SquareCM, 1E+4M);
SetUnitFactor(Units.SquareDM, 1E+2M);
SetUnitFactor(Units.SquareKM, 1E-6M);
SetUnitFactor(Units.SquareMile, 3.8610215854245E-7M);
SetUnitFactor(Units.SquareYard, 1.1959900463011M);
SetUnitFactor(Units.SquareFoot, 10.76391041671M);
SetUnitFactor(Units.SquareInch, 1550.0031000062M);
SetUnitFactor(Units.Acre, 2.4710538146717E-4M);
SetUnitFactor(Units.Deciare, 1E-1M);
SetUnitFactor(Units.Are, 1E-2M);
SetUnitFactor(Units.Decare, 1E-3M);
SetUnitFactor(Units.Hectare, 1E-4M);
SetInvalidUnitObject(Units.Invalid);
} }
} }

View File

@ -4,86 +4,86 @@ using System.Linq;
using AppConv.General; using AppConv.General;
using AppConv.Utils; using AppConv.Utils;
namespace AppConv.Units { namespace AppConv.Units;
class Length : DecimalUnitConverterSimple<Length.Units> {
internal enum Units { sealed class Length : DecimalUnitConverterSimple<Length.Units> {
Invalid = 0, internal enum Units {
Meter, Invalid = 0,
Inch, Meter,
Foot, Inch,
Yard, Foot,
Mile Yard,
Mile
}
private static readonly string[] NamesInch = [ "in", "inch", "inches", "\"", "''" ];
private static readonly string[] NamesFoot = [ "ft", "foot", "feet", "'" ];
private static readonly char[] Separator = [ ' ' ];
public Length() {
AddUnit(Units.Meter, "m", "meter", "metre", "meters", "metres");
AddUnit(Units.Inch, NamesInch);
AddUnit(Units.Foot, NamesFoot);
AddUnit(Units.Yard, "yd", "yard", "yards");
AddUnit(Units.Mile, "mi", "mile", "miles");
SetUnitFactor(Units.Inch, 39.37007874M);
SetUnitFactor(Units.Foot, 3.280839895M);
SetUnitFactor(Units.Yard, 1.093613298M);
SetUnitFactor(Units.Mile, 0.0006213711922M);
SetInvalidUnitObject(Units.Invalid);
SI.AddSupport(Units.Meter, [ "m" ], [ "meter", "metre", "meters", "metres" ], ConvertFrom, ConvertTo, Names);
}
protected override string ProcessSrc(string src) {
string updatedStr = src;
updatedStr = updatedStr.Replace("&", " ");
updatedStr = updatedStr.Replace(",", " ");
string inchName = NamesInch.FirstOrDefault(name => src.Contains(name, StringComparison.OrdinalIgnoreCase));
if (inchName == null) {
return src;
} }
private static readonly string[] NamesInch = { "in", "inch", "inches", "\"", "''" }; int inchIndex = src.IndexOf(inchName, StringComparison.OrdinalIgnoreCase);
private static readonly string[] NamesFoot = { "ft", "foot", "feet", "'" }; updatedStr = updatedStr.Remove(inchIndex, inchName.Length).Insert(inchIndex, new string(' ', inchName.Length));
public Length() { string footName = NamesFoot.FirstOrDefault(name => updatedStr.Contains(name, StringComparison.OrdinalIgnoreCase));
AddUnit(Units.Meter, "m", "meter", "metre", "meters", "metres");
AddUnit(Units.Inch, NamesInch);
AddUnit(Units.Foot, NamesFoot);
AddUnit(Units.Yard, "yd", "yard", "yards");
AddUnit(Units.Mile, "mi", "mile", "miles");
SetUnitFactor(Units.Inch, 39.37007874M); if (footName == null) {
SetUnitFactor(Units.Foot, 3.280839895M); return src;
SetUnitFactor(Units.Yard, 1.093613298M);
SetUnitFactor(Units.Mile, 0.0006213711922M);
SetInvalidUnitObject(Units.Invalid);
SI.AddSupport(typeof(Units), Units.Meter, new [] { "m" }, new [] { "meter", "metre", "meters", "metres" }, ConvertFrom, ConvertTo, Names);
} }
protected override string ProcessSrc(string src) { int footIndex = updatedStr.IndexOf(footName, StringComparison.OrdinalIgnoreCase);
string updatedStr = src; updatedStr = updatedStr.Remove(footIndex, footName.Length).Insert(footIndex, new string(' ', footName.Length));
updatedStr = updatedStr.Replace("&", " "); string[] tokens = updatedStr.Split(Separator, StringSplitOptions.RemoveEmptyEntries);
updatedStr = updatedStr.Replace(",", " "); decimal[] numbers = new decimal[2];
int numberIndex = 0;
string inchName = NamesInch.FirstOrDefault(name => src.IndexOf(name, StringComparison.OrdinalIgnoreCase) != -1); foreach (string token in tokens) {
if (decimal.TryParse(token.Trim(), NumberStyle, CultureInfo.InvariantCulture, out decimal number)) {
if (inchName == null) { if (numberIndex < numbers.Length) {
return src; numbers[numberIndex++] = number;
} }
else {
int inchIndex = src.IndexOf(inchName, StringComparison.OrdinalIgnoreCase); return src;
updatedStr = updatedStr.Remove(inchIndex, inchName.Length).Insert(inchIndex, new string(' ', inchName.Length));
string footName = NamesFoot.FirstOrDefault(name => updatedStr.IndexOf(name, StringComparison.OrdinalIgnoreCase) != -1);
if (footName == null) {
return src;
}
int footIndex = updatedStr.IndexOf(footName, StringComparison.OrdinalIgnoreCase);
updatedStr = updatedStr.Remove(footIndex, footName.Length).Insert(footIndex, new string(' ', footName.Length));
string[] tokens = updatedStr.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
decimal[] numbers = new decimal[2];
int numberIndex = 0;
foreach (string token in tokens) {
decimal number;
if (decimal.TryParse(token.Trim(), NumberStyle, CultureInfo.InvariantCulture, out number)) {
if (numberIndex < numbers.Length) {
numbers[numberIndex++] = number;
}
else {
return src;
}
} }
} }
if (numberIndex != numbers.Length) {
return src;
}
decimal srcFeet = numbers[footIndex < inchIndex ? 0 : 1];
decimal srcInches = numbers[inchIndex < footIndex ? 0 : 1];
return srcInches + srcFeet * 12M + " in";
} }
if (numberIndex != numbers.Length) {
return src;
}
decimal srcFeet = numbers[footIndex < inchIndex ? 0 : 1];
decimal srcInches = numbers[inchIndex < footIndex ? 0 : 1];
return srcInches + srcFeet * 12M + " in";
} }
} }

View File

@ -5,120 +5,115 @@ using AppConv.General;
using AppConv.Utils; using AppConv.Utils;
using Base; using Base;
namespace AppConv.Units { namespace AppConv.Units;
class Radix : IUnitType {
private static readonly Dictionary<string, int> RadixDescriptions = new Dictionary<string, int> {
{ "UNARY", 1 },
{ "BINARY", 2 },
{ "BIN", 2 },
{ "TERNARY", 3 },
{ "QUATERNARY", 4 },
{ "QUINARY", 5 },
{ "SENARY", 6 },
{ "OCTAL", 8 },
{ "OCT", 8 },
{ "DECIMAL", 10 },
{ "DEC", 10 },
{ "UNDECIMAL", 11 },
{ "DUODECIMAL", 12 },
{ "DOZENAL", 12 },
{ "TRIDECIMAL", 13 },
{ "TETRADECIMAL", 14 },
{ "PENTADECIMAL", 15 },
{ "HEXADECIMAL", 16 },
{ "HEX", 16 }
};
static Radix() { sealed class Radix : IUnitType {
for (int baseNumber = 1; baseNumber <= 16; baseNumber++) { private static readonly Dictionary<string, int> RadixDescriptions = new () {
RadixDescriptions.Add("RADIX " + baseNumber, baseNumber); { "UNARY", 1 },
RadixDescriptions.Add("BASE " + baseNumber, baseNumber); { "BINARY", 2 },
} { "BIN", 2 },
{ "TERNARY", 3 },
{ "QUATERNARY", 4 },
{ "QUINARY", 5 },
{ "SENARY", 6 },
{ "OCTAL", 8 },
{ "OCT", 8 },
{ "DECIMAL", 10 },
{ "DEC", 10 },
{ "UNDECIMAL", 11 },
{ "DUODECIMAL", 12 },
{ "DOZENAL", 12 },
{ "TRIDECIMAL", 13 },
{ "TETRADECIMAL", 14 },
{ "PENTADECIMAL", 15 },
{ "HEXADECIMAL", 16 },
{ "HEX", 16 }
};
static Radix() {
for (int baseNumber = 1; baseNumber <= 16; baseNumber++) {
RadixDescriptions.Add("RADIX " + baseNumber, baseNumber);
RadixDescriptions.Add("BASE " + baseNumber, baseNumber);
}
}
public bool TryProcess(string src, string dst, out string result) {
if (!RadixDescriptions.TryGetValue(dst.ToUpperInvariant(), out int targetBase)) {
result = string.Empty;
return false;
} }
public bool TryProcess(string src, string dst, out string result) { if (!ParseSrc(src, out string contents, out int sourceBase)) {
int targetBase; result = string.Empty;
return false;
}
if (!RadixDescriptions.TryGetValue(dst.ToUpperInvariant(), out targetBase)) { if (contents[0] == '-') {
result = string.Empty; throw new CommandException("Negative numbers are not supported.");
return false; }
} else if (!RadixConversion.IsBaseValid(sourceBase) || !RadixConversion.IsBaseValid(targetBase)) {
throw new CommandException("Only bases between 1 and 16 allowed.");
string contents; }
int sourceBase; else if (!RadixConversion.IsNumberValid(contents, sourceBase)) {
throw new CommandException("The input is not a valid base " + sourceBase + " number: " + contents);
if (!ParseSrc(src, out contents, out sourceBase)) { }
result = string.Empty;
return false;
}
if (contents[0] == '-') {
throw new CommandException("Negative numbers are not supported.");
}
else if (!RadixConversion.IsBaseValid(sourceBase) || !RadixConversion.IsBaseValid(targetBase)) {
throw new CommandException("Only bases between 1 and 16 allowed.");
}
else if (!RadixConversion.IsNumberValid(contents, sourceBase)) {
throw new CommandException("The input is not a valid base " + sourceBase + " number: " + contents);
}
if (sourceBase == targetBase) {
result = src;
return true;
}
try {
result = RadixConversion.Do(contents, sourceBase, targetBase);
} catch (OverflowException) {
throw new CommandException("The number has overflown.");
}
if (sourceBase == targetBase) {
result = src;
return true; return true;
} }
private static bool ParseSrc(string src, out string sourceContent, out int sourceBase) { try {
if (src.All(chr => chr >= '0' && chr <= '9')) { result = RadixConversion.Do(contents, sourceBase, targetBase);
sourceContent = src; } catch (OverflowException) {
sourceBase = 10; throw new CommandException("The number has overflown.");
return true;
}
string upper = src.ToUpperInvariant();
if (upper.StartsWith("0X")) {
sourceContent = upper.Substring(2);
sourceBase = 16;
return true;
}
if (upper.StartsWith("0B")) {
sourceContent = upper.Substring(2);
sourceBase = 2;
return true;
}
int fromIndex = src.IndexOf("FROM", StringComparison.InvariantCultureIgnoreCase);
if (fromIndex != -1) {
src = src.Remove(fromIndex, 4);
}
foreach (KeyValuePair<string, int> kvp in RadixDescriptions) {
if (src.StartsWith(kvp.Key, StringComparison.InvariantCultureIgnoreCase)) {
sourceContent = src.Substring(kvp.Key.Length).Trim();
sourceBase = kvp.Value;
return true;
}
else if (src.EndsWith(kvp.Key, StringComparison.InvariantCultureIgnoreCase)) {
sourceContent = src.Substring(0, src.Length - kvp.Key.Length).Trim();
sourceBase = kvp.Value;
return true;
}
}
sourceContent = string.Empty;
sourceBase = 0;
return false;
} }
return true;
}
private static bool ParseSrc(string src, out string sourceContent, out int sourceBase) {
if (src.All(static chr => chr is >= '0' and <= '9')) {
sourceContent = src;
sourceBase = 10;
return true;
}
string upper = src.ToUpperInvariant();
if (upper.StartsWith("0X")) {
sourceContent = upper[2..];
sourceBase = 16;
return true;
}
if (upper.StartsWith("0B")) {
sourceContent = upper[2..];
sourceBase = 2;
return true;
}
int fromIndex = src.IndexOf("FROM", StringComparison.InvariantCultureIgnoreCase);
if (fromIndex != -1) {
src = src.Remove(fromIndex, 4);
}
foreach (KeyValuePair<string, int> kvp in RadixDescriptions) {
if (src.StartsWith(kvp.Key, StringComparison.InvariantCultureIgnoreCase)) {
sourceContent = src[kvp.Key.Length..].Trim();
sourceBase = kvp.Value;
return true;
}
else if (src.EndsWith(kvp.Key, StringComparison.InvariantCultureIgnoreCase)) {
sourceContent = src[..^kvp.Key.Length].Trim();
sourceBase = kvp.Value;
return true;
}
}
sourceContent = string.Empty;
sourceBase = 0;
return false;
} }
} }

View File

@ -1,31 +1,33 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using AppConv.General; using AppConv.General;
using AppConv.Utils; using AppConv.Utils;
namespace AppConv.Units { namespace AppConv.Units;
class Storage : DecimalUnitConverterSimple<Storage.Units> {
internal enum Units {
Invalid = 0,
Byte,
Bit
}
public Storage() { sealed class Storage : DecimalUnitConverterSimple<Storage.Units> {
AddUnit(Units.Byte, "B", "byte", "bytes"); internal enum Units {
AddUnit(Units.Bit, "b", "bit", "bits"); Invalid = 0,
Byte,
Bit
}
SetUnitFactor(Units.Bit, 8M); [SuppressMessage("ReSharper", "PossibleLossOfFraction")]
public Storage() {
AddUnit(Units.Byte, "B", "byte", "bytes");
AddUnit(Units.Bit, "b", "bit", "bits");
SetInvalidUnitObject(Units.Invalid); SetUnitFactor(Units.Bit, 8M);
var bitConversionProperties = new SI.ExtededProperties { SetInvalidUnitObject(Units.Invalid);
FactorPredicate = factor => factor > 0 && factor % 3 == 0,
FromFunctionGenerator = exponent => () => (decimal) Math.Pow(1024, -(int) (exponent / 3)),
ToFunctionGenerator = exponent => () => (decimal) Math.Pow(1024, (int) (exponent / 3))
};
SI.AddSupportCustom(typeof(Units), Units.Byte, new [] { "B" }, new [] { "byte", "bytes" }, ConvertFrom, ConvertTo, Names, bitConversionProperties); var bitConversionProperties = new SI.ExtededProperties {
SI.AddSupportCustom(typeof(Units), Units.Bit, new [] { "b" }, new [] { "bit", "bits" }, ConvertFrom, ConvertTo, Names, bitConversionProperties); FactorPredicate = static factor => factor > 0 && factor % 3 == 0,
} FromFunctionGenerator = static exponent => () => (decimal) Math.Pow(1024, -(exponent / 3)),
ToFunctionGenerator = static exponent => () => (decimal) Math.Pow(1024, exponent / 3)
};
SI.AddSupportCustom(Units.Byte, [ "B" ], [ "byte", "bytes" ], ConvertFrom, ConvertTo, Names, bitConversionProperties);
SI.AddSupportCustom(Units.Bit, [ "b" ], [ "bit", "bits" ], ConvertFrom, ConvertTo, Names, bitConversionProperties);
} }
} }

View File

@ -1,90 +1,92 @@
using System.Globalization; using System.Globalization;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using AppConv.General; using AppConv.General;
using Base.Utils;
namespace AppConv.Units { namespace AppConv.Units;
class Temperature : DecimalUnitConverterBase<Temperature.Units> {
internal enum Units {
Invalid = 0,
Celsius,
Kelvin,
Fahrenheit,
Rankine,
Delisle,
Newton,
Reaumur,
Romer
}
private static readonly NameMap UnitNames = new NameMap(21) { sealed partial class Temperature : DecimalUnitConverterBase<Temperature.Units> {
{ "C", Units.Celsius }, internal enum Units {
{ "Celsius", Units.Celsius }, Invalid = 0,
{ "K", Units.Kelvin }, Celsius,
{ "Kelvin", Units.Kelvin }, Kelvin,
{ "F", Units.Fahrenheit }, Fahrenheit,
{ "Fahrenheit", Units.Fahrenheit }, Rankine,
{ "R", Units.Rankine }, Delisle,
{ "Ra", Units.Rankine }, Newton,
{ "Rankine", Units.Rankine }, Reaumur,
{ "De", Units.Delisle }, Romer
{ "Delisle", Units.Delisle }, }
{ "N", Units.Newton },
{ "Newton", Units.Newton },
{ "Re", Units.Reaumur },
{ "Ré", Units.Reaumur },
{ "Reaumur", Units.Reaumur },
{ "Réaumur", Units.Reaumur },
{ "Ro", Units.Romer },
{ "Rø", Units.Romer },
{ "Romer", Units.Romer },
{ "Rømer", Units.Romer }
};
private static readonly DecimalFuncMap FromCelsius = new DecimalFuncMap(8) { private static readonly NameMap UnitNames = new (21) {
{ Units.Celsius, val => val }, { "C", Units.Celsius },
{ Units.Kelvin, val => val + 273.15M }, { "Celsius", Units.Celsius },
{ Units.Fahrenheit, val => val * 1.8M + 32M }, { "K", Units.Kelvin },
{ Units.Rankine, val => (val + 273.15M) * 1.8M }, { "Kelvin", Units.Kelvin },
{ Units.Delisle, val => (100M - val) * 1.5M }, { "F", Units.Fahrenheit },
{ Units.Newton, val => val * 0.33M }, { "Fahrenheit", Units.Fahrenheit },
{ Units.Reaumur, val => val * 0.8M }, { "R", Units.Rankine },
{ Units.Romer, val => val * 0.525M + 7.5M } { "Ra", Units.Rankine },
}; { "Rankine", Units.Rankine },
{ "De", Units.Delisle },
{ "Delisle", Units.Delisle },
{ "N", Units.Newton },
{ "Newton", Units.Newton },
{ "Re", Units.Reaumur },
{ "Ré", Units.Reaumur },
{ "Reaumur", Units.Reaumur },
{ "Réaumur", Units.Reaumur },
{ "Ro", Units.Romer },
{ "Rø", Units.Romer },
{ "Romer", Units.Romer },
{ "Rømer", Units.Romer }
};
private static readonly DecimalFuncMap ToCelsius = new DecimalFuncMap(8) { private static readonly DecimalFuncMap FromCelsius = new (8) {
{ Units.Celsius, val => val }, { Units.Celsius, static val => val },
{ Units.Kelvin, val => val - 273.15M }, { Units.Kelvin, static val => val + 273.15M },
{ Units.Fahrenheit, val => (val - 32M) * 5M / 9M }, { Units.Fahrenheit, static val => val * 1.8M + 32M },
{ Units.Rankine, val => (val - 491.67M) * 5M / 9M }, { Units.Rankine, static val => (val + 273.15M) * 1.8M },
{ Units.Delisle, val => 100M - val * 2M / 3M }, { Units.Delisle, static val => (100M - val) * 1.5M },
{ Units.Newton, val => val * 100M / 33M }, { Units.Newton, static val => val * 0.33M },
{ Units.Reaumur, val => val * 1.25M }, { Units.Reaumur, static val => val * 0.8M },
{ Units.Romer, val => (val - 7.5M) * 40M / 21M } { Units.Romer, static val => val * 0.525M + 7.5M }
}; };
private static readonly Regex RegexCleanup = new Regex("deg(?:rees?)?|°", RegexUtils.Text); private static readonly DecimalFuncMap ToCelsius = new (8) {
{ Units.Celsius, static val => val },
{ Units.Kelvin, static val => val - 273.15M },
{ Units.Fahrenheit, static val => (val - 32M) * 5M / 9M },
{ Units.Rankine, static val => (val - 491.67M) * 5M / 9M },
{ Units.Delisle, static val => 100M - val * 2M / 3M },
{ Units.Newton, static val => val * 100M / 33M },
{ Units.Reaumur, static val => val * 1.25M },
{ Units.Romer, static val => (val - 7.5M) * 40M / 21M }
};
protected override NameMap Names => UnitNames; private static readonly Regex RegexCleanup = GetRegexCleanup();
protected override DecimalFuncMap ConvertFrom => FromCelsius; [GeneratedRegex("deg(?:rees?)?|°", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions.Compiled | RegexOptions.CultureInvariant)]
private static partial Regex GetRegexCleanup();
protected override DecimalFuncMap ConvertTo => ToCelsius; protected override NameMap Names => UnitNames;
protected override int Precision => 2; protected override DecimalFuncMap ConvertFrom => FromCelsius;
protected override NumberStyles NumberStyle => NumberStyles.Float; protected override DecimalFuncMap ConvertTo => ToCelsius;
protected override string ProcessSrc(string src) { protected override int Precision => 2;
return RegexCleanup.Replace(src, "");
}
protected override string ProcessDst(string dst) { protected override NumberStyles NumberStyle => NumberStyles.Float;
return RegexCleanup.Replace(dst, "");
}
protected override bool IsValueInvalid(Units value) { protected override string ProcessSrc(string src) {
return value == Units.Invalid; return RegexCleanup.Replace(src, "");
} }
protected override string ProcessDst(string dst) {
return RegexCleanup.Replace(dst, "");
}
protected override bool IsValueInvalid(Units value) {
return value == Units.Invalid;
} }
} }

View File

@ -1,34 +1,34 @@
using AppConv.General; using AppConv.General;
using AppConv.Utils; using AppConv.Utils;
namespace AppConv.Units { namespace AppConv.Units;
class Volume : DecimalUnitConverterSimple<Volume.Units> {
internal enum Units {
Invalid = 0,
Liter,
CubicMM,
CubicCM,
CubicDM,
CubicM,
CubicKM
}
public Volume() { sealed class Volume : DecimalUnitConverterSimple<Volume.Units> {
AddUnit(Units.Liter, "l", "liter", "liters", "litre", "litres"); internal enum Units {
AddUnit(Units.CubicMM, "mm3", "cubic mm", "cubic millimeter", "cubic millimeters", "cubic millimetre", "cubic millimetres"); Invalid = 0,
AddUnit(Units.CubicCM, "cm3", "cubic cm", "cubic centimeter", "cubic centimeters", "cubic centimetre", "cubic centimetres"); Liter,
AddUnit(Units.CubicDM, "dm3", "cubic dm", "cubic decimeter", "cubic decimeters", "cubic decimetre", "cubic decimetres"); CubicMM,
AddUnit(Units.CubicM, "m3", "cubic m", "cubic meter", "cubic meters", "cubic metre", "cubic metres"); CubicCM,
AddUnit(Units.CubicKM, "km3", "cubic km", "cubic kilometer", "cubic kilometers", "cubic kilometre", "cubic kilometres"); CubicDM,
CubicM,
CubicKM
}
SetUnitFactor(Units.CubicMM, 1000000M); public Volume() {
SetUnitFactor(Units.CubicCM, 1000M); AddUnit(Units.Liter, "l", "liter", "liters", "litre", "litres");
SetUnitFactor(Units.CubicM, 0.001M); AddUnit(Units.CubicMM, "mm3", "cubic mm", "cubic millimeter", "cubic millimeters", "cubic millimetre", "cubic millimetres");
SetUnitFactor(Units.CubicKM, 1E-12M); AddUnit(Units.CubicCM, "cm3", "cubic cm", "cubic centimeter", "cubic centimeters", "cubic centimetre", "cubic centimetres");
AddUnit(Units.CubicDM, "dm3", "cubic dm", "cubic decimeter", "cubic decimeters", "cubic decimetre", "cubic decimetres");
AddUnit(Units.CubicM, "m3", "cubic m", "cubic meter", "cubic meters", "cubic metre", "cubic metres");
AddUnit(Units.CubicKM, "km3", "cubic km", "cubic kilometer", "cubic kilometers", "cubic kilometre", "cubic kilometres");
SetInvalidUnitObject(Units.Invalid); SetUnitFactor(Units.CubicMM, 1000000M);
SetUnitFactor(Units.CubicCM, 1000M);
SetUnitFactor(Units.CubicM, 0.001M);
SetUnitFactor(Units.CubicKM, 1E-12M);
SI.AddSupport(typeof(Units), Units.Liter, new [] { "l" }, new [] { "liter", "litre", "liters", "litres" }, ConvertFrom, ConvertTo, Names); SetInvalidUnitObject(Units.Invalid);
}
SI.AddSupport(Units.Liter, [ "l" ], [ "liter", "litre", "liters", "litres" ], ConvertFrom, ConvertTo, Names);
} }
} }

View File

@ -1,29 +1,29 @@
using AppConv.General; using AppConv.General;
using AppConv.Utils; using AppConv.Utils;
namespace AppConv.Units { namespace AppConv.Units;
class Weight : DecimalUnitConverterSimple<Weight.Units> {
internal enum Units {
Invalid = 0,
Gram,
Pound,
Ounce,
Stone
}
public Weight() { sealed class Weight : DecimalUnitConverterSimple<Weight.Units> {
AddUnit(Units.Gram, "g", "gram", "grams"); internal enum Units {
AddUnit(Units.Pound, "lb", "lbs", "pound", "pounds"); Invalid = 0,
AddUnit(Units.Ounce, "oz", "ounce", "ounces"); Gram,
AddUnit(Units.Stone, "st", "stone", "stones"); Pound,
Ounce,
Stone
}
SetUnitFactor(Units.Pound, 0.0022046226218M); public Weight() {
SetUnitFactor(Units.Ounce, 0.03527396195M); AddUnit(Units.Gram, "g", "gram", "grams");
SetUnitFactor(Units.Stone, 0.0001574730444177697M); AddUnit(Units.Pound, "lb", "lbs", "pound", "pounds");
AddUnit(Units.Ounce, "oz", "ounce", "ounces");
AddUnit(Units.Stone, "st", "stone", "stones");
SetInvalidUnitObject(Units.Invalid); SetUnitFactor(Units.Pound, 0.0022046226218M);
SetUnitFactor(Units.Ounce, 0.03527396195M);
SetUnitFactor(Units.Stone, 0.0001574730444177697M);
SI.AddSupport(typeof(Units), Units.Gram, new [] { "g" }, new [] { "gram", "grams" }, ConvertFrom, ConvertTo, Names); SetInvalidUnitObject(Units.Invalid);
}
SI.AddSupport(Units.Gram, [ "g" ], [ "gram", "grams" ], ConvertFrom, ConvertTo, Names);
} }
} }

View File

@ -3,76 +3,76 @@ using System.Globalization;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
namespace AppConv.Utils { namespace AppConv.Utils;
static class RadixConversion {
private const string Characters = "0123456789ABCDEF";
public static bool IsBaseValid(int checkedBase) { static class RadixConversion {
return checkedBase >= 1 && checkedBase <= 16; private const string Characters = "0123456789ABCDEF";
public static bool IsBaseValid(int checkedBase) {
return checkedBase is >= 1 and <= 16;
}
public static bool IsNumberValid(string contents, int checkedBase) {
if (checkedBase == 1) {
return contents.All(static chr => chr == '1');
} }
public static bool IsNumberValid(string contents, int checkedBase) { if (IsBaseValid(checkedBase)) {
if (checkedBase == 1) { return contents.Select(static chr => Characters.IndexOf(char.ToUpper(chr))).All(index => index != -1 && index < checkedBase);
return contents.All(chr => chr == '1');
}
if (IsBaseValid(checkedBase)) {
return contents.Select(chr => Characters.IndexOf(char.ToUpper(chr))).All(index => index != -1 && index < checkedBase);
}
return false;
} }
public static string Do(string contents, int fromBase, int toBase) { // TODO biginteger return false;
if (fromBase == 1) { }
contents = contents.Length.ToString(CultureInfo.InvariantCulture);
fromBase = 10;
}
long wip; public static string Do(string contents, int fromBase, int toBase) { // TODO biginteger
if (fromBase == 1) {
contents = contents.Length.ToString(CultureInfo.InvariantCulture);
fromBase = 10;
}
if (fromBase == 10) { long wip;
wip = long.Parse(contents, NumberStyles.None, CultureInfo.InvariantCulture);
}
else {
wip = 0;
for (int chr = 0; chr < contents.Length; chr++) { if (fromBase == 10) {
int index = Characters.IndexOf(char.ToUpperInvariant(contents[chr])); wip = long.Parse(contents, NumberStyles.None, CultureInfo.InvariantCulture);
}
else {
wip = 0;
if (index > 0) { for (int chr = 0; chr < contents.Length; chr++) {
wip += index * (long) Math.Pow(fromBase, contents.Length - chr - 1); int index = Characters.IndexOf(char.ToUpperInvariant(contents[chr]));
if (wip < 0) { if (index > 0) {
throw new OverflowException(); wip += index * (long) Math.Pow(fromBase, contents.Length - chr - 1);
}
if (wip < 0) {
throw new OverflowException();
} }
} }
} }
}
if (toBase == 1) { if (toBase == 1) {
if (wip <= int.MaxValue) { if (wip <= int.MaxValue) {
return new string('1', (int) wip); return new string('1', (int) wip);
}
else {
throw new OverflowException();
}
}
else if (wip < toBase) {
return Characters[(int) wip].ToString();
} }
else { else {
var converted = new StringBuilder(); throw new OverflowException();
while (wip >= toBase) {
int index = (int) (wip % toBase);
converted.Insert(0, Characters[index]);
wip = wip / toBase;
}
return converted.Insert(0, Characters[(int) wip]).ToString();
} }
} }
else if (wip < toBase) {
return Characters[(int) wip].ToString();
}
else {
var converted = new StringBuilder();
while (wip >= toBase) {
int index = (int) (wip % toBase);
converted.Insert(0, Characters[index]);
wip /= toBase;
}
return converted.Insert(0, Characters[(int) wip]).ToString();
}
} }
} }

View File

@ -2,88 +2,86 @@
using System.Collections.Generic; using System.Collections.Generic;
using AppConv.General; using AppConv.General;
namespace AppConv.Utils { namespace AppConv.Utils;
static class SI {
private static readonly List<Tuple<string, string, int>> Factors = new List<Tuple<string, string, int>> {
new Tuple<string, string, int>("yotta", "Y", 24),
new Tuple<string, string, int>("zetta", "Z", 21),
new Tuple<string, string, int>("exa", "E", 18),
new Tuple<string, string, int>("peta", "P", 15),
new Tuple<string, string, int>("tera", "T", 12),
new Tuple<string, string, int>("giga", "G", 9),
new Tuple<string, string, int>("mega", "M", 6),
new Tuple<string, string, int>("kilo", "k", 3),
new Tuple<string, string, int>("hecto", "h", 2),
new Tuple<string, string, int>("deca", "da", 1),
new Tuple<string, string, int>("deci", "d", -1),
new Tuple<string, string, int>("centi", "c", -2),
new Tuple<string, string, int>("milli", "m", -3),
new Tuple<string, string, int>("micro", "μ", -6),
new Tuple<string, string, int>("nano", "n", -9),
new Tuple<string, string, int>("pico", "p", -12),
new Tuple<string, string, int>("femto", "f", -15),
new Tuple<string, string, int>("atto", "a", -18),
new Tuple<string, string, int>("zepto", "z", -21),
new Tuple<string, string, int>("yocto", "y", -24)
};
public static void AddSupport<T>(Type enumType, T unitObject, string[] unitShortNames, string[] unitLongNames, DecimalUnitConverterBase<T>.DecimalFuncMap funcFrom, DecimalUnitConverterBase<T>.DecimalFuncMap funcTo, DecimalUnitConverterBase<T>.NameMap nameMap) where T : struct { static class SI {
int enumCounter = 1000 + Factors.Count * (int) (object) unitObject; private static readonly List<Tuple<string, string, int>> Factors = [
new Tuple<string, string, int>("yotta", "Y", 24),
new Tuple<string, string, int>("zetta", "Z", 21),
new Tuple<string, string, int>("exa", "E", 18),
new Tuple<string, string, int>("peta", "P", 15),
new Tuple<string, string, int>("tera", "T", 12),
new Tuple<string, string, int>("giga", "G", 9),
new Tuple<string, string, int>("mega", "M", 6),
new Tuple<string, string, int>("kilo", "k", 3),
new Tuple<string, string, int>("hecto", "h", 2),
new Tuple<string, string, int>("deca", "da", 1),
new Tuple<string, string, int>("deci", "d", -1),
new Tuple<string, string, int>("centi", "c", -2),
new Tuple<string, string, int>("milli", "m", -3),
new Tuple<string, string, int>("micro", "μ", -6),
new Tuple<string, string, int>("nano", "n", -9),
new Tuple<string, string, int>("pico", "p", -12),
new Tuple<string, string, int>("femto", "f", -15),
new Tuple<string, string, int>("atto", "a", -18),
new Tuple<string, string, int>("zepto", "z", -21),
new Tuple<string, string, int>("yocto", "y", -24)
];
Func<decimal, decimal> convertFrom = funcFrom[unitObject]; public static void AddSupport<T>(T unitObject, string[] unitShortNames, string[] unitLongNames, DecimalUnitConverterBase<T>.DecimalFuncMap funcFrom, DecimalUnitConverterBase<T>.DecimalFuncMap funcTo, DecimalUnitConverterBase<T>.NameMap nameMap) where T : struct {
Func<decimal, decimal> convertTo = funcTo[unitObject]; int enumCounter = 1000 + Factors.Count * (int) (object) unitObject;
foreach (Tuple<string, string, int> factor in Factors) { Func<decimal, decimal> convertFrom = funcFrom[unitObject];
T enumObject = (T) (object) enumCounter++; Func<decimal, decimal> convertTo = funcTo[unitObject];
int exponent = factor.Item3;
foreach (string unitShortName in unitShortNames) { foreach ((string unitLongNamePrefix, string unitShotNamePrefix, int exponent) in Factors) {
nameMap.Add(factor.Item2 + unitShortName, enumObject); T enumObject = (T) (object) enumCounter++;
}
foreach (string unitLongName in unitLongNames) { foreach (string unitShortName in unitShortNames) {
nameMap.Add(factor.Item1 + unitLongName, enumObject); nameMap.Add(unitShotNamePrefix + unitShortName, enumObject);
}
funcFrom.Add(enumObject, val => convertFrom(val) * (decimal) Math.Pow(10, -exponent));
funcTo.Add(enumObject, val => convertTo(val) * (decimal) Math.Pow(10, exponent));
} }
}
public static void AddSupportCustom<T>(Type enumType, T unitObject, string[] unitShortNames, string[] unitLongNames, DecimalUnitConverterBase<T>.DecimalFuncMap funcFrom, DecimalUnitConverterBase<T>.DecimalFuncMap funcTo, DecimalUnitConverterBase<T>.NameMap nameMap, ExtededProperties extendedProps) where T : struct { foreach (string unitLongName in unitLongNames) {
int enumCounter = 1000 + Factors.Count * (int) (object) unitObject; nameMap.Add(unitLongNamePrefix + unitLongName, enumObject);
Func<decimal, decimal> convertFrom = funcFrom[unitObject];
Func<decimal, decimal> convertTo = funcTo[unitObject];
foreach (Tuple<string, string, int> factor in Factors) {
if (extendedProps.FactorPredicate != null && !extendedProps.FactorPredicate(factor.Item3)) {
continue;
}
T enumObject = (T) (object) enumCounter++;
int exponent = factor.Item3;
foreach (string unitShortName in unitShortNames) {
nameMap.Add(factor.Item2 + unitShortName, enumObject);
}
foreach (string unitLongName in unitLongNames) {
nameMap.Add(factor.Item1 + unitLongName, enumObject);
}
Func<decimal> genFrom = extendedProps.FromFunctionGenerator(exponent);
Func<decimal> genTo = extendedProps.ToFunctionGenerator(exponent);
funcFrom.Add(enumObject, val => convertFrom(val) * genFrom());
funcTo.Add(enumObject, val => convertTo(val) * genTo());
} }
}
internal class ExtededProperties { funcFrom.Add(enumObject, val => convertFrom(val) * (decimal) Math.Pow(10, -exponent));
public Predicate<int> FactorPredicate { get; set; } funcTo.Add(enumObject, val => convertTo(val) * (decimal) Math.Pow(10, exponent));
public Func<int, Func<decimal>> FromFunctionGenerator { get; set; }
public Func<int, Func<decimal>> ToFunctionGenerator { get; set; }
} }
} }
public static void AddSupportCustom<T>(T unitObject, string[] unitShortNames, string[] unitLongNames, DecimalUnitConverterBase<T>.DecimalFuncMap funcFrom, DecimalUnitConverterBase<T>.DecimalFuncMap funcTo, DecimalUnitConverterBase<T>.NameMap nameMap, ExtededProperties extendedProps) where T : struct {
int enumCounter = 1000 + Factors.Count * (int) (object) unitObject;
Func<decimal, decimal> convertFrom = funcFrom[unitObject];
Func<decimal, decimal> convertTo = funcTo[unitObject];
foreach ((string unitLongNamePrefix, string unitShortNamePrefix, int exponent) in Factors) {
if (extendedProps.FactorPredicate != null && !extendedProps.FactorPredicate(exponent)) {
continue;
}
T enumObject = (T) (object) enumCounter++;
foreach (string unitShortName in unitShortNames) {
nameMap.Add(unitShortNamePrefix + unitShortName, enumObject);
}
foreach (string unitLongName in unitLongNames) {
nameMap.Add(unitLongNamePrefix + unitLongName, enumObject);
}
Func<decimal> genFrom = extendedProps.FromFunctionGenerator(exponent);
Func<decimal> genTo = extendedProps.ToFunctionGenerator(exponent);
funcFrom.Add(enumObject, val => convertFrom(val) * genFrom());
funcTo.Add(enumObject, val => convertTo(val) * genTo());
}
}
internal sealed class ExtededProperties {
public Predicate<int> FactorPredicate { get; init; }
public Func<int, Func<decimal>> FromFunctionGenerator { get; init; }
public Func<int, Func<decimal>> ToFunctionGenerator { get; init; }
}
} }

View File

@ -1,25 +1,25 @@
using System.Collections.Generic; using System.Collections.Generic;
using Base; using Base;
namespace AppMeme { namespace AppMeme;
public sealed class App : IApp {
private static readonly Dictionary<string, string> Map = new Dictionary<string, string> {
{ "shrug", @"¯\_(ツ)_/¯" },
{ "lenny", @"( ͡° ͜ʖ ͡°)" },
{ "flip", @"(╯°□°)╯︵ ┻━┻" },
{ "tableflip", @"(╯°□°)╯︵ ┻━┻" }
};
public string[] RecognizedNames => new string[] { public sealed class App : IApp {
"meme" private static readonly Dictionary<string, string> Map = new () {
}; { "shrug", @"¯\_(ツ)_/¯" },
{ "lenny", @"( ͡° ͜ʖ ͡°)" },
{ "flip", @"(╯°□°)╯︵ ┻━┻" },
{ "tableflip", @"(╯°□°)╯︵ ┻━┻" }
};
public MatchConfidence GetConfidence(Command cmd) { public string[] RecognizedNames => [
return Map.ContainsKey(cmd.Text) ? MatchConfidence.Full : MatchConfidence.None; "meme"
} ];
public string ProcessCommand(Command cmd) { public MatchConfidence GetConfidence(Command cmd) {
return Map[cmd.Text]; return Map.ContainsKey(cmd.Text) ? MatchConfidence.Full : MatchConfidence.None;
} }
public string ProcessCommand(Command cmd) {
return Map[cmd.Text];
} }
} }

View File

@ -2,25 +2,25 @@
using AppSys.Handlers; using AppSys.Handlers;
using Base; using Base;
namespace AppSys { namespace AppSys;
public sealed class App : IApp {
private static readonly IHandler[] Handlers = {
new HandlerProcesses(),
new HandlerApps()
};
public string[] RecognizedNames => new string[] { public sealed class App : IApp {
"sys", private static readonly IHandler[] Handlers = [
"os", new HandlerProcesses(),
"win" new HandlerApps()
}; ];
public MatchConfidence GetConfidence(Command cmd) { public string[] RecognizedNames => [
return Handlers.Any(handler => handler.Matches(cmd)) ? MatchConfidence.Full : MatchConfidence.None; "sys",
} "os",
"win"
];
public string ProcessCommand(Command cmd) { public MatchConfidence GetConfidence(Command cmd) {
return Handlers.First(handler => handler.Matches(cmd)).Handle(cmd); return Handlers.Any(handler => handler.Matches(cmd)) ? MatchConfidence.Full : MatchConfidence.None;
} }
public string ProcessCommand(Command cmd) {
return Handlers.First(handler => handler.Matches(cmd)).Handle(cmd);
} }
} }

View File

@ -4,58 +4,56 @@ using System.Diagnostics;
using System.IO; using System.IO;
using Base; using Base;
namespace AppSys.Handlers { namespace AppSys.Handlers;
class HandlerApps : IHandler {
private static readonly string PathSystem = Environment.GetFolderPath(Environment.SpecialFolder.System);
private static readonly Dictionary<string, ProcessStartInfo> Mappings = new Dictionary<string, ProcessStartInfo> { sealed class HandlerApps : IHandler {
{ private static readonly string PathSystem = Environment.GetFolderPath(Environment.SpecialFolder.System);
"audio", new ProcessStartInfo {
FileName = Path.Combine(PathSystem, "control.exe"), private static readonly Dictionary<string, ProcessStartInfo> Mappings = new () {
Arguments = "mmsys.cpl" {
} "audio", new ProcessStartInfo {
}, { FileName = Path.Combine(PathSystem, "control.exe"),
"programs", new ProcessStartInfo { Arguments = "mmsys.cpl"
FileName = Path.Combine(PathSystem, "control.exe"),
Arguments = "appwiz.cpl"
}
}, {
"system", new ProcessStartInfo {
FileName = Path.Combine(PathSystem, "control.exe"),
Arguments = "sysdm.cpl"
}
}, {
"environment", new ProcessStartInfo {
FileName = Path.Combine(PathSystem, "rundll32.exe"),
Arguments = "sysdm.cpl,EditEnvironmentVariables"
}
} }
}; }, {
"programs", new ProcessStartInfo {
FileName = Path.Combine(PathSystem, "control.exe"),
Arguments = "appwiz.cpl"
}
}, {
"system", new ProcessStartInfo {
FileName = Path.Combine(PathSystem, "control.exe"),
Arguments = "sysdm.cpl"
}
}, {
"environment", new ProcessStartInfo {
FileName = Path.Combine(PathSystem, "rundll32.exe"),
Arguments = "sysdm.cpl,EditEnvironmentVariables"
}
}
};
private static readonly Dictionary<string, string> Substitutions = new Dictionary<string, string> { private static readonly Dictionary<string, string> Substitutions = new () {
{ "sounds", "audio" }, { "sounds", "audio" },
{ "apps", "programs" }, { "apps", "programs" },
{ "appwiz", "programs" }, { "appwiz", "programs" },
{ "env", "environment" }, { "env", "environment" },
{ "envvars", "environment" }, { "envvars", "environment" },
{ "vars", "environment" }, { "vars", "environment" },
{ "variables", "environment" } { "variables", "environment" }
}; };
public bool Matches(Command cmd) { public bool Matches(Command cmd) {
return Mappings.ContainsKey(cmd.Text) || Substitutions.ContainsKey(cmd.Text); return Mappings.ContainsKey(cmd.Text) || Substitutions.ContainsKey(cmd.Text);
}
public string Handle(Command cmd) {
if (!Substitutions.TryGetValue(cmd.Text, out string key)) {
key = cmd.Text;
} }
public string Handle(Command cmd) { using (Process.Start(Mappings[key])) {}
string key;
if (!Substitutions.TryGetValue(cmd.Text, out key)) { return null;
key = cmd.Text;
}
using (Process.Start(Mappings[key])) {}
return null;
}
} }
} }

View File

@ -3,47 +3,47 @@ using System.Diagnostics;
using System.Text; using System.Text;
using Base; using Base;
namespace AppSys.Handlers { namespace AppSys.Handlers;
class HandlerProcesses : IHandler {
public bool Matches(Command cmd) {
return cmd.Text.StartsWith("kill ", StringComparison.InvariantCultureIgnoreCase);
}
public string Handle(Command cmd) { sealed class HandlerProcesses : IHandler {
string[] processNames = cmd.Text.Substring("kill ".Length).Split(',', ';'); public bool Matches(Command cmd) {
int succeeded = 0, failed = 0; return cmd.Text.StartsWith("kill ", StringComparison.InvariantCultureIgnoreCase);
}
foreach (string processName in processNames) { public string Handle(Command cmd) {
try { string[] processNames = cmd.Text["kill ".Length..].Split(',', ';');
Process[] processes = Process.GetProcessesByName(processName.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase) ? processName.Substring(0, processName.Length - 4) : processName); int succeeded = 0, failed = 0;
foreach (Process process in processes) { foreach (string processName in processNames) {
try { try {
process.Kill(); Process[] processes = Process.GetProcessesByName(processName.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase) ? processName[..^4] : processName);
++succeeded;
} catch {
++failed;
}
process.Close(); foreach (Process process in processes) {
try {
process.Kill();
++succeeded;
} catch {
++failed;
} }
} catch {
++failed; process.Close();
} }
} catch {
++failed;
} }
if (succeeded == 0 && failed == 0 && (cmd.Text.Equals("kill me", StringComparison.InvariantCultureIgnoreCase) || cmd.Text.StartsWith("kill me ", StringComparison.InvariantCultureIgnoreCase) || cmd.Text.StartsWith("kill me,", StringComparison.InvariantCultureIgnoreCase))) {
return "No.";
}
var build = new StringBuilder();
build.Append("Killed ").Append(succeeded).Append(" process").Append(succeeded == 1 ? "" : "es");
if (failed > 0) {
build.Append(", failed ").Append(failed);
}
return build.Append('.').ToString();
} }
if (succeeded == 0 && failed == 0 && (cmd.Text.Equals("kill me", StringComparison.InvariantCultureIgnoreCase) || cmd.Text.StartsWith("kill me ", StringComparison.InvariantCultureIgnoreCase) || cmd.Text.StartsWith("kill me,", StringComparison.InvariantCultureIgnoreCase))) {
return "No.";
}
var build = new StringBuilder();
build.Append("Killed ").Append(succeeded).Append(" process").Append(succeeded == 1 ? "" : "es");
if (failed > 0) {
build.Append(", failed ").Append(failed);
}
return build.Append('.').ToString();
} }
} }

View File

@ -1,8 +1,8 @@
using Base; using Base;
namespace AppSys { namespace AppSys;
interface IHandler {
bool Matches(Command cmd); interface IHandler {
string Handle(Command cmd); bool Matches(Command cmd);
} string Handle(Command cmd);
} }

View File

@ -2,39 +2,35 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Base.Utils; using Base.Utils;
namespace Base { namespace Base;
public sealed class Command {
private static readonly Regex RegexBalancedBrackets = new Regex(RegexUtils.Balance(@"\[", @"\]"), RegexOptions.Compiled);
private static readonly Regex RegexBalancedParentheses = new Regex(RegexUtils.Balance(@"\(", @"\)"), RegexOptions.Compiled);
public string Text { get; private set; } public sealed class Command(string text) {
private static readonly Regex RegexBalancedBrackets = new (RegexUtils.Balance(@"\[", @"\]"), RegexOptions.Compiled);
private static readonly Regex RegexBalancedParentheses = new (RegexUtils.Balance(@"\(", @"\)"), RegexOptions.Compiled);
public string PotentialAppName { public string Text { get; } = text;
get {
int firstSpace = Text.IndexOf(' ');
if (firstSpace == -1) { public string PotentialAppName {
return null; get {
} int firstSpace = Text.IndexOf(' ');
string firstToken = Text.Substring(0, firstSpace); if (firstSpace == -1) {
return null;
if (!firstToken.All(char.IsLetter)) {
return null;
}
return firstToken;
} }
}
public bool IsSingleToken => Text.IndexOf(' ') == -1; string firstToken = Text[..firstSpace];
public Command(string text) { if (!firstToken.All(char.IsLetter)) {
Text = text; return null;
} }
public Command ReplaceBrackets(MatchEvaluator evaluator) { return firstToken;
return new Command(RegexBalancedParentheses.Replace(RegexBalancedBrackets.Replace(Text, evaluator), evaluator));
} }
} }
public bool IsSingleToken => !Text.Contains(' ');
public Command ReplaceBrackets(MatchEvaluator evaluator) {
return new Command(RegexBalancedParentheses.Replace(RegexBalancedBrackets.Replace(Text, evaluator), evaluator));
}
} }

View File

@ -1,11 +1,7 @@
using System; using System;
namespace Base { namespace Base;
public class CommandEventArgs : EventArgs {
public Command Command { get; private set; }
public CommandEventArgs(string text) { public sealed class CommandEventArgs(string text) : EventArgs {
Command = new Command(text); public Command Command { get; private set; } = new (text);
}
}
} }

View File

@ -1,8 +1,8 @@
using System; using System;
namespace Base { namespace Base;
public class CommandException : Exception {
public CommandException(string message) : base(message) {} public sealed class CommandException : Exception {
public CommandException(string message, Exception innerException) : base(message, innerException) {} public CommandException(string message) : base(message) {}
} public CommandException(string message, Exception innerException) : base(message, innerException) {}
} }

View File

@ -1,8 +1,8 @@
namespace Base { namespace Base;
public interface IApp {
string[] RecognizedNames { get; }
MatchConfidence GetConfidence(Command cmd); public interface IApp {
string ProcessCommand(Command cmd); string[] RecognizedNames { get; }
}
MatchConfidence GetConfidence(Command cmd);
string ProcessCommand(Command cmd);
} }

View File

@ -1,8 +1,8 @@
namespace Base { namespace Base;
public enum MatchConfidence {
None = 0, public enum MatchConfidence {
Low = 1, None = 0,
Possible = 2, Low = 1,
Full = 3 Possible = 2,
} Full = 3
} }

View File

@ -1,23 +1,23 @@
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace Base.Utils { namespace Base.Utils;
public static class RegexUtils {
public static readonly RegexOptions Text = RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions.Compiled;
public static string Balance(string escapedStart, string escapedEnd) { // \(((?>[^()]+|\((?<n>)|\)(?<-n>))+(?(n)(?!)))\) public static class RegexUtils {
return new StringBuilder() public const RegexOptions Text = RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions.Compiled;
.Append(escapedStart)
.Append(@"((?>[^") internal static string Balance(string escapedStart, string escapedEnd) { // \(((?>[^()]+|\((?<n>)|\)(?<-n>))+(?(n)(?!)))\)
.Append(escapedStart) return new StringBuilder()
.Append(escapedEnd) .Append(escapedStart)
.Append(@"]+|") .Append("((?>[^")
.Append(escapedStart) .Append(escapedStart)
.Append(@"(?<n>)|") .Append(escapedEnd)
.Append(escapedEnd) .Append("]+|")
.Append(@"(?<-n>))+(?(n)(?!)))") .Append(escapedStart)
.Append(escapedEnd) .Append("(?<n>)|")
.ToString(); .Append(escapedEnd)
} .Append("(?<-n>))+(?(n)(?!)))")
.Append(escapedEnd)
.ToString();
} }
} }

View File

@ -2,45 +2,45 @@
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
namespace Query.Controls { namespace Query.Controls;
sealed partial class QueryHistoryLog : UserControl {
public enum EntryType {
UserInput,
CommandResult,
Information,
Error
}
private static readonly Dictionary<EntryType, Color> EntryColorMap = new Dictionary<EntryType, Color> { sealed partial class QueryHistoryLog : UserControl {
{ EntryType.UserInput, Color.FromArgb(160, 160, 160) }, public enum EntryType {
{ EntryType.CommandResult, Color.FromArgb(240, 240, 240) }, UserInput,
{ EntryType.Information, Color.FromArgb(160, 255, 140) }, CommandResult,
{ EntryType.Error, Color.FromArgb(255, 40, 40) } Information,
Error
}
private static readonly Dictionary<EntryType, Color> EntryColorMap = new () {
{ EntryType.UserInput, Color.FromArgb(160, 160, 160) },
{ EntryType.CommandResult, Color.FromArgb(240, 240, 240) },
{ EntryType.Information, Color.FromArgb(160, 255, 140) },
{ EntryType.Error, Color.FromArgb(255, 40, 40) }
};
public QueryHistoryLog() {
InitializeComponent();
}
public void AddEntry(string text, EntryType type) {
int width = container.Width - SystemInformation.VerticalScrollBarWidth;
Label label = new Label {
AutoSize = true,
Font = container.Font,
ForeColor = EntryColorMap[type],
Text = text,
Margin = new Padding(0, 1, 0, 1),
MaximumSize = new Size(width, 0),
Width = width
}; };
public QueryHistoryLog() { container.Controls.Add(label);
InitializeComponent(); container.AutoScrollPosition = new Point(0, container.VerticalScroll.Maximum);
} }
public void AddEntry(string text, EntryType type) { public void ClearEntries() {
int width = container.Width - SystemInformation.VerticalScrollBarWidth; container.Controls.Clear();
Label label = new Label {
AutoSize = true,
Font = container.Font,
ForeColor = EntryColorMap[type],
Text = text,
Margin = new Padding(0, 1, 0, 1),
MaximumSize = new Size(width, 0),
Width = width
};
container.Controls.Add(label);
container.AutoScrollPosition = new Point(0, container.VerticalScroll.Maximum);
}
public void ClearEntries() {
container.Controls.Clear();
}
} }
} }

View File

@ -5,154 +5,154 @@ using System.Windows.Forms;
using Base; using Base;
using Query.Core; using Query.Core;
namespace Query.Controls { namespace Query.Controls;
sealed partial class QueryTextBox : UserControl {
public event EventHandler<CommandEventArgs> CommandRan;
private CommandHistory history; sealed partial class QueryTextBox : UserControl {
private Action<string> log; public event EventHandler<CommandEventArgs> CommandRan;
public QueryTextBox() { private CommandHistory history;
InitializeComponent(); private Action<string> log;
public QueryTextBox() {
InitializeComponent();
}
public void Setup(CommandHistory historyObj, Action<string> logFunc) {
history = historyObj;
log = logFunc;
}
private void OnCommandRan() {
CommandRan?.Invoke(this, new CommandEventArgs(tb.Text));
}
private sealed class CustomTextBox : TextBox {
private string lastInputStr = string.Empty;
private int lastInputPos = 0;
private bool doResetHistoryMemory;
private bool lastArrowShift;
private int historyOffset;
public CustomTextBox() {
TextChanged += CustomTextBox_TextChanged;
} }
public void Setup(CommandHistory historyObj, Action<string> logFunc) { protected override void OnKeyDown(KeyEventArgs e) {
history = historyObj; QueryTextBox input = (QueryTextBox) Parent;
log = logFunc; CommandHistory history = input!.history;
}
private void OnCommandRan() { Keys key = e.KeyCode;
CommandRan?.Invoke(this, new CommandEventArgs(tb.Text)); bool handled = false;
}
private sealed class CustomTextBox : TextBox { switch (key) {
private string lastInputStr = string.Empty; case Keys.Enter:
private int lastInputPos = 0; if (Text != string.Empty) {
input.OnCommandRan();
private bool doResetHistoryMemory; Text = string.Empty;
private bool lastArrowShift; doResetHistoryMemory = true;
private int historyOffset; handled = true;
}
public CustomTextBox() { break;
TextChanged += CustomTextBox_TextChanged;
}
protected override void OnKeyDown(KeyEventArgs e) { case Keys.Up:
QueryTextBox input = (QueryTextBox) Parent; if (lastArrowShift != e.Shift) {
CommandHistory history = input.history; lastArrowShift = e.Shift;
historyOffset = 0;
}
Keys key = e.KeyCode; --historyOffset;
bool handled = false;
switch (key) { if (InsertFromHistory(e.Shift ? history.Results : history.Queries)) {
case Keys.Enter: ++historyOffset;
if (Text != string.Empty) { }
input.OnCommandRan();
Text = string.Empty; handled = true;
doResetHistoryMemory = true; break;
case Keys.Down:
if (lastArrowShift != e.Shift) {
lastArrowShift = e.Shift;
historyOffset = 0;
}
++historyOffset;
if (InsertFromHistory(e.Shift ? history.Results : history.Queries)) {
--historyOffset;
}
handled = true;
break;
case Keys.C:
if (e.Modifiers == Keys.Control) {
if (SelectionLength == 0 && history.Results.Count > 0) {
Clipboard.SetText(history.Results.Last(), TextDataFormat.UnicodeText);
input.log("Copied to clipboard.");
handled = true; handled = true;
} }
}
break; break;
case Keys.Up:
if (lastArrowShift != e.Shift) {
lastArrowShift = e.Shift;
historyOffset = 0;
}
--historyOffset;
if (InsertFromHistory(e.Shift ? history.Results : history.Queries)) {
++historyOffset;
}
handled = true;
break;
case Keys.Down:
if (lastArrowShift != e.Shift) {
lastArrowShift = e.Shift;
historyOffset = 0;
}
++historyOffset;
if (InsertFromHistory(e.Shift ? history.Results : history.Queries)) {
--historyOffset;
}
handled = true;
break;
case Keys.C:
if (e.Modifiers == Keys.Control) {
if (SelectionLength == 0 && history.Results.Count > 0) {
Clipboard.SetText(history.Results.Last(), TextDataFormat.UnicodeText);
input.log("Copied to clipboard.");
handled = true;
}
}
break;
}
if (!handled && key != Keys.ControlKey && key != Keys.ShiftKey && key != Keys.Menu) {
doResetHistoryMemory = true;
}
e.Handled = e.SuppressKeyPress = handled;
base.OnKeyDown(e);
} }
protected override void OnKeyUp(KeyEventArgs e) { if (!handled && key != Keys.ControlKey && key != Keys.ShiftKey && key != Keys.Menu) {
base.OnKeyUp(e); doResetHistoryMemory = true;
if (doResetHistoryMemory) {
doResetHistoryMemory = false;
ResetHistoryMemory();
}
} }
private void CustomTextBox_TextChanged(object sender, EventArgs e) { e.Handled = e.SuppressKeyPress = handled;
base.OnKeyDown(e);
}
protected override void OnKeyUp(KeyEventArgs e) {
base.OnKeyUp(e);
if (doResetHistoryMemory) {
doResetHistoryMemory = false;
ResetHistoryMemory(); ResetHistoryMemory();
} }
}
// Management private void CustomTextBox_TextChanged(object sender, EventArgs e) {
ResetHistoryMemory();
}
private void ResetHistoryMemory() { // Management
lastInputStr = Text;
lastInputPos = SelectionStart; private void ResetHistoryMemory() {
historyOffset = 0; lastInputStr = Text;
lastInputPos = SelectionStart;
historyOffset = 0;
}
private bool InsertFromHistory(IList<string> collection) {
if (collection.Count == 0) {
return true;
} }
private bool InsertFromHistory(IList<string> collection) { int index = collection.Count + historyOffset;
if (collection.Count == 0) { bool wasClamped = false;
return true;
}
int index = collection.Count + historyOffset; if (index < 0) {
bool wasClamped = false; index = 0;
wasClamped = true;
if (index < 0) {
index = 0;
wasClamped = true;
}
else if (index >= collection.Count) {
index = collection.Count - 1;
wasClamped = true;
}
TextChanged -= CustomTextBox_TextChanged;
Text = lastInputStr.Insert(lastInputPos, collection[index]);
SelectionStart = lastInputPos + collection[index].Length;
SelectionLength = 0;
TextChanged += CustomTextBox_TextChanged;
return wasClamped;
} }
else if (index >= collection.Count) {
index = collection.Count - 1;
wasClamped = true;
}
TextChanged -= CustomTextBox_TextChanged;
Text = lastInputStr.Insert(lastInputPos, collection[index]);
SelectionStart = lastInputPos + collection[index].Length;
SelectionLength = 0;
TextChanged += CustomTextBox_TextChanged;
return wasClamped;
} }
} }
} }

View File

@ -1,25 +1,25 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace Query.Core { namespace Query.Core;
sealed class CommandHistory {
private readonly List<string> queries = new List<string>();
private readonly List<string> results = new List<string>();
public IList<string> Queries => queries; sealed class CommandHistory {
private readonly List<string> queries = [];
private readonly List<string> results = [];
public IList<string> Results => results; public IList<string> Queries => queries;
public void AddQuery(string text) { public IList<string> Results => results;
queries.Add(text);
}
public void AddResult(string text) { public void AddQuery(string text) {
results.Add(text); queries.Add(text);
} }
public void Clear() { public void AddResult(string text) {
queries.Clear(); results.Add(text);
results.Clear(); }
}
public void Clear() {
queries.Clear();
results.Clear();
} }
} }

View File

@ -3,63 +3,62 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Base; using Base;
namespace Query.Core { namespace Query.Core;
sealed class CommandProcessor {
private readonly Dictionary<string, IApp> appNames = new Dictionary<string, IApp>(8);
private readonly HashSet<IApp> appSet = new HashSet<IApp>();
public Func<string, bool> SingleTokenProcessor { get; set; } sealed class CommandProcessor {
private readonly Dictionary<string, IApp> appNames = new (8);
private readonly HashSet<IApp> appSet = [];
public void AddApp<T>() where T : IApp, new() { public Func<string, bool> SingleTokenProcessor { get; init; }
IApp app = new T();
foreach (string name in app.RecognizedNames) { public void AddApp<T>() where T : IApp, new() {
appNames.Add(name, app); IApp app = new T();
appSet.Add(app);
}
}
public string Run(Command cmd) { foreach (string name in app.RecognizedNames) {
cmd = cmd.ReplaceBrackets(match => Run(new Command(match.Groups[1].Value))); appNames.Add(name, app);
appSet.Add(app);
string appName = cmd.PotentialAppName;
IApp app;
if (appName != null && appNames.TryGetValue(appName.ToLowerInvariant(), out app)) {
return app.ProcessCommand(new Command(cmd.Text.Substring(appName.Length + 1)));
}
if (cmd.IsSingleToken && SingleTokenProcessor != null && SingleTokenProcessor(cmd.Text)) {
return null;
}
var list = appSet.Select(iapp => new { App = iapp, Confidence = iapp.GetConfidence(cmd) }).OrderByDescending(obj => obj.Confidence).Where(obj => obj.Confidence != MatchConfidence.None).ToList();
if (list.Count == 0) {
throw new CommandException("Could not find any suitable app, please write the app name and press Up Arrow.");
}
else if (list.Count == 1) {
app = list[0].App;
}
else {
List<IApp> plausible = new List<IApp> { list[0].App };
MatchConfidence topConfidence = list[0].Confidence;
for (int index = 1; index < list.Count; index++) {
if (list[index].Confidence == topConfidence) {
plausible.Add(list[index].App);
}
}
if (plausible.Count == 1) {
app = plausible.First();
}
else {
throw new CommandException("Command is ambiguous, please write the app name and press Up Arrow. Suggested apps: " + string.Join(", ", plausible.Select(iapp => iapp.RecognizedNames.First())));
}
}
return app.ProcessCommand(cmd);
} }
} }
public string Run(Command cmd) {
cmd = cmd.ReplaceBrackets(match => Run(new Command(match.Groups[1].Value)));
string appName = cmd.PotentialAppName;
if (appName != null && appNames.TryGetValue(appName.ToLowerInvariant(), out IApp app)) {
return app.ProcessCommand(new Command(cmd.Text[(appName.Length + 1)..]));
}
if (cmd.IsSingleToken && SingleTokenProcessor != null && SingleTokenProcessor(cmd.Text)) {
return null;
}
var list = appSet.Select(iapp => new { App = iapp, Confidence = iapp.GetConfidence(cmd) }).OrderByDescending(static obj => obj.Confidence).Where(static obj => obj.Confidence != MatchConfidence.None).ToList();
if (list.Count == 0) {
throw new CommandException("Could not find any suitable app, please write the app name and press Up Arrow.");
}
else if (list.Count == 1) {
app = list[0].App;
}
else {
List<IApp> plausible = [list[0].App];
MatchConfidence topConfidence = list[0].Confidence;
for (int index = 1; index < list.Count; index++) {
if (list[index].Confidence == topConfidence) {
plausible.Add(list[index].App);
}
}
if (plausible.Count == 1) {
app = plausible.First();
}
else {
throw new CommandException("Command is ambiguous, please write the app name and press Up Arrow. Suggested apps: " + string.Join(", ", plausible.Select(static iapp => iapp.RecognizedNames.First())));
}
}
return app.ProcessCommand(cmd);
}
} }

View File

@ -2,63 +2,62 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Windows.Forms; using System.Windows.Forms;
namespace Query.Core { namespace Query.Core;
sealed class KeyboardHook {
public event EventHandler Triggered;
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable sealed class KeyboardHook {
private readonly NativeMethods.HookProc keyboardHookDelegate; public event EventHandler Triggered;
private IntPtr keyboardHook;
public KeyboardHook() { // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
keyboardHookDelegate = KeyboardHookProc; private readonly NativeMethods.HookProc keyboardHookDelegate;
private IntPtr keyboardHook;
public KeyboardHook() {
keyboardHookDelegate = KeyboardHookProc;
}
public void StartHook() {
if (keyboardHook != IntPtr.Zero) {
NativeMethods.UnhookWindowsHookEx(keyboardHook);
} }
public void StartHook() { keyboardHook = NativeMethods.SetWindowsHookEx(NativeMethods.WH_KEYBOARD_LL, keyboardHookDelegate, IntPtr.Zero, 0);
if (keyboardHook != IntPtr.Zero) { }
NativeMethods.UnhookWindowsHookEx(keyboardHook);
}
keyboardHook = NativeMethods.SetWindowsHookEx(NativeMethods.WH_KEYBOARD_LL, keyboardHookDelegate, IntPtr.Zero, 0); public void StopHook() {
} if (keyboardHook != IntPtr.Zero) {
NativeMethods.UnhookWindowsHookEx(keyboardHook);
public void StopHook() { keyboardHook = IntPtr.Zero;
if (keyboardHook != IntPtr.Zero) {
NativeMethods.UnhookWindowsHookEx(keyboardHook);
keyboardHook = IntPtr.Zero;
}
}
private IntPtr KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam) {
if (wParam == (IntPtr) NativeMethods.WM_KEYDOWN) {
Keys key = (Keys) Marshal.ReadInt32(lParam);
if ((key == Keys.LWin || key == Keys.RWin) && Control.ModifierKeys.HasFlag(Keys.Control)) {
Triggered?.Invoke(this, EventArgs.Empty);
return NativeMethods.HookHandled;
}
}
return NativeMethods.CallNextHookEx(keyboardHook, nCode, wParam, lParam);
}
private static class NativeMethods {
public const int WH_KEYBOARD_LL = 13;
public const int WM_KEYDOWN = 0x0100;
public const int WM_KEYUP = 0x0101;
public static readonly IntPtr HookHandled = new IntPtr(-1);
public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
[DllImport("user32.dll")]
public static extern bool UnhookWindowsHookEx(IntPtr idHook);
[DllImport("user32.dll")]
public static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, IntPtr wParam, IntPtr lParam);
} }
} }
private IntPtr KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam) {
if (wParam == NativeMethods.WM_KEYDOWN) {
Keys key = (Keys) Marshal.ReadInt32(lParam);
if (key is Keys.LWin or Keys.RWin && Control.ModifierKeys.HasFlag(Keys.Control)) {
Triggered?.Invoke(this, EventArgs.Empty);
return NativeMethods.HookHandled;
}
}
return NativeMethods.CallNextHookEx(keyboardHook, nCode, wParam, lParam);
}
private static class NativeMethods {
public const int WH_KEYBOARD_LL = 13;
public const int WM_KEYDOWN = 0x0100;
public static readonly IntPtr HookHandled = new (-1);
public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
[DllImport("user32.dll")]
public static extern bool UnhookWindowsHookEx(IntPtr idHook);
[DllImport("user32.dll")]
public static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, IntPtr wParam, IntPtr lParam);
}
} }

View File

@ -1,5 +1,5 @@
namespace Query { namespace Query {
partial class MainForm { sealed partial class MainForm {
/// <summary> /// <summary>
/// Required designer variable. /// Required designer variable.
/// </summary> /// </summary>

View File

@ -5,142 +5,142 @@ using Base;
using Query.Controls; using Query.Controls;
using Query.Core; using Query.Core;
namespace Query { namespace Query;
partial class MainForm : Form {
private readonly CommandProcessor processor;
private readonly CommandHistory history;
private readonly Timer focusTimer; sealed partial class MainForm : Form {
private readonly KeyboardHook keyboardHook; private readonly CommandProcessor processor;
private readonly CommandHistory history;
private bool isLoaded; private readonly Timer focusTimer;
private readonly KeyboardHook keyboardHook;
public MainForm() { private bool isLoaded;
InitializeComponent();
processor = new CommandProcessor { public MainForm() {
SingleTokenProcessor = ProcessSingleToken InitializeComponent();
};
processor.AddApp<AppCalc.App>(); processor = new CommandProcessor {
processor.AddApp<AppConv.App>(); SingleTokenProcessor = ProcessSingleToken
processor.AddApp<AppMeme.App>(); };
processor.AddApp<AppSys.App>();
history = new CommandHistory(); processor.AddApp<AppCalc.App>();
queryBox.Setup(history, str => queryLog.AddEntry(str, QueryHistoryLog.EntryType.Information)); processor.AddApp<AppConv.App>();
processor.AddApp<AppMeme.App>();
processor.AddApp<AppSys.App>();
keyboardHook = new KeyboardHook(); history = new CommandHistory();
keyboardHook.Triggered += keyboardHook_Triggered; queryBox.Setup(history, str => queryLog.AddEntry(str, QueryHistoryLog.EntryType.Information));
focusTimer = new Timer { keyboardHook = new KeyboardHook();
Interval = 1 keyboardHook.Triggered += keyboardHook_Triggered;
};
focusTimer.Tick += focusTimer_Tick; focusTimer = new Timer {
Interval = 1
};
Disposed += MainForm_Disposed; focusTimer.Tick += focusTimer_Tick;
queryBox.CommandRan += queryBox_CommandRan;
Disposed += MainForm_Disposed;
queryBox.CommandRan += queryBox_CommandRan;
}
private void SetShown(bool show) {
if (show) {
focusTimer.Start();
} }
else {
private void SetShown(bool show) { Hide();
if (show) {
focusTimer.Start();
}
else {
Hide();
}
} }
}
private void MainForm_Shown(object sender, EventArgs e) { private void MainForm_Shown(object sender, EventArgs e) {
Rectangle screenRect = Screen.PrimaryScreen.WorkingArea; Rectangle screenRect = Screen.PrimaryScreen.WorkingArea;
Location = new Point(screenRect.X + screenRect.Width - Width, screenRect.Y + screenRect.Height - Height); Location = new Point(screenRect.X + screenRect.Width - Width, screenRect.Y + screenRect.Height - Height);
if (!isLoaded) { if (!isLoaded) {
isLoaded = true; isLoaded = true;
keyboardHook.StartHook();
}
}
private void MainForm_Deactivate(object sender, EventArgs e) {
SetShown(false);
}
private void MainForm_Disposed(object sender, EventArgs e) {
keyboardHook.StopHook();
}
private void trayIcon_Click(object sender, EventArgs e) {
if (((MouseEventArgs) e).Button == MouseButtons.Left) {
SetShown(true);
}
}
private void showToolStripMenuItem_Click(object sender, EventArgs e) {
SetShown(true);
}
private void hookToolStripMenuItem_Click(object sender, EventArgs e) {
keyboardHook.StopHook();
keyboardHook.StartHook(); keyboardHook.StartHook();
} }
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e) { private void MainForm_Deactivate(object sender, EventArgs e) {
Application.Exit(); SetShown(false);
}
private void MainForm_Disposed(object sender, EventArgs e) {
keyboardHook.StopHook();
}
private void trayIcon_Click(object sender, EventArgs e) {
if (((MouseEventArgs) e).Button == MouseButtons.Left) {
SetShown(true);
} }
}
private void keyboardHook_Triggered(object sender, EventArgs e) { private void showToolStripMenuItem_Click(object sender, EventArgs e) {
SetShown(!Visible); SetShown(true);
} }
private void focusTimer_Tick(object sender, EventArgs e) { private void hookToolStripMenuItem_Click(object sender, EventArgs e) {
WindowState = FormWindowState.Minimized; keyboardHook.StopHook();
Show(); keyboardHook.StartHook();
Activate(); }
WindowState = FormWindowState.Normal;
queryBox.Focus(); private void exitToolStripMenuItem_Click(object sender, EventArgs e) {
focusTimer.Stop(); Application.Exit();
} }
private void queryBox_CommandRan(object sender, CommandEventArgs e) { private void keyboardHook_Triggered(object sender, EventArgs e) {
try { SetShown(!Visible);
string result = processor.Run(e.Command); }
if (result != null) { private void focusTimer_Tick(object sender, EventArgs e) {
queryLog.AddEntry("> " + e.Command.Text, QueryHistoryLog.EntryType.UserInput); WindowState = FormWindowState.Minimized;
history.AddQuery(e.Command.Text); Show();
Activate();
WindowState = FormWindowState.Normal;
queryLog.AddEntry(result, QueryHistoryLog.EntryType.CommandResult); queryBox.Focus();
history.AddResult(result); focusTimer.Stop();
} }
} catch (CommandException ex) {
private void queryBox_CommandRan(object sender, CommandEventArgs e) {
try {
string result = processor.Run(e.Command);
if (result != null) {
queryLog.AddEntry("> " + e.Command.Text, QueryHistoryLog.EntryType.UserInput); queryLog.AddEntry("> " + e.Command.Text, QueryHistoryLog.EntryType.UserInput);
history.AddQuery(e.Command.Text); history.AddQuery(e.Command.Text);
queryLog.AddEntry(ex.Message, QueryHistoryLog.EntryType.Error); queryLog.AddEntry(result, QueryHistoryLog.EntryType.CommandResult);
history.AddResult(result);
} }
} catch (CommandException ex) {
queryLog.AddEntry("> " + e.Command.Text, QueryHistoryLog.EntryType.UserInput);
history.AddQuery(e.Command.Text);
queryLog.AddEntry(ex.Message, QueryHistoryLog.EntryType.Error);
} }
}
private bool ProcessSingleToken(string token) { private bool ProcessSingleToken(string token) {
switch (token) { switch (token) {
case "exit": case "exit":
case "quit": case "quit":
Application.Exit(); Application.Exit();
return true; return true;
case "clear": case "clear":
queryLog.ClearEntries(); queryLog.ClearEntries();
history.Clear(); history.Clear();
return true; return true;
case "hide": case "hide":
Hide(); Hide();
return true; return true;
default: default:
return false; return false;
}
} }
} }
} }

View File

@ -1,13 +1,13 @@
using System; using System;
using System.Windows.Forms; using System.Windows.Forms;
namespace Query { namespace Query;
static class Program {
[STAThread] static class Program {
private static void Main() { [STAThread]
Application.EnableVisualStyles(); private static void Main() {
Application.SetCompatibleTextRenderingDefault(false); Application.EnableVisualStyles();
Application.Run(new MainForm()); Application.SetCompatibleTextRenderingDefault(false);
} Application.Run(new MainForm());
} }
} }