Source

Resharper Spell Checker / ReSpeller / SpellEngine / SuggestionGenerator.cs

using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Application;
using JetBrains.Application.Components;

namespace ReSpeller.SpellEngine
{
  [ShellComponent(ProgramConfigurations.VS_ADDIN)]
  public class SuggestionGenerator
  {
    private readonly ISpellChecker myChecker;

    public SuggestionGenerator(ISpellChecker checker)
    {
      myChecker = checker;
    }

    public List<string> GenerateForWord(string word)
    {
      return myChecker.Suggestions(word);
    }

    public List<string> Generate(string text)
    {
      return Generate(TextSplitter.Split(text));
    }

    public List<string> Generate(List<TextPart> innerNameParts, string prefix, string suffix)
    {
      List<string> withoutAffixes = Generate(innerNameParts);
      return withoutAffixes.Select(s => prefix + s + suffix).ToList();
    }

    private static List<TextWeight> GenerateMixes(List<List<TextWeight>> variants, int columnIndex = 0)
    {
      if (columnIndex == variants.Count - 1)
        return variants.Last();
      List<TextWeight> nextMixes = GenerateMixes(variants, columnIndex + 1);
      List<TextWeight> current = variants[columnIndex];
      IEnumerable<TextWeight> result = from curPart in current
                                       from nextMix in nextMixes
                                       select new TextWeight {Text = curPart.Text + nextMix.Text, Weight = curPart.Weight*nextMix.Weight};
      return result.ToList();
    } 

    public List<string> Generate(List<TextPart> parts)
    {
      List<List<TextWeight>> suggestVariants = (from part in parts 
                                                  let isNotWordOrCorrect = part.Type == TextPartType.NotAWord || myChecker.CheckWordSpelling(part.Text)
                                                  select isNotWordOrCorrect 
                                                    ? (new List<TextWeight> {new TextWeight {Text = part.Text}})
                                                    : (from text in myChecker.Suggestions(part.Text)
                                                       where text.All(Char.IsLetter)
                                                       select new TextWeight {Text = text}).ToList()).ToList();

      foreach (var variant in suggestVariants)
      {
        for (int i = 0; i < variant.Count; ++i)
        {
          variant[i].Weight = 1.0 - i*(1.0/variant.Count);
        }
      }

      List<TextWeight> suggestions = GenerateMixes(suggestVariants);
      suggestions = suggestions.Distinct().ToList();
      suggestions.Sort((textWeight1, textWeight2) => Math.Sign(textWeight2.Weight - textWeight1.Weight));
      List<string> result = (from t in suggestions select t.Text).ToList();
      return result;
    }

    #region Nested type: TextWeight

    private class TextWeight
    {
      public string Text;
      public double Weight;

      public bool Equals(TextWeight other)
      {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Equals(other.Text, Text);
      }

      public override bool Equals(object obj)
      {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != typeof (TextWeight)) return false;
        return Equals((TextWeight) obj);
      }

      public override int GetHashCode()
      {
        return (Text != null ? Text.GetHashCode() : 0);
      }
    }

    #endregion
  }
}