Commits

aarondandy committed a1027b1

some refactoring and optimization

  • Participants
  • Parent commits 395797a

Comments (0)

Files changed (13)

 ============== ============ ============ ===========
 VS Verstion:   VS 2012 (11) VS 2010 (10) VS 2008 (9)
 ReSharper v6.1 Maybe        Yep          Yep
-ReSharper v7.0 Maybe        Yep          Probably
+ReSharper v7.0 Maybe        Yep          Yep
 ============== ============ ============ ===========
 
 Download

File src/YouCantSpell.ReSharper.Shared/CSharp/CSharpFreeTextSpellingHighlightFinder.cs

-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
 using System.Linq;
 using JetBrains.DocumentModel;
 using JetBrains.ReSharper.Daemon;
 	/// <summary>
 	/// Locates and created spelling error highlightings in free form text nodes such as comments or string literals.
 	/// </summary>
+	[Obsolete]
 	public class CSharpFreeTextSpellingHighlightFinder
 	{
 

File src/YouCantSpell.ReSharper.Shared/CSharp/CSharpSpellCheckDaemonStageProcess.cs

 using System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Text.RegularExpressions;
-using JetBrains.Application;
 using JetBrains.Application.Settings;
 using JetBrains.DocumentModel;
 using JetBrains.ReSharper.Daemon;
-using JetBrains.ReSharper.Daemon.CSharp.Stages;
 using JetBrains.ReSharper.Psi;
 using JetBrains.ReSharper.Psi.CSharp;
 using JetBrains.ReSharper.Psi.CSharp.Tree;
-using JetBrains.ReSharper.Psi.Caches;
-using JetBrains.ReSharper.Psi.Naming;
-using JetBrains.ReSharper.Psi.Naming.Interfaces;
 using JetBrains.ReSharper.Psi.Tree;
-using JetBrains.ReSharper.Psi.Naming.Settings;
-using JetBrains.ReSharper.Psi.Naming.Impl;
 using JetBrains.Util;
 
+
 namespace YouCantSpell.ReSharper.CSharp
 {
 	/// <summary>
 	/// This is the core process responsible for locating spelling mistakes within a C# source file.
 	/// </summary>
-	public class CSharpSpellCheckDaemonStageProcess : CSharpDaemonStageProcessBase
+	public class CSharpSpellCheckDaemonStageProcess : CStyleSpellCheckDaemonStageProcessBase<ICSharpFile>
 	{
 
-		private const int MinWordSize = 2;
-		private static readonly int MaxSuggestions = 10;
+		public CSharpSpellCheckDaemonStageProcess(IDaemonProcess process, IContextBoundSettingsStore settingsStore, ICSharpFile cSharpFile)
+			: base(process, settingsStore, cSharpFile, CSharpLanguage.Instance)
+		{ }
 
-		/// <summary>
-		/// Determines if a node is and acceptable identifier for spell checking.
-		/// </summary>
-		/// <param name="node">The node to test.</param>
-		/// <returns>True when the node may be spell checked.</returns>
-		private static bool IsValidSpellCheckIdentifierNode(ITreeNode node) {
-			if(null == node)
-				return false;
-
-			if(node is IConstructorDeclaration || node is IDestructorDeclaration)
-				return false; // constructor/finalizer naming is based on the class name so it would be best to flag the error there
-
-			if (node is ITypeMemberDeclaration) {
-				// if the member inherits from above, the spelling error would be above as we have no choice but to use that name
-				var declaredElement = (node as IDeclaration).DeclaredElement as IOverridableMember;
-				if (null != declaredElement) {
-					var supers = declaredElement.GetImmediateSuperMembers();
-					return null != supers && supers.IsEmpty();
-				}
-			}
-
-			return node is ITypeDeclaration
-				|| node is ITypeParameterDeclaration
-				|| node is ILocalVariableDeclaration
-				|| node is ITypeMemberDeclaration
-				|| node is IVariableDeclaration
-				|| node is INamespaceDeclaration;
+		protected override SpellingErrorHighlightingBase CreateErrorHighlighting(ITreeNode node, DocumentRange errorRange, string wordInError, Func<string, string[]> getSuggestions) {
+			return new CSharpSpellingErrorHighlighting(node, errorRange, wordInError, getSuggestions);
 		}
 
-		private readonly ShellSpellCheckResources _spellCheckResources;
-		private readonly NamingManager _namingManager;
-		private readonly INamingPolicyProvider _namingPolicyProvider;
-		private readonly IPsiSourceFile _sourceFile;
-		private readonly ICSharpFile _cSharpFile;
-
-		public CSharpSpellCheckDaemonStageProcess(IDaemonProcess process, IContextBoundSettingsStore settingsStore, ICSharpFile cSharpFile) :
-#if RSHARP6
-			base(process)
-#else
-			base(process, cSharpFile)
-#endif
-		{
-			if (null == cSharpFile)
-				throw new ArgumentNullException("cSharpFile");
-			if (null == CSharpLanguage.Instance)
-				throw new InvalidOperationException("C# language instance not loaded.");
-
-			_cSharpFile = cSharpFile;
-
-			_spellCheckResources = Shell.Instance.GetComponent<ShellSpellCheckResources>();
-			_sourceFile = process.SourceFile;
-			var psiServices = _sourceFile.PsiModule.GetPsiServices();
-			_namingManager = psiServices.Naming;
-			_namingPolicyProvider = _namingManager.Policy.GetPolicyProvider(CSharpLanguage.Instance, _sourceFile, settingsStore);
+		protected override bool IsIgnored(string text) {
+			return base.IsIgnored(text) || CSharpUtil.IsKeyword(text);
 		}
 
-		public ShellSpellCheckResources SpellCheckResources {
-			get { return _spellCheckResources; }
-		}
-
-		public ISpellChecker PrimarySpellChecker {
-			get { return SpellCheckResources.Primary; }
-		}
-
-		private Name ParseName(string name) {
-			return _namingManager.Parsing.Parse(name, NamingRule.Default, _namingPolicyProvider);
-		}
-
-		private Name ParseName(IIdentifier identifier) {
-			var declared = ReSharperUtil.GetDeclaredElement(identifier);
-			return _namingManager.Parsing.Parse(
-				identifier.Name,
-				null == declared ? NamingRule.Default : _namingPolicyProvider.GetPolicy(declared).NamingRule,
-				_namingPolicyProvider
-			);
-		}
-
-		private IEnumerable<IDeclaredType> GetSuperTypes(ITypeDeclaration typeElement) {
-			if (null == typeElement)
-				return Enumerable.Empty<IDeclaredType>();
-			return typeElement.SuperTypes;
-		}
-
-		private IEnumerable<TextSubString> ParseNameParts(string nameText)
-		{
-			var parsedName = ParseName(nameText);
-			if(parsedName.InnerElements == null || parsedName.InnerElements.Count == 0)
-				yield break;
-
-			int identifierNamePartIndex = null != parsedName.NamePrefix
-				? parsedName.NamePrefix.Text.Length
-				: 0;
-
-			foreach(var identifierNamePart in parsedName.InnerElements)
-			{
-				if (!identifierNamePart.IsSeparator) {
-					yield return new TextSubString(nameText, identifierNamePartIndex, identifierNamePart.Text.Length);
-				}
-				identifierNamePartIndex += identifierNamePart.Text.Length;
-			}
-		}
-
-		private IEnumerable<IType> GetRelatedTypes(IDeclaration declaration) {
-			if (declaration is ITypeDeclaration)
+		protected override IEnumerable<IType> GetRelatedTypes(IDeclaration declaration) {
+			if(declaration is ITypeDeclaration)
 				return GetSuperTypes(declaration as ITypeDeclaration).Cast<IType>();
-			if (declaration is IFieldDeclaration)
-				return new[] {(declaration as IFieldDeclaration).Type};
-			if (declaration is IParameterDeclaration)
-				return new[] {(declaration as IParameterDeclaration).Type};
-			if (declaration is IPropertyDeclaration)
-				return new[] {(declaration as IPropertyDeclaration).Type};
-			if (declaration is ILocalVariableDeclaration)
-				return new[] {(declaration as ILocalVariableDeclaration).Type};
-			if (declaration is IMethodDeclaration) {
+			if(declaration is IFieldDeclaration)
+				return new[] { (declaration as IFieldDeclaration).Type };
+			if(declaration is IParameterDeclaration)
+				return new[] { (declaration as IParameterDeclaration).Type };
+			if(declaration is IPropertyDeclaration)
+				return new[] { (declaration as IPropertyDeclaration).Type };
+			if(declaration is ILocalVariableDeclaration)
+				return new[] { (declaration as ILocalVariableDeclaration).Type };
+			if(declaration is IMethodDeclaration) {
 				var methodDeclaration = declaration as IMethodDeclaration;
 				return new List<IType>(methodDeclaration.ParameterDeclarations.Select(x => x.Type)) {
 					methodDeclaration.Type
 			return Enumerable.Empty<IType>();
 		}
 
-		private IEnumerable<HighlightingInfo> FindHighlightings(IIdentifier identifier) {
-
-			var validRange = FindTrueDocumentRange(identifier.GetTreeTextRange());
-			if (!validRange.HasValue)
-				yield break;
-			
-			var validPartRange = validRange.Value;
-			var declaration = ReSharperUtil.GetDeclaration(identifier);
-			if(null == declaration)
-				yield break;
-
-			var declared = declaration.DeclaredElement;
-			if (null == declared)
-				yield break;
-
-			var localIgnoredWords = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
-			var relatedTypeNames = GetRelatedTypes(declaration)
-				.Select(x => x.GetPresentableName(CSharpLanguage.Instance))
-				.ToList();
-
-			localIgnoredWords.AddRange(relatedTypeNames);
-			localIgnoredWords.AddRange(relatedTypeNames.SelectMany(ParseNameParts).Select(x => x.SubText));
-
-			var parsedNameParts = ParseNameParts(identifier.Name).ToList();
-			var formatter = new CSharpSpellingSuggestionFormatter(
-				identifier,
-				_namingPolicyProvider.GetPolicy(declared),
-				_namingManager,
-				_sourceFile.PrimaryPsiLanguage,
-				_namingPolicyProvider,
-				parsedNameParts.Count
-			);
-
-			for (int i = 0; i < parsedNameParts.Count; i++)
-			{
-				var namePart = parsedNameParts[i];
-				var wordPart = Utility.StringUtil.LetterParserRegex
-					.Matches(namePart.SubText)
-					.Cast<Match>()
-					.SingleOrDefault(x => x.Success);
-				if(null == wordPart || wordPart.Length <= MinWordSize)
-					continue;
-
-				var wordPartText = wordPart.Value;
-
-				if(
-					String.IsNullOrEmpty(wordPartText)
-					|| localIgnoredWords.Contains(wordPartText)
-					|| CSharpUtil.IsKeyword(wordPartText)
-					|| SpellCheckResources.IsIgnoredInsensitive(wordPartText)
-					|| PrimarySpellChecker.Check(wordPartText)
-				) continue;
-
-				/*var suggestions = PrimarySpellChecker
-					.GetRecommendations(wordPartText)
-					.Select(x => formatter.FormatSuggestion(x, i))
-					.Distinct()
-					.Take(MaxSuggestions)
-					.ToArray();
-
-				if (suggestions.Contains(wordPartText))
-					continue; // its probably OK*/
-
-				var identifierLeftTrim = namePart.Offset + wordPart.Index;
-				var localValidPartRange = (identifierLeftTrim > 0)
-					? validPartRange.TrimLeft(identifierLeftTrim)
-					: validPartRange;
-
-				localValidPartRange = localValidPartRange.SetEndTo(localValidPartRange.TextRange.StartOffset + wordPart.Value.Length);
-
-				int wordPosition = i;
-				yield return new HighlightingInfo(
-					localValidPartRange,
-					new CSharpSpellingErrorHighlighting(
-						identifier,
-						localValidPartRange,
-						wordPart.Value,
-						word => PrimarySpellChecker
-							.GetRecommendations(word)
-							.Select(x => formatter.FormatSuggestion(x, wordPosition))
-							.Where(x => word != x)
-							.Distinct()
-							.Take(MaxSuggestions)
-							.ToArray()
-					)
-				);
-			}
-
-		}
-
-		private IEnumerable<HighlightingInfo> FindHighlightings(IComment comment, HashSet<string> ignoredIdentifiers, IDeclarationsCache declarationsCache)
-		{
-
-			var fullText = comment.CommentText;
-			if (String.IsNullOrEmpty(fullText) || ReSharperUtil.ReSharperLineRegex.IsMatch(fullText))
-				return Enumerable.Empty<HighlightingInfo>(); // ignore resharper disable/restore lines
-
-			var validRange = FindTrueDocumentRange(comment.GetCommentRange());
-			if (!validRange.HasValue)
-				return Enumerable.Empty<HighlightingInfo>();
-			var documentRange = validRange.Value;
-
-			return new CSharpFreeTextSpellingHighlightFinder(SpellCheckResources, Document, ignoredIdentifiers, declarationsCache)
-				.FindXmlTextHighlightings(comment, fullText, documentRange);
-		}
-
-		private IEnumerable<HighlightingInfo> FindStringHighlightings(IExpression node, HashSet<string> localIdentifierNames, IDeclarationsCache declarationsCache) {
-			var constantValue = node.ConstantValue;
-			if (!constantValue.IsString())
-				return Enumerable.Empty<HighlightingInfo>();
-
-			var validRange = FindTrueDocumentRange(node.GetTreeTextRange());
-			if (!validRange.HasValue)
-				return Enumerable.Empty<HighlightingInfo>();
-			var textRange = validRange.Value;
-
-			var coreMatch = CSharpUtil.StringLiteralContentParser.Match(node.GetText());
-			if(!coreMatch.Success)
-				return Enumerable.Empty<HighlightingInfo>();
-
-			var coreMatchGroup = coreMatch.Groups[1];
-			if (coreMatchGroup.Index > 0)
-				textRange = textRange.SetStartTo(textRange.TextRange.StartOffset + coreMatchGroup.Index);
-
-			return new CSharpFreeTextSpellingHighlightFinder(SpellCheckResources, Document, localIdentifierNames, declarationsCache)
-				.FindXmlTextHighlightings(node, coreMatchGroup.Value, textRange);
-		}
-
-		public string RemovePrefixAndSuffix(IIdentifier identifier) {
-			var name = ParseName(identifier);
-			var nameParts = name.InnerElements.ToList();
-			if (nameParts.Count > 0 && nameParts[0].IsSeparator)
-				nameParts.RemoveAt(0);
-			if (nameParts.Count > 0 && nameParts[nameParts.Count - 1].IsSeparator)
-				nameParts.RemoveAt(nameParts.Count - 1);
-			return String.Concat(nameParts.Select(x => x.Text));
-		}
-
-		private DocumentRange? FindTrueDocumentRange(TreeTextRange range) {
-			var validPartRanges = File.GetIntersectingRanges(range)
-					.Where(x => x.Document == Document)
-					.ToList();
-			if (validPartRanges.Count != 1)
-				return null;
-			return validPartRanges[0];
-		}
-
 		public override void Execute(Action<DaemonStageResult> committer) {
 			var localIdentifierNames = new HashSet<string>(StringComparer.CurrentCultureIgnoreCase);
 			var highlightings = new List<HighlightingInfo>();
 
-			_cSharpFile.ProcessChildren<IIdentifier>(
+			CodeFile.ProcessChildren<IIdentifier>(
 				node => {
 					localIdentifierNames.Add(node.Name);
 					localIdentifierNames.Add(RemovePrefixAndSuffix(node));
-					if(IsValidSpellCheckIdentifierNode(node.Parent)) {
+					if(IsValidIdentifierForSpellCheck(node.Parent)) {
 						highlightings.AddRange(FindHighlightings(node));
 					}
 				}
 			);
 
-			var declarationsCache = _cSharpFile.GetPsiServices().CacheManager.GetDeclarationsCache(_cSharpFile.GetPsiModule(), true, true);
+			var declarationsCache = CodeFile.GetPsiServices().CacheManager.GetDeclarationsCache(CodeFile.GetPsiModule(), true, true);
 
-			_cSharpFile.ProcessChildren<ITreeNode>(
+			CodeFile.ProcessChildren<ITreeNode>(
 				node => {
 					if(node is ICSharpLiteralExpression && (node as ICSharpLiteralExpression).ConstantValue.IsString()) {
 						highlightings.AddRange(FindStringHighlightings(node as IExpression, localIdentifierNames, declarationsCache));
 			committer(new DaemonStageResult(highlightings));
 		}
 
+
+		public override bool IsValidIdentifierForSpellCheck(ITreeNode node) {
+			if(null == node)
+				return false;
+
+			if(node is IConstructorDeclaration || node is IDestructorDeclaration)
+				return false; // constructor/finalizer naming is based on the class name so it would be best to flag the error there
+
+			if(node is ITypeMemberDeclaration) {
+				// if the member inherits from above, the spelling error would be above as we have no choice but to use that name
+				var declaredElement = (node as IDeclaration).DeclaredElement as IOverridableMember;
+				if(null != declaredElement) {
+					var supers = declaredElement.GetImmediateSuperMembers();
+					return null != supers && supers.IsEmpty();
+				}
+			}
+
+			return node is ITypeDeclaration
+				|| node is ITypeParameterDeclaration
+				|| node is ILocalVariableDeclaration
+				|| node is ITypeMemberDeclaration
+				|| node is IVariableDeclaration
+				|| node is INamespaceDeclaration;
+		}
 	}
 }

File src/YouCantSpell.ReSharper.Shared/CSharp/CSharpSpellingSuggestionFormatter.cs

-using System;
-using System.Linq;
-using System.Text.RegularExpressions;
-using JetBrains.ReSharper.Psi.Naming;
-using JetBrains.ReSharper.Psi.Naming.Impl;
-using JetBrains.ReSharper.Psi.Naming.Settings;
-using JetBrains.ReSharper.Psi.Tree;
-using JetBrains.ReSharper.Psi;
-using JetBrains.ReSharper.Psi.Naming.Interfaces;
-
-namespace YouCantSpell.ReSharper.CSharp
-{
-	/// <summary>
-	/// This class is used to generate valid spelling suggestions for identifiers.
-	/// </summary>
-	public class CSharpSpellingSuggestionFormatter
-	{
-		// TODO: why does this regex have an '_' and why is it not named 'Suggestion Word Part Parser' or something similar?
-		protected static readonly Regex IdentifierRecombineRegex = new Regex(@"[^\s_-]+", RegexOptions.Compiled);
-
-		/// <summary>
-		/// Force the casing of the first character in a string to the desired case.
-		/// </summary>
-		/// <param name="text">The text to adjust.</param>
-		/// <param name="textCase">The case to adjust the first letter to.</param>
-		/// <returns>The adjusted string.</returns>
-		private static string ForceFirstCharCase(string text, TextCaseClassification textCase) {
-			if(String.IsNullOrEmpty(text))
-				return text;
-
-			char firstLetter;
-			switch(textCase) {
-				case TextCaseClassification.Upper: { firstLetter = Char.ToUpper(text[0]); break; }
-				case TextCaseClassification.Lower: { firstLetter = Char.ToLower(text[0]); break; }
-				default: return text;
-			}
-
-			if(firstLetter == text[0])
-				return text; // it was already correct, so just return it
-
-			return String.Concat(firstLetter, text.Length > 1 ? text.Substring(1) : String.Empty);
-		}
-
-		/// <summary>
-		/// Determines the case of the first letter of the second word within an identifier of the given name style.
-		/// </summary>
-		/// <param name="kind">The name style.</param>
-		/// <returns>The case of the first letter of the second word within an identifier.</returns>
-		private static TextCaseClassification SecondWordFirstLetterClassification(NamingStyleKinds kind) {
-			switch(kind) {
-			case NamingStyleKinds.AaBb:
-			case NamingStyleKinds.AA_BB:
-			case NamingStyleKinds.aaBb:
-				return TextCaseClassification.Upper;
-			case NamingStyleKinds.Aa_bb:
-			case NamingStyleKinds.aa_bb:
-				return TextCaseClassification.Lower;
-			default:
-				return TextCaseClassification.Unknown;
-			}
-		}
-
-		private readonly NamingPolicy _namingPolicy;
-		private readonly NamingManager _namingManager;
-		private readonly PsiLanguageType _psiLanguageType;
-		private readonly INamingPolicyProvider _namingPolicyProvider;
-		private readonly int _wordCount;
-		private readonly TextCaseClassification _textClassification;
-
-		public CSharpSpellingSuggestionFormatter(
-			IIdentifier identifier,
-			NamingPolicy namingPolicy,
-			NamingManager namingManager,
-			PsiLanguageType psiLanguageType,
-			INamingPolicyProvider namingPolicyProvider,
-			int wordCount
-		) {
-			if(null == identifier)
-				throw new ArgumentNullException("identifier");
-
-			_namingPolicy = namingPolicy;
-			_namingManager = namingManager;
-			_psiLanguageType = psiLanguageType;
-			_namingPolicyProvider = namingPolicyProvider;
-			_wordCount = wordCount;
-			_textClassification = Utility.StringUtil.ClassifyCharCase(identifier.Name);
-		}
-
-		/// <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)
-		{
-			// Get the word parts of the suggestion.
-			var matches = IdentifierRecombineRegex.Matches(suggestion).Cast<Match>().Select(x => x.Value).ToArray();
-			if(matches.Length == 0)
-				return suggestion;
-
-			// Use resharper to reformat the word parts
-			var newSuggestion = _namingManager.Parsing.RenderNameSafe(
-				NameRoot.FromWords(Emphasis.Unknown, false, matches),
-				_namingPolicy.NamingRule,
-				_psiLanguageType,
-				_namingPolicyProvider);
-
-			var literalIsOk = _wordCount == 1; // literals (@object) are only OK if there is one word in the identifier
-
-			// 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 != "@")) {
-				// 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("@"))
-				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 && _namingPolicy.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) {
-				// 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.
-				newSuggestion = ForceFirstCharCase(
-					newSuggestion,
-					SecondWordFirstLetterClassification(_namingPolicy.NamingRule.NamingStyleKind)
-				);
-			}
-
-			return newSuggestion;
-		}
-
-	}
-}

File src/YouCantSpell.ReSharper.Shared/CStyleSpellCheckDaemonStageProcessBase.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using JetBrains.Application;
+using JetBrains.Application.Settings.Store.Implementation;
+using JetBrains.DocumentModel;
+using JetBrains.ReSharper.Daemon;
+using JetBrains.ReSharper.Psi;
+using JetBrains.ReSharper.Psi.Caches;
+using JetBrains.ReSharper.Psi.Naming;
+using JetBrains.ReSharper.Psi.Naming.Interfaces;
+using JetBrains.ReSharper.Psi.Naming.Settings;
+using JetBrains.ReSharper.Psi.Tree;
+using JetBrains.Application.Settings;
+using JetBrains.ReSharper.Psi.Naming.Impl;
+using JetBrains.Util;
+using YouCantSpell.CStyle;
+using YouCantSpell.ReSharper.CSharp;
+using YouCantSpell.Utility;
+
+namespace YouCantSpell.ReSharper
+{
+	public abstract class CStyleSpellCheckDaemonStageProcessBase<TCodeFile>
+		: IDaemonStageProcess
+		where TCodeFile : class, IFile
+	{
+
+		private const int MinWordSize = 2;
+		private const int MaxSuggestions = 10;
+
+		/// <summary>
+		/// Force the casing of the first character in a string to the desired case.
+		/// </summary>
+		/// <param name="text">The text to adjust.</param>
+		/// <param name="textCase">The case to adjust the first letter to.</param>
+		/// <returns>The adjusted string.</returns>
+		protected static string ForceFirstCharCase(string text, TextCaseClassification textCase) {
+			if(String.IsNullOrEmpty(text))
+				return text;
+
+			char firstLetter;
+			switch(textCase) {
+			case TextCaseClassification.Upper: { firstLetter = Char.ToUpper(text[0]); break; }
+			case TextCaseClassification.Lower: { firstLetter = Char.ToLower(text[0]); break; }
+			default: return text;
+			}
+
+			if(firstLetter == text[0])
+				return text; // it was already correct, so just return it
+
+			return String.Concat(firstLetter, text.Length > 1 ? text.Substring(1) : String.Empty);
+
+		}
+
+		/// <summary>
+		/// Determines the case of the first letter of the second word within an identifier of the given name style.
+		/// </summary>
+		/// <param name="kind">The name style.</param>
+		/// <returns>The case of the first letter of the second word within an identifier.</returns>
+		protected static TextCaseClassification SecondWordFirstLetterClassification(NamingStyleKinds kind) {
+			switch(kind) {
+			case NamingStyleKinds.AaBb:
+			case NamingStyleKinds.AA_BB:
+			case NamingStyleKinds.aaBb:
+				return TextCaseClassification.Upper;
+			case NamingStyleKinds.Aa_bb:
+			case NamingStyleKinds.aa_bb:
+				return TextCaseClassification.Lower;
+			default:
+				return TextCaseClassification.Unknown;
+			}
+		}
+
+		protected CStyleSpellCheckDaemonStageProcessBase(
+			IDaemonProcess process,
+			IContextBoundSettingsStore settingsStore,
+			TCodeFile codeFile,
+			PsiLanguageType languageType
+		) {
+			if(null == process) throw new ArgumentNullException("process");
+			if(null == settingsStore) throw new ArgumentNullException("settingsStore");
+			if(null == codeFile) throw new ArgumentNullException("codeFile");
+			if(null == languageType) throw new ArgumentNullException("languageType");
+
+			DaemonProcess = process;
+			SettingsStore = settingsStore;
+			CodeFile = codeFile;
+			LanguageType = languageType;
+			PsiSourceFile = process.SourceFile;
+			SpellCheckResources = Shell.Instance.GetComponent<ShellSpellCheckResources>();
+			Document = process.Document;
+			var psiServices = process.SourceFile.PsiModule.GetPsiServices();
+			NamingManager = psiServices.Naming;
+			NamingPolicyProvider = psiServices.Naming.Policy.GetPolicyProvider(languageType, process.SourceFile, settingsStore);
+		}
+
+		public IDaemonProcess DaemonProcess { get; private set; }
+
+		public IContextBoundSettingsStore SettingsStore { get; private set; }
+
+		public TCodeFile CodeFile { get; private set; }
+
+		public IPsiSourceFile PsiSourceFile { get; private set; }
+
+		public IDocument Document { get; private set; }
+
+		public ShellSpellCheckResources SpellCheckResources { get; private set; }
+
+		public NamingManager NamingManager { get; private set; }
+
+		public INamingPolicyProvider NamingPolicyProvider { get; private set; }
+
+		public PsiLanguageType LanguageType { get; private set; }
+
+		public abstract void Execute(Action<DaemonStageResult> commiter);
+
+		protected Name ParseName(string name) {
+			return NamingManager.Parsing.Parse(name, NamingRule.Default, NamingPolicyProvider);
+		}
+
+		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
+			);
+		}
+
+		protected IEnumerable<IDeclaredType> GetSuperTypes(ITypeDeclaration typeElement) {
+			if (null == typeElement)
+				return Enumerable.Empty<IDeclaredType>();
+			return typeElement.SuperTypes;
+		}
+
+		protected DocumentRange? FindTrueDocumentRange(TreeTextRange range) {
+			var validPartRanges = CodeFile.GetIntersectingRanges(range)
+					.Where(x => x.Document == Document)
+					.ToList();
+			if(validPartRanges.Count != 1)
+				return null;
+			return validPartRanges[0];
+		}
+
+		protected IEnumerable<TextSubString> ParseNameParts(string nameText) {
+			var parsedName = ParseName(nameText);
+			if(parsedName.InnerElements == null || parsedName.InnerElements.Count == 0)
+				yield break;
+
+			int identifierNamePartIndex = null != parsedName.NamePrefix
+				? parsedName.NamePrefix.Text.Length
+				: 0;
+
+			foreach(var identifierNamePart in parsedName.InnerElements) {
+				if(!identifierNamePart.IsSeparator)
+					yield return new TextSubString(nameText, identifierNamePartIndex, identifierNamePart.Text.Length);
+
+				identifierNamePartIndex += identifierNamePart.Text.Length;
+			}
+		}
+
+		protected virtual bool IsIgnored(string text)
+		{
+			return String.IsNullOrEmpty(text)
+				|| text.Length <= MinWordSize
+				|| SpellCheckResources.IsIgnoredInsensitive(text);
+		}
+
+		protected string RemovePrefixAndSuffix(IIdentifier identifier) {
+			var name = ParseName(identifier);
+			var nameParts = name.InnerElements.ToList();
+			if(nameParts.Count > 0 && nameParts[0].IsSeparator)
+				nameParts.RemoveAt(0);
+			if(nameParts.Count > 0 && nameParts[nameParts.Count - 1].IsSeparator)
+				nameParts.RemoveAt(nameParts.Count - 1);
+			return String.Concat(nameParts.Select(x => x.Text));
+		}
+
+		protected virtual IEnumerable<HighlightingInfo> FindFreeTextHighlightings(ITreeNode node, string text, DocumentRange textRange, HashSet<string> localIdentifierNames, IDeclarationsCache declarationsCache) {
+			var parser = new CStyleFreeTextParser();
+			var xmlTextParts = parser.ParseXmlTextParts(new TextSubString(text));
+			var wordParts = xmlTextParts.SelectMany(parser.ParseSentenceWordsForSpellCheck);
+			foreach(var wordPart in wordParts) {
+				var word = wordPart.SubText;
+
+				// Make sure that the word is not to be ignored for any reason.
+				if(IsIgnored(word))
+					continue;
+				if(CStyleFreeTextParser.LooksLikeCodeWord(word) || localIdentifierNames.Contains(word))
+					continue;
+				if(declarationsCache.GetElementsByShortName(word).NotNullAndHasAny())
+					continue;
+
+				// Finally we check the spelling of the word.
+				if(SpellCheckResources.Primary.Check(word))
+					continue;
+
+				// If we got this far we need to offer spelling suggestions.
+				var wordPartDocumentOffset = textRange.TextRange.StartOffset + wordPart.Offset;
+				var wordRange = new DocumentRange(
+					Document,
+					new TextRange(wordPartDocumentOffset, wordPartDocumentOffset + word.Length)
+				);
+				yield return new HighlightingInfo(wordRange, CreateErrorHighlighting(node,wordRange,word,null));
+			}
+		}
+
+		protected IEnumerable<HighlightingInfo> FindHighlightings(IComment comment, HashSet<string> localIdentifierNames, IDeclarationsCache declarationsCache) {
+			var fullText = comment.CommentText;
+			if(String.IsNullOrEmpty(fullText) || ReSharperUtil.ReSharperLineRegex.IsMatch(fullText))
+				return Enumerable.Empty<HighlightingInfo>(); // ignore resharper disable/restore lines
+
+			var validRange = FindTrueDocumentRange(comment.GetCommentRange());
+			if(!validRange.HasValue)
+				return Enumerable.Empty<HighlightingInfo>();
+			var documentRange = validRange.Value;
+
+			return FindFreeTextHighlightings(comment, fullText, documentRange, localIdentifierNames, declarationsCache);
+		}
+
+		protected IEnumerable<HighlightingInfo> FindStringHighlightings(
+			ITreeNode node,
+			HashSet<string> localIdentifierNames,
+			IDeclarationsCache declarationsCache
+		) {
+			var validRange = FindTrueDocumentRange(node.GetTreeTextRange());
+			if(!validRange.HasValue)
+				return Enumerable.Empty<HighlightingInfo>();
+			var textRange = validRange.Value;
+
+			var coreMatch = CSharpUtil.StringLiteralContentParser.Match(node.GetText());
+			if(!coreMatch.Success)
+				return Enumerable.Empty<HighlightingInfo>();
+
+			var coreMatchGroup = coreMatch.Groups[1];
+			if(coreMatchGroup.Index > 0)
+				textRange = textRange.SetStartTo(textRange.TextRange.StartOffset + coreMatchGroup.Index);
+
+			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;
+
+			// Use resharper to reformat the word parts
+			var newSuggestion = NamingManager.Parsing.RenderNameSafe(
+				NameRoot.FromWords(Emphasis.Unknown, false, matches),
+				namingRule,
+				LanguageType,
+				NamingPolicyProvider);
+
+			var literalIsOk = wordCount == 1; // literals (@object) are only OK if there is one word in the identifier
+
+			// 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 != "@")) {
+				// 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("@"))
+				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) {
+				// 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.
+				newSuggestion = ForceFirstCharCase(
+					newSuggestion,
+					SecondWordFirstLetterClassification(namingRule.NamingStyleKind)
+				);
+			}
+
+			return newSuggestion;
+		}
+
+		protected IEnumerable<HighlightingInfo> FindHighlightings(IIdentifier identifier) {
+			var results = new List<HighlightingInfo>(0);
+
+			var declaration = ReSharperUtil.GetDeclaration(identifier);
+			if(null == declaration)
+				return results;
+
+			var declared = declaration.DeclaredElement;
+			if(null == declared)
+				return results;
+
+			var validRange = FindTrueDocumentRange(identifier.GetTreeTextRange());
+			if(!validRange.HasValue)
+				return results;
+
+			var validPartRange = validRange.Value;
+
+			var localIgnoredWords = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
+			var relatedTypeNames = GetRelatedTypes(declaration)
+				.Select(x => x.GetPresentableName(LanguageType))
+				.ToList();
+
+			localIgnoredWords.AddRange(relatedTypeNames);
+			localIgnoredWords.AddRange(relatedTypeNames.SelectMany(ParseNameParts).Select(x => x.SubText));
+
+			if(IsIgnored(identifier.Name) || localIgnoredWords.Contains(identifier.Name))
+				return results;
+
+			var parsedNameParts = ParseNameParts(identifier.Name).ToList();
+			var namingRule = NamingPolicyProvider.GetPolicy(declared).NamingRule;
+			var textClassification = Utility.StringUtil.ClassifyCharCase(identifier.Name);
+
+			for(int i = 0; i < parsedNameParts.Count; i++) {
+				var namePart = parsedNameParts[i];
+				var wordPart = Utility.StringUtil.LetterParserRegex
+					.Matches(namePart.SubText)
+					.Cast<Match>()
+					.SingleOrDefault(x => x.Success);
+
+				if(null == wordPart || IsIgnored(wordPart.Value) || localIgnoredWords.Contains(wordPart.Value))
+					continue;
+
+				if(SpellCheckResources.Primary.Check(wordPart.Value))
+					continue;
+
+				var identifierLeftTrim = namePart.Offset + wordPart.Index;
+				var localValidPartRange = (identifierLeftTrim > 0)
+					? validPartRange.TrimLeft(identifierLeftTrim)
+					: validPartRange;
+
+				localValidPartRange = localValidPartRange.SetEndTo(localValidPartRange.TextRange.StartOffset + wordPart.Value.Length);
+
+				int wordPosition = i;
+				results.Add(new HighlightingInfo(
+					localValidPartRange,
+					CreateErrorHighlighting(
+						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)
+							.Distinct()
+							.Take(MaxSuggestions)
+							.ToArray()
+					)
+				));
+			}
+			return results;
+
+		}
+
+		protected abstract SpellingErrorHighlightingBase CreateErrorHighlighting(ITreeNode node, DocumentRange errorRange, string wordInError, Func<string, string[]> getSuggestions);
+
+		public abstract bool IsValidIdentifierForSpellCheck(ITreeNode node);
+
+		protected abstract IEnumerable<IType> GetRelatedTypes(IDeclaration declaration);
+
+
+	}
+}

File src/YouCantSpell.ReSharper.Shared/JavaScript/JavaScriptFreeTextSpellingHighlightFinder.cs

-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
 using System.Linq;
 using JetBrains.DocumentModel;
 using JetBrains.ReSharper.Daemon;
 
 namespace YouCantSpell.ReSharper.JavaScript
 {
+	[Obsolete]
 	public class JavaScriptFreeTextSpellingHighlightFinder
 	{
 
 		) {
 			var parser = new CStyleFreeTextParser();
 			var xmlTextParts = parser.ParseXmlTextParts(new TextSubString(text));
-			var wordParts = xmlTextParts.SelectMany(x => parser.ParseSentenceWordsForSpellCheck(x));
+			var wordParts = xmlTextParts.SelectMany(parser.ParseSentenceWordsForSpellCheck);
 			foreach(var wordPart in wordParts) {
 				var word = wordPart.SubText;
 

File src/YouCantSpell.ReSharper.Shared/JavaScript/JavaScriptSpellCheckDaemonStageProcess.cs

 using System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Text.RegularExpressions;
-using JetBrains.Application;
 using JetBrains.Application.Settings;
 using JetBrains.DocumentModel;
 using JetBrains.ReSharper.Daemon;
-using JetBrains.ReSharper.Daemon.JavaScript.Impl;
 using JetBrains.ReSharper.Psi;
-using JetBrains.ReSharper.Psi.Caches;
 using JetBrains.ReSharper.Psi.JavaScript.Impl.Tree;
 using JetBrains.ReSharper.Psi.JavaScript.LanguageImpl;
 using JetBrains.ReSharper.Psi.JavaScript.Parsing;
 using JetBrains.ReSharper.Psi.JavaScript.Tree;
-using JetBrains.ReSharper.Psi.Naming;
-using JetBrains.ReSharper.Psi.Naming.Impl;
-using JetBrains.ReSharper.Psi.Naming.Interfaces;
-using JetBrains.ReSharper.Psi.Naming.Settings;
 using JetBrains.ReSharper.Psi.Tree;
 
+
 namespace YouCantSpell.ReSharper.JavaScript
 {
-	public class JavaScriptSpellCheckDaemonStageProcess : JavaScriptDaemonStageProcessBase
+	public class JavaScriptSpellCheckDaemonStageProcess : CStyleSpellCheckDaemonStageProcessBase<IJavaScriptFile>
 	{
 
-		private const int MinWordSize = 2;
-		private const int MaxSuggestions = 10;
-
-		/// <summary>
-		/// Finds text that is only letters.
-		/// </summary>
-		private static readonly Regex LetterParserRegex = new Regex(@"[\p{L}]+", RegexOptions.Compiled);
-		/// <summary>
-		/// Locates resharper in-code configuration comments.
-		/// </summary>
-		private static readonly Regex ReSharperLineRegex = new Regex(@"ReSharper\s+(disable|restore)\s+(.*)", RegexOptions.Compiled);
-
-		private static readonly Regex StringLiteralContentParser = new Regex(@"^""(.+)""$", RegexOptions.Compiled | RegexOptions.Singleline);
-
-		private readonly ShellSpellCheckResources _spellCheckResources;
-		private readonly NamingManager _namingManager;
-		private readonly INamingPolicyProvider _namingPolicyProvider;
-		private readonly IPsiSourceFile _sourceFile;
-		private readonly IJavaScriptFile _javaScriptFile;
-		private readonly IDocument _document;
-
-		public JavaScriptSpellCheckDaemonStageProcess(IDaemonProcess process, IContextBoundSettingsStore settingsStore, IJavaScriptFile javaScriptFile)
-#if RSHARP6
-			: base(process, settingsStore)
-#else
-			: base(process, settingsStore, javaScriptFile)
-#endif
- {
-			if(null == JavaScriptLanguage.Instance)
-				throw new InvalidOperationException("JavaScript language instance not supported.");
-			if (null == javaScriptFile)
-				throw new ArgumentNullException("javaScriptFile");
-
-			_javaScriptFile = javaScriptFile;
-			_spellCheckResources = Shell.Instance.GetComponent<ShellSpellCheckResources>();
-			_sourceFile = process.SourceFile;
-			_document = _sourceFile.Document;
-			var psiServices = _sourceFile.PsiModule.GetPsiServices();
-			_namingManager = psiServices.Naming;
-			_namingPolicyProvider = _namingManager.Policy.GetPolicyProvider(JavaScriptLanguage.Instance, _sourceFile, settingsStore);
-		}
-
-		private IJavaScriptFile GetPsiFile() {
-#if RSHARP6
-			return DaemonProcess.SourceFile.GetPsiFile(JavaScriptLanguage.Instance) as IJavaScriptFile;
-#else
-			return DaemonProcess.SourceFile.GetPsiFiles<JavaScriptLanguage>().OfType<IJavaScriptFile>().Single();
-#endif
-		}
-
-		
 		/// <summary>
 		/// Determines if an node node is acceptable for spell checking.
 		/// </summary>
 				&& IsValidSpellCheckIdentifierNodeSingleCheck(node.Parent.Parent);
 		}
 
-		private static bool IsValidSpellCheckIdentifierNodeSingleCheck(ITreeNode node)
-		{
+		private static bool IsValidSpellCheckIdentifierNodeSingleCheck(ITreeNode node) {
 			if(null == node)
 				return false;
 			if(node is IReferenceExpression)
 			return true;
 		}
 
-		private struct NamePart
-		{
-			public string Text;
-			public int IdentifierOffset;
+		public JavaScriptSpellCheckDaemonStageProcess(IDaemonProcess process, IContextBoundSettingsStore settingsStore, IJavaScriptFile javaScriptFile)
+			: base(process,settingsStore,javaScriptFile,JavaScriptLanguage.Instance)
+		{ }
+
+		protected override SpellingErrorHighlightingBase CreateErrorHighlighting(ITreeNode node, DocumentRange errorRange, string wordInError, Func<string, string[]> getSuggestions) {
+			return new JavaScriptSpellingErrorHighlighting(node,errorRange,wordInError,getSuggestions);
 		}
 
-		private Name ParseName(string name) {
-			return _namingManager.Parsing.Parse(name, NamingRule.Default, _namingPolicyProvider);
-		}
+		protected override bool IsIgnored(string text) {
+			return base.IsIgnored(text) || JavaScriptUtil.IsKeyword(text);
+		} 
 
-		private IEnumerable<string> ParseSuperWords(ITypeElement typeElement) {
-			if(null == typeElement)
-				return Enumerable.Empty<string>();
-
-			return typeElement
-				.GetSuperTypes()
-				.Select(x => ParseNameParts(x.GetClrName().ShortName))
-				.SelectMany(x => x.Select(y => y.Text))
-				.Distinct();
-
-		}
-
-		private readonly Dictionary<string, NamePart[]> _parseNamePartsCache = new Dictionary<string, NamePart[]>();
-		private readonly object _parseNamePartsCacheMutex = new object();
-		private NamePart[] ParseNameParts(string nameText) {
-			lock(_parseNamePartsCacheMutex) {
-				NamePart[] result;
-				if (!_parseNamePartsCache.TryGetValue(nameText, out result)) {
-					result = ParseNamePartsCore(nameText).ToArray();
-					_parseNamePartsCache.Add(nameText, result);
-				}
-				return result;
-			}
-		}
-
-		private List<NamePart> ParseNamePartsCore(string nameText) {
-			var nameParts = new List<NamePart>(0);
-			var parsedName = ParseName(nameText);
-			if (parsedName.InnerElements == null || parsedName.InnerElements.Count == 0)
-				return nameParts;
-
-			int identifierNamePartIndex = null != parsedName.NamePrefix ? parsedName.NamePrefix.Text.Length : 0;
-
-			foreach(var identifierNamePart in parsedName.InnerElements) {
-				if(!identifierNamePart.IsSeparator)
-					nameParts.Add(new NamePart { Text = identifierNamePart.Text, IdentifierOffset = identifierNamePartIndex });
-				
-				identifierNamePartIndex += identifierNamePart.Text.Length;
-			}
-
-			return nameParts;
-		}
-
-		public ShellSpellCheckResources SpellCheckResources {
-			get { return _spellCheckResources; }
-		}
-
-		public ISpellChecker PrimarySpellChecker {
-			get { return SpellCheckResources.Primary; }
-		}
-
-		private bool IsIgnored(string text, HashSet<string> ignored) {
-			return String.IsNullOrEmpty(text)
-				|| text.Length <= MinWordSize
-				|| ignored.Contains(text)
-				|| JavaScriptUtil.IsKeyword(text)
-				|| SpellCheckResources.IsIgnoredInsensitive(text);
-		}  
-
-		private IEnumerable<HighlightingInfo> FindHighlightings(IIdentifier identifier) {
-			var results = new List<HighlightingInfo>(0);
-
-			var declared = ReSharperUtil.GetDeclaredElement(identifier);
-			if(null == declared)
-				return results;
-
-			var validRange = FindTrueDocumentRange(identifier.GetTreeTextRange());
-			if (!validRange.HasValue)
-				return results;
-
-			var validPartRange = validRange.Value;
-
-			var localIgnoredWords = new HashSet<string>(
-				ParseSuperWords(declared as ITypeElement),
-				StringComparer.InvariantCultureIgnoreCase);
-
-			if (IsIgnored(identifier.Name, localIgnoredWords))
-				return results;
-
-			var parsedNameParts = ParseNameParts(identifier.Name).ToList();
-			var formatter = new JavaScriptSpellingSuggestionFormatter(
-				identifier.Name,
-				_namingPolicyProvider.GetPolicy(declared).NamingRule,
-				_namingManager.Parsing,
-				_sourceFile.PrimaryPsiLanguage,
-				_namingPolicyProvider,
-				parsedNameParts.Count
-			);
-
-			for(int i = 0; i < parsedNameParts.Count; i++) {
-				var namePart = parsedNameParts[i];
-				var wordPart = LetterParserRegex
-					.Matches(namePart.Text)
-					.Cast<Match>()
-					.SingleOrDefault(x => x.Success);
-
-				if(null == wordPart || IsIgnored(wordPart.Value, localIgnoredWords))
-					continue;
-
-				if (PrimarySpellChecker.Check(wordPart.Value))
-					continue;
-
-				var identifierLeftTrim = namePart.IdentifierOffset + wordPart.Index;
-				var localValidPartRange = (identifierLeftTrim > 0)
-					? validPartRange.TrimLeft(identifierLeftTrim)
-					: validPartRange;
-
-				localValidPartRange = localValidPartRange.SetEndTo(localValidPartRange.TextRange.StartOffset + wordPart.Value.Length);
-
-				int wordPosition = i;
-				var highlight = new JavaScriptSpellingErrorHighlighting(
-					identifier,
-					localValidPartRange,
-					wordPart.Value,
-					word => PrimarySpellChecker
-						.GetRecommendations(word)
-						.Select(x => formatter.FormatSuggestion(x, wordPosition))
-						.Where(x => word != x)
-						.Distinct()
-						.Take(MaxSuggestions)
-						.ToArray()
-				);
-				results.Add(new HighlightingInfo(localValidPartRange, highlight));
-
-			}
-			return results;
-		}
-
-		private IEnumerable<HighlightingInfo> FindHighlightings(IComment comment, HashSet<string> ignoredIdentifiers, IDeclarationsCache declarationsCache) {
-
-			var fullText = comment.CommentText;
-			if(ReSharperLineRegex.IsMatch(fullText))
-				return Enumerable.Empty<HighlightingInfo>(); // ignore resharper disable/restore lines
-
-			var validRange = FindTrueDocumentRange(comment.GetCommentRange());
-			if (!validRange.HasValue)
-				return Enumerable.Empty<HighlightingInfo>();
-			var documentRange = validRange.Value;
-
-			return new JavaScriptFreeTextSpellingHighlightFinder(SpellCheckResources, _document, ignoredIdentifiers, declarationsCache)
-				.FindXmlTextHighlightings(comment, fullText, documentRange);
-		}
-
-		private IEnumerable<HighlightingInfo> FindStringHighlightings(
-			ITreeNode node,
-			HashSet<string> localIdentifierNames, IDeclarationsCache declarationsCache) {
-			
-			var coreMatch = StringLiteralContentParser.Match(node.GetText());
-			if(!coreMatch.Success)
-				return Enumerable.Empty<HighlightingInfo>();
-
-			var validRange = FindTrueDocumentRange(node.GetTreeTextRange());
-			if (!validRange.HasValue)
-				return Enumerable.Empty<HighlightingInfo>();
-			var textRange = validRange.Value;
-
-			if(coreMatch.Groups[1].Index > 0)
-				textRange = textRange.SetStartTo(textRange.TextRange.StartOffset + coreMatch.Groups[1].Index);
-
-			return new JavaScriptFreeTextSpellingHighlightFinder(SpellCheckResources, _document, localIdentifierNames, declarationsCache)
-				.FindXmlTextHighlightings(node, coreMatch.Groups[1].Value, textRange);
-		}
-
-		private DocumentRange? FindTrueDocumentRange(TreeTextRange range) {
-			var validPartRanges = GetPsiFile().GetIntersectingRanges(range)
-					.Where(x => x.Document == _document)
-					.ToList();
-			if (validPartRanges.Count != 1)
-				return null;
-			return validPartRanges[0];
+		protected override IEnumerable<IType> GetRelatedTypes(IDeclaration declaration) {
+			if(declaration is ITypeDeclaration)
+				return GetSuperTypes(declaration as ITypeDeclaration).Cast<IType>();
+			return Enumerable.Empty<IType>();
 		}
 
 		public override void Execute(Action<DaemonStageResult> committer) {
 			var localIdentifierNames = new HashSet<string>(StringComparer.CurrentCultureIgnoreCase);
 			var highlightings = new List<HighlightingInfo>();
 
-			_javaScriptFile.ProcessChildren<IIdentifier>(
+			CodeFile.ProcessChildren<IIdentifier>(
 				node => {
 					localIdentifierNames.Add(node.Name);
+					localIdentifierNames.Add(RemovePrefixAndSuffix(node));
 					if(IsValidSpellCheckIdentifierNode(node)) {
 						highlightings.AddRange(FindHighlightings(node));
 					}
 				}
 			);
 
-			var declarationsCache = _javaScriptFile.GetPsiServices().CacheManager.GetDeclarationsCache(_javaScriptFile.GetPsiModule(), true, true);
+			var declarationsCache = CodeFile.GetPsiServices().CacheManager.GetDeclarationsCache(CodeFile.GetPsiModule(), true, true);
 
-			_javaScriptFile.ProcessChildren<ITreeNode>(
+			CodeFile.ProcessChildren<ITreeNode>(
 				node => {
 #if RSHARP6
 					if (node is JavaScriptGenericToken && (node as JavaScriptGenericToken).NodeType == JavaScriptTokenType.STRING_LITERAL) {
 					}
 #else
 					if (node is IJavaScriptLiteralExpression && (node as IJavaScriptLiteralExpression).ConstantValueType == ConstantValueTypes.String) {
-						highlightings.AddRange(FindStringHighlightings(node as IJavaScriptLiteralExpression, localIdentifierNames, declarationsCache));
+						highlightings.AddRange(FindStringHighlightings(node, localIdentifierNames, declarationsCache));
 					}
 #endif
 					else if(node is IComment) {
 
 			committer(new DaemonStageResult(highlightings));
 		}
+
+		public override bool IsValidIdentifierForSpellCheck(ITreeNode node)
+		{
+			return IsValidSpellCheckIdentifierNode(node);
+		}
 	}
 }

File src/YouCantSpell.ReSharper.Shared/JavaScript/JavaScriptSpellingSuggestionFormatter.cs

-using System;
-using System.Linq;
-using System.Text.RegularExpressions;
-using JetBrains.ReSharper.Psi;
-using JetBrains.ReSharper.Psi.Naming;
-using JetBrains.ReSharper.Psi.Naming.Impl;
-using JetBrains.ReSharper.Psi.Naming.Interfaces;
-using JetBrains.ReSharper.Psi.Naming.Settings;
-using JetBrains.ReSharper.Psi.Tree;
-using YouCantSpell.Utility;
-
-namespace YouCantSpell.ReSharper.JavaScript
-{
-	[Obsolete("Merge with CSharpSpellingSuggestionFormatter or maybe it should be the C-Style naming formatter")]
-	public class JavaScriptSpellingSuggestionFormatter
-	{
-
-		
-		/// <summary>
-		/// Force the casing of the first character in a string to the desired case.
-		/// </summary>
-		/// <param name="text">The text to adjust.</param>
-		/// <param name="textCase">The case to adjust the first letter to.</param>
-		/// <returns>The adjusted string.</returns>
-		private static string ForceFirstCharCase(string text, TextCaseClassification textCase) {
-			if(String.IsNullOrEmpty(text))
-				return text;
-
-			char firstLetter;
-			switch(textCase) {
-			case TextCaseClassification.Upper: { firstLetter = Char.ToUpper(text[0]); break; }
-			case TextCaseClassification.Lower: { firstLetter = Char.ToLower(text[0]); break; }
-			default: return text;
-			}
-
-			if(firstLetter == text[0])
-				return text; // it was already correct, so just return it
-
-			return String.Concat(firstLetter, text.Length > 1 ? text.Substring(1) : String.Empty);
-
-		}
-
-		/// <summary>
-		/// Determines the case of the first letter of the second word within an identifier of the given name style.
-		/// </summary>
-		/// <param name="kind">The name style.</param>
-		/// <returns>The case of the first letter of the second word within an identifier.</returns>
-		private static TextCaseClassification SecondWordFirstLetterClassification(NamingStyleKinds kind) {
-			switch(kind) {
-			case NamingStyleKinds.AaBb:
-			case NamingStyleKinds.AA_BB:
-			case NamingStyleKinds.aaBb:
-				return TextCaseClassification.Upper;
-			case NamingStyleKinds.Aa_bb:
-			case NamingStyleKinds.aa_bb:
-				return TextCaseClassification.Lower;
-			default:
-				return TextCaseClassification.Unknown;
-			}
-		}
-
-		private readonly NamingRule _namingRule;
-		private readonly NameParser _nameParser;
-		private readonly PsiLanguageType _psiLanguageType;
-		private readonly INamingPolicyProvider _namingPolicyProvider;
-		private readonly int _wordCount;
-		private readonly TextCaseClassification _textClassification;
-
-		public JavaScriptSpellingSuggestionFormatter(
-			string identifierName,
-			NamingRule namingRule,
-			NameParser nameParser,
-			PsiLanguageType psiLanguageType,
-			INamingPolicyProvider namingPolicyProvider,
-			int wordCount
-		) {
-			if(String.IsNullOrEmpty(identifierName))
-				throw new ArgumentNullException("identifierName");
-			if(null == namingRule)
-				throw new ArgumentNullException("namingRule");
-			if(null == nameParser)
-				throw new ArgumentNullException("nameParser");
-
-			_namingRule = namingRule;
-			_nameParser = nameParser;
-			_psiLanguageType = psiLanguageType;
-			_namingPolicyProvider = namingPolicyProvider;
-			_wordCount = wordCount;
-			_textClassification = StringUtil.ClassifyCharCase(identifierName);
-		}
-
-		/// <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) {
-			// Get the word parts of the suggestion.
-			var matches = StringUtil.LetterParserRegex.Matches(suggestion).Cast<Match>().Select(x => x.Value).ToArray();
-			if(matches.Length == 0)
-				return suggestion;
-
-			// Use resharper to reformat the word parts
-			var newSuggestion = _nameParser.RenderNameSafe(
-				NameRoot.FromWords(Emphasis.Unknown, false, matches),
-				_namingRule,
-				_psiLanguageType,
-				_namingPolicyProvider);
-
-			var literalIsOk = _wordCount == 1; // literals (@object) are only OK if there is one word in the identifier
-
-			// 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 = _nameParser.Parse(newSuggestion, NamingRule.Default, _namingPolicyProvider);
-			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("@"))
-				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) {
-				// 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.
-				newSuggestion = ForceFirstCharCase(
-					newSuggestion,
-					SecondWordFirstLetterClassification(_namingRule.NamingStyleKind)
-				);
-			}
-
-			return newSuggestion;
-		}
-
-	}
-}

File src/YouCantSpell.ReSharper.Shared/JavaScript/JavaScriptUtil.cs

 using System;
+using System.Text.RegularExpressions;
 using JetBrains.ReSharper.Psi.Tree;
 using JetBrains.ReSharper.Psi.JavaScript.Tree;
 using JetBrains.ReSharper.Psi.JavaScript.Parsing;
 	public static class JavaScriptUtil
 	{
 
+		/// <summary>
+		/// Used to extract the string from within a string literal without including the quotes or literal flag symbol.
+		/// </summary>
+		public static readonly Regex StringLiteralContentParser = new Regex(@"^""(.+)""$", RegexOptions.Compiled);
+
 		public static bool IsKeyword(string word)
 		{
 			if(String.IsNullOrEmpty(word))

File src/YouCantSpell.ReSharper.v61.Net35/YouCantSpell.ReSharper.v61.Net35.csproj

     <Compile Include="..\YouCantSpell.ReSharper.Shared\CSharp\CSharpSpellingQuikFix.cs">
       <Link>CSharp\CSharpSpellingQuikFix.cs</Link>
     </Compile>
-    <Compile Include="..\YouCantSpell.ReSharper.Shared\CSharp\CSharpSpellingSuggestionFormatter.cs">
-      <Link>CSharp\CSharpSpellingSuggestionFormatter.cs</Link>
-    </Compile>
     <Compile Include="..\YouCantSpell.ReSharper.Shared\CSharp\CSharpUtil.cs">
       <Link>CSharp\CSharpUtil.cs</Link>
     </Compile>
+    <Compile Include="..\YouCantSpell.ReSharper.Shared\CStyleSpellCheckDaemonStageProcessBase.cs">
+      <Link>CStyleSpellCheckDaemonStageProcessBase.cs</Link>
+    </Compile>
     <Compile Include="..\YouCantSpell.ReSharper.Shared\Html\HtmlFreeTextSpellingHighlightFinder.cs">
       <Link>Html\HtmlFreeTextSpellingHighlightFinder.cs</Link>
     </Compile>
     <Compile Include="..\YouCantSpell.ReSharper.Shared\JavaScript\JavaScriptSpellingQuickFix.cs">
       <Link>JavaScript\JavaScriptSpellingQuickFix.cs</Link>
     </Compile>
-    <Compile Include="..\YouCantSpell.ReSharper.Shared\JavaScript\JavaScriptSpellingSuggestionFormatter.cs">
-      <Link>JavaScript\JavaScriptSpellingSuggestionFormatter.cs</Link>
-    </Compile>
     <Compile Include="..\YouCantSpell.ReSharper.Shared\JavaScript\JavaScriptUtil.cs">
       <Link>JavaScript\JavaScriptUtil.cs</Link>
     </Compile>

File src/YouCantSpell.ReSharper.v61/YouCantSpell.ReSharper.v61.csproj

     <Compile Include="..\YouCantSpell.ReSharper.Shared\CSharp\CSharpSpellingQuikFix.cs">
       <Link>CSharp\CSharpSpellingQuikFix.cs</Link>
     </Compile>
-    <Compile Include="..\YouCantSpell.ReSharper.Shared\CSharp\CSharpSpellingSuggestionFormatter.cs">
-      <Link>CSharp\CSharpSpellingSuggestionFormatter.cs</Link>
-    </Compile>
     <Compile Include="..\YouCantSpell.ReSharper.Shared\CSharp\CSharpUtil.cs">
       <Link>CSharp\CSharpUtil.cs</Link>
     </Compile>
+    <Compile Include="..\YouCantSpell.ReSharper.Shared\CStyleSpellCheckDaemonStageProcessBase.cs">
+      <Link>CStyleSpellCheckDaemonStageProcessBase.cs</Link>
+    </Compile>
     <Compile Include="..\YouCantSpell.ReSharper.Shared\Html\HtmlFreeTextSpellingHighlightFinder.cs">
       <Link>Html\HtmlFreeTextSpellingHighlightFinder.cs</Link>
     </Compile>
     <Compile Include="..\YouCantSpell.ReSharper.Shared\JavaScript\JavaScriptSpellingQuickFix.cs">
       <Link>JavaScript\JavaScriptSpellingQuickFix.cs</Link>
     </Compile>
-    <Compile Include="..\YouCantSpell.ReSharper.Shared\JavaScript\JavaScriptSpellingSuggestionFormatter.cs">
-      <Link>JavaScript\JavaScriptSpellingSuggestionFormatter.cs</Link>
-    </Compile>
     <Compile Include="..\YouCantSpell.ReSharper.Shared\JavaScript\JavaScriptUtil.cs">
       <Link>JavaScript\JavaScriptUtil.cs</Link>
     </Compile>

File src/YouCantSpell.ReSharper.v70.Net35/YouCantSpell.ReSharper.v70.Net35.csproj

     <Compile Include="..\YouCantSpell.ReSharper.Shared\CSharp\CSharpSpellingQuikFix.cs">
       <Link>CSharp\CSharpSpellingQuikFix.cs</Link>
     </Compile>
-    <Compile Include="..\YouCantSpell.ReSharper.Shared\CSharp\CSharpSpellingSuggestionFormatter.cs">
-      <Link>CSharp\CSharpSpellingSuggestionFormatter.cs</Link>
-    </Compile>
     <Compile Include="..\YouCantSpell.ReSharper.Shared\CSharp\CSharpUtil.cs">
       <Link>CSharp\CSharpUtil.cs</Link>
     </Compile>
+    <Compile Include="..\YouCantSpell.ReSharper.Shared\CStyleSpellCheckDaemonStageProcessBase.cs">
+      <Link>CStyleSpellCheckDaemonStageProcessBase.cs</Link>
+    </Compile>
     <Compile Include="..\YouCantSpell.ReSharper.Shared\Html\HtmlFreeTextSpellingHighlightFinder.cs">
       <Link>Html\HtmlFreeTextSpellingHighlightFinder.cs</Link>
     </Compile>
     <Compile Include="..\YouCantSpell.ReSharper.Shared\JavaScript\JavaScriptSpellingQuickFix.cs">
       <Link>JavaScript\JavaScriptSpellingQuickFix.cs</Link>
     </Compile>
-    <Compile Include="..\YouCantSpell.ReSharper.Shared\JavaScript\JavaScriptSpellingSuggestionFormatter.cs">
-      <Link>JavaScript\JavaScriptSpellingSuggestionFormatter.cs</Link>
-    </Compile>
     <Compile Include="..\YouCantSpell.ReSharper.Shared\JavaScript\JavaScriptUtil.cs">
       <Link>JavaScript\JavaScriptUtil.cs</Link>
     </Compile>

File src/YouCantSpell.ReSharper.v70/YouCantSpell.ReSharper.v70.csproj

     <Compile Include="..\YouCantSpell.ReSharper.Shared\CSharp\CSharpSpellingQuikFix.cs">
       <Link>CSharp\CSharpSpellingQuikFix.cs</Link>
     </Compile>
-    <Compile Include="..\YouCantSpell.ReSharper.Shared\CSharp\CSharpSpellingSuggestionFormatter.cs">
-      <Link>CSharp\CSharpSpellingSuggestionFormatter.cs</Link>
-    </Compile>
     <Compile Include="..\YouCantSpell.ReSharper.Shared\CSharp\CSharpUtil.cs">
       <Link>CSharp\CSharpUtil.cs</Link>
     </Compile>
+    <Compile Include="..\YouCantSpell.ReSharper.Shared\CStyleSpellCheckDaemonStageProcessBase.cs">
+      <Link>CStyleSpellCheckDaemonStageProcessBase.cs</Link>
+    </Compile>
     <Compile Include="..\YouCantSpell.ReSharper.Shared\Html\HtmlFreeTextSpellingHighlightFinder.cs">
       <Link>Html\HtmlFreeTextSpellingHighlightFinder.cs</Link>
     </Compile>
     <Compile Include="..\YouCantSpell.ReSharper.Shared\JavaScript\JavaScriptSpellingQuickFix.cs">
       <Link>JavaScript\JavaScriptSpellingQuickFix.cs</Link>
     </Compile>
-    <Compile Include="..\YouCantSpell.ReSharper.Shared\JavaScript\JavaScriptSpellingSuggestionFormatter.cs">
-      <Link>JavaScript\JavaScriptSpellingSuggestionFormatter.cs</Link>
-    </Compile>
     <Compile Include="..\YouCantSpell.ReSharper.Shared\JavaScript\JavaScriptUtil.cs">
       <Link>JavaScript\JavaScriptUtil.cs</Link>
     </Compile>