aarondandy avatar aarondandy committed 672a948

some performance and organization tweaks

Comments (0)

Files changed (2)

src/YouCantSpell.ReSharper.Shared/CStyleSpellCheckDaemonStageProcessBase.cs

 using System.Linq;
 using System.Text;
 using System.Text.RegularExpressions;
+using System.Threading;
 using JetBrains.Application;
 using JetBrains.Application.Settings.Store.Implementation;
 using JetBrains.DocumentModel;
 
 		public abstract void Execute(Action<DaemonStageResult> commiter);
 
+		private struct ParseKey : IEquatable<ParseKey>
+		{
+			public readonly NamingRule NamingRule;
+			public readonly string Text;
+
+			public ParseKey(string text, NamingRule namingRule) {
+				Text = text;
+				NamingRule = namingRule;
+			}
+
+			public override bool Equals(object obj) {
+				return obj is ParseKey && Equals((ParseKey) obj);
+			}
+
+			public bool Equals(ParseKey other) {
+				return String.Equals(Text, other.Text) && NamingRule.Equals(other.NamingRule);
+			}
+
+			public override int GetHashCode() {
+				return Text.GetHashCode() ^ -NamingRule.GetHashCode();
+			}
+		}
+
+		private readonly Dictionary<ParseKey, Name> _nameParseCache = new Dictionary<ParseKey,Name>();
+		private readonly ReaderWriterLockSlim _nameParseCacheLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
+
+		private Name ParseName(string name, NamingRule namingRule) {
+			Name result;
+			var key = new ParseKey(name, namingRule);
+			_nameParseCacheLock.EnterUpgradeableReadLock();
+			try {
+				if (!_nameParseCache.TryGetValue(key, out result)) {
+					_nameParseCacheLock.EnterWriteLock();
+					try {
+						result = NamingManager.Parsing.Parse(name, namingRule, NamingPolicyProvider);
+						_nameParseCache.Add(key, result);
+					}
+					finally {
+						_nameParseCacheLock.ExitWriteLock();
+					}
+				}
+			}
+			finally {
+				_nameParseCacheLock.ExitUpgradeableReadLock();
+			}
+			return result;
+		}
+
 		protected Name ParseName(string name) {
-			return NamingManager.Parsing.Parse(name, NamingRule.Default, NamingPolicyProvider);
+			return ParseName(name, NamingRule.Default);
 		}
 
 		protected Name ParseName(IIdentifier identifier) {
 			var declared = ReSharperUtil.GetDeclaredElement(identifier);
-			return NamingManager.Parsing.Parse(
-				identifier.Name,
-				null == declared ? NamingRule.Default : NamingPolicyProvider.GetPolicy(declared).NamingRule,
-				NamingPolicyProvider
-			);
+			return ParseName(identifier.Name,null == declared ? NamingRule.Default : NamingPolicyProvider.GetPolicy(declared).NamingRule);
 		}
 
 		protected IEnumerable<IDeclaredType> GetSuperTypes(ITypeDeclaration typeElement) {
 			return FindFreeTextHighlightings(node, coreMatchGroup.Value, textRange, localIdentifierNames, declarationsCache);
 		}
 
-		/// <summary>
-		/// Formats a suggestion to match identifier naming conventions.
-		/// </summary>
-		/// <param name="suggestion">The text suggestion.</param>
-		/// <param name="position">The zero based word index within the identifier that the suggestion is for.</param>
-		/// <returns>The suggestion formatted in a way so as to respect the configured naming conventions.</returns>
-		public string FormatSuggestion(string suggestion, int position, NamingRule namingRule, int wordCount, TextCaseClassification textClassification) {
-			// Get the word parts of the suggestion.
-			var matches = Utility.StringUtil.LetterParserRegex.Matches(suggestion).Cast<Match>().Select(x => x.Value).ToArray();
-			if(matches.Length == 0)
-				return suggestion;
-
+		private IEnumerable<string> ProposeSuggestions(string[] suggestionWords, NamingRule namingRule, TextCaseClassification textCaseClassification) {
 			// Use resharper to reformat the word parts
-			var newSuggestion = NamingManager.Parsing.RenderNameSafe(
-				NameRoot.FromWords(Emphasis.Unknown, false, matches),
+			var normalSuggestion = NamingManager.Parsing.RenderNameSafe(
+				NameRoot.FromWords(Emphasis.Unknown, false, suggestionWords),
 				namingRule,
 				LanguageType,
-				NamingPolicyProvider);
+				NamingPolicyProvider
+			);
 
-			var literalIsOk = wordCount == 1; // literals (@object) are only OK if there is one word in the identifier
+			if (textCaseClassification == TextCaseClassification.Upper && namingRule.NamingStyleKind != NamingStyleKinds.AA_BB) {
+				normalSuggestion = normalSuggestion.ToUpper();
+				yield return NamingManager.Parsing.RenderNameSafe(
+					NameRoot.FromWords(Emphasis.Unknown, false, suggestionWords),
+					ReSharperUtil.OgreCaps,
+					LanguageType,
+					NamingPolicyProvider
+				);
+			}
 
+			yield return normalSuggestion;
+		}
+
+		private string MassageProposedSuggestion(string newSuggestion, bool literalIsOk, int position, NamingRule namingRule) {
 			// after rendering the suggestion as an identifier name we need to parse it again to remove some stuff
 			// just in case anything funny like a prefix or @ was added we need to get rid of that (unless @ is OK)
 			var reparsedNamed = NamingManager.Parsing.Parse(newSuggestion, NamingRule.Default, NamingPolicyProvider);
-			if(reparsedNamed.NamePrefix.Text.Length > 0 && (!literalIsOk || reparsedNamed.NamePrefix.Text != "@")) {
+			if (reparsedNamed.NamePrefix.Text.Length > 0 && (!literalIsOk || reparsedNamed.NamePrefix.Text != "@")) {
 				// we need to pull the prefix off except for one special situation, with literals, if that is a prefix
 				newSuggestion = newSuggestion.Substring(reparsedNamed.NamePrefix.Text.Length);
 			}
-			if(!literalIsOk && newSuggestion.Length > 1 && newSuggestion.StartsWith("@"))
+			if (!literalIsOk && newSuggestion.Length > 1 && newSuggestion.StartsWith("@"))
 				newSuggestion = newSuggestion.Substring(1); // if the @ prefix is not OK we need to get rid of that
 
-			// Preserve the ugly all upper-case variables by forcing the new identifier part to uppercase if the identifier was uppercase to begin with
-			if(textClassification == TextCaseClassification.Upper && namingRule.NamingStyleKind != NamingStyleKinds.AA_BB)
-				return newSuggestion.ToUpper();
-
-			// Don't know what to do if the suggestion was made to be blank
-			if(String.IsNullOrEmpty(newSuggestion))
-				return newSuggestion;
-
 			// the first word within an identifier often has special casing that differs from the other words: firstSecondThird
-			if(0 != position) {
+			if (!String.IsNullOrEmpty(newSuggestion) && 0 != position) {
 				// Here we may need to correct the first letter if this is not the first word as the naming provider assumes the first word.
 				// The R# methods assume the words I am giving it are the entire identifier name so the first letter may not be correct for
 				// a suggestion derived from the 2nd word part within an identifier, we will need to adjust that.
 					SecondWordFirstLetterClassification(namingRule.NamingStyleKind)
 				);
 			}
+			return newSuggestion;
+		}
 
-			return newSuggestion;
+		public IEnumerable<string> FormatSuggestions(IEnumerable<string> suggestions, int position, int wordCount, NamingRule namingRule, TextCaseClassification textCaseClassification) {
+			var literalIsOk = wordCount == 1; // literals (@object) are only OK if there is one word in the identifier
+
+			foreach(var suggestion in suggestions) {
+				
+				// Get the word parts of the suggestion.
+				var matches = Utility.StringUtil.LetterParserRegex.Matches(suggestion).Cast<Match>().Select(x => x.Value).ToArray();
+				if (matches.Length == 0) {
+					yield return suggestion;
+					continue;
+				}
+
+				var newProposedSuggestions = ProposeSuggestions(matches, namingRule, textCaseClassification);
+
+				if (textCaseClassification != TextCaseClassification.Unknown)
+					newProposedSuggestions = newProposedSuggestions.Select(x => MassageProposedSuggestion(x, literalIsOk, position, namingRule));
+
+				foreach (var newSuggestion in newProposedSuggestions) {
+					yield return newSuggestion;
+				}
+			}
 		}
 
 		protected IEnumerable<HighlightingInfo> FindHighlightings(IIdentifier identifier) {
 						identifier,
 						localValidPartRange,
 						wordPart.Value,
-						word => Shell.Instance.GetComponent<ShellSpellCheckResources>().Primary
-							.GetRecommendations(word)
-							.Select(x => FormatSuggestion(x, wordPosition, namingRule, parsedNameParts.Count, textClassification))
-							.Where(x => word != x)
+						word => FormatSuggestions(
+								Shell.Instance.GetComponent<ShellSpellCheckResources>().Primary.GetRecommendations(word),
+								wordPosition,
+								parsedNameParts.Count,
+								namingRule,
+								textClassification
+							)
+							//.Select(x => FormatSuggestion(x, wordPosition, namingRule, parsedNameParts.Count, textClassification))
+							.Where(x => !String.IsNullOrEmpty(x) && word != x)
 							.Distinct()
 							.Take(MaxSuggestions)
 							.ToArray()

src/YouCantSpell.ReSharper.Shared/ReSharperUtil.cs

 using System.Text.RegularExpressions;
 using JetBrains.ReSharper.Psi;
+using JetBrains.ReSharper.Psi.Naming.Settings;
 using JetBrains.ReSharper.Psi.Tree;
 
 namespace YouCantSpell.ReSharper
 		public static readonly Regex ReSharperLineRegex = new Regex(@"ReSharper\s+(disable|restore)\s+(.*)", RegexOptions.Compiled);
 
 		/// <summary>
+		/// THIS NAMING RULE GIVES US OGRE CAPS
+		/// </summary>
+		public static readonly NamingRule OgreCaps = new NamingRule{NamingStyleKind = NamingStyleKinds.AA_BB};
+
+		/// <summary>
 		/// Extracts the declaration from a tree node.
 		/// </summary>
 		/// <param name="node">The node to get the declaration from.</param>
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.