From c986dbe8893f3667bf649992b6c93e022d271c3f Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 29 Nov 2020 09:56:34 +0100 Subject: [PATCH] Fixes #2232: We only need a small percentage of features provided by Humanizer. Therefore we copy the relevant parts and remove the dependency in order to avoid such problems in the future. --- ICSharpCode.Decompiler/Humanizer/LICENSE | 20 ++ .../Humanizer/Vocabularies.cs | 149 +++++++++++++++ .../Humanizer/Vocabulary.cs | 178 ++++++++++++++++++ .../ICSharpCode.Decompiler.csproj | 4 +- .../ICSharpCode.Decompiler.nuspec.template | 55 +++--- .../IL/Transforms/AssignVariableNames.cs | 4 +- ILSpy/TreeNodes/BaseTypesTreeNode.cs | 2 - packages.props | 34 ++-- 8 files changed, 396 insertions(+), 50 deletions(-) create mode 100644 ICSharpCode.Decompiler/Humanizer/LICENSE create mode 100644 ICSharpCode.Decompiler/Humanizer/Vocabularies.cs create mode 100644 ICSharpCode.Decompiler/Humanizer/Vocabulary.cs diff --git a/ICSharpCode.Decompiler/Humanizer/LICENSE b/ICSharpCode.Decompiler/Humanizer/LICENSE new file mode 100644 index 000000000..354d69534 --- /dev/null +++ b/ICSharpCode.Decompiler/Humanizer/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Scott Kirkland + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/ICSharpCode.Decompiler/Humanizer/Vocabularies.cs b/ICSharpCode.Decompiler/Humanizer/Vocabularies.cs new file mode 100644 index 000000000..efb2a12cd --- /dev/null +++ b/ICSharpCode.Decompiler/Humanizer/Vocabularies.cs @@ -0,0 +1,149 @@ +using System; +using System.Threading; + +namespace Humanizer.Inflections +{ + /// + /// Container for registered Vocabularies. At present, only a single vocabulary is supported: Default. + /// + internal static class Vocabularies + { + private static readonly Lazy Instance; + + static Vocabularies() + { + Instance = new Lazy(BuildDefault, LazyThreadSafetyMode.PublicationOnly); + } + + /// + /// The default vocabulary used for singular/plural irregularities. + /// Rules can be added to this vocabulary and will be picked up by called to Singularize() and Pluralize(). + /// At this time, multiple vocabularies and removing existing rules are not supported. + /// + public static Vocabulary Default => Instance.Value; + + private static Vocabulary BuildDefault() + { + var _default = new Vocabulary(); + + _default.AddPlural("$", "s"); + _default.AddPlural("s$", "s"); + _default.AddPlural("(ax|test)is$", "$1es"); + _default.AddPlural("(octop|vir|alumn|fung|cact|foc|hippopotam|radi|stimul|syllab|nucle)us$", "$1i"); + _default.AddPlural("(alias|bias|iris|status|campus|apparatus|virus|walrus|trellis)$", "$1es"); + _default.AddPlural("(buffal|tomat|volcan|ech|embarg|her|mosquit|potat|torped|vet)o$", "$1oes"); + _default.AddPlural("([dti])um$", "$1a"); + _default.AddPlural("sis$", "ses"); + _default.AddPlural("(?:([^f])fe|([lr])f)$", "$1$2ves"); + _default.AddPlural("(hive)$", "$1s"); + _default.AddPlural("([^aeiouy]|qu)y$", "$1ies"); + _default.AddPlural("(x|ch|ss|sh)$", "$1es"); + _default.AddPlural("(matr|vert|ind|d)ix|ex$", "$1ices"); + _default.AddPlural("(^[m|l])ouse$", "$1ice"); + _default.AddPlural("^(ox)$", "$1en"); + _default.AddPlural("(quiz)$", "$1zes"); + _default.AddPlural("(buz|blit|walt)z$", "$1zes"); + _default.AddPlural("(hoo|lea|loa|thie)f$", "$1ves"); + _default.AddPlural("(alumn|alg|larv|vertebr)a$", "$1ae"); + _default.AddPlural("(criteri|phenomen)on$", "$1a"); + + _default.AddSingular("s$", ""); + _default.AddSingular("(n)ews$", "$1ews"); + _default.AddSingular("([dti])a$", "$1um"); + _default.AddSingular("(analy|ba|diagno|parenthe|progno|synop|the|ellip|empha|neuro|oa|paraly)ses$", "$1sis"); + _default.AddSingular("([^f])ves$", "$1fe"); + _default.AddSingular("(hive)s$", "$1"); + _default.AddSingular("(tive)s$", "$1"); + _default.AddSingular("([lr]|hoo|lea|loa|thie)ves$", "$1f"); + _default.AddSingular("(^zomb)?([^aeiouy]|qu)ies$", "$2y"); + _default.AddSingular("(s)eries$", "$1eries"); + _default.AddSingular("(m)ovies$", "$1ovie"); + _default.AddSingular("(x|ch|ss|sh)es$", "$1"); + _default.AddSingular("(^[m|l])ice$", "$1ouse"); + _default.AddSingular("(o)es$", "$1"); + _default.AddSingular("(shoe)s$", "$1"); + _default.AddSingular("(cris|ax|test)es$", "$1is"); + _default.AddSingular("(octop|vir|alumn|fung|cact|foc|hippopotam|radi|stimul|syllab|nucle)i$", "$1us"); + _default.AddSingular("(alias|bias|iris|status|campus|apparatus|virus|walrus|trellis)es$", "$1"); + _default.AddSingular("^(ox)en", "$1"); + _default.AddSingular("(matr|d)ices$", "$1ix"); + _default.AddSingular("(vert|ind)ices$", "$1ex"); + _default.AddSingular("(quiz)zes$", "$1"); + _default.AddSingular("(buz|blit|walt)zes$", "$1z"); + _default.AddSingular("(alumn|alg|larv|vertebr)ae$", "$1a"); + _default.AddSingular("(criteri|phenomen)a$", "$1on"); + _default.AddSingular("([b|r|c]ook|room|smooth)ies$", "$1ie"); + + _default.AddIrregular("person", "people"); + _default.AddIrregular("man", "men"); + _default.AddIrregular("human", "humans"); + _default.AddIrregular("child", "children"); + _default.AddIrregular("sex", "sexes"); + _default.AddIrregular("glove", "gloves"); + _default.AddIrregular("move", "moves"); + _default.AddIrregular("goose", "geese"); + _default.AddIrregular("wave", "waves"); + _default.AddIrregular("die", "dice"); + _default.AddIrregular("foot", "feet"); + _default.AddIrregular("tooth", "teeth"); + _default.AddIrregular("curriculum", "curricula"); + _default.AddIrregular("database", "databases"); + _default.AddIrregular("zombie", "zombies"); + _default.AddIrregular("personnel", "personnel"); + //Fix #789 + _default.AddIrregular("cache", "caches"); + + //Fix 975 + _default.AddIrregular("ex", "exes", matchEnding: false); + + _default.AddIrregular("is", "are", matchEnding: false); + _default.AddIrregular("that", "those", matchEnding: false); + _default.AddIrregular("this", "these", matchEnding: false); + _default.AddIrregular("bus", "buses", matchEnding: false); + _default.AddIrregular("staff", "staff", matchEnding: false); + _default.AddIrregular("training", "training", matchEnding: false); + + _default.AddUncountable("equipment"); + _default.AddUncountable("information"); + _default.AddUncountable("corn"); + _default.AddUncountable("milk"); + _default.AddUncountable("rice"); + _default.AddUncountable("money"); + _default.AddUncountable("species"); + _default.AddUncountable("series"); + _default.AddUncountable("fish"); + _default.AddUncountable("sheep"); + _default.AddUncountable("deer"); + _default.AddUncountable("aircraft"); + _default.AddUncountable("oz"); + _default.AddUncountable("tsp"); + _default.AddUncountable("tbsp"); + _default.AddUncountable("ml"); + _default.AddUncountable("l"); + _default.AddUncountable("water"); + _default.AddUncountable("waters"); + _default.AddUncountable("semen"); + _default.AddUncountable("sperm"); + _default.AddUncountable("bison"); + _default.AddUncountable("grass"); + _default.AddUncountable("hair"); + _default.AddUncountable("mud"); + _default.AddUncountable("elk"); + _default.AddUncountable("luggage"); + _default.AddUncountable("moose"); + _default.AddUncountable("offspring"); + _default.AddUncountable("salmon"); + _default.AddUncountable("shrimp"); + _default.AddUncountable("someone"); + _default.AddUncountable("swine"); + _default.AddUncountable("trout"); + _default.AddUncountable("tuna"); + _default.AddUncountable("corps"); + _default.AddUncountable("scissors"); + _default.AddUncountable("means"); + _default.AddUncountable("mail"); + + return _default; + } + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler/Humanizer/Vocabulary.cs b/ICSharpCode.Decompiler/Humanizer/Vocabulary.cs new file mode 100644 index 000000000..f90125416 --- /dev/null +++ b/ICSharpCode.Decompiler/Humanizer/Vocabulary.cs @@ -0,0 +1,178 @@ +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace Humanizer.Inflections +{ + /// + /// A container for exceptions to simple pluralization/singularization rules. + /// Vocabularies.Default contains an extensive list of rules for US English. + /// At this time, multiple vocabularies and removing existing rules are not supported. + /// + internal class Vocabulary + { + internal Vocabulary() + { + } + + private readonly List _plurals = new List(); + private readonly List _singulars = new List(); + private readonly List _uncountables = new List(); + + /// + /// Adds a word to the vocabulary which cannot easily be pluralized/singularized by RegEx, e.g. "person" and "people". + /// + /// The singular form of the irregular word, e.g. "person". + /// The plural form of the irregular word, e.g. "people". + /// True to match these words on their own as well as at the end of longer words. False, otherwise. + public void AddIrregular(string singular, string plural, bool matchEnding = true) + { + if (matchEnding) + { + AddPlural("(" + singular[0] + ")" + singular.Substring(1) + "$", "$1" + plural.Substring(1)); + AddSingular("(" + plural[0] + ")" + plural.Substring(1) + "$", "$1" + singular.Substring(1)); + } + else + { + AddPlural($"^{singular}$", plural); + AddSingular($"^{plural}$", singular); + } + } + + /// + /// Adds an uncountable word to the vocabulary, e.g. "fish". Will be ignored when plurality is changed. + /// + /// Word to be added to the list of uncountables. + public void AddUncountable(string word) + { + _uncountables.Add(word.ToLower()); + } + + /// + /// Adds a rule to the vocabulary that does not follow trivial rules for pluralization, e.g. "bus" -> "buses" + /// + /// RegEx to be matched, case insensitive, e.g. "(bus)es$" + /// RegEx replacement e.g. "$1" + public void AddPlural(string rule, string replacement) + { + _plurals.Add(new Rule(rule, replacement)); + } + + /// + /// Adds a rule to the vocabulary that does not follow trivial rules for singularization, e.g. "vertices/indices -> "vertex/index" + /// + /// RegEx to be matched, case insensitive, e.g. ""(vert|ind)ices$"" + /// RegEx replacement e.g. "$1ex" + public void AddSingular(string rule, string replacement) + { + _singulars.Add(new Rule(rule, replacement)); + } + + /// + /// Pluralizes the provided input considering irregular words + /// + /// Word to be pluralized + /// Normally you call Pluralize on singular words; but if you're unsure call it with false + /// + public string Pluralize(string word, bool inputIsKnownToBeSingular = true) + { + var result = ApplyRules(_plurals, word, false); + + if (inputIsKnownToBeSingular) + { + return result ?? word; + } + + var asSingular = ApplyRules(_singulars, word, false); + var asSingularAsPlural = ApplyRules(_plurals, asSingular, false); + if (asSingular != null && asSingular != word && asSingular + "s" != word && asSingularAsPlural == word && result != word) + { + return word; + } + + return result; + } + + /// + /// Singularizes the provided input considering irregular words + /// + /// Word to be singularized + /// Normally you call Singularize on plural words; but if you're unsure call it with false + /// Skip singularizing single words that have an 's' on the end + /// + public string Singularize(string word, bool inputIsKnownToBePlural = true, bool skipSimpleWords = false) + { + var result = ApplyRules(_singulars, word, skipSimpleWords); + + if (inputIsKnownToBePlural) + { + return result ?? word; + } + + // the Plurality is unknown so we should check all possibilities + var asPlural = ApplyRules(_plurals, word, false); + var asPluralAsSingular = ApplyRules(_singulars, asPlural, false); + if (asPlural != word && word + "s" != asPlural && asPluralAsSingular == word && result != word) + { + return word; + } + + return result ?? word; + } + + private string ApplyRules(IList rules, string word, bool skipFirstRule) + { + if (word == null) + { + return null; + } + + if (word.Length < 1) + { + return word; + } + + if (IsUncountable(word)) + { + return word; + } + + var result = word; + var end = skipFirstRule ? 1 : 0; + for (var i = rules.Count - 1; i >= end; i--) + { + if ((result = rules[i].Apply(word)) != null) + { + break; + } + } + return result; + } + + private bool IsUncountable(string word) + { + return _uncountables.Contains(word.ToLower()); + } + + private class Rule + { + private readonly Regex _regex; + private readonly string _replacement; + + public Rule(string pattern, string replacement) + { + _regex = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled); + _replacement = replacement; + } + + public string Apply(string word) + { + if (!_regex.IsMatch(word)) + { + return null; + } + + return _regex.Replace(word, _replacement); + } + } + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 6d00a9407..9d334c9da 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -49,7 +49,6 @@ - @@ -75,6 +74,8 @@ + + @@ -633,6 +634,7 @@ CSharp\Syntax\PatternMatching\Pattern Matching.html + diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template index 38668d5c3..fb17b9d97 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template @@ -1,31 +1,30 @@  - - ICSharpCode.Decompiler - $INSERTVERSION$$INSERTVERSIONNAMEPOSTFIX$$INSERTBUILDCONFIG$ - ILSpy Decompiler Engine - ILSpy Contributors - Daniel Grunwald, SharpDevelop - MIT - https://github.com/icsharpcode/ILSpy/ - images\DecompilerNuGetPackageIcon.png - false - ICSharpCode.Decompiler is the decompiler engine used in ILSpy. - - Copyright 2011-$INSERTYEAR$ AlphaSierraPapa - C# Decompiler ILSpy - - - - - - - - - - - - - - + + ICSharpCode.Decompiler + $INSERTVERSION$$INSERTVERSIONNAMEPOSTFIX$$INSERTBUILDCONFIG$ + ILSpy Decompiler Engine + ILSpy Contributors + Daniel Grunwald, SharpDevelop + MIT + https://github.com/icsharpcode/ILSpy/ + images\DecompilerNuGetPackageIcon.png + false + ICSharpCode.Decompiler is the decompiler engine used in ILSpy. + + Copyright 2011-$INSERTYEAR$ AlphaSierraPapa + C# Decompiler ILSpy + + + + + + + + + + + + + \ No newline at end of file diff --git a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs index 2d36fcced..d86271eac 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs @@ -22,7 +22,7 @@ using System.Linq; using System.Reflection; using System.Reflection.Metadata; -using Humanizer; +using Humanizer.Inflections; using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.TypeSystem; @@ -785,7 +785,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms private static bool IsPlural(string baseName, ref string proposedName) { - var newName = baseName.Singularize(inputIsKnownToBePlural: false); + var newName = Vocabularies.Default.Singularize(baseName, inputIsKnownToBePlural: false); if (newName == baseName) return false; proposedName = newName; diff --git a/ILSpy/TreeNodes/BaseTypesTreeNode.cs b/ILSpy/TreeNodes/BaseTypesTreeNode.cs index 7d52a33bf..f90147935 100644 --- a/ILSpy/TreeNodes/BaseTypesTreeNode.cs +++ b/ILSpy/TreeNodes/BaseTypesTreeNode.cs @@ -21,8 +21,6 @@ using System.Linq; using System.Reflection.Metadata; using System.Windows.Threading; -using Humanizer.Localisation; - using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; diff --git a/packages.props b/packages.props index 62331b0ae..9bc1121cc 100644 --- a/packages.props +++ b/packages.props @@ -1,20 +1,20 @@  - - - 2.2.0 - 5.0.0 - 5.0.0 - 5.0.0 - 5.0.0 - 3.8.0 - 0.10.3 - 6.1.0-preview1 - 3.12.0 - 3.13.0 - 4.14.1 - 2017.7.26.1241 - + + + 5.0.0 + 5.0.0 + 5.0.0 + 5.0.0 + 3.8.0 + 0.10.3 + 6.1.0-preview1 + 3.12.0 + 3.13.0 + 4.14.1 + 2017.7.26.1241 +