├── .gitignore ├── AlwaysAlignedVS.sln ├── AlwaysAlignedVS ├── About.xaml ├── About.xaml.cs ├── AlwaysAligned.vsct ├── AlwaysAlignedConfigurationService.cs ├── AlwaysAlignedPackage.cs ├── AlwaysAlignedVS.csproj ├── AlwaysAlignedVS.csproj.user ├── AppInfo.cs ├── CommandFilter.cs ├── ElasticTabstopsConverter.cs ├── ElasticTabstopsFormatter.cs ├── ElasticTabstopsProvider.cs ├── ElasticTabstopsSizeManager.cs ├── ExternalSettingsTracker.cs ├── GlobalSuppressions.cs ├── Guids.cs ├── Logo.xaml ├── Logo.xaml.cs ├── MiscGui.cs ├── PkgCmdID.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Settings.Designer.cs │ └── Settings.settings ├── Resources.Designer.cs ├── Resources.resx ├── Resources │ ├── Images_32bit.png │ ├── Package.ico │ ├── icon.png │ ├── logoframes.png │ └── previewimage.png ├── SettingsDialog.xaml ├── SettingsDialog.xaml.cs ├── TextBufferToViewMapService.cs ├── TextMeasureService.cs ├── UpdateTabCreationListener.cs ├── VSPackage.resx ├── VsTextViewCreationListener.cs ├── WeakEvent.cs ├── WpfTextConnectionListener.cs ├── app.config ├── packages.config └── source.extension.vsixmanifest ├── ElasticTabstopsConverterTest ├── ElasticTabstopsConverterTest.cs ├── ElasticTabstopsConverterTest.csproj └── Properties │ └── AssemblyInfo.cs ├── LICENSE.md ├── Local.testsettings ├── README.md ├── TraceAndTestImpact.testsettings └── screencapture.gif /.gitignore: -------------------------------------------------------------------------------- 1 | [Oo]bj 2 | [Bb]in 3 | *.suo 4 | *.[Cc]ache 5 | *.ncb 6 | *.log 7 | *.DS_Store 8 | *.bak 9 | *~ 10 | [Tt]humbs.db 11 | TestResults 12 | packages 13 | 14 | # Visual Studio cache/options directory 15 | .vs/ 16 | -------------------------------------------------------------------------------- /AlwaysAlignedVS.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26730.10 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{789894EA-EFA3-4108-A5BA-49CF882F902E}" 7 | ProjectSection(SolutionItems) = preProject 8 | AlwaysAligned.vsmdi = AlwaysAligned.vsmdi 9 | Local.testsettings = Local.testsettings 10 | TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings 11 | EndProjectSection 12 | EndProject 13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlwaysAlignedVS", "AlwaysAlignedVS\AlwaysAlignedVS.csproj", "{2939A962-7734-4BDA-937F-25DC423B3843}" 14 | EndProject 15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ElasticTabstopsConverterTest", "ElasticTabstopsConverterTest\ElasticTabstopsConverterTest.csproj", "{7A729CF5-BE92-44A2-8A98-B58E92A3AC45}" 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Release|Any CPU = Release|Any CPU 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {2939A962-7734-4BDA-937F-25DC423B3843}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {2939A962-7734-4BDA-937F-25DC423B3843}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {2939A962-7734-4BDA-937F-25DC423B3843}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {2939A962-7734-4BDA-937F-25DC423B3843}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {7A729CF5-BE92-44A2-8A98-B58E92A3AC45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {7A729CF5-BE92-44A2-8A98-B58E92A3AC45}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {7A729CF5-BE92-44A2-8A98-B58E92A3AC45}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {7A729CF5-BE92-44A2-8A98-B58E92A3AC45}.Release|Any CPU.Build.0 = Release|Any CPU 31 | EndGlobalSection 32 | GlobalSection(SolutionProperties) = preSolution 33 | HideSolutionNode = FALSE 34 | EndGlobalSection 35 | GlobalSection(ExtensibilityGlobals) = postSolution 36 | SolutionGuid = {1648C81C-E903-4AB3-9E8C-A52F0321BBD9} 37 | EndGlobalSection 38 | GlobalSection(TestCaseManagementSettings) = postSolution 39 | CategoryFile = AlwaysAligned.vsmdi 40 | EndGlobalSection 41 | EndGlobal 42 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/About.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | This is free software, released under the MIT License. To learn about elastic tabstops, see 24 | nickgravgaard.com/elastic-tabstops/ 25 | 26 | 27 | Copyright © 2010-2017 Nick Gravgaard 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/About.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows; 6 | using System.Windows.Controls; 7 | using System.Windows.Data; 8 | using System.Windows.Documents; 9 | using System.Windows.Input; 10 | using System.Windows.Media; 11 | using System.Windows.Media.Imaging; 12 | using System.Windows.Shapes; 13 | using System.Windows.Navigation; 14 | using System.Diagnostics; 15 | using System.Reflection; 16 | 17 | namespace AlwaysAligned 18 | { 19 | /// 20 | /// Interaction logic for About.xaml 21 | /// 22 | public partial class About : Window 23 | { 24 | public About() 25 | { 26 | InitializeComponent(); 27 | 28 | string informationalVersion = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).ProductVersion; 29 | var fieldsParagraph = new Paragraph(); 30 | fieldsParagraph.Inlines.Add(new Run("Version: ")); 31 | fieldsParagraph.Inlines.Add(new Bold(new Run(informationalVersion))); 32 | 33 | AboutFlowDoc.Blocks.InsertBefore(AboutFlowDoc.Blocks.FirstBlock, fieldsParagraph); 34 | } 35 | 36 | private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e) 37 | { 38 | System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(e.Uri.AbsoluteUri)); 39 | e.Handled = true; 40 | } 41 | 42 | private void Button_Click(object sender, RoutedEventArgs e) 43 | { 44 | this.Close(); 45 | } 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/AlwaysAligned.vsct: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 32 | 39 | 40 | 41 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | DefaultInvisible 55 | DontCache 56 | DynamicVisibility 57 | TextChanges 58 | 59 | Always Aligned 60 | Always Aligned 61 | 62 | 63 | 64 | 65 | 66 | 68 | 69 | 81 | 93 | 105 | 117 | 118 | 119 | 120 | 121 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/AlwaysAlignedConfigurationService.cs: -------------------------------------------------------------------------------- 1 | using AlwaysAligned.Properties; 2 | using System; 3 | 4 | namespace AlwaysAligned 5 | { 6 | public class AlwaysAlignedConfiguration : ICloneable 7 | { 8 | public int MinimumCellWidth { get; set; } 9 | public int CellPadding { get; set; } 10 | public bool Enabled { get; set; } 11 | public bool ConvertOnLoadSave { get; set; } 12 | 13 | public object Clone() 14 | { 15 | return new AlwaysAlignedConfiguration 16 | { 17 | ConvertOnLoadSave = ConvertOnLoadSave, 18 | Enabled = Enabled, 19 | MinimumCellWidth = MinimumCellWidth, 20 | CellPadding = CellPadding 21 | }; 22 | } 23 | } 24 | 25 | public class ConfigurationSavedEventArgs : EventArgs 26 | { 27 | public bool HasChanges { get; internal set; } 28 | } 29 | 30 | public class AlwaysAlignedConfigurationService 31 | { 32 | static readonly Lazy LazyInstance = new Lazy(() => new AlwaysAlignedConfigurationService()); 33 | 34 | private readonly WeakEvent> _configurationSavedWeakEvent = new WeakEvent>(); 35 | 36 | internal event EventHandler ConfigurationChanged 37 | { 38 | add 39 | { 40 | _configurationSavedWeakEvent.AddHandler(value); 41 | } 42 | remove 43 | { 44 | _configurationSavedWeakEvent.RemoveHandler(value); 45 | } 46 | } 47 | 48 | private AlwaysAlignedConfigurationService() 49 | { 50 | 51 | } 52 | 53 | public static AlwaysAlignedConfigurationService Instance 54 | { 55 | get 56 | { 57 | return LazyInstance.Value; 58 | } 59 | } 60 | 61 | internal AlwaysAlignedConfiguration Config; 62 | 63 | public AlwaysAlignedConfiguration GetConfiguration() 64 | { 65 | if (Config == null) 66 | { 67 | Config = new AlwaysAlignedConfiguration 68 | { 69 | Enabled = Settings.Default.Enabled, 70 | ConvertOnLoadSave = Settings.Default.ConvertOnLoadSave, 71 | MinimumCellWidth = Settings.Default.MinimumCellWidth, 72 | CellPadding = Settings.Default.CellPadding 73 | }; 74 | } 75 | return Config; 76 | } 77 | 78 | public void Save() 79 | { 80 | Save(Config); 81 | } 82 | 83 | public void Save(AlwaysAlignedConfiguration config) 84 | { 85 | bool hasChanges = Settings.Default.Enabled != config.Enabled; 86 | Settings.Default.Enabled = config.Enabled; 87 | 88 | hasChanges = hasChanges || (Settings.Default.ConvertOnLoadSave != config.ConvertOnLoadSave); 89 | Settings.Default.ConvertOnLoadSave = config.ConvertOnLoadSave; 90 | 91 | hasChanges = hasChanges || (Settings.Default.MinimumCellWidth != config.MinimumCellWidth); 92 | Settings.Default.MinimumCellWidth = (int)config.MinimumCellWidth; 93 | 94 | hasChanges = hasChanges || (Settings.Default.CellPadding != config.CellPadding); 95 | Settings.Default.CellPadding = (int)config.CellPadding; 96 | 97 | Settings.Default.Save(); 98 | 99 | //Refresh config 100 | Config = null; 101 | OnConfigurationSaved(new ConfigurationSavedEventArgs { HasChanges = hasChanges }); 102 | } 103 | 104 | public string GetSettingsAsString() 105 | { 106 | var templ = "Enabled: {0}, ConvertOnLoadSave: {1}, MinimumCellWidth: {2}, CellPadding: {3}"; 107 | if (Config == null) 108 | { 109 | return string.Format(templ, 110 | Settings.Default.Enabled, 111 | Settings.Default.ConvertOnLoadSave, 112 | Settings.Default.MinimumCellWidth, 113 | Settings.Default.CellPadding 114 | ); 115 | } 116 | else 117 | { 118 | return string.Format(templ, 119 | Config.Enabled, 120 | Config.ConvertOnLoadSave, 121 | Config.MinimumCellWidth, 122 | Config.CellPadding 123 | ); 124 | } 125 | } 126 | 127 | public void OnExternalConfigurationChanged() 128 | { 129 | OnConfigurationSaved(new ConfigurationSavedEventArgs { HasChanges = true }); 130 | } 131 | 132 | private void OnConfigurationSaved(ConfigurationSavedEventArgs eventArgs) 133 | { 134 | _configurationSavedWeakEvent.Raise(this, eventArgs); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/AlwaysAlignedPackage.cs: -------------------------------------------------------------------------------- 1 | using EnvDTE; 2 | using Microsoft.VisualStudio.Shell; 3 | using Microsoft.VisualStudio.Shell.Interop; 4 | using System; 5 | using System.ComponentModel.Design; 6 | using System.Diagnostics; 7 | using System.Globalization; 8 | using System.Runtime.InteropServices; 9 | 10 | namespace AlwaysAligned 11 | { 12 | /// 13 | /// This is the class that implements the package exposed by this assembly. 14 | /// 15 | /// The minimum requirement for a class to be considered a valid package for Visual Studio 16 | /// is to implement the IVsPackage interface and register itself with the shell. 17 | /// This package uses the helper classes defined inside the Managed Package Framework (MPF) 18 | /// to do it: it derives from the Package class that provides the implementation of the 19 | /// IVsPackage interface and uses the registration attributes defined in the framework to 20 | /// register itself and its components with the shell. 21 | /// 22 | // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is 23 | // a package. 24 | [PackageRegistration(UseManagedResourcesOnly = true)] 25 | // This attribute is used to register the informations needed to show the this package 26 | // in the Help/About dialog of Visual Studio. 27 | [InstalledProductRegistration("#110", "#112", "2017.0.0", IconResourceID = 400)] 28 | // This attribute is needed to let the shell know that this package exposes some menus. 29 | [ProvideMenuResource("Menus.ctmenu", 1)] 30 | [Guid(GuidList.guidAlwaysAlignedPkgString)] 31 | [ProvideAutoLoad(Microsoft.VisualStudio.Shell.Interop.UIContextGuids80.NoSolution)] 32 | public sealed class AlwaysAlignedPackage : Package 33 | { 34 | private static AlwaysAlignedPackage _package; 35 | 36 | public static AlwaysAlignedPackage Instance 37 | { 38 | get { return _package; } 39 | } 40 | 41 | /// 42 | /// Default constructor of the package. 43 | /// Inside this method you can place any initialization code that does not require 44 | /// any Visual Studio service because at this point the package object is created but 45 | /// not sited yet inside Visual Studio environment. The place to do all the other 46 | /// initialization is the Initialize method. 47 | /// 48 | public AlwaysAlignedPackage() 49 | { 50 | Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this.ToString())); 51 | _package = this; 52 | } 53 | 54 | ///////////////////////////////////////////////////////////////////////////// 55 | // Overriden Package Implementation 56 | 57 | /// 58 | /// Initialization of the package; this method is called right after the package is sited, so this is the place 59 | /// where you can put all the initilaization code that rely on services provided by VisualStudio. 60 | /// 61 | protected override void Initialize() 62 | { 63 | Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString())); 64 | 65 | base.Initialize(); 66 | 67 | AppInfo.appObject = (EnvDTE80.DTE2)ServiceProvider.GlobalProvider.GetService(typeof(DTE)); 68 | 69 | ExternalSettingsTracker.Start(); 70 | 71 | OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService; 72 | if (mcs != null) 73 | { 74 | // Create the command for the menu item. 75 | CommandID menuCommandID = new CommandID(GuidList.guidAlwaysAlignedCmdSet, (int)PkgCmdIDList.AlwaysAlignedMenu); 76 | OleMenuCommand menuItem = new OleMenuCommand(MenuItemCallback, menuCommandID); 77 | menuItem.BeforeQueryStatus += new EventHandler(OnBeforeQueryStatusSetMenuText); 78 | mcs.AddCommand(menuItem); 79 | } 80 | } 81 | 82 | private void MenuItemCallback(object sender, EventArgs e) 83 | { 84 | IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell)); 85 | Guid clsid = Guid.Empty; 86 | int result; 87 | 88 | Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure( 89 | uiShell.ShowMessageBox( 90 | 0, ref clsid, 91 | "FirstPackage", 92 | string.Format(CultureInfo.CurrentCulture, 93 | "Inside {0}.MenuItemCallback()", this.ToString()), 94 | string.Empty, 0, 95 | OLEMSGBUTTON.OLEMSGBUTTON_OK, 96 | OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, 97 | OLEMSGICON.OLEMSGICON_INFO, 98 | 0, out result)); 99 | } 100 | 101 | bool menuTextSet = false; 102 | private void OnBeforeQueryStatusSetMenuText(object sender, EventArgs e) 103 | { 104 | if (!menuTextSet) 105 | { 106 | var myCommand = sender as OleMenuCommand; 107 | if (myCommand != null) 108 | { 109 | myCommand.Text = "Always Aligned"; 110 | menuTextSet = true; 111 | } 112 | } 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/AlwaysAlignedVS.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | {2939A962-7734-4BDA-937F-25DC423B3843} 9 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10 | Library 11 | Properties 12 | AlwaysAligned 13 | AlwaysAlignedVS 14 | True 15 | 16 | 17 | v4.6 18 | 15.0 19 | 20 | 21 | 22 | 23 | 4.0 24 | false 25 | 26 | publish\ 27 | true 28 | Disk 29 | false 30 | Foreground 31 | 7 32 | Days 33 | false 34 | false 35 | true 36 | 0 37 | 1.0.0.%2a 38 | false 39 | true 40 | 41 | 42 | true 43 | full 44 | false 45 | bin\Debug\ 46 | TRACE;DEBUG 47 | prompt 48 | 4 49 | false 50 | 51 | 52 | pdbonly 53 | true 54 | bin\Release\ 55 | TRACE 56 | prompt 57 | 4 58 | true 59 | false 60 | 61 | 62 | 63 | False 64 | 65 | 66 | False 67 | 68 | 69 | 70 | True 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | True 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | {00020430-0000-0000-C000-000000000046} 107 | 2 108 | 0 109 | 0 110 | primary 111 | False 112 | False 113 | 114 | 115 | 116 | 117 | About.xaml 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | Logo.xaml 130 | 131 | 132 | 133 | True 134 | True 135 | Settings.settings 136 | 137 | 138 | True 139 | True 140 | Resources.resx 141 | 142 | 143 | 144 | 145 | 146 | 147 | SettingsDialog.xaml 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | ResXFileCodeGenerator 159 | Resources.Designer.cs 160 | Designer 161 | 162 | 163 | true 164 | VSPackage 165 | 166 | 167 | 168 | 169 | Designer 170 | 171 | 172 | 173 | SettingsSingleFileGenerator 174 | Settings.Designer.cs 175 | 176 | 177 | Designer 178 | 179 | 180 | 181 | 182 | Menus.ctmenu 183 | Designer 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | Always 193 | true 194 | 195 | 196 | true 197 | Always 198 | 199 | 200 | true 201 | Always 202 | 203 | 204 | 205 | 206 | 207 | MSBuild:Compile 208 | Designer 209 | 210 | 211 | MSBuild:Compile 212 | Designer 213 | 214 | 215 | MSBuild:Compile 216 | Designer 217 | 218 | 219 | 220 | 221 | False 222 | Microsoft .NET Framework 4 %28x86 and x64%29 223 | true 224 | 225 | 226 | False 227 | .NET Framework 3.5 SP1 Client Profile 228 | false 229 | 230 | 231 | False 232 | .NET Framework 3.5 SP1 233 | false 234 | 235 | 236 | False 237 | Windows Installer 4.5 238 | true 239 | 240 | 241 | 242 | true 243 | 244 | 245 | 10.0 246 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 247 | 248 | 249 | 250 | 251 | 258 | 259 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/AlwaysAlignedVS.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Program 5 | C:\Program Files %28x86%29\Microsoft Visual Studio\2017\Community\Common7\IDE\devenv.exe 6 | /rootsuffix Exp 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | en-US 16 | false 17 | 18 | 19 | Program 20 | C:\Program Files %28x86%29\Microsoft Visual Studio\2017\Community\Common7\IDE\devenv.exe 21 | /rootsuffix Exp 22 | 23 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/AppInfo.cs: -------------------------------------------------------------------------------- 1 | using EnvDTE80; 2 | 3 | namespace AlwaysAligned 4 | { 5 | static class AppInfo 6 | { 7 | public static DTE2 appObject = null; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/CommandFilter.cs: -------------------------------------------------------------------------------- 1 | using EnvDTE; 2 | using Microsoft.VisualStudio; 3 | using Microsoft.VisualStudio.OLE.Interop; 4 | using Microsoft.VisualStudio.Text.Editor; 5 | using System; 6 | 7 | namespace AlwaysAligned 8 | { 9 | class CommandFilter : IOleCommandTarget 10 | { 11 | public CommandFilter(IWpfTextView view) 12 | { 13 | //changeMenuText(); 14 | } 15 | 16 | internal IOleCommandTarget Next { get; set; } 17 | 18 | public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) 19 | { 20 | if (pguidCmdGroup == GuidList.guidAlwaysAlignedCmdSet) 21 | { 22 | switch (nCmdID) 23 | { 24 | case PkgCmdIDList.cmdidSettings: 25 | _btnConfigure_Click(); 26 | return VSConstants.S_OK; 27 | case PkgCmdIDList.cmdidConvertToSpaces: 28 | _btnAlignSpaces_Click(); 29 | return VSConstants.S_OK; 30 | case PkgCmdIDList.cmdidConvertToElasticTabstops: 31 | _btnAlignTabsElastic_Click(); 32 | return VSConstants.S_OK; 33 | case PkgCmdIDList.cmdidAbout: 34 | _btnInfoDialog_Click(); 35 | return VSConstants.S_OK; 36 | default: 37 | break; 38 | } 39 | } 40 | 41 | return Next.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); 42 | } 43 | 44 | public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) 45 | { 46 | if (pguidCmdGroup == GuidList.guidAlwaysAlignedCmdSet) 47 | { 48 | switch (prgCmds[0].cmdID) 49 | { 50 | case PkgCmdIDList.cmdidSettings: 51 | prgCmds[0].cmdf = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); 52 | break; 53 | case PkgCmdIDList.cmdidConvertToSpaces: 54 | prgCmds[0].cmdf = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); 55 | break; 56 | case PkgCmdIDList.cmdidConvertToElasticTabstops: 57 | prgCmds[0].cmdf = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); 58 | break; 59 | case PkgCmdIDList.cmdidAbout: 60 | prgCmds[0].cmdf = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); 61 | break; 62 | default: 63 | break; 64 | } 65 | return VSConstants.S_OK; 66 | } 67 | 68 | return Next.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); 69 | } 70 | 71 | private void _btnAlignSpaces_Click() 72 | { 73 | if (AppInfo.appObject.ActiveDocument == null) return; 74 | 75 | var doc = (TextDocument)AppInfo.appObject.ActiveDocument.Object("TextDocument"); 76 | if (doc == null) return; 77 | 78 | string text = doc.StartPoint.CreateEditPoint().GetText(doc.EndPoint); 79 | string convertedText = ElasticTabstopsConverter.ToSpaces(text, doc.TabSize); 80 | doc.ReplaceText(text, convertedText); 81 | } 82 | 83 | private void _btnAlignTabsElastic_Click() 84 | { 85 | if (AppInfo.appObject.ActiveDocument == null) return; 86 | 87 | var doc = (TextDocument)AppInfo.appObject.ActiveDocument.Object("TextDocument"); 88 | if (doc == null) return; 89 | 90 | string text = doc.StartPoint.CreateEditPoint().GetText(doc.EndPoint); 91 | string convertedText = ElasticTabstopsConverter.ToElasticTabstops(text, doc.TabSize); 92 | doc.ReplaceText(text, convertedText); 93 | } 94 | 95 | private void _btnConfigure_Click() 96 | { 97 | var sd = new SettingsDialog 98 | { 99 | WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen, 100 | ShowInTaskbar = false 101 | }; 102 | sd.ShowDialog(); 103 | } 104 | 105 | private void _btnInfoDialog_Click() 106 | { 107 | var sd = new About 108 | { 109 | WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen, 110 | ShowInTaskbar = false 111 | }; 112 | sd.ShowDialog(); 113 | } 114 | 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/ElasticTabstopsConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | // TODO: Rewrite this so it's based off my much nicer implementation at: 7 | // https://github.com/nickgravgaard/ElasticNotepad/blob/master/src/main/scala/elasticTabstops.scala 8 | 9 | namespace AlwaysAligned 10 | { 11 | public class Cell 12 | { 13 | public int IndexFromBOF; 14 | public int Length; 15 | 16 | public Cell() 17 | { 18 | Length = 0; 19 | } 20 | 21 | public Cell(int indexFromBOF, int length) 22 | { 23 | IndexFromBOF = indexFromBOF; 24 | Length = length; 25 | } 26 | } 27 | 28 | public class Line 29 | { 30 | public SortedDictionary Cells; 31 | public bool EndsInCR; 32 | 33 | public Line() 34 | { 35 | Cells = new SortedDictionary(); 36 | EndsInCR = false; 37 | } 38 | 39 | public Line(SortedDictionary cells) 40 | { 41 | Cells = cells; 42 | EndsInCR = false; 43 | } 44 | } 45 | 46 | 47 | public class ElasticTabstopsConverter 48 | { 49 | public static bool CellExists(List lines, int lineNum, int cellNum) 50 | { 51 | return (lineNum < lines.Count) && (cellNum < lines[lineNum].Cells.Count); 52 | } 53 | public static bool CellExists(List> list, int lineNum, int cellNum) 54 | { 55 | return (lineNum < list.Count) && (cellNum < list[lineNum].Count); 56 | } 57 | 58 | 59 | private static int CalcFixedCellSize(int textLen, int tabSize) 60 | { 61 | if (tabSize > 0) 62 | { 63 | return ((int)Math.Ceiling((textLen + 2.0) / tabSize)) * tabSize; 64 | } 65 | return tabSize; 66 | } 67 | 68 | 69 | public static List GetLines(string text, int tabSize) 70 | { 71 | var lines = new List(); 72 | var line = new Line(); 73 | lines.Add(line); 74 | var inText = false; 75 | var previousCharIsSpace = false; 76 | var textLength = 0; 77 | var pos = 0; 78 | var startPos = 0; 79 | var startCharNum = 0; 80 | for (var charNum = 0; charNum < text.Length; charNum++) 81 | { 82 | var currentChar = text[charNum]; 83 | switch (currentChar) 84 | { 85 | case '\r': 86 | { 87 | line.EndsInCR = true; 88 | break; 89 | } 90 | case '\n': 91 | { 92 | if (inText) 93 | { 94 | line.Cells.Add(startPos, new Cell(startCharNum, textLength)); 95 | } 96 | line = new Line(); 97 | lines.Add(line); 98 | pos = 0; 99 | inText = false; 100 | previousCharIsSpace = false; 101 | break; 102 | } 103 | case '\t': 104 | { 105 | if (inText) 106 | { 107 | if (previousCharIsSpace) 108 | { 109 | line.Cells.Add(startPos, new Cell(startCharNum, textLength - 1)); 110 | } 111 | else 112 | { 113 | line.Cells.Add(startPos, new Cell(startCharNum, textLength)); 114 | } 115 | inText = false; 116 | } 117 | previousCharIsSpace = false; 118 | int expand = tabSize - (pos % tabSize); 119 | pos += expand; 120 | break; 121 | } 122 | case ' ': 123 | { 124 | if (previousCharIsSpace && inText) 125 | { 126 | line.Cells.Add(startPos, new Cell(startCharNum, textLength - 1)); 127 | inText = false; 128 | } 129 | previousCharIsSpace = true; 130 | textLength++; 131 | pos++; 132 | break; 133 | } 134 | default: 135 | { 136 | if (!inText) 137 | { 138 | startPos = pos; 139 | startCharNum = charNum; 140 | textLength = 0; 141 | } 142 | inText = true; 143 | previousCharIsSpace = false; 144 | textLength++; 145 | pos++; 146 | break; 147 | } 148 | } 149 | } 150 | if (inText) 151 | { 152 | line.Cells.Add(startPos, new Cell(startCharNum, textLength)); 153 | } 154 | return lines; 155 | } 156 | 157 | 158 | enum AtPosResults 159 | { 160 | PastEndOfLine, 161 | CellStart, 162 | CellMiddle, 163 | Space, 164 | }; 165 | 166 | static AtPosResults CellExistsAtPos(int position, SortedDictionary cells) 167 | { 168 | if (cells.Count == 0) return AtPosResults.PastEndOfLine; 169 | 170 | if (cells.ContainsKey(position)) return AtPosResults.CellStart; 171 | 172 | foreach (KeyValuePair cell in cells) 173 | { 174 | if (cell.Key > position) 175 | { 176 | return AtPosResults.Space; 177 | } 178 | 179 | if (position >= cell.Key && position <= cell.Key + cell.Value.Length + 2) 180 | { 181 | return AtPosResults.CellMiddle; 182 | } 183 | } 184 | return AtPosResults.PastEndOfLine; 185 | } 186 | 187 | 188 | static void InsertEmptyCells(ref List lines, int pos, int firstBlockLineNum, int lastBlockLineNum) 189 | { 190 | for (var blockLineNum = firstBlockLineNum; blockLineNum <= lastBlockLineNum; blockLineNum++) 191 | { 192 | lines[blockLineNum].Cells.Add(pos, new Cell()); 193 | } 194 | } 195 | 196 | 197 | public static String ToElasticTabstops(String text, int tabSize = 4) 198 | { 199 | var lines = GetLines(text, tabSize); 200 | var maxCells = lines.Aggregate(0, (current, line) => Math.Max(line.Cells.Count, current)); 201 | 202 | for (var lineNum = 0; lineNum < lines.Count; lineNum++) 203 | { 204 | var line = lines[lineNum]; 205 | foreach (KeyValuePair cell in line.Cells) 206 | { 207 | int position = cell.Key; 208 | for (var i = lineNum + 1; i <= lines.Count - 1; i++) 209 | { 210 | var atPosResult = CellExistsAtPos(position, lines[i].Cells); 211 | if (atPosResult == AtPosResults.CellStart) 212 | { 213 | continue; 214 | } 215 | if (atPosResult == AtPosResults.Space) 216 | { 217 | lines[i].Cells.Add(position, new Cell()); 218 | continue; 219 | } 220 | else 221 | { 222 | break; 223 | } 224 | } 225 | } 226 | } 227 | 228 | var maxPos = 0; 229 | foreach (var line in lines) 230 | { 231 | var cells = line.Cells; 232 | if (cells.Count > 0) 233 | { 234 | var lastCell = cells.Last(); 235 | var lineEnd = lastCell.Key + lastCell.Value.Length; 236 | maxPos = Math.Max(maxPos, lineEnd); 237 | } 238 | } 239 | 240 | for (var pos = 0; pos < maxPos; pos += tabSize) 241 | { 242 | var startingNewBlock = true; 243 | var firstBlockLineNum = 0; 244 | var lastBlockLineNum = 0; 245 | var allSpaces = true; 246 | 247 | for (var lineNum = 0; lineNum < lines.Count; lineNum++) 248 | { 249 | var line = lines[lineNum]; 250 | 251 | var atPosResult = CellExistsAtPos(pos, line.Cells); 252 | if (atPosResult == AtPosResults.Space || atPosResult == AtPosResults.CellMiddle) 253 | { 254 | if (atPosResult != AtPosResults.Space) 255 | { 256 | allSpaces = false; 257 | } 258 | if (startingNewBlock) 259 | { 260 | firstBlockLineNum = lineNum; 261 | startingNewBlock = false; 262 | } 263 | lastBlockLineNum = lineNum; 264 | } 265 | else 266 | { 267 | if (!startingNewBlock) 268 | { 269 | if (allSpaces) 270 | { 271 | InsertEmptyCells(ref lines, pos, firstBlockLineNum, lastBlockLineNum); 272 | } 273 | startingNewBlock = true; 274 | allSpaces = true; 275 | } 276 | } 277 | } 278 | if (!startingNewBlock && allSpaces) 279 | { 280 | InsertEmptyCells(ref lines, pos, firstBlockLineNum, lastBlockLineNum); 281 | } 282 | } 283 | 284 | var builder = new StringBuilder(text.Length); 285 | var lastLine = lines.Last(); 286 | foreach (var line in lines) 287 | { 288 | if (line.Cells.Count > 0) 289 | { 290 | var lastCell = line.Cells.Last(); 291 | foreach (var cell in line.Cells) 292 | { 293 | if (cell.Value.Length > 0) 294 | { 295 | builder.Append(text.Substring(cell.Value.IndexFromBOF, cell.Value.Length)); 296 | } 297 | if (!cell.Equals(lastCell)) 298 | { 299 | builder.Append('\t'); 300 | } 301 | } 302 | } 303 | if (!line.Equals(lastLine)) 304 | { 305 | if (line.EndsInCR) 306 | { 307 | builder.Append('\r'); 308 | } 309 | builder.Append('\n'); 310 | } 311 | } 312 | return builder.ToString(); 313 | } 314 | 315 | 316 | public static String ToSpaces(String text, int tabSize = 4) 317 | { 318 | var textLines = text.Split('\n'); 319 | IList> lines = textLines.Select(textLine => textLine.Split('\t')).Cast>().ToList(); 320 | 321 | List> sizes = new List>(); 322 | foreach (var line in lines) 323 | { 324 | List sizesLine = new List(); 325 | foreach (var cell in line) 326 | { 327 | sizesLine.Add(CalcFixedCellSize(cell.Length, tabSize)); 328 | } 329 | sizes.Add(sizesLine); 330 | } 331 | 332 | var maxCells = lines.Aggregate(0, (current, line) => Math.Max(current, line.Count)); 333 | var nofLines = lines.Count; 334 | 335 | for (int cellNum = 0; cellNum < maxCells; cellNum++) 336 | { 337 | var startingNewBlock = true; 338 | int startRange = 0; 339 | int endRange = 0; 340 | int maxWidth = 0; 341 | 342 | for (int lineNum = 0; lineNum < nofLines; lineNum++) 343 | { 344 | if (CellExists(sizes, lineNum, cellNum) && CellExists(sizes, lineNum, cellNum + 1)) 345 | { 346 | if (startingNewBlock) 347 | { 348 | startRange = lineNum; 349 | startingNewBlock = false; 350 | } 351 | maxWidth = Math.Max(maxWidth, sizes[lineNum][cellNum]); 352 | endRange = lineNum; 353 | } 354 | else 355 | { 356 | 357 | if (!startingNewBlock) 358 | { 359 | for (var blockcellNum = startRange; blockcellNum <= endRange; blockcellNum++) 360 | { 361 | sizes[blockcellNum][cellNum] = maxWidth; 362 | } 363 | startingNewBlock = true; 364 | maxWidth = 0; 365 | } 366 | } 367 | } 368 | 369 | if (!startingNewBlock) 370 | { 371 | for (int blockcellNum = startRange; blockcellNum <= endRange; blockcellNum++) 372 | { 373 | sizes[blockcellNum][cellNum] = maxWidth; 374 | } 375 | } 376 | } 377 | 378 | // build final string 379 | IList newText = new List(); 380 | for (var lineNum = 0; lineNum < nofLines; lineNum++) 381 | { 382 | string newLine = ""; 383 | for (var cellNum = 0; cellNum < lines[lineNum].Count; cellNum++) 384 | { 385 | newLine += lines[lineNum][cellNum]; 386 | if (cellNum != lines[lineNum].Count - 1) 387 | { 388 | var nofSpaces = sizes[lineNum][cellNum] - lines[lineNum][cellNum].Length; 389 | newLine += new string(' ', nofSpaces); 390 | } 391 | } 392 | newText.Add(newLine); 393 | } 394 | return String.Join("\n", newText); 395 | } 396 | 397 | } 398 | } 399 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/ElasticTabstopsFormatter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Text.Formatting; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows.Media.TextFormatting; 5 | 6 | namespace AlwaysAligned 7 | { 8 | /// 9 | /// Provides text formatting properties. 10 | /// 11 | internal class ElasticTabstopsFormatter : TextFormattingParagraphProperties 12 | { 13 | private readonly double[] _tabSizes; 14 | 15 | /// 16 | /// Creates an instance of ElasticTabstopsFormatter 17 | /// 18 | internal ElasticTabstopsFormatter( 19 | TextFormattingRunProperties textProperties, 20 | IFormattedLineSource formattedLineSource, double[] tabSizes) 21 | : base(textProperties, formattedLineSource.ColumnWidth * formattedLineSource.TabSize) 22 | { 23 | _tabSizes = tabSizes; 24 | } 25 | 26 | /// 27 | /// Gets a collection of tab definitions. 28 | /// 29 | public override IList Tabs 30 | { 31 | get 32 | { 33 | var tabList = _tabSizes.Select((ts, i) => new TextTabProperties(TextTabAlignment.Left, ts, 0, 0)).ToList(); 34 | return tabList; 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/ElasticTabstopsProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Text; 2 | using Microsoft.VisualStudio.Text.Editor; 3 | using Microsoft.VisualStudio.Text.Formatting; 4 | using Microsoft.VisualStudio.Utilities; 5 | using System.ComponentModel.Composition; 6 | using System.Linq; 7 | using System.Windows.Media.TextFormatting; 8 | 9 | namespace AlwaysAligned 10 | { 11 | /// 12 | /// Creates ElasticTabstopsFormatter classes 13 | /// to be used when lines on the view are being formatted. 14 | /// 15 | [Export(typeof(ITextParagraphPropertiesFactoryService))] 16 | [ContentType("text")] 17 | [TextViewRole(PredefinedTextViewRoles.Document)] 18 | internal class ElasticTabstopsProvider : ITextParagraphPropertiesFactoryService 19 | { 20 | [Import] 21 | private ITextBufferToViewMapService _textBufferToViewMapService = null; 22 | 23 | /// 24 | /// Creates an ElasticTabstopsFormatters for 25 | /// the provided configuration. 26 | /// 27 | public TextParagraphProperties Create(IFormattedLineSource formattedLineSource, TextFormattingRunProperties textProperties, 28 | IMappingSpan line, IMappingPoint lineStart, int lineSegment) 29 | { 30 | if (!AlwaysAlignedConfigurationService.Instance.GetConfiguration().Enabled) 31 | { 32 | return new TextFormattingParagraphProperties(textProperties, formattedLineSource.ColumnWidth * formattedLineSource.TabSize); 33 | } 34 | 35 | IWpfTextView textView = _textBufferToViewMapService.GetViewByFormattedLineSource(formattedLineSource); 36 | //View is not initialized yet 37 | if (textView == null) 38 | { 39 | return new TextFormattingParagraphProperties(textProperties, formattedLineSource.ColumnWidth * formattedLineSource.TabSize); 40 | } 41 | var manager = ElasticTabstopsSizeManager.Get(textView); 42 | 43 | ITextSnapshot textSnapshot = formattedLineSource.SourceTextSnapshot; 44 | ITextBuffer textBuffer = textSnapshot.TextBuffer; 45 | 46 | var normalizedspancoll = line.GetSpans(textBuffer); 47 | ITextSnapshotLine currentLine = textSnapshot.GetLineFromPosition(normalizedspancoll.First().Start.Position); 48 | 49 | //Get tab offset calculated by ElasticTabstopsSizeManager 50 | double[] tabOffsets = manager.GetTabOffsets(currentLine); 51 | 52 | return new ElasticTabstopsFormatter(textProperties, formattedLineSource, tabOffsets); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/ElasticTabstopsSizeManager.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Text; 2 | using Microsoft.VisualStudio.Text.Editor; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | namespace AlwaysAligned 8 | { 9 | /// 10 | /// Represents class that calculates elastic tabstops for TextBuffer 11 | /// 12 | internal class ElasticTabstopsSizeManager 13 | { 14 | #region Inner Definitions 15 | 16 | private enum CalculateDirection 17 | { 18 | Down, 19 | Up, 20 | DownUp 21 | } 22 | 23 | /// 24 | /// Keep Elastic Tabstop data for a line 25 | /// 26 | private class ElasticTabstopsLine 27 | { 28 | /// 29 | /// Gets or sets Elastic Columns in the line 30 | /// 31 | internal ElasticTabstopsColumn[] ElasticColumns { get; set; } 32 | 33 | /// 34 | /// Returns elastic column information if exists, or null otherwise 35 | /// 36 | internal ElasticTabstopsColumn GetColumnOrDefault(int colNum) 37 | { 38 | if (ElasticColumns.Length > colNum) 39 | return ElasticColumns[colNum]; 40 | return null; 41 | } 42 | 43 | /// 44 | /// Returns true if given column is the last column in the line, 45 | /// false otherwise 46 | /// 47 | internal bool IsLastColumnInLine(int colNum) 48 | { 49 | return colNum + 1 == ElasticColumns.Length; 50 | } 51 | 52 | /// 53 | /// Returns true if current line offsets changed regarding to given line 54 | /// false otherwise 55 | /// 56 | internal bool ChangedRegardingTo(ElasticTabstopsLine oldLine) 57 | { 58 | if (ElasticColumns == oldLine.ElasticColumns) 59 | return false; 60 | 61 | if (ElasticColumns.Length != oldLine.ElasticColumns.Length) 62 | return true; 63 | 64 | return ElasticColumns.Where((etc, i) => etc.ChangedRegardingTo(oldLine.ElasticColumns[i])).Any(); 65 | } 66 | } 67 | 68 | /// 69 | /// Keeps data about ElasticColumn 70 | /// 71 | private class ElasticTabstopsColumn 72 | { 73 | /// 74 | /// Gets or sets start position of this column in the TextBuffer 75 | /// 76 | internal int Start { get; set; } 77 | 78 | /// 79 | /// Gets or sets text length in the line 80 | /// 81 | internal int ColumnTextLength { get; set; } 82 | /// 83 | /// Gets or sets Tab Offset of the line 84 | /// 85 | internal ColumnSizeInfo TabOffset { get; set; } 86 | 87 | /// 88 | /// Returns true if column contains changed, false otherwise 89 | /// 90 | internal bool ChangedRegardingTo(ElasticTabstopsColumn elasticTabstopsColumn) 91 | { 92 | if (this == elasticTabstopsColumn) 93 | return false; 94 | 95 | if (ColumnTextLength != elasticTabstopsColumn.ColumnTextLength) 96 | return true; 97 | 98 | return TabOffset.ChangedRegardingTo(elasticTabstopsColumn.TabOffset); 99 | } 100 | } 101 | 102 | /// 103 | /// Keep data about tab ofset 104 | /// 105 | private class ColumnSizeInfo 106 | { 107 | /// 108 | /// Real tab offset of the column 109 | /// 110 | internal double TabOffset { get; set; } 111 | /// 112 | /// Column real width 113 | /// 114 | internal double ColumnWidth { get; set; } 115 | 116 | /// 117 | /// returns true if column size contains change, false otherwise 118 | /// 119 | /// 120 | /// 121 | internal bool ChangedRegardingTo(ColumnSizeInfo columnSizeInfo) 122 | { 123 | return Math.Abs(TabOffset - columnSizeInfo.TabOffset) > 1.0E-10 || Math.Abs(ColumnWidth - columnSizeInfo.ColumnWidth) > 1.0E-10; 124 | } 125 | } 126 | 127 | #endregion 128 | 129 | private List _elasticTabstopsLinesCache; 130 | private readonly IWpfTextView _textView; 131 | 132 | private double _minCellWidth; 133 | private double _paddingWidth; 134 | private readonly TextMeasureService _textMeasureService; 135 | /// 136 | /// Initialize new instance of ElasticTabstopsSizeManager for IWpfTextView 137 | /// 138 | private ElasticTabstopsSizeManager(IWpfTextView textView) 139 | { 140 | _textView = textView; 141 | _textMeasureService = TextMeasureService.Create(_textView); 142 | 143 | InvalidateTabstops(); 144 | } 145 | 146 | /// 147 | /// Get tab offsets for line 148 | /// 149 | internal double[] GetTabOffsets(ITextSnapshotLine line) 150 | { 151 | //VS is trying to Format textbuffer with old version, 152 | //not actual for AlwaysAligned 153 | if (line.LineNumber >= _elasticTabstopsLinesCache.Count) 154 | { 155 | return new double[0]; 156 | } 157 | //returns tab offsets from cache 158 | var tabOffsets = _elasticTabstopsLinesCache[line.LineNumber].ElasticColumns.TakeWhile(etc => etc.TabOffset != null).Select(etc => etc.TabOffset.TabOffset).ToArray(); 159 | return tabOffsets; 160 | } 161 | 162 | /// 163 | /// Invalidates tabs cache depending given changes, and return changed line numbers 164 | /// 165 | internal void InvalidateChanges() 166 | { 167 | InvalidateTabstops(); 168 | } 169 | 170 | /// 171 | /// Invalidates tabs cache depending given changes, and return changed line numbers 172 | /// 173 | /// changed made 174 | internal void InvalidateChanges(INormalizedTextChangeCollection changes) 175 | { 176 | if (!changes.Any()) return; 177 | 178 | #region Old 179 | 180 | var firstChange = changes.First(); 181 | 182 | var start = Math.Min(firstChange.OldSpan.Start, firstChange.NewSpan.Start); 183 | var end = Math.Max(firstChange.OldSpan.End, firstChange.NewSpan.End); 184 | 185 | foreach (var change in changes) 186 | { 187 | var lineNumber = _textView.TextSnapshot.GetLineNumberFromPosition(change.NewPosition); 188 | 189 | if (change.LineCountDelta > 0) 190 | { 191 | _elasticTabstopsLinesCache.InsertRange(lineNumber, 192 | Enumerable.Range(0, change.LineCountDelta).Select( 193 | c => new ElasticTabstopsLine())); 194 | } 195 | else if (change.LineCountDelta < 0) 196 | { 197 | _elasticTabstopsLinesCache.RemoveRange(lineNumber, -change.LineCountDelta); 198 | } 199 | 200 | start = Math.Min(start, Math.Min(change.OldSpan.Start, change.NewSpan.Start)); 201 | end = Math.Max(end, Math.Max(change.OldSpan.End, change.NewSpan.End)); 202 | } 203 | 204 | var topLine = _textView.TextSnapshot.GetLineFromPosition(start); 205 | var topLineNumber = topLine.LineNumber; 206 | 207 | if (changes.IncludesLineChanges && topLineNumber != 0) 208 | { 209 | topLineNumber--; 210 | topLine = _textView.TextSnapshot.GetLineFromLineNumber(topLineNumber); 211 | } 212 | 213 | while (topLineNumber > 0 214 | && topLine.Start != topLine.End) 215 | { 216 | topLineNumber--; 217 | topLine = _textView.TextSnapshot.GetLineFromLineNumber(topLineNumber); 218 | } 219 | 220 | end = Math.Min(end, Math.Max(0, _textView.TextSnapshot.Length - 1)); 221 | 222 | var bottomLine = _textView.TextSnapshot.GetLineFromPosition(end); 223 | var bottomLineNumber = bottomLine.LineNumber; 224 | 225 | if (changes.IncludesLineChanges && bottomLineNumber < _textView.TextSnapshot.LineCount - 1) 226 | { 227 | bottomLineNumber++; 228 | bottomLine = _textView.TextSnapshot.GetLineFromLineNumber(bottomLineNumber); 229 | } 230 | 231 | 232 | while (bottomLineNumber < _textView.TextSnapshot.LineCount - 1 233 | && bottomLine.Start != bottomLine.End) 234 | { 235 | bottomLineNumber++; 236 | bottomLine = _textView.TextSnapshot.GetLineFromLineNumber(bottomLineNumber); 237 | } 238 | 239 | #endregion 240 | 241 | //InvalidateChanges(); 242 | for (var i = topLineNumber; i <= bottomLineNumber; i++) 243 | { 244 | _elasticTabstopsLinesCache[i] = new ElasticTabstopsLine(); 245 | } 246 | 247 | for (var i = topLineNumber; i <= bottomLineNumber; i++) 248 | { 249 | var line = _textView.TextSnapshot.GetLineFromLineNumber(i); 250 | CalculateTabOffsets(line, CalculateDirection.Down, false); 251 | } 252 | 253 | } 254 | 255 | /// 256 | /// Invalidates tabstops cache for textBuffer 257 | /// 258 | private void InvalidateTabstops() 259 | { 260 | _minCellWidth = AlwaysAlignedConfigurationService.Instance.GetConfiguration().MinimumCellWidth; 261 | _paddingWidth = AlwaysAlignedConfigurationService.Instance.GetConfiguration().CellPadding; 262 | 263 | ITextSnapshot textSnapshot = _textView.TextSnapshot; 264 | //Create empty ElasticTabstopsLine for each line 265 | _elasticTabstopsLinesCache = Enumerable.Range(0, textSnapshot.LineCount).Select(i => new ElasticTabstopsLine()).ToList(); 266 | 267 | //Build _elasticTabstopsLinesCache by calculating from top to down 268 | foreach (var line in textSnapshot.Lines) 269 | { 270 | CalculateTabOffsets(line, CalculateDirection.Down, false); 271 | } 272 | } 273 | 274 | /// 275 | /// Calculate tab offsets for line in a given direction 276 | /// 277 | private void CalculateTabOffsets(ITextSnapshotLine line, CalculateDirection direction, bool forceInvalidate) 278 | { 279 | //Calculates tab offset for a given line for the given direction 280 | ElasticTabstopsLine elasticLine = GetElasticTabstopsLine(line, forceInvalidate); 281 | 282 | for (int colNumber = 0; colNumber < elasticLine.ElasticColumns.Length; colNumber++) 283 | { 284 | ElasticTabstopsColumn column = elasticLine.ElasticColumns[colNumber]; 285 | 286 | //Tab offset is allready calculated during other line calculation 287 | if (!forceInvalidate && column.TabOffset != null) 288 | continue; 289 | 290 | //Assign the same ColumnTabOffset to all columns in the same block 291 | ColumnSizeInfo colTabOffset = new ColumnSizeInfo 292 | { 293 | TabOffset = CalculateInitialTabOffset(elasticLine, colNumber), 294 | ColumnWidth = CalculateInitialWidth(elasticLine, colNumber) 295 | }; 296 | 297 | column.TabOffset = colTabOffset; 298 | 299 | switch (direction) 300 | { 301 | case CalculateDirection.Up: 302 | CalculateTabOffsetUp(line, colNumber, colTabOffset); 303 | break; 304 | case CalculateDirection.Down: 305 | CalculateTabOffsetDown(line, colNumber, colTabOffset); 306 | break; 307 | case CalculateDirection.DownUp: 308 | CalculateTabOffsetDown(line, colNumber, colTabOffset); 309 | CalculateTabOffsetUp(line, colNumber, colTabOffset); 310 | break; 311 | default: 312 | throw new ArgumentException("direction"); 313 | } 314 | } 315 | } 316 | 317 | /// 318 | /// Calculate tab offsets by going down 319 | /// 320 | private void CalculateTabOffsetDown(ITextSnapshotLine curLine, int colNumber, ColumnSizeInfo colTabOffset) 321 | { 322 | int curLineNumber = curLine.LineNumber + 1; 323 | 324 | ITextSnapshot textSnapshot = _textView.TextSnapshot; 325 | 326 | ElasticTabstopsLine elasticLine = _elasticTabstopsLinesCache[curLine.LineNumber]; 327 | bool isLastColumnInLine = elasticLine.IsLastColumnInLine(colNumber); 328 | 329 | while ( curLineNumber < textSnapshot.LineCount) 330 | { 331 | ITextSnapshotLine downLine = textSnapshot.GetLineFromLineNumber(curLineNumber); 332 | ElasticTabstopsLine downElasticLine = GetElasticTabstopsLine(downLine); 333 | 334 | if (downElasticLine.IsLastColumnInLine(colNumber) != isLastColumnInLine) 335 | { 336 | break; 337 | } 338 | 339 | ElasticTabstopsColumn downColumn = downElasticLine.GetColumnOrDefault(colNumber); 340 | 341 | if (downColumn == null) 342 | { 343 | break; 344 | } 345 | 346 | downColumn.TabOffset = colTabOffset; 347 | ShrinkTabOffset(downElasticLine, colNumber); 348 | curLineNumber++; 349 | } 350 | } 351 | 352 | /// 353 | /// Calculate tab offsets by going up 354 | /// 355 | private void CalculateTabOffsetUp(ITextSnapshotLine curLine, int colNumber, ColumnSizeInfo colTabOffset) 356 | { 357 | int curLineNumber = curLine.LineNumber - 1; 358 | 359 | ITextSnapshot textSnapshot = _textView.TextSnapshot; 360 | 361 | ElasticTabstopsLine elasticLine = _elasticTabstopsLinesCache[curLine.LineNumber]; 362 | bool isLastColumnInLine = elasticLine.IsLastColumnInLine(colNumber); 363 | 364 | while (curLineNumber >= 0) 365 | { 366 | ITextSnapshotLine upLine = textSnapshot.GetLineFromLineNumber(curLineNumber); 367 | ElasticTabstopsLine upElasticLine = GetElasticTabstopsLine(upLine); 368 | 369 | if (upElasticLine.IsLastColumnInLine(colNumber) != isLastColumnInLine) 370 | { 371 | break; 372 | } 373 | 374 | ElasticTabstopsColumn upColumn = upElasticLine.GetColumnOrDefault(colNumber); 375 | 376 | if (upColumn == null) 377 | { 378 | break; 379 | } 380 | 381 | upColumn.TabOffset = colTabOffset; 382 | ShrinkTabOffset(upElasticLine, colNumber); 383 | curLineNumber--; 384 | } 385 | } 386 | 387 | /// 388 | /// Fix tab offset for a column if needed 389 | /// 390 | private void ShrinkTabOffset(ElasticTabstopsLine tabLine, int colNumber) 391 | { 392 | ElasticTabstopsColumn colTabOffset = tabLine.ElasticColumns[colNumber]; 393 | 394 | double width = CalculateInitialWidth(tabLine, colNumber); 395 | 396 | if (colTabOffset.TabOffset.ColumnWidth < width) 397 | { 398 | colTabOffset.TabOffset.ColumnWidth = width; 399 | colTabOffset.TabOffset.TabOffset = CalculateInitialTabOffset(tabLine, colNumber); 400 | } 401 | } 402 | 403 | /// 404 | /// Calculates column width for a specific column in specific line 405 | /// 406 | private double CalculateInitialWidth(ElasticTabstopsLine elasticLine, int colNumber) 407 | { 408 | ITextSnapshot textSnapshot = _textView.TextSnapshot; 409 | ElasticTabstopsColumn column = elasticLine.ElasticColumns[colNumber]; 410 | Span span = new Span(column.Start, column.ColumnTextLength); 411 | if (span.Start > textSnapshot.Length || span.End > textSnapshot.Length) 412 | { 413 | return 0; 414 | } 415 | 416 | SnapshotSpan columnSpan = new SnapshotSpan(textSnapshot, span); 417 | 418 | double columnWidth = _textMeasureService.GetWidth(columnSpan); 419 | 420 | return Math.Max(columnWidth, _minCellWidth); 421 | } 422 | 423 | /// 424 | /// Calulates tab offset depending column widthes before current column 425 | /// This method assume that column widthes before current column are calculated allready 426 | /// 427 | private double CalculateInitialTabOffset(ElasticTabstopsLine tabLine, int colNumber) 428 | { 429 | return tabLine.ElasticColumns.Take(colNumber).Sum(ct => ct.TabOffset.ColumnWidth) + colNumber * _paddingWidth; 430 | } 431 | 432 | /// 433 | /// Returns ElasticTabstopsLine with initialized ElasticColumns 434 | /// 435 | private ElasticTabstopsLine GetElasticTabstopsLine(ITextSnapshotLine line, bool forceInvalidateColumns = false) 436 | { 437 | ElasticTabstopsLine elasticTabstopsLine = _elasticTabstopsLinesCache[line.LineNumber]; 438 | if (elasticTabstopsLine.ElasticColumns == null || forceInvalidateColumns) 439 | { 440 | string lineText = line.GetText(); 441 | string[] tabSplits = lineText.Split('\t'); 442 | elasticTabstopsLine.ElasticColumns = new ElasticTabstopsColumn[tabSplits.Length]; 443 | int curPosInLine = line.Start.Position; 444 | for (int i = 0; i < tabSplits.Length; i++) 445 | { 446 | string ts = tabSplits[i]; 447 | ElasticTabstopsColumn column = new ElasticTabstopsColumn {ColumnTextLength = ts.Length, Start = curPosInLine}; 448 | //skeep tab 449 | curPosInLine += ts.Length + 1; 450 | elasticTabstopsLine.ElasticColumns[i] = column; 451 | } 452 | } 453 | return elasticTabstopsLine; 454 | } 455 | 456 | internal static ElasticTabstopsSizeManager Get(IWpfTextView textView) 457 | { 458 | if (textView == null) 459 | return null; 460 | ElasticTabstopsSizeManager outManager; 461 | textView.Properties.TryGetProperty(typeof(ElasticTabstopsSizeManager), out outManager); 462 | return outManager; 463 | } 464 | 465 | internal static ElasticTabstopsSizeManager Create(IWpfTextView textView) 466 | { 467 | if (textView == null) 468 | return null; 469 | return textView.Properties.GetOrCreateSingletonProperty(() => new ElasticTabstopsSizeManager(textView)); 470 | } 471 | } 472 | } 473 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/ExternalSettingsTracker.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.OLE.Interop; 2 | using Microsoft.VisualStudio.Shell; 3 | using Microsoft.VisualStudio.TextManager.Interop; 4 | using System; 5 | using System.Linq; 6 | 7 | namespace AlwaysAligned 8 | { 9 | internal class ExternalSettingsTracker 10 | { 11 | public class TextManagerEvents : IVsTextManagerEvents 12 | { 13 | public void OnRegisterMarkerType(int iMarkerType) 14 | { 15 | } 16 | 17 | public void OnRegisterView(IVsTextView pView) 18 | { 19 | } 20 | 21 | public void OnUnregisterView(IVsTextView pView) 22 | { 23 | } 24 | 25 | private FONTCOLORPREFERENCES[] _pColorPrefs; 26 | public void OnUserPreferencesChanged(VIEWPREFERENCES[] pViewPrefs, FRAMEPREFERENCES[] pFramePrefs, LANGPREFERENCES[] pLangPrefs, FONTCOLORPREFERENCES[] pColorPrefs) 27 | { 28 | if (pColorPrefs == null || !pColorPrefs.Any()) 29 | { 30 | _pColorPrefs = null; 31 | return; 32 | } 33 | if (_pColorPrefs != null 34 | && _pColorPrefs.Count() == pColorPrefs.Count() 35 | && _pColorPrefs.Zip(pColorPrefs, (fr1, fr2) => new Tuple(fr1, fr2)).All( 36 | t => CheckFontColorReferencesAreEqual(t.Item1, t.Item2))) 37 | { 38 | return; 39 | } 40 | 41 | _pColorPrefs = pColorPrefs; 42 | 43 | AlwaysAlignedConfigurationService.Instance.OnExternalConfigurationChanged(); 44 | } 45 | 46 | private bool CheckFontColorReferencesAreEqual(FONTCOLORPREFERENCES fr1, FONTCOLORPREFERENCES fr2) 47 | { 48 | return fr1.hBoldViewFont == fr2.hBoldViewFont 49 | && fr1.hRegularViewFont == fr2.hRegularViewFont 50 | && fr1.pguidColorCategory == fr2.pguidColorCategory 51 | && fr1.pguidFontCategory == fr2.pguidFontCategory; 52 | } 53 | } 54 | 55 | public static void Start() 56 | { 57 | var textManager = ServiceProvider.GlobalProvider.GetService(typeof(SVsTextManager)) as IVsTextManager; 58 | 59 | var container = (IConnectionPointContainer)textManager; 60 | IConnectionPoint textManagerEventsConnection = null; 61 | var eventGuid = typeof(IVsTextManagerEvents).GUID; 62 | if (container != null) 63 | { 64 | container.FindConnectionPoint(ref eventGuid, out textManagerEventsConnection); 65 | } 66 | var textManagerEvents = new TextManagerEvents(); 67 | uint textManagerCookie; 68 | if (textManagerEventsConnection != null) 69 | { 70 | textManagerEventsConnection.Advise(textManagerEvents, out textManagerCookie); 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. Project-level 3 | // suppressions either have no target or are given a specific target 4 | // and scoped to a namespace, type, member, etc. 5 | // 6 | // To add a suppression to this file, right-click the message in the 7 | // Error List, point to "Suppress Message(s)", and click "In Project 8 | // Suppression File". You do not need to add suppressions to this 9 | // file manually. 10 | 11 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1017:MarkAssembliesWithComVisible")] 12 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/Guids.cs: -------------------------------------------------------------------------------- 1 | // Guids.cs 2 | // MUST match guids.h 3 | using System; 4 | 5 | namespace AlwaysAligned 6 | { 7 | static class GuidList 8 | { 9 | public const string guidAlwaysAlignedPkgString = "c982f983-3dce-4114-91c0-e534dd039dda"; 10 | public const string guidAlwaysAlignedCmdSetString = "234580c4-8a2c-4ae1-8e4f-5bc708b188fe"; 11 | 12 | public static readonly Guid guidAlwaysAlignedCmdSet = new Guid(guidAlwaysAlignedCmdSetString); 13 | }; 14 | } -------------------------------------------------------------------------------- /AlwaysAlignedVS/Logo.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/Logo.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | using System.Windows.Threading; 5 | 6 | namespace AlwaysAligned 7 | { 8 | /// 9 | /// Interaction logic for Logo.xaml 10 | /// 11 | public partial class Logo : UserControl 12 | { 13 | DispatcherTimer dispatcherTimer = new DispatcherTimer(); 14 | private int preAnimDelay = 6; 15 | private readonly int numFrames = 14; 16 | private int currentFrameNum = 0; 17 | private int logoHeight; 18 | 19 | public Logo() 20 | { 21 | InitializeComponent(); 22 | 23 | logoHeight = (int)spritesheet.Source.Height / numFrames; 24 | 25 | dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick); 26 | dispatcherTimer.Interval = TimeSpan.FromMilliseconds(250); 27 | dispatcherTimer.Start(); 28 | 29 | this.DataContext = this; // so we can bind LogoHeight 30 | } 31 | 32 | public double LogoHeight 33 | { 34 | get { return logoHeight; } 35 | } 36 | 37 | private void dispatcherTimer_Tick(object sender, EventArgs e) 38 | { 39 | if (preAnimDelay > 0) 40 | { 41 | preAnimDelay--; 42 | return; 43 | } 44 | currentFrameNum++; 45 | if (currentFrameNum >= numFrames - 1) 46 | { 47 | dispatcherTimer.Stop(); 48 | } 49 | scrollViewer.ScrollToVerticalOffset(logoHeight * currentFrameNum); 50 | } 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/MiscGui.cs: -------------------------------------------------------------------------------- 1 | using EnvDTE; 2 | using Microsoft.VisualStudio.Shell; 3 | using Microsoft.VisualStudio.Shell.Interop; 4 | using System; 5 | 6 | namespace AlwaysAligned 7 | { 8 | class MiscGui 9 | { 10 | internal abstract class EnvDTEConstants 11 | { 12 | public const string vsWindowKindOutput = "{34E76E81-EE4A-11D0-AE2E-00A0C90FFFC3}"; 13 | } 14 | 15 | public static void WriteOutput(string outputText) 16 | { 17 | IVsOutputWindow outputWindow = Package.GetGlobalService(typeof(SVsOutputWindow)) as IVsOutputWindow; 18 | 19 | Guid guidGeneral = Microsoft.VisualStudio.VSConstants.OutputWindowPaneGuid.GeneralPane_guid; 20 | IVsOutputWindowPane pane; 21 | int hr = outputWindow.CreatePane(guidGeneral, "General", 1, 0); 22 | hr = outputWindow.GetPane(guidGeneral, out pane); 23 | pane.Activate(); 24 | pane.OutputString(outputText); 25 | pane.Activate(); 26 | 27 | DTE dte = Package.GetGlobalService(typeof(SDTE)) as DTE; 28 | Window win = dte.Windows.Item(EnvDTEConstants.vsWindowKindOutput); 29 | win.Visible = true; 30 | } 31 | 32 | public static void ShowModal(string title, string caption, OLEMSGBUTTON msgbtn, OLEMSGDEFBUTTON msgdefbtn, OLEMSGICON msgicon) 33 | { 34 | ServiceProvider serviceProvider = new ServiceProvider(((DTE)Microsoft.VisualStudio.Shell.ServiceProvider.GlobalProvider.GetService(typeof(DTE))) as Microsoft.VisualStudio.OLE.Interop.IServiceProvider); 35 | IVsUIShell uiShell = serviceProvider.GetService(typeof(SVsUIShell)) as IVsUIShell; 36 | 37 | var id = Guid.Empty; 38 | int result; 39 | uiShell.ShowMessageBox( 40 | 0, ref id, 41 | title, caption, 42 | string.Empty, 0, 43 | msgbtn, msgdefbtn, msgicon, 44 | 0, out result); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/PkgCmdID.cs: -------------------------------------------------------------------------------- 1 | // PkgCmdID.cs 2 | // MUST match PkgCmdID.h 3 | 4 | namespace AlwaysAligned 5 | { 6 | static class PkgCmdIDList 7 | { 8 | public const uint AlwaysAlignedMenu = 0x100; 9 | public const uint cmdidConvertToSpaces = 0x300; 10 | public const uint cmdidConvertToElasticTabstops = 0x301; 11 | public const uint cmdidSettings = 0x302; 12 | public const uint cmdidAbout = 0x303; 13 | 14 | }; 15 | } -------------------------------------------------------------------------------- /AlwaysAlignedVS/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Resources; 4 | using System.Runtime.CompilerServices; 5 | using System.Runtime.InteropServices; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("AlwaysAlignedVS")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("Always Aligned VS (elastic tabstops for Visual Studio)")] 15 | [assembly: AssemblyCopyright("Copyright © 2010-2017 Nick Gravgaard")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | [assembly: ComVisible(false)] 19 | [assembly: CLSCompliant(false)] 20 | [assembly: NeutralResourcesLanguage("en-US")] 21 | 22 | // Version information for an assembly consists of the following four values: 23 | // 24 | // Major Version 25 | // Minor Version 26 | // Build Number 27 | // Revision 28 | // 29 | // You can specify all the values or you can default the Revision and Build Numbers 30 | // by using the '*' as shown below: 31 | 32 | [assembly: AssemblyVersion("2017.0.0.*")] 33 | [assembly: AssemblyInformationalVersion("2017.0.0")] 34 | // [assembly: AssemblyFileVersion("0.0.0.0")] 35 | 36 | [assembly: InternalsVisibleTo("MyProject.Domain.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c5270c496849ad8424ebb61da8fc757eb1f7bca4e9efaa9a7b742dad21093784d4218f7a786e7ee4985266904ec76b992bda7c4ead130af05390c47c0504c93278e4da28bdf5fac8e35144f787479793085d5d2df821cfb915a44be90534df569531dc3d3fdc34d2c336d50be53211473af866ea701e475bd3c194a0a8b2b9a3")] 37 | 38 | //[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("ElasticTabstopsConverterTest")] 39 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace AlwaysAligned.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.3.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | 26 | [global::System.Configuration.UserScopedSettingAttribute()] 27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 28 | [global::System.Configuration.DefaultSettingValueAttribute("True")] 29 | public bool Enabled { 30 | get { 31 | return ((bool)(this["Enabled"])); 32 | } 33 | set { 34 | this["Enabled"] = value; 35 | } 36 | } 37 | 38 | [global::System.Configuration.UserScopedSettingAttribute()] 39 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 40 | [global::System.Configuration.DefaultSettingValueAttribute("False")] 41 | public bool ConvertOnLoadSave { 42 | get { 43 | return ((bool)(this["ConvertOnLoadSave"])); 44 | } 45 | set { 46 | this["ConvertOnLoadSave"] = value; 47 | } 48 | } 49 | 50 | [global::System.Configuration.UserScopedSettingAttribute()] 51 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 52 | [global::System.Configuration.DefaultSettingValueAttribute("16")] 53 | public int MinimumCellWidth { 54 | get { 55 | return ((int)(this["MinimumCellWidth"])); 56 | } 57 | set { 58 | this["MinimumCellWidth"] = value; 59 | } 60 | } 61 | 62 | [global::System.Configuration.UserScopedSettingAttribute()] 63 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 64 | [global::System.Configuration.DefaultSettingValueAttribute("16")] 65 | public int CellPadding { 66 | get { 67 | return ((int)(this["CellPadding"])); 68 | } 69 | set { 70 | this["CellPadding"] = value; 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | True 7 | 8 | 9 | False 10 | 11 | 12 | 16 13 | 14 | 15 | 16 16 | 17 | 18 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace AlwaysAligned { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AlwaysAligned.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to -----BEGIN PUBLIC KEY----- 65 | ///MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqTR3rMSVbkuIIWsU1O/M 66 | ///MVZahfkwA+Vz+UYshxFfrfc4NOHGIElGWirXEtIp4OtufPgmMGpkBMqQwQQVtb8p 67 | ///fl0EVv+wq78RQm6HIrG0qUZJrPcx/Y7M5EQy5+qUfTVj0t1MB9Zi/2SmjaSqp4Uv 68 | ///Ka3syOcHvH92ZNm78PuL/h7hC4uTyJQR9+CdY1/LnfhloWwuK2nvFEoLWxbPEzou 69 | ///7UjRiqxfAEQlGs4fAKYtQGid4QBFgQ4LT8siCEbgE2lO8pFAtJvjJtaE6/SpJD5t 70 | ///R1IqIQgsqdg2Y+mSUymdZlN2OoWU5LRex1cKcecq9FcBFoILBmHOEBOewSPJ8NG8 71 | ///zQIDAQAB 72 | ///-----END PUBLIC KEY-----. 73 | /// 74 | internal static string pubkey_pem { 75 | get { 76 | return ResourceManager.GetString("pubkey_pem", resourceCulture); 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | -----BEGIN PUBLIC KEY----- 122 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqTR3rMSVbkuIIWsU1O/M 123 | MVZahfkwA+Vz+UYshxFfrfc4NOHGIElGWirXEtIp4OtufPgmMGpkBMqQwQQVtb8p 124 | fl0EVv+wq78RQm6HIrG0qUZJrPcx/Y7M5EQy5+qUfTVj0t1MB9Zi/2SmjaSqp4Uv 125 | Ka3syOcHvH92ZNm78PuL/h7hC4uTyJQR9+CdY1/LnfhloWwuK2nvFEoLWxbPEzou 126 | 7UjRiqxfAEQlGs4fAKYtQGid4QBFgQ4LT8siCEbgE2lO8pFAtJvjJtaE6/SpJD5t 127 | R1IqIQgsqdg2Y+mSUymdZlN2OoWU5LRex1cKcecq9FcBFoILBmHOEBOewSPJ8NG8 128 | zQIDAQAB 129 | -----END PUBLIC KEY----- 130 | 131 | -------------------------------------------------------------------------------- /AlwaysAlignedVS/Resources/Images_32bit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nick-gravgaard/AlwaysAlignedVS/b6c7fb2e938e87c2a14f1b2c682fac3222448168/AlwaysAlignedVS/Resources/Images_32bit.png -------------------------------------------------------------------------------- /AlwaysAlignedVS/Resources/Package.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nick-gravgaard/AlwaysAlignedVS/b6c7fb2e938e87c2a14f1b2c682fac3222448168/AlwaysAlignedVS/Resources/Package.ico -------------------------------------------------------------------------------- /AlwaysAlignedVS/Resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nick-gravgaard/AlwaysAlignedVS/b6c7fb2e938e87c2a14f1b2c682fac3222448168/AlwaysAlignedVS/Resources/icon.png -------------------------------------------------------------------------------- /AlwaysAlignedVS/Resources/logoframes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nick-gravgaard/AlwaysAlignedVS/b6c7fb2e938e87c2a14f1b2c682fac3222448168/AlwaysAlignedVS/Resources/logoframes.png -------------------------------------------------------------------------------- /AlwaysAlignedVS/Resources/previewimage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nick-gravgaard/AlwaysAlignedVS/b6c7fb2e938e87c2a14f1b2c682fac3222448168/AlwaysAlignedVS/Resources/previewimage.png -------------------------------------------------------------------------------- /AlwaysAlignedVS/SettingsDialog.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |