1. Nerzhul500
  2. Resharper Spell Checker

Commits

derigel  committed dacfd2d

Multilayer add word dictionary dialog

  • Participants
  • Parent commits 88e31e5
  • Branches resharper8

Comments (0)

Files changed (14)

File ReSpeller.8.0/ReSpeller.8.0.csproj

View file
  • Ignore whitespace
     <DebugType>full</DebugType>
     <Optimize>false</Optimize>
     <OutputPath>..\target\Debug\8.0\bin\</OutputPath>
-    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DefineConstants>TRACE;DEBUG;RS_80</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
     <DebugType>pdbonly</DebugType>
     <Optimize>true</Optimize>
     <OutputPath>..\target\Release\8.0\bin\</OutputPath>
-    <DefineConstants>TRACE</DefineConstants>
+    <DefineConstants>TRACE;RS_80</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
     <Compile Include="..\ReSpeller\Actions\AddArbitraryWordAction.cs">
       <Link>Actions\AddArbitraryWordAction.cs</Link>
     </Compile>
+    <Compile Include="..\ReSpeller\Actions\AddArbitraryWordDialog.xaml.cs">
+      <Link>Actions\AddArbitraryWordDialog.xaml.cs</Link>
+      <DependentUpon>AddArbitraryWordDialog.xaml</DependentUpon>
+    </Compile>
     <Compile Include="..\ReSpeller\Actions\AddArbitraryWordHelper.cs">
       <Link>Actions\AddArbitraryWordHelper.cs</Link>
     </Compile>
     <ThemedIconPng Include="Img\icon.png" />
   </ItemGroup>
   <ItemGroup>
+    <Page Include="..\ReSpeller\Actions\AddArbitraryWordDialog.xaml">
+      <Link>Actions\AddArbitraryWordDialog.xaml</Link>
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
     <Page Include="..\ReSpeller\Settings\ReSpellerSettingsControl.xaml">
       <Link>Settings\ReSpellerSettingsControl.xaml</Link>
       <Generator>MSBuild:Compile</Generator>

File ReSpeller.RSTests/SpellEngine/SpellCheckerTest.cs

View file
  • Ignore whitespace
 using System.Collections.Generic;
+using JetBrains.Application;
 using JetBrains.ProjectModel;
 using ReSpeller.SpellEngine;
 
 namespace ReSpeller.RSTests.SpellEngine
 {
-  [SolutionComponent]
+  [ShellComponent]
   public class SpellCheckerStub : ISpellChecker, ISingleLanguageWordStorage
   {
     public bool CheckWordSpelling(string word)
       
     }
 
+    public string Languge
+    {
+      get { return null; }
+    }
+
     public void AddWordToUserDict(string word)
     {
       

File ReSpeller/Actions/AddArbitraryWordAction.cs

View file
  • Ignore whitespace
 using JetBrains.ActionManagement;
+using JetBrains.Application.Components;
 using JetBrains.Application.DataContext;
+using JetBrains.Application.Settings;
 
 namespace ReSpeller.Actions
 {
 
     public void Execute(IDataContext context, DelegateExecute nextExecute)
     {
-      AddArbitraryWordHelper.ShowWordInput("");
+      var settingsStore = context.GetComponent<ISettingsStore>().BindToContextTransient(ContextRange.Smart((lifetime, contexts) => context));
+
+      AddArbitraryWordHelper.ShowWordInput(settingsStore, new DataContextContainerWrapper(context));
+    }
+
+    private class DataContextContainerWrapper : AddArbitraryWordDialog.IComponentContainerWrapper
+    {
+      private readonly IDataContext myContext;
+
+      public DataContextContainerWrapper(IDataContext context)
+      {
+        myContext = context;
+      }
+
+      public T GetComponent<T>() where T : class
+      {
+        return myContext.GetComponent<T>();
+      }
     }
   }
 }

File ReSpeller/Actions/AddArbitraryWordDialog.xaml

View file
  • Ignore whitespace
+<Window x:Class="ReSpeller.Actions.AddArbitraryWordDialog"
+        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"      
+        Width="290"
+        WindowStartupLocation="CenterOwner" ShowInTaskbar="False"
+        Title="Add word to dictionary" SizeToContent="Height"
+        Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" 
+        ResizeMode="NoResize" WindowStyle="SingleBorderWindow"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+        xmlns:Controls1="clr-namespace:JetBrains.UI.Controls;assembly=JetBrains.Platform.ReSharper.UI"
+        xmlns:Converters="clr-namespace:JetBrains.UI.Wpf.Converters;assembly=JetBrains.Platform.ReSharper.UI"
+        xmlns:ReSpeller="clr-namespace:ReSpeller"
+        xmlns:icons="urn:schemas-jetbrains-com:jetbrains-ui-avalon"
+        mc:Ignorable="d" >
+
+    <Window.Resources>
+        <Converters:BooleanToVisibilityConverter x:Key="boolToVisibilityConverter" NonVisibleValue="Collapsed" />
+        <Converters:BooleanToVisibilityConverter x:Key="boolToVisibilityConverterInverted" NonVisibleValue="Collapsed" Inverted="True" />
+    </Window.Resources>
+    <Grid>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="Auto"/>
+        </Grid.RowDefinitions>
+
+    <TextBox Grid.Row="0" Margin="10,10,10,0" Text="{Binding Word}" TextWrapping="Wrap" x:Name="WordTextBox" />
+
+        <Grid Grid.Row="1" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Height="45">
+            <StackPanel HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,10,0" Orientation="Horizontal">
+                <Button Width="75" Height="23" Margin="4,0" Content="Save" IsDefault="True" Click="OnSave"/>
+                <Controls1:DropDownButton Width="85" Height="23" Margin="4,0" HorizontalContentAlignment="Stretch">
+                    <Controls1:DropDownButton.Content>
+                        <Grid>
+                            <TextBlock Text="Save To" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,0,0,0" />
+                            <Polyline Points="-1,0,1,0,0,2" Fill="Black" Width="7" Height="4" Stretch="Fill" Margin="4,4,7,0" HorizontalAlignment="Right" VerticalAlignment="Center" />
+                        </Grid>
+                    </Controls1:DropDownButton.Content>
+                    <Controls1:DropDownButton.DropDownMenu>
+                        <ContextMenu ItemsSource="{Binding SettingsLayers}">
+                            <ContextMenu.ItemTemplate>
+                                <DataTemplate>
+                                    <Grid x:Name="grid" ToolTip="{Binding Path=OriginText.Value}">
+                                        <Grid.ColumnDefinitions>
+                                            <ColumnDefinition />
+                                            <ColumnDefinition />
+                                        </Grid.ColumnDefinitions>
+
+                                        <TextBlock Text="{Binding DisplayName.Value}"/>                                        
+                                    </Grid>
+                                    <DataTemplate.Triggers>
+                                        <DataTrigger Binding="{Binding IsLocked.Value}" Value="False">
+                                            <Setter Property="Margin" Value="20,0,0,0" TargetName="grid" />
+                                        </DataTrigger>
+                                    </DataTemplate.Triggers>
+                                </DataTemplate>
+                            </ContextMenu.ItemTemplate>
+                            <ContextMenu.ItemContainerStyle>
+                                <Style TargetType="MenuItem">
+                                    <EventSetter Event="Click" Handler="OnSaveTo"/>
+                                </Style>
+                            </ContextMenu.ItemContainerStyle>
+                        </ContextMenu>
+                    </Controls1:DropDownButton.DropDownMenu>
+                </Controls1:DropDownButton>                
+                <Button Width="75" Height="23" Margin="4,0,0,0" Content="Cancel" IsCancel="True" />
+            </StackPanel>            
+        </Grid>
+    </Grid>
+</Window>

File ReSpeller/Actions/AddArbitraryWordDialog.xaml.cs

View file
  • Ignore whitespace
+using System.Linq;
+using System.Windows;
+using System.Windows.Input;
+using JetBrains.Application;
+using JetBrains.Application.Settings.UserInterface;
+using JetBrains.DataFlow;
+using JetBrains.UI.Extensions;
+using JetBrains.UI.Icons;
+using JetBrains.UI.Options.OptionsDialog2;
+using MenuItem = System.Windows.Controls.MenuItem;
+
+namespace ReSpeller.Actions
+{
+  public partial class AddArbitraryWordDialog : Window
+  {
+    protected AddArbitraryWordDialog()
+    {
+      // Extracted from XAML due to 
+      // it produces errors in VS designer
+      WindowStyles.SetCanMaximize(this, false);
+      WindowStyles.SetCanMinimize(this, false);
+
+      InitializeComponent();
+      Loaded += (s, e) => Keyboard.Focus(WordTextBox);
+    }
+
+    public AddArbitraryWordDialog(Lifetime lifetime, IComponentContainerWrapper container) : this()
+    {
+      var iconman = container.GetComponent<IThemedIconManager>();
+
+      Icon = iconman.GetIcon<ImgThemedIcons.Icon>().CurrentImageSource;
+      var userFriendlySettingsLayers = container.GetComponent<UserFriendlySettingsLayers>();
+      var injector = container.GetComponent<UserInjectedSettingsLayers>();
+      var layers = userFriendlySettingsLayers
+#if RS_80
+        .WritableLayers
+#else
+        .UserEditableLayers
+#endif
+        .Where(x => !x.IsDefault)
+        .OrderBy(a => a.Priority)
+        .Select(layer => new UserFriendlySettingsLayerUidata(lifetime, layer, injector, iconman))
+        .ToArray();
+
+      lifetime.AddBracket(() => SettingsLayers = layers, () => SettingsLayers = null);
+
+      DataContext = this;
+    }
+
+    public string Word { get; set; }
+
+    public UserFriendlySettingsLayerUidata[] SettingsLayers { get; private set; }
+
+    /// <summary>
+    /// Gets settings layer if user used Save To, null otherwise
+    /// </summary>
+    public UserFriendlySettingsLayerUidata SelectedSettingsLayer { get; private set; }
+
+    void OnSave(object sender, RoutedEventArgs e)
+    {
+      DialogResult = true;
+    }
+
+    void OnSaveTo(object sender, RoutedEventArgs e)
+    {
+      SelectedSettingsLayer = ((MenuItem) sender).DataContext as UserFriendlySettingsLayerUidata;
+      DialogResult = true;
+    }
+
+    public interface IComponentContainerWrapper
+    {
+      T GetComponent<T>() where T : class;
+    }
+  }
+}

File ReSpeller/Actions/AddArbitraryWordHelper.cs

View file
  • Ignore whitespace
 using System.Windows.Forms;
 using JetBrains.Application;
 using JetBrains.Application.Interop.NativeHook;
+using JetBrains.Application.Settings;
 using JetBrains.CommonControls.Validation;
+using JetBrains.DataFlow;
 using JetBrains.UI.Application;
 using JetBrains.UI.CommonDialogs;
 using ReSpeller.SpellEngine;
+using ReSpeller.SpellEngine.Dictionaries;
 
 namespace ReSpeller.Actions
 {
   static internal class AddArbitraryWordHelper
   {
-    public static void ShowWordInput(string defaultWord)
+    public static void ShowWordInput(IContextBoundSettingsStore store, AddArbitraryWordDialog.IComponentContainerWrapper container, string defaultWord = null)
     {
+      LifetimeDefinition definition = Lifetimes.Define(EternalLifetime.Instance);
+      var lifetime = definition.Lifetime;
+      try
+      {
+        var dialog = new AddArbitraryWordDialog(lifetime, container) { Word = defaultWord };
+
+        if (dialog.ShowDialog() == true)
+        {
+          if (dialog.SelectedSettingsLayer != null)
+          {
+            store = dialog.SelectedSettingsLayer.Model.SettingsStoreContext;
+          }
+
+          var singleLanguageWordStorage = container.GetComponent<ISingleLanguageWordStorage>();
+
+          var dictionaryStorage = new SettingsDictionaryStorage(lifetime, store, singleLanguageWordStorage.Languge);
+
+          dictionaryStorage.AddWord(dialog.Word);
+        }
+      }
+      finally
+      {
+        definition.Terminate();
+      }
+      return;
+
       using (var promptWinForm = new PromptWinForm(null, "Add word to dictionary", "Word:", defaultWord,
                                                              arg =>
                                                                {

File ReSpeller/Bulbs/EditAddToDictionaryBulb.cs

View file
  • Ignore whitespace
-using JetBrains.ProjectModel;
+using JetBrains.Application.Settings;
+using JetBrains.ProjectModel;
 using JetBrains.ReSharper.Daemon;
 using JetBrains.ReSharper.Intentions.Extensibility;
 using JetBrains.TextControl;
+using JetBrains.TextControl.DataConstants;
 using ReSpeller.Actions;
 
 namespace ReSpeller.Bulbs
       myWord = word;
     }
 
+    public string Text { get { return "Add custom word to user dictionary"; } }
+
     public void Execute(ISolution solution, ITextControl textControl)
     {
-      AddArbitraryWordHelper.ShowWordInput(myWord);
+      var settingsStore = solution.GetComponent<ISettingsStore>().BindToContextTransient(textControl.ToContextRange());
+      AddArbitraryWordHelper.ShowWordInput(settingsStore, new SolutionComponentContainerWrapper(solution), myWord);
       Daemon.GetInstance(solution).Invalidate();
     }
 
-    public string Text { get { return "Add custom word to user dictionary"; }
+    private class SolutionComponentContainerWrapper : AddArbitraryWordDialog.IComponentContainerWrapper
+    {
+      private readonly ISolution mySolution;
+
+      public SolutionComponentContainerWrapper(ISolution solution)
+      {
+        mySolution = solution;
+      }
+
+      public T GetComponent<T>() where T : class
+      {
+        return mySolution.GetComponent<T>();
+      }
     }
   }
 }

File ReSpeller/ReSpeller8.x.csproj

View file
  • Ignore whitespace
     <Compile Include="Actions\AboutAction.cs" />
     <Compile Include="Actions\AddArbitraryWordAction.cs" />
     <Compile Include="Actions\AddArbitraryWordHelper.cs" />
+    <Compile Include="Actions\AddArbitraryWordDialog.xaml.cs">
+      <DependentUpon>AddArbitraryWordDialog.xaml</DependentUpon>
+    </Compile>
     <Compile Include="Actions\SpaceshipAction.cs" />
     <Compile Include="Analyzers\AnalyzerHelper.cs" />
     <Compile Include="Analyzers\CommentTypoAnalyzer.cs" />
     </None>
   </ItemGroup>
   <ItemGroup>
+    <Page Include="Actions\AddArbitraryWordDialog.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
     <Page Include="Img\ThemedIcons.Img.Generated.Xaml">
       <DependentUpon>icon.png</DependentUpon>
       <Generator>MSBuild:Compile</Generator>

File ReSpeller/Settings/ReSpellerSettingsControl.xaml

View file
  • Ignore whitespace
         </TabControl.ItemTemplate>
           <TabControl.ContentTemplate>
           <DataTemplate>
-            <TextBox AcceptsReturn="True" Text="{Binding content.Value}"></TextBox>
+            <TextBox AcceptsReturn="True" Text="{Binding content.Value, UpdateSourceTrigger=PropertyChanged}" ></TextBox>
             </DataTemplate>
           </TabControl.ContentTemplate>
         </TabControl>

File ReSpeller/Settings/ReSpellerSettingsControl.xaml.cs

View file
  • Ignore whitespace
   [OptionsPage("ReSpellerSettings", "ReSpeller", typeof(ImgThemedIcons.Icon), ParentId = EnvironmentPage.Pid, Sequence = 100)]
   public partial class ReSpellerSettingsControl : IOptionsPage
   {
-    private readonly OptionsSettingsSmartContext myStore;
-
     public ReSpellerSettingsControl(Lifetime lifetime, OptionsSettingsSmartContext store)
     {
-      myStore = store;
-      myStore.GetKey<ReSpellerSettings>(SettingsOptimization.DoMeSlowly);
       InitializeComponent();
       store.SetBinding(lifetime, (ReSpellerSettings s) => (bool?)s.SplitInPairs, SplitInPairsCheckBox, ToggleButton.IsCheckedProperty);
       store.SetBinding(lifetime, (ReSpellerSettings s) => (int?)s.MinWordLength, MinWordLengthUpDown, IntegerUpDown.ValueProperty);
       var settingsAccessor = OneToSetSettingAccessor<UserDictionariesSettings, string, string>.Create(store, settings => settings.Words);
       UserDictionariesControl.ItemsSource = settingsAccessor
         .GetIndicesWithValues()
-        .Select(_ =>
+        .Select(lang =>
         {
-          var content = new Property<string>(lifetime, "UserDictionaryContent", settingsAccessor.GetValues(_).AggregateString("\r\n"));
-          content.Change.Advise_HasNew(lifetime, args =>
+          var content = new Property<string>(lifetime, "UserDictionaryContent", settingsAccessor.GetValues(lang).AggregateString("\r\n"));
+          content.Change.Advise_RaisingFront(lifetime, args =>
           {
-            settingsAccessor.Clear();
-            settingsAccessor.Add(_, args.New.SplitByNewLine());
+            var oldWords = settingsAccessor.GetValues(lang);
+            var newWords = args.New.SplitByNewLine();
+            foreach (var wordToDelete in oldWords.Except(newWords))
+            {
+              settingsAccessor.Remove(lang, wordToDelete);
+            }
+            settingsAccessor.Add(lang, newWords);
           });
           return new
           {
-            lang = CultureInfo.GetCultureInfo(_.Replace('_', '-')),
+            lang = CultureInfo.GetCultureInfo(lang.Replace('_', '-')),
             content
           };
         });
     }
 
     public EitherControl Control { get { return this; }  }
+
     public string Id { get { return "ReSpellerSettings"; } }
   }
 }

File ReSpeller/SpellEngine/Dictionaries/HunspellDictionaryFactory.cs

View file
  • Ignore whitespace
   public class HunspellDictionaryFactory : IDictionaryFactory
   {
     private readonly Lifetime myLifetime;
-    private readonly IContextBoundSettingsStoreLive myStore;
+    private readonly IContextBoundSettingsStore myStore;
 
-    public HunspellDictionaryFactory(Lifetime lifetime, IContextBoundSettingsStoreLive store)
+    public HunspellDictionaryFactory(Lifetime lifetime, IContextBoundSettingsStore store)
     {
       myLifetime = lifetime;
       myStore = store;

File ReSpeller/SpellEngine/Dictionaries/SettingsDictionaryStorage.cs

View file
  • Ignore whitespace
     private readonly string myLanguage;
     private readonly OneToSetSettingAccessor<UserDictionariesSettings, string, string> mySettingsAccessor;
 
-    public SettingsDictionaryStorage(Lifetime lifetime, IContextBoundSettingsStoreLive contextBoundSettingsStore, string language)
+    public SettingsDictionaryStorage(Lifetime lifetime, IContextBoundSettingsStore contextBoundSettingsStore, string language)
     {
       myLanguage = language;
       mySettingsAccessor = OneToSetSettingAccessor<UserDictionariesSettings, string, string>.Create(contextBoundSettingsStore, settings => settings.Words);
-      contextBoundSettingsStore.AdviseChange(lifetime,
+      contextBoundSettingsStore.SettingsStore.AdviseChange(lifetime,
         contextBoundSettingsStore.Schema.GetIndexedEntry<UserDictionariesSettings, string, bool>(settings => settings.Words),
         ContentChanged);
     }

File ReSpeller/SpellEngine/ISingleLanguageWordStorage.cs

View file
  • Ignore whitespace
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace ReSpeller.SpellEngine
+namespace ReSpeller.SpellEngine
 {
   public interface ISingleLanguageWordStorage
   {
+    string Languge { get; }
+
     void AddWordToUserDict(string word);
+
     // What that?
     void AddWordToCustomDict(string word);
   }

File ReSpeller/SpellEngine/SpellChecker.cs

View file
  • Ignore whitespace
 using System;
 using System.Collections.Generic;
+using JetBrains.Application;
 using JetBrains.Application.Components;
 using JetBrains.Application.Settings;
 using JetBrains.DataFlow;
 
 namespace ReSpeller.SpellEngine
 {
-  [SolutionComponent(ProgramConfigurations.ALL & ~ProgramConfigurations.TEST)]
-  public sealed class SpellChecker : IDisposable, ISpellChecker, ISingleLanguageWordStorage
+  [ShellComponent(ProgramConfigurations.ALL & ~ProgramConfigurations.TEST)]
+  public class SpellChecker : IDisposable, ISpellChecker, ISingleLanguageWordStorage
   {
     private readonly IContextBoundSettingsStoreLive myStore;
     private const string CurrentLanguage = "en_us";
     private readonly ConfigurableWordChecker myWordChecker;
     private readonly IWordSuggestor myWordSuggester;
 
-    public SpellChecker(Lifetime lifetime, ISolution solution, ISettingsStore store)
+    public SpellChecker(Lifetime lifetime, ISettingsStore store)
+      : this(lifetime, store, ContextRange.ApplicationWide) { }
+
+    protected SpellChecker(Lifetime lifetime, ISettingsStore store, ContextRange contextRange)
     {
-      myStore = store.BindToContextLive(lifetime, ContextRange.Smart(solution.ToDataContext()));
+      myStore = store.BindToContextLive(lifetime, contextRange);
       var factory = new HunspellDictionaryFactory(lifetime, myStore);
       myDictionary = factory.CreateDictionary(CurrentLanguage);
       myWordChecker = new ConfigurableWordChecker(new List<ILanguageDictionary> {myDictionary}, new SettingsSpellCheckerConfig(lifetime, myStore));
       myWordSuggester = new SingleDictionarySuggestor(myDictionary);
     }
 
+    public string Languge
+    {
+      get { return myDictionary.Language; }
+    }
+
     void IDisposable.Dispose()
     {
       myDictionary.Dispose();
       }
     }
   }
+
+  [SolutionComponent(ProgramConfigurations.ALL & ~ProgramConfigurations.TEST)]
+  public class SolutionSpellChecker : SpellChecker
+  {
+    public SolutionSpellChecker(Lifetime lifetime, ISolution solution, ISettingsStore store)
+      : base(lifetime, store, ContextRange.Smart(solution.ToDataContext()))
+    {
+    }
+  }
 }