├── images ├── ContextMenu.png ├── CommandWindow.png ├── FontsAndColors.png ├── EditorGuidelines_90px.png └── EditorGuidelines_128px.png ├── src ├── ColumnGuidePackage │ ├── Resources │ │ ├── Package.ico │ │ └── Images_32bit.png │ ├── packages.config │ ├── PkgCmdID.cs │ ├── app.config │ ├── Guids.cs │ ├── GlobalSuppressions.cs │ ├── Telemetry.cs │ ├── TextEditorGuidesSettingsRendezvous.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── VSPackage.resx │ ├── ColumnGuidePackage.vsct │ ├── ColumnGuidePackage.csproj │ └── ColumnGuidePackage.cs ├── References │ └── 11.0.0.0 │ │ ├── Microsoft.VisualStudio.Editor.dll │ │ ├── Microsoft.VisualStudio.Text.UI.dll │ │ ├── Microsoft.VisualStudio.CoreUtility.dll │ │ ├── Microsoft.VisualStudio.Shell.11.0.dll │ │ ├── Microsoft.VisualStudio.Text.Data.dll │ │ ├── Microsoft.VisualStudio.Text.UI.Wpf.dll │ │ └── Microsoft.VisualStudio.ComponentModelHost.dll ├── ColumnGuide │ ├── packages.config │ ├── ITextEditorGuidesSettings.cs │ ├── ITelemetry.cs │ ├── ITextEditorGuidesSettingsChanger.cs │ ├── GuidelineColorDefinition.cs │ ├── HostServices.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Telemetry.cs │ ├── ColumnGuide.ruleset │ ├── ColumnGuideFactory.cs │ ├── ColumnGuide.cs │ ├── ColumnGuide.csproj │ └── TextEditorGuidesSettings.cs ├── VSIX │ ├── app.config │ ├── ReleaseNotes.txt │ ├── source.extension.vsixmanifest │ └── Editor Guidelines.csproj └── Editor Guidelines.sln ├── LICENSE ├── README.md └── .gitignore /images/ContextMenu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/EditorGuidelines/master/images/ContextMenu.png -------------------------------------------------------------------------------- /images/CommandWindow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/EditorGuidelines/master/images/CommandWindow.png -------------------------------------------------------------------------------- /images/FontsAndColors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/EditorGuidelines/master/images/FontsAndColors.png -------------------------------------------------------------------------------- /images/EditorGuidelines_90px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/EditorGuidelines/master/images/EditorGuidelines_90px.png -------------------------------------------------------------------------------- /images/EditorGuidelines_128px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/EditorGuidelines/master/images/EditorGuidelines_128px.png -------------------------------------------------------------------------------- /src/ColumnGuidePackage/Resources/Package.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/EditorGuidelines/master/src/ColumnGuidePackage/Resources/Package.ico -------------------------------------------------------------------------------- /src/ColumnGuidePackage/Resources/Images_32bit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/EditorGuidelines/master/src/ColumnGuidePackage/Resources/Images_32bit.png -------------------------------------------------------------------------------- /src/References/11.0.0.0/Microsoft.VisualStudio.Editor.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/EditorGuidelines/master/src/References/11.0.0.0/Microsoft.VisualStudio.Editor.dll -------------------------------------------------------------------------------- /src/References/11.0.0.0/Microsoft.VisualStudio.Text.UI.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/EditorGuidelines/master/src/References/11.0.0.0/Microsoft.VisualStudio.Text.UI.dll -------------------------------------------------------------------------------- /src/ColumnGuide/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/References/11.0.0.0/Microsoft.VisualStudio.CoreUtility.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/EditorGuidelines/master/src/References/11.0.0.0/Microsoft.VisualStudio.CoreUtility.dll -------------------------------------------------------------------------------- /src/References/11.0.0.0/Microsoft.VisualStudio.Shell.11.0.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/EditorGuidelines/master/src/References/11.0.0.0/Microsoft.VisualStudio.Shell.11.0.dll -------------------------------------------------------------------------------- /src/References/11.0.0.0/Microsoft.VisualStudio.Text.Data.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/EditorGuidelines/master/src/References/11.0.0.0/Microsoft.VisualStudio.Text.Data.dll -------------------------------------------------------------------------------- /src/References/11.0.0.0/Microsoft.VisualStudio.Text.UI.Wpf.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/EditorGuidelines/master/src/References/11.0.0.0/Microsoft.VisualStudio.Text.UI.Wpf.dll -------------------------------------------------------------------------------- /src/ColumnGuidePackage/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/References/11.0.0.0/Microsoft.VisualStudio.ComponentModelHost.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/EditorGuidelines/master/src/References/11.0.0.0/Microsoft.VisualStudio.ComponentModelHost.dll -------------------------------------------------------------------------------- /src/ColumnGuide/ITextEditorGuidesSettings.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ColumnGuide 4 | { 5 | interface ITextEditorGuidesSettings 6 | { 7 | IEnumerable GuideLinePositionsInChars { get; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/ColumnGuidePackage/PkgCmdID.cs: -------------------------------------------------------------------------------- 1 | // PkgCmdID.cs 2 | // MUST match PkgCmdID.h 3 | using System; 4 | 5 | namespace Microsoft.ColumnGuidePackage 6 | { 7 | enum PkgCmdIDList 8 | { 9 | cmdidAddColumnGuideline = 0x100, 10 | cmdidRemoveColumnGuideline = 0x101, 11 | cmdidRemoveAllColumnGuidelines = 0x103 12 | }; 13 | } -------------------------------------------------------------------------------- /src/ColumnGuide/ITelemetry.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.ApplicationInsights; 2 | 3 | namespace ColumnGuide 4 | { 5 | public interface ITelemetry 6 | { 7 | /// 8 | /// Access the Application Insights telemetry client shared between both components. 9 | /// 10 | TelemetryClient Client { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/ColumnGuide/ITextEditorGuidesSettingsChanger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ColumnGuide 4 | { 5 | public interface ITextEditorGuidesSettingsChanger 6 | { 7 | bool AddGuideline(int column); 8 | bool RemoveGuideline(int column); 9 | bool CanAddGuideline(int column); 10 | bool CanRemoveGuideline(int column); 11 | void RemoveAllGuidelines(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/VSIX/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/ColumnGuidePackage/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/ColumnGuidePackage/Guids.cs: -------------------------------------------------------------------------------- 1 | // Guids.cs 2 | // MUST match guids.h 3 | using System; 4 | 5 | namespace Microsoft.ColumnGuidePackage 6 | { 7 | static class GuidList 8 | { 9 | public const string guidColumnGuidePkgString = "a0b80b01-be16-4c42-ab44-7f8d057faa2f"; 10 | public const string guidColumnGuideCmdSetString = "5aa4cf31-6030-4655-99e7-239b331103f3"; 11 | 12 | public static readonly Guid guidColumnGuideCmdSet = new Guid(guidColumnGuideCmdSetString); 13 | }; 14 | } -------------------------------------------------------------------------------- /src/ColumnGuidePackage/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 | -------------------------------------------------------------------------------- /src/ColumnGuidePackage/Telemetry.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.ApplicationInsights; 2 | using Microsoft.VisualStudio.ComponentModelHost; 3 | using Microsoft.VisualStudio.Shell; 4 | using ColumnGuide; 5 | 6 | namespace Microsoft.ColumnGuidePackage 7 | { 8 | internal static class Telemetry 9 | { 10 | public static readonly TelemetryClient Client = ImportClient(); 11 | 12 | private static TelemetryClient ImportClient() 13 | { 14 | IComponentModel componentModel = (IComponentModel)Package.GetGlobalService(typeof(SComponentModel)); 15 | var telemetry = componentModel.GetService(); 16 | return telemetry.Client; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/ColumnGuide/GuidelineColorDefinition.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Text.Classification; 2 | using Microsoft.VisualStudio.Utilities; 3 | using System.ComponentModel.Composition; 4 | using System.Windows.Media; 5 | 6 | namespace ColumnGuide 7 | { 8 | [Export(typeof(EditorFormatDefinition)), UserVisible(true), Name(GuidelineColorDefinition.Name)] 9 | public class GuidelineColorDefinition : EditorFormatDefinition 10 | { 11 | internal const string Name = "Guideline"; 12 | 13 | public GuidelineColorDefinition() 14 | { 15 | this.DisplayName = "Guideline"; 16 | this.ForegroundCustomizable = false; 17 | this.BackgroundColor = Colors.DarkRed; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/ColumnGuidePackage/TextEditorGuidesSettingsRendezvous.cs: -------------------------------------------------------------------------------- 1 | using ColumnGuide; 2 | using Microsoft.VisualStudio.ComponentModelHost; 3 | using Microsoft.VisualStudio.Shell; 4 | 5 | namespace Microsoft.ColumnGuidePackage 6 | { 7 | static class TextEditorGuidesSettingsRendezvous 8 | { 9 | private static ITextEditorGuidesSettingsChanger _instance; 10 | public static ITextEditorGuidesSettingsChanger Instance 11 | { 12 | get 13 | { 14 | if (_instance == null) 15 | { 16 | IComponentModel componentModel = (IComponentModel)Package.GetGlobalService(typeof(SComponentModel)); 17 | _instance = componentModel.GetService(); 18 | } 19 | 20 | return _instance; 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/ColumnGuide/HostServices.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Shell; 4 | using Microsoft.VisualStudio.Shell.Interop; 5 | 6 | namespace ColumnGuide 7 | { 8 | [Export] 9 | sealed class HostServices 10 | { 11 | [Import(typeof(SVsServiceProvider))] 12 | IServiceProvider ServiceProvider 13 | { 14 | get; 15 | set; 16 | } 17 | 18 | public T GetService(Type serviceType) where T : class 19 | { 20 | return ServiceProvider.GetService(serviceType) as T; 21 | } 22 | 23 | // Add services here 24 | 25 | public IVsSettingsManager SettingsManagerService 26 | { 27 | get 28 | { 29 | return GetService(typeof(SVsSettingsManager)); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/VSIX/ReleaseNotes.txt: -------------------------------------------------------------------------------- 1 | Editor Guidelines Version 2.0.3 (December 26th 2017) 1. Updated icon with one from the PPT artwork team. Version 2.0.2 (December 24th 2017) 1. Update icon again. Version 2.0.1 (December 24th 2017) 1. Update icon (with permission from PPT team). Version 2.0.0 (December 23rd 2017) 1. Changed to MIT license. 2. Open sourced on https://github.com/pharring/EditorGuidelines Version 1.15.61202.0 (December 2nd 2016) 1. Thinned out telemetry events. Version 1.15.61129.0 (November 29th 2016) 1. Moved color selection into 'Fonts and Colors' section in Tools/Options. Version 1.15.61103.1 (November 3rd 2016) 1. Added usage telemetry Version 1.15.61102.0 (November 2nd 2016) 1. Updated to support VS "15" RC 2. Updated copyright year to 2016 Version 1.11.70722.0 (July 22nd 2015) 1. Fixed a bug where the guideline menu was missing from context menu in HTML files in VS 2013. 2. Added "Remove All Guidelines" command. 3. "Edit.AddGuideline" and "Edit.RemoveGuideline" can now take a parameter when invoked from the command window indicating which column to add or remove. 4. Updated description to indicate that VS 2015 is supported. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Paul Harrington 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/ColumnGuidePackage/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("Editor Guidelines UI Package")] 11 | [assembly: AssemblyDescription("Adds commands for the Editor Guidelines extension")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("Paul Harrington")] 14 | [assembly: AssemblyProduct("ColumnGuidePackage")] 15 | [assembly: AssemblyCopyright("Copyright © 2017 Paul Harrington")] 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("2.0.3")] 33 | [assembly: AssemblyFileVersion("2.0.3")] 34 | -------------------------------------------------------------------------------- /src/ColumnGuide/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ColumnGuide")] 9 | [assembly: AssemblyDescription("Editor Guidelines Extension")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Paul Harrington")] 12 | [assembly: AssemblyProduct("ColumnGuide")] 13 | [assembly: AssemblyCopyright("Copyright © 2017 Paul Harrington")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 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 Build and Revision Numbers 30 | // by using the '*' as shown below: 31 | // [assembly: AssemblyVersion("1.0.*")] 32 | [assembly: AssemblyVersion("2.0.3")] 33 | [assembly: AssemblyFileVersion("2.0.3")] 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Editor Guidelines 2 | A Visual Studio extension that adds vertical column guides to the text editor. 3 | 4 | The extension adds vertical column guides behind your code. This is useful if you are trying to tabulate columns of data or if you want to ensure that your lines don't extend beyond a certain length. You specify where the guides go and what color they should be. 5 | 6 | ## Getting Started 7 | [Download](https://marketplace.visualstudio.com/items?itemName=PaulHarrington.EditorGuidelines) and run the extension (VSIX) for Visual Studio 2012 or later from the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=PaulHarrington.EditorGuidelines) or, from within Visual Studio, search for "Editor Guidelines" in the "Extensions and Updates" UI. 8 | 9 | Control guidelines via the context (right-click) menu on the editor surface. You will see a *Guidelines* flyout with three commands: 10 | 11 | ![GuidelinesContextMenu](images/ContextMenu.png) 12 | 13 | * When *Add Guideline* is selected, a vertical dashed line will be drawn at the same position as the caret (insertion point). 14 | * *Remove Guideline* will remove any guideline at the current insertion point. 15 | * *Remove All Guidelines* does exactly that. 16 | 17 | You can change the guideline color from the Fonts and Colors page in `Tools|Options`. Look for *Guideline* in the Text Editor category: 18 | 19 | ![GuidelinesToolsOptions](images/FontsAndColors.png) 20 | 21 | These commands may also be accessed from Visual Studio's Command Window. 22 | 23 | ![GuidelinesCommandWindow](images/CommandWindow.png) 24 | 25 | Note that the column numbers used for the `Edit.AddGuideline` and `Edit.RemoveGuideline` commands refer to the right side of the given column of text. 26 | i.e. To place a guide to the right of column 80, use `Edit.AddGuideline 80`. To place a guide to the left of the first column use `Edit.AddGuideline 0`. 27 | 28 | _Note: This extension collects and transmits anonymized usage statistics to the extension author for product improvement purposes._ 29 | -------------------------------------------------------------------------------- /src/ColumnGuide/Telemetry.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.ApplicationInsights; 2 | using Microsoft.ApplicationInsights.Channel; 3 | using Microsoft.ApplicationInsights.Extensibility; 4 | using System; 5 | using System.ComponentModel.Composition; 6 | 7 | namespace ColumnGuide 8 | { 9 | [Export(typeof(ITelemetry))] 10 | internal class Telemetry : ITelemetry 11 | { 12 | private const string c_InstrumentationKey = "f8324fcc-eb39-4931-bebc-968aab7d3d7d"; 13 | 14 | private readonly TelemetryClient _telemetryClient = CreateClient(); 15 | 16 | public TelemetryClient Client => _telemetryClient; 17 | 18 | private Telemetry() 19 | { 20 | } 21 | 22 | private static TelemetryClient CreateClient() 23 | { 24 | var configuration = new TelemetryConfiguration 25 | { 26 | InstrumentationKey = c_InstrumentationKey, 27 | TelemetryChannel = new InMemoryChannel 28 | { 29 | #if DEBUG 30 | DeveloperMode = true 31 | #else 32 | DeveloperMode = false 33 | #endif 34 | } 35 | }; 36 | 37 | var client = new TelemetryClient(configuration); 38 | client.Context.User.Id = Anonymize(Environment.UserDomainName + "\\" + Environment.UserName); 39 | client.Context.Session.Id = Guid.NewGuid().ToString(); 40 | client.Context.Device.OperatingSystem = Environment.OSVersion.ToString(); 41 | client.Context.Component.Version = typeof(Telemetry).Assembly.GetName().Version.ToString(); 42 | 43 | return client; 44 | } 45 | 46 | private static string Anonymize(string str) 47 | { 48 | using (var sha1 = System.Security.Cryptography.SHA1.Create()) 49 | { 50 | byte[] inputBytes = System.Text.Encoding.Unicode.GetBytes(str); 51 | byte[] hash = sha1.ComputeHash(inputBytes); 52 | string base64 = System.Convert.ToBase64String(hash); 53 | return base64; 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/VSIX/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Editor Guidelines 6 | Adds vertical column guides to the Visual Studio text editor. 7 | https://marketplace.visualstudio.com/items?itemName=PaulHarrington.EditorGuidelines 8 | LICENSE 9 | https://github.com/pharring/EditorGuidelines#getting-started 10 | ReleaseNotes.txt 11 | EditorGuidelines_128px.png 12 | Editor;Editing;Guidelines;Column 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/Editor Guidelines.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.25901.2 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ColumnGuide", "ColumnGuide\ColumnGuide.csproj", "{0C5B092D-506C-4F92-9902-205874908ACC}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ColumnGuidePackage", "ColumnGuidePackage\ColumnGuidePackage.csproj", "{B2BA96C9-1020-4D3E-9E4F-9D2525FD4DC3}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Editor Guidelines", "VSIX\Editor Guidelines.csproj", "{BD32086D-4DD4-43E4-B1DF-69DA079DA88C}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {0C5B092D-506C-4F92-9902-205874908ACC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {0C5B092D-506C-4F92-9902-205874908ACC}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {0C5B092D-506C-4F92-9902-205874908ACC}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {0C5B092D-506C-4F92-9902-205874908ACC}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {B2BA96C9-1020-4D3E-9E4F-9D2525FD4DC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {B2BA96C9-1020-4D3E-9E4F-9D2525FD4DC3}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {B2BA96C9-1020-4D3E-9E4F-9D2525FD4DC3}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {B2BA96C9-1020-4D3E-9E4F-9D2525FD4DC3}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {BD32086D-4DD4-43E4-B1DF-69DA079DA88C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {BD32086D-4DD4-43E4-B1DF-69DA079DA88C}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {BD32086D-4DD4-43E4-B1DF-69DA079DA88C}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {BD32086D-4DD4-43E4-B1DF-69DA079DA88C}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {64E283A2-5918-46A4-9D2B-83B601BBEBBD} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /src/ColumnGuidePackage/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 Microsoft.ColumnGuidePackage { 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", "4.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("Microsoft.ColumnGuidePackage.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 | -------------------------------------------------------------------------------- /src/ColumnGuide/ColumnGuide.ruleset: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/ColumnGuide/ColumnGuideFactory.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using Microsoft.VisualStudio.Text.Editor; 3 | using Microsoft.VisualStudio.Utilities; 4 | using Microsoft.VisualStudio.Text.Classification; 5 | using System.Collections.Generic; 6 | using System.Windows.Media; 7 | using System.ComponentModel; 8 | using System.Globalization; 9 | 10 | namespace ColumnGuide 11 | { 12 | #region Adornment Factory 13 | /// 14 | /// Establishes an to place the adornment on and exports the 15 | /// that instantiates the adornment on the event of a 's creation 16 | /// 17 | [Export(typeof(IWpfTextViewCreationListener))] 18 | [ContentType("text")] 19 | [TextViewRole(PredefinedTextViewRoles.Document)] 20 | internal sealed class ColumnGuideAdornmentFactory : IWpfTextViewCreationListener, IPartImportsSatisfiedNotification 21 | { 22 | /// 23 | /// Defines the adornment layer for the adornment. This layer is ordered 24 | /// below the text in the Z-order 25 | /// 26 | [Export(typeof(AdornmentLayerDefinition))] 27 | [Name("ColumnGuide")] 28 | [Order(Before = PredefinedAdornmentLayers.Text)] 29 | [TextViewRole(PredefinedTextViewRoles.Document)] 30 | public AdornmentLayerDefinition editorAdornmentLayer = null; 31 | 32 | /// 33 | /// Instantiates a ColumnGuide manager when a textView is created. 34 | /// 35 | /// The upon which the adornment should be placed 36 | public void TextViewCreated(IWpfTextView textView) 37 | { 38 | // Always create the adornment, even if there are no guidelines, since we 39 | // respond to dynamic changes. 40 | var formatMap = EditorFormatMapService.GetEditorFormatMap(textView); 41 | new ColumnGuide(textView, TextEditorGuidesSettings, formatMap, Telemetry); 42 | 43 | // To reduce the amount of telemetry, only report the color for the first instance. 44 | if (!_colorReported) 45 | { 46 | _colorReported = true; 47 | var brush = GetGuidelineBrushFromFontsAndColors(formatMap); 48 | if (brush != null) 49 | { 50 | Telemetry.Client.TrackEvent("CreateGuidelines", new Dictionary { ["Color"] = brush.ToString() }); 51 | } 52 | } 53 | } 54 | 55 | public void OnImportsSatisfied() 56 | { 57 | Telemetry.Client.TrackEvent(nameof(ColumnGuideAdornmentFactory) + " initialized"); 58 | 59 | TrackSettings("CreateGuidelines"); 60 | if (TextEditorGuidesSettings is INotifyPropertyChanged settingsChanged) 61 | { 62 | settingsChanged.PropertyChanged += OnSettingsChanged; 63 | } 64 | } 65 | 66 | private void OnSettingsChanged(object sender, PropertyChangedEventArgs e) 67 | { 68 | if (e.PropertyName == nameof(ITextEditorGuidesSettings.GuideLinePositionsInChars)) 69 | { 70 | TrackSettings("SettingsChanged"); 71 | } 72 | } 73 | 74 | private void TrackSettings(string eventName) 75 | { 76 | var telemetryProperties = new Dictionary(); 77 | foreach (var column in TextEditorGuidesSettings.GuideLinePositionsInChars) 78 | { 79 | telemetryProperties.Add("guide" + telemetryProperties.Count.ToString(CultureInfo.InvariantCulture), column.ToString(CultureInfo.InvariantCulture)); 80 | } 81 | 82 | Telemetry.Client.TrackEvent(eventName, telemetryProperties, new Dictionary { ["Count"] = telemetryProperties.Count }); 83 | } 84 | 85 | internal static Brush GetGuidelineBrushFromFontsAndColors(IEditorFormatMap formatMap) 86 | { 87 | var resourceDictionary = formatMap.GetProperties(GuidelineColorDefinition.Name); 88 | if (resourceDictionary.Contains(EditorFormatDefinition.BackgroundBrushId)) 89 | { 90 | return resourceDictionary[EditorFormatDefinition.BackgroundBrushId] as Brush; 91 | } 92 | 93 | return null; 94 | } 95 | 96 | [Import] 97 | private ITextEditorGuidesSettings TextEditorGuidesSettings { get; set; } 98 | 99 | [Import] 100 | private ITelemetry Telemetry { get; set; } 101 | 102 | [Import] 103 | private IEditorFormatMapService EditorFormatMapService { get; set; } 104 | 105 | private bool _colorReported; 106 | } 107 | #endregion //Adornment Factory 108 | } 109 | -------------------------------------------------------------------------------- /src/VSIX/Editor Guidelines.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15.0 5 | 11.0 6 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 7 | 4.0 8 | 9 | 10 | 11 | Debug 12 | AnyCPU 13 | 2.0 14 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | {BD32086D-4DD4-43E4-B1DF-69DA079DA88C} 16 | Library 17 | Properties 18 | Editor_Guidelines 19 | EditorGuidelines 20 | v4.5 21 | false 22 | false 23 | false 24 | false 25 | false 26 | false 27 | 28 | 29 | true 30 | full 31 | false 32 | bin\Debug\ 33 | DEBUG;TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | pdbonly 40 | true 41 | bin\Release\ 42 | TRACE 43 | prompt 44 | 4 45 | 46 | 47 | 48 | 49 | 50 | EditorGuidelines_128px.png 51 | Always 52 | true 53 | 54 | 55 | LICENSE 56 | Always 57 | true 58 | 59 | 60 | Designer 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | Always 69 | true 70 | 71 | 72 | 73 | 74 | {B2BA96C9-1020-4D3E-9E4F-9D2525FD4DC3} 75 | ColumnGuidePackage 76 | BuiltProjectOutputGroup%3bBuiltProjectOutputGroupDependencies%3bGetCopyToOutputDirectoryItems%3bSatelliteDllsProjectOutputGroup%3bPkgdefProjectOutputGroup%3b 77 | DebugSymbolsProjectOutputGroup%3b 78 | 79 | 80 | {0C5B092D-506C-4F92-9902-205874908ACC} 81 | ColumnGuide 82 | BuiltProjectOutputGroup%3bBuiltProjectOutputGroupDependencies%3bGetCopyToOutputDirectoryItems%3bSatelliteDllsProjectOutputGroup%3b 83 | DebugSymbolsProjectOutputGroup%3b 84 | 85 | 86 | 87 | 88 | 95 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | -------------------------------------------------------------------------------- /src/ColumnGuidePackage/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 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 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | text/microsoft-resx 119 | 120 | 121 | 2.0 122 | 123 | 124 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 125 | 126 | 127 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 128 | 129 | -------------------------------------------------------------------------------- /src/ColumnGuidePackage/VSPackage.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 | Editor Guidelines Package 122 | 123 | 124 | Adds and handles context menu commands for the Editor Guidelines editor extension. 125 | 126 | 127 | 128 | Resources\Package.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 129 | 130 | -------------------------------------------------------------------------------- /src/ColumnGuide/ColumnGuide.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Windows; 4 | using System.Windows.Media; 5 | using System.Windows.Shapes; 6 | using Microsoft.VisualStudio.Text.Editor; 7 | using Microsoft.VisualStudio.Text.Formatting; 8 | using System.ComponentModel; 9 | using Microsoft.VisualStudio.Text.Classification; 10 | 11 | namespace ColumnGuide 12 | { 13 | /// 14 | /// Adornment class that draws vertical guide lines beneath the text 15 | /// 16 | class ColumnGuide 17 | { 18 | private const double _lineThickness = 1.0; 19 | 20 | private IList _guidelines; 21 | private IWpfTextView _view; 22 | private bool _firstLayoutDone; 23 | private double _baseIndentation; 24 | private double _columnWidth; 25 | private INotifyPropertyChanged _settingsChanged; 26 | private IEditorFormatMap _formatMap; 27 | private ITelemetry _telemetry; 28 | private Brush _guidelineBrush; 29 | 30 | /// 31 | /// Creates editor column guidelines 32 | /// 33 | /// The upon which the adornment will be drawn 34 | /// The guideline settings. 35 | /// The editor format map used to discover formatting options (guideline color). 36 | public ColumnGuide(IWpfTextView view, ITextEditorGuidesSettings settings, IEditorFormatMap editorFormatMap, ITelemetry telemetry) 37 | { 38 | _view = view; 39 | _formatMap = editorFormatMap; 40 | _telemetry = telemetry; 41 | 42 | InitializeGuidelines(settings); 43 | 44 | _view.LayoutChanged += OnViewLayoutChanged; 45 | _settingsChanged = settings as INotifyPropertyChanged; 46 | if (_settingsChanged != null) 47 | { 48 | _settingsChanged.PropertyChanged += SettingsChanged; 49 | } 50 | 51 | _formatMap.FormatMappingChanged += FormatMappingChanged; 52 | _view.Closed += ViewClosed; 53 | } 54 | 55 | private void FormatMappingChanged(object sender, FormatItemsEventArgs e) 56 | { 57 | GuidelineBrush = GetGuidelineBrushFromFontsAndColors(); 58 | } 59 | 60 | void ViewClosed(object sender, EventArgs e) 61 | { 62 | _view.LayoutChanged -= OnViewLayoutChanged; 63 | _view.Closed -= ViewClosed; 64 | if (_settingsChanged != null) 65 | { 66 | _settingsChanged.PropertyChanged -= SettingsChanged; 67 | _settingsChanged = null; 68 | } 69 | 70 | if (_formatMap != null) 71 | { 72 | _formatMap.FormatMappingChanged -= FormatMappingChanged; 73 | _formatMap = null; 74 | } 75 | } 76 | 77 | private void InitializeGuidelines(ITextEditorGuidesSettings settings) 78 | { 79 | _guidelines = CreateGuidelines(settings); 80 | } 81 | 82 | void SettingsChanged(object sender, PropertyChangedEventArgs e) 83 | { 84 | ITextEditorGuidesSettings settings = sender as ITextEditorGuidesSettings; 85 | if (settings != null && e.PropertyName == nameof(ITextEditorGuidesSettings.GuideLinePositionsInChars)) 86 | { 87 | InitializeGuidelines(settings); 88 | UpdatePositions(); 89 | AddGuidelinesToAdornmentLayer(); 90 | } 91 | } 92 | 93 | private Brush GetGuidelineBrushFromFontsAndColors() 94 | { 95 | return ColumnGuideAdornmentFactory.GetGuidelineBrushFromFontsAndColors(_formatMap); 96 | } 97 | 98 | private Brush GuidelineBrush 99 | { 100 | get => _guidelineBrush ?? (_guidelineBrush = GetGuidelineBrushFromFontsAndColors()); 101 | 102 | set 103 | { 104 | if (value != _guidelineBrush) 105 | { 106 | _guidelineBrush = value; 107 | _telemetry.Client.TrackEvent("GuidelineColorChanged", new Dictionary { ["Color"] = value.ToString() }); 108 | if (_guidelines != null) 109 | { 110 | foreach (var guideline in _guidelines) 111 | { 112 | guideline.Stroke = value; 113 | } 114 | } 115 | } 116 | } 117 | } 118 | 119 | void OnViewLayoutChanged(object sender, TextViewLayoutChangedEventArgs e) 120 | { 121 | bool fUpdatePositions = false; 122 | 123 | IFormattedLineSource lineSource = _view.FormattedLineSource; 124 | if (lineSource == null) 125 | { 126 | return; 127 | } 128 | 129 | if(_columnWidth != lineSource.ColumnWidth ) 130 | { 131 | _columnWidth = lineSource.ColumnWidth; 132 | fUpdatePositions = true; 133 | } 134 | 135 | if (_baseIndentation != lineSource.BaseIndentation) 136 | { 137 | _baseIndentation = lineSource.BaseIndentation; 138 | fUpdatePositions = true; 139 | } 140 | 141 | if (fUpdatePositions || 142 | e.VerticalTranslation || 143 | e.NewViewState.ViewportTop != e.OldViewState.ViewportTop || 144 | e.NewViewState.ViewportBottom != e.OldViewState.ViewportBottom) 145 | { 146 | UpdatePositions(); 147 | } 148 | 149 | if (!_firstLayoutDone) 150 | { 151 | AddGuidelinesToAdornmentLayer(); 152 | _firstLayoutDone = true; 153 | } 154 | } 155 | 156 | private IList CreateGuidelines(ITextEditorGuidesSettings settings) 157 | { 158 | var lineBrush = GuidelineBrush; 159 | var dashArray = new DoubleCollection(new double[] { 1.0, 3.0 }); 160 | var result = new List(); 161 | 162 | foreach (int column in settings.GuideLinePositionsInChars) 163 | { 164 | var line = new Line() 165 | { 166 | DataContext = column, 167 | Stroke = lineBrush, 168 | StrokeThickness = _lineThickness, 169 | StrokeDashArray = dashArray 170 | }; 171 | 172 | result.Add(line); 173 | } 174 | 175 | return result; 176 | } 177 | 178 | void UpdatePosition(Line line) 179 | { 180 | int column = (int)line.DataContext; 181 | 182 | line.X1 = line.X2 = _baseIndentation + 0.5 + column * _columnWidth; 183 | line.Y1 = _view.ViewportTop; 184 | line.Y2 = _view.ViewportBottom; 185 | } 186 | 187 | void UpdatePositions() 188 | { 189 | foreach (Line line in _guidelines) 190 | { 191 | UpdatePosition(line); 192 | } 193 | } 194 | 195 | void AddGuidelinesToAdornmentLayer() 196 | { 197 | //Grab a reference to the adornment layer that this adornment should be added to 198 | IAdornmentLayer adornmentLayer = _view.GetAdornmentLayer("ColumnGuide"); 199 | if (adornmentLayer == null) 200 | { 201 | return; 202 | } 203 | 204 | adornmentLayer.RemoveAllAdornments(); 205 | 206 | // Add the guidelines to the adornment layer and make them relative to the viewport 207 | foreach (UIElement element in _guidelines) 208 | { 209 | adornmentLayer.AddAdornment(AdornmentPositioningBehavior.OwnerControlled, null, null, element, null); 210 | } 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/ColumnGuide/ColumnGuide.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 10.0.20305 8 | 2.0 9 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10 | {0C5B092D-506C-4F92-9902-205874908ACC} 11 | Library 12 | Properties 13 | ColumnGuide 14 | ColumnGuide 15 | v4.5 16 | 512 17 | false 18 | 15.0 19 | 20 | 21 | 22 | 23 | 4.0 24 | false 25 | 26 | SAK 27 | SAK 28 | SAK 29 | SAK 30 | publish\ 31 | true 32 | Disk 33 | false 34 | Foreground 35 | 7 36 | Days 37 | false 38 | false 39 | true 40 | 0 41 | 1.0.0.%2a 42 | false 43 | true 44 | 45 | 46 | true 47 | full 48 | false 49 | bin\Debug\ 50 | DEBUG;TRACE 51 | prompt 52 | 4 53 | False 54 | False 55 | false 56 | ColumnGuide.ruleset 57 | 58 | 59 | pdbonly 60 | true 61 | bin\Release\ 62 | TRACE 63 | prompt 64 | 4 65 | False 66 | False 67 | false 68 | true 69 | ColumnGuide.ruleset 70 | 71 | 72 | false 73 | 74 | 75 | 76 | 77 | 78 | 79 | bin\SDV analysis\ 80 | false 81 | ColumnGuide.ruleset 82 | 83 | 84 | 85 | ..\packages\Microsoft.ApplicationInsights.2.1.0\lib\net45\Microsoft.ApplicationInsights.dll 86 | True 87 | 88 | 89 | True 90 | ..\References\11.0.0.0\Microsoft.VisualStudio.CoreUtility.dll 91 | 92 | 93 | 94 | 95 | True 96 | 97 | 98 | True 99 | ..\References\11.0.0.0\Microsoft.VisualStudio.Text.Data.dll 100 | 101 | 102 | True 103 | ..\References\11.0.0.0\Microsoft.VisualStudio.Text.UI.dll 104 | 105 | 106 | True 107 | ..\References\11.0.0.0\Microsoft.VisualStudio.Text.UI.Wpf.dll 108 | 109 | 110 | True 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | False 138 | Microsoft .NET Framework 4 %28x86 and x64%29 139 | true 140 | 141 | 142 | False 143 | .NET Framework 3.5 SP1 Client Profile 144 | false 145 | 146 | 147 | False 148 | .NET Framework 3.5 SP1 149 | false 150 | 151 | 152 | 153 | 10.0 154 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 155 | 156 | 157 | 158 | 159 | 166 | -------------------------------------------------------------------------------- /src/ColumnGuidePackage/ColumnGuidePackage.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 | 51 | 52 | 53 | 54 | 55 | 56 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | &Guidelines 70 | 71 | 72 | 73 | 74 | 75 | 77 | 78 | 85 | 86 | 95 | 96 | 105 | 106 | 113 | 114 | 115 | 116 | 117 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /src/ColumnGuide/TextEditorGuidesSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.Composition; 4 | using System.Runtime.InteropServices; 5 | using System.Windows.Media; 6 | using Microsoft.VisualStudio.Shell.Interop; 7 | using System.ComponentModel; 8 | using System.Text; 9 | 10 | namespace ColumnGuide 11 | { 12 | [Export(typeof(ITextEditorGuidesSettings))] 13 | [Export(typeof(ITextEditorGuidesSettingsChanger))] 14 | sealed class TextEditorGuidesSettings : ITextEditorGuidesSettings, INotifyPropertyChanged, ITextEditorGuidesSettingsChanger 15 | { 16 | private const int _maxGuides = 12; 17 | 18 | [Import] 19 | Lazy HostServices { get; set; } 20 | 21 | IVsSettingsStore ReadOnlyUserSettings 22 | { 23 | get 24 | { 25 | IVsSettingsManager manager = HostServices.Value.SettingsManagerService; 26 | Marshal.ThrowExceptionForHR(manager.GetReadOnlySettingsStore((uint)__VsSettingsScope.SettingsScope_UserSettings, out var store)); 27 | return store; 28 | } 29 | } 30 | 31 | IVsWritableSettingsStore ReadWriteUserSettings 32 | { 33 | get 34 | { 35 | IVsSettingsManager manager = HostServices.Value.SettingsManagerService; 36 | Marshal.ThrowExceptionForHR(manager.GetWritableSettingsStore((uint)__VsSettingsScope.SettingsScope_UserSettings, out var store)); 37 | return store; 38 | } 39 | } 40 | 41 | 42 | private string GetUserSettingsString(string key, string value) 43 | { 44 | IVsSettingsStore store = ReadOnlyUserSettings; 45 | Marshal.ThrowExceptionForHR(store.GetStringOrDefault(key, value, String.Empty, out string result)); 46 | return result; 47 | } 48 | 49 | private void WriteUserSettingsString(string key, string propertyName, string value) 50 | { 51 | IVsWritableSettingsStore store = ReadWriteUserSettings; 52 | Marshal.ThrowExceptionForHR(store.SetString(key, propertyName, value)); 53 | } 54 | 55 | private void WriteSettings(Color color, IEnumerable columns) 56 | { 57 | string value = ComposeSettingsString(color, columns); 58 | GuidelinesConfiguration = value; 59 | } 60 | 61 | private static string ComposeSettingsString(Color color, IEnumerable columns) 62 | { 63 | StringBuilder sb = new StringBuilder(); 64 | sb.AppendFormat("RGB({0},{1},{2})", color.R, color.G, color.B); 65 | IEnumerator columnsEnumerator = columns.GetEnumerator(); 66 | if( columnsEnumerator.MoveNext() ) 67 | { 68 | sb.AppendFormat(" {0}", columnsEnumerator.Current); 69 | while( columnsEnumerator.MoveNext() ) 70 | { 71 | sb.AppendFormat(", {0}", columnsEnumerator.Current); 72 | } 73 | } 74 | 75 | return sb.ToString(); 76 | } 77 | 78 | #region ITextEditorGuidesSettingsChanger Members 79 | 80 | public bool AddGuideline(int column) 81 | { 82 | if (!IsValidColumn(column)) 83 | { 84 | throw new ArgumentOutOfRangeException("column", "The paramenter must be between 1 and 10,000"); 85 | } 86 | 87 | if (GetCountOfGuidelines() >= _maxGuides) 88 | { 89 | return false; // Cannot add more than _maxGuides guidelines 90 | } 91 | 92 | // Check for duplicates 93 | List columns = new List(GuideLinePositionsInChars); 94 | if (columns.Contains(column)) 95 | { 96 | return false; 97 | } 98 | 99 | columns.Add(column); 100 | 101 | WriteSettings(this.GuidelinesColor, columns); 102 | return true; 103 | } 104 | 105 | public bool RemoveGuideline(int column) 106 | { 107 | if (!IsValidColumn(column)) 108 | { 109 | throw new ArgumentOutOfRangeException("column", "The paramenter must be between 1 and 10,000"); 110 | } 111 | 112 | List columns = new List(GuideLinePositionsInChars); 113 | if (!columns.Remove(column)) 114 | { 115 | // Not present 116 | // Allow user to remove the last column even if they're not on the right column 117 | if (columns.Count != 1) 118 | { 119 | return false; 120 | } 121 | 122 | columns.Clear(); 123 | } 124 | 125 | WriteSettings(this.GuidelinesColor, columns); 126 | return true; 127 | } 128 | 129 | public bool CanAddGuideline(int column) 130 | { 131 | if (!IsValidColumn(column)) 132 | { 133 | return false; 134 | } 135 | 136 | if (GetCountOfGuidelines() >= _maxGuides) 137 | { 138 | return false; 139 | } 140 | 141 | return !IsGuidelinePresent(column); 142 | } 143 | 144 | public bool CanRemoveGuideline(int column) 145 | { 146 | if (!IsValidColumn(column)) 147 | { 148 | return false; 149 | } 150 | 151 | return IsGuidelinePresent(column) 152 | || HasExactlyOneGuideline(); // Allow user to remove the last guideline regardless of the column 153 | } 154 | 155 | public void RemoveAllGuidelines() 156 | { 157 | WriteSettings(this.GuidelinesColor, new int[0]); 158 | } 159 | 160 | #endregion 161 | 162 | private bool HasExactlyOneGuideline() 163 | { 164 | using (var enumerator = GuideLinePositionsInChars.GetEnumerator()) 165 | { 166 | return enumerator.MoveNext() && !enumerator.MoveNext(); 167 | } 168 | } 169 | 170 | private int GetCountOfGuidelines() 171 | { 172 | int i = 0; 173 | foreach (int value in GuideLinePositionsInChars) 174 | { 175 | i++; 176 | } 177 | return i; 178 | } 179 | 180 | private static bool IsValidColumn(int column) 181 | { 182 | // -ve is not allowed 183 | // zero is allowed (per user request) 184 | // 10000 seems like a sensible upper limit 185 | return 0 <= column && column <= 10000; 186 | } 187 | 188 | private bool IsGuidelinePresent(int column) 189 | { 190 | foreach (int value in GuideLinePositionsInChars) 191 | { 192 | if (value == column) 193 | { 194 | return true; 195 | } 196 | } 197 | 198 | return false; 199 | } 200 | 201 | private string _guidelinesConfiguration; 202 | private string GuidelinesConfiguration 203 | { 204 | get 205 | { 206 | if (_guidelinesConfiguration == null) 207 | { 208 | _guidelinesConfiguration = GetUserSettingsString("Text Editor", "Guides").Trim(); 209 | } 210 | return _guidelinesConfiguration; 211 | } 212 | 213 | set 214 | { 215 | if (value != _guidelinesConfiguration) 216 | { 217 | _guidelinesConfiguration = value; 218 | WriteUserSettingsString("Text Editor", "Guides", value); 219 | FirePropertyChanged(nameof(ITextEditorGuidesSettings.GuideLinePositionsInChars)); 220 | } 221 | } 222 | } 223 | 224 | private void FirePropertyChanged(string propertyName) 225 | { 226 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 227 | } 228 | 229 | // Parse a color out of a string that begins like "RGB(255,0,0)" 230 | public Color GuidelinesColor 231 | { 232 | get 233 | { 234 | string config = GuidelinesConfiguration; 235 | if (!String.IsNullOrEmpty(config) && config.StartsWith("RGB(", StringComparison.Ordinal)) 236 | { 237 | int lastParen = config.IndexOf(')'); 238 | if (lastParen > 4) 239 | { 240 | string[] rgbs = config.Substring(4, lastParen - 4).Split(','); 241 | 242 | if (rgbs.Length >= 3) 243 | { 244 | if (byte.TryParse(rgbs[0], out byte r) && 245 | byte.TryParse(rgbs[1], out byte g) && 246 | byte.TryParse(rgbs[2], out byte b)) 247 | { 248 | return Color.FromRgb(r, g, b); 249 | } 250 | } 251 | } 252 | } 253 | return Colors.DarkRed; 254 | } 255 | 256 | set 257 | { 258 | WriteSettings(value, GuideLinePositionsInChars); 259 | } 260 | } 261 | 262 | // Parse a list of integer values out of a string that looks like "RGB(255,0,0) 1,5,10,80" 263 | public IEnumerable GuideLinePositionsInChars 264 | { 265 | get 266 | { 267 | string config = GuidelinesConfiguration; 268 | if (String.IsNullOrEmpty(config)) 269 | { 270 | yield break; 271 | } 272 | if (!config.StartsWith("RGB(", StringComparison.Ordinal)) 273 | { 274 | yield break; 275 | } 276 | 277 | int lastParen = config.IndexOf(')'); 278 | if (lastParen <= 4) 279 | { 280 | yield break; 281 | } 282 | 283 | string[] columns = config.Substring(lastParen + 1).Split(','); 284 | 285 | int columnCount = 0; 286 | foreach (string columnText in columns) 287 | { 288 | int column = -1; 289 | if (int.TryParse(columnText, out column) && column >= 0 /*Note: VS 2008 didn't allow zero, but we do, per user request*/ ) 290 | { 291 | columnCount++; 292 | yield return column; 293 | if (columnCount >= _maxGuides) 294 | { 295 | break; 296 | } 297 | } 298 | } 299 | } 300 | } 301 | 302 | #region INotifyPropertyChanged Members 303 | 304 | public event PropertyChangedEventHandler PropertyChanged; 305 | 306 | #endregion 307 | } 308 | } 309 | -------------------------------------------------------------------------------- /src/ColumnGuidePackage/ColumnGuidePackage.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | {B2BA96C9-1020-4D3E-9E4F-9D2525FD4DC3} 9 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10 | Library 11 | Properties 12 | Microsoft.ColumnGuidePackage 13 | ColumnGuidePackage 14 | false 15 | 16 | 17 | v4.5 18 | 15.0 19 | 20 | 21 | 22 | 23 | 4.0 24 | false 25 | 26 | SAK 27 | SAK 28 | SAK 29 | SAK 30 | publish\ 31 | true 32 | Disk 33 | false 34 | Foreground 35 | 7 36 | Days 37 | false 38 | false 39 | true 40 | 0 41 | 1.0.0.%2a 42 | false 43 | true 44 | 45 | 46 | true 47 | full 48 | false 49 | bin\Debug\ 50 | DEBUG;TRACE 51 | prompt 52 | 4 53 | False 54 | False 55 | false 56 | 57 | 58 | 59 | pdbonly 60 | true 61 | bin\Release\ 62 | TRACE 63 | prompt 64 | 4 65 | true 66 | False 67 | False 68 | false 69 | 70 | 71 | 72 | 73 | False 74 | 75 | 76 | ..\packages\Microsoft.ApplicationInsights.2.1.0\lib\net45\Microsoft.ApplicationInsights.dll 77 | True 78 | 79 | 80 | 81 | True 82 | ..\References\11.0.0.0\Microsoft.VisualStudio.ComponentModelHost.dll 83 | 84 | 85 | True 86 | ..\References\11.0.0.0\Microsoft.VisualStudio.CoreUtility.dll 87 | 88 | 89 | True 90 | ..\References\11.0.0.0\Microsoft.VisualStudio.Editor.dll 91 | 92 | 93 | 94 | 95 | 96 | True 97 | 98 | 99 | 100 | 101 | 102 | True 103 | ..\References\11.0.0.0\Microsoft.VisualStudio.Text.UI.dll 104 | 105 | 106 | True 107 | ..\References\11.0.0.0\Microsoft.VisualStudio.Text.UI.Wpf.dll 108 | 109 | 110 | 111 | True 112 | ..\References\11.0.0.0\Microsoft.VisualStudio.Shell.11.0.dll 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | {00020430-0000-0000-C000-000000000046} 123 | 2 124 | 0 125 | 0 126 | primary 127 | False 128 | False 129 | 130 | 131 | 132 | 133 | 134 | True 135 | True 136 | Resources.resx 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | ResXFileCodeGenerator 148 | Resources.Designer.cs 149 | Designer 150 | 151 | 152 | true 153 | VSPackage 154 | 155 | 156 | 157 | 158 | 159 | Designer 160 | 161 | 162 | 163 | 164 | Menus.ctmenu 165 | Designer 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | {0C5B092D-506C-4F92-9902-205874908ACC} 175 | ColumnGuide 176 | 177 | 178 | 179 | 180 | False 181 | Microsoft .NET Framework 4 %28x86 and x64%29 182 | true 183 | 184 | 185 | False 186 | .NET Framework 3.5 SP1 Client Profile 187 | false 188 | 189 | 190 | False 191 | .NET Framework 3.5 SP1 192 | false 193 | 194 | 195 | 196 | true 197 | 198 | 199 | bin\SDV analysis\ 200 | false 201 | 202 | 203 | 204 | 10.0 205 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 206 | 207 | 208 | 209 | 210 | 217 | -------------------------------------------------------------------------------- /src/ColumnGuidePackage/ColumnGuidePackage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Design; 3 | using System.Diagnostics; 4 | using System.Globalization; 5 | using System.Runtime.InteropServices; 6 | using Microsoft.VisualStudio.Shell; 7 | using Microsoft.VisualStudio.Shell.Interop; 8 | using Microsoft.VisualStudio; 9 | using Microsoft.VisualStudio.TextManager.Interop; 10 | using Microsoft.VisualStudio.Text.Editor; 11 | using System.Collections.Generic; 12 | 13 | namespace Microsoft.ColumnGuidePackage 14 | { 15 | /// 16 | /// This is the class that implements the package exposed by this assembly. 17 | /// 18 | /// The minimum requirement for a class to be considered a valid package for Visual Studio 19 | /// is to implement the IVsPackage interface and register itself with the shell. 20 | /// This package uses the helper classes defined inside the Managed Package Framework (MPF) 21 | /// to do it: it derives from the Package class that provides the implementation of the 22 | /// IVsPackage interface and uses the registration attributes defined in the framework to 23 | /// register itself and its components with the shell. 24 | /// 25 | // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is 26 | // a package. 27 | [PackageRegistration(UseManagedResourcesOnly = true)] 28 | // This attribute is needed to let the shell know that this package exposes some menus. 29 | [ProvideMenuResource("Menus.ctmenu", 2)] 30 | [Guid(GuidList.guidColumnGuidePkgString)] 31 | public sealed class ColumnGuidePackage : Package 32 | { 33 | /// 34 | /// Default constructor of the package. 35 | /// Inside this method you can place any initialization code that does not require 36 | /// any Visual Studio service because at this point the package object is created but 37 | /// not sited yet inside Visual Studio environment. The place to do all the other 38 | /// initialization is the Initialize method. 39 | /// 40 | public ColumnGuidePackage() 41 | { 42 | } 43 | 44 | ///////////////////////////////////////////////////////////////////////////// 45 | // Overriden Package Implementation 46 | #region Package Members 47 | 48 | /// 49 | /// Initialization of the package; this method is called right after the package is sited, so this is the place 50 | /// where you can put all the initilaization code that rely on services provided by VisualStudio. 51 | /// 52 | protected override void Initialize() 53 | { 54 | base.Initialize(); 55 | 56 | Telemetry.Client.TrackEvent(nameof(ColumnGuidePackage) + "." + nameof(Initialize), new Dictionary() { ["VSVersion"] = GetShellVersion() }); 57 | 58 | // Add our command handlers for menu (commands must exist in the .vsct file) 59 | 60 | OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService; 61 | if (null != mcs) 62 | { 63 | _addGuidelineCommand = new OleMenuCommand(AddColumnGuideExecuted, null, AddColumnGuideBeforeQueryStatus, new CommandID(GuidList.guidColumnGuideCmdSet, (int)PkgCmdIDList.cmdidAddColumnGuideline)) 64 | { 65 | ParametersDescription = "" 66 | }; 67 | mcs.AddCommand(_addGuidelineCommand); 68 | 69 | _removeGuidelineCommand = new OleMenuCommand(RemoveColumnGuideExecuted, null, RemoveColumnGuideBeforeChangeQueryStatus, new CommandID(GuidList.guidColumnGuideCmdSet, (int)PkgCmdIDList.cmdidRemoveColumnGuideline)) 70 | { 71 | ParametersDescription = "" 72 | }; 73 | mcs.AddCommand(_removeGuidelineCommand); 74 | 75 | mcs.AddCommand(new MenuCommand(RemoveAllGuidelinesExecuted, new CommandID(GuidList.guidColumnGuideCmdSet, (int)PkgCmdIDList.cmdidRemoveAllColumnGuidelines))); 76 | } 77 | } 78 | 79 | #endregion 80 | 81 | private string GetShellVersion() 82 | { 83 | var shell = GetService(typeof(SVsShell)) as IVsShell; 84 | if (shell != null) 85 | { 86 | object obj; 87 | if (ErrorHandler.Succeeded(shell.GetProperty((int)__VSSPROPID5.VSSPROPID_ReleaseVersion, out obj)) && obj != null) 88 | { 89 | return obj.ToString(); 90 | } 91 | } 92 | 93 | return "Unknown"; 94 | } 95 | 96 | OleMenuCommand _addGuidelineCommand; 97 | OleMenuCommand _removeGuidelineCommand; 98 | 99 | private void AddColumnGuideBeforeQueryStatus(object sender, EventArgs e) 100 | { 101 | int currentColumn = GetCurrentEditorColumn(); 102 | _addGuidelineCommand.Enabled = TextEditorGuidesSettingsRendezvous.Instance.CanAddGuideline(currentColumn); 103 | } 104 | 105 | private void RemoveColumnGuideBeforeChangeQueryStatus(object sender, EventArgs e) 106 | { 107 | int currentColumn = GetCurrentEditorColumn(); 108 | _removeGuidelineCommand.Enabled = TextEditorGuidesSettingsRendezvous.Instance.CanRemoveGuideline(currentColumn); 109 | } 110 | 111 | /// 112 | /// Determine the applicable column number for an add or remove command. 113 | /// The column is parsed from command arguments, if present. Otherwise 114 | /// the current position of the caret is used to determine the column. 115 | /// 116 | /// Event args passed to the command handler. 117 | /// The column number. May be negative to indicate the column number is unavailable. 118 | /// The column number parsed from event args was not a valid integer. 119 | private int GetApplicableColumn(EventArgs e) 120 | { 121 | var inValue = ((OleMenuCmdEventArgs)e).InValue as string; 122 | if (!string.IsNullOrEmpty(inValue)) 123 | { 124 | int column; 125 | if (!int.TryParse(inValue, out column) || column < 0) 126 | throw new ArgumentException("Invalid column"); 127 | 128 | Telemetry.Client.TrackEvent("Command parameter used"); 129 | return column; 130 | } 131 | 132 | return GetCurrentEditorColumn(); 133 | } 134 | 135 | /// 136 | /// This function is the callback used to execute a command when the a menu item is clicked. 137 | /// See the Initialize method to see how the menu item is associated to this function using 138 | /// the OleMenuCommandService service and the MenuCommand class. 139 | /// 140 | private void AddColumnGuideExecuted(object sender, EventArgs e) 141 | { 142 | int column = GetApplicableColumn(e); 143 | if (column >= 0) 144 | { 145 | Telemetry.Client.TrackEvent(nameof(AddColumnGuideExecuted), new Dictionary() { ["Column"] = column.ToString() }); 146 | TextEditorGuidesSettingsRendezvous.Instance.AddGuideline(column); 147 | } 148 | } 149 | 150 | private void RemoveColumnGuideExecuted(object sender, EventArgs e) 151 | { 152 | int column = GetApplicableColumn(e); 153 | if (column >= 0) 154 | { 155 | Telemetry.Client.TrackEvent(nameof(RemoveColumnGuideExecuted), new Dictionary() { ["Column"] = column.ToString() }); 156 | TextEditorGuidesSettingsRendezvous.Instance.RemoveGuideline(column); 157 | } 158 | } 159 | 160 | private void RemoveAllGuidelinesExecuted(object sender, EventArgs e) 161 | { 162 | Telemetry.Client.TrackEvent(nameof(RemoveAllGuidelinesExecuted)); 163 | TextEditorGuidesSettingsRendezvous.Instance.RemoveAllGuidelines(); 164 | } 165 | 166 | /// 167 | /// Find the active text view (if any) in the active document. 168 | /// 169 | /// The IVsTextView of the active view, or null if there is no active document or the 170 | /// active view in the active document is not a text view. 171 | private IVsTextView GetActiveTextView() 172 | { 173 | IVsMonitorSelection selection = GetService(typeof(IVsMonitorSelection)) as IVsMonitorSelection; 174 | object frameObj = null; 175 | ErrorHandler.ThrowOnFailure(selection.GetCurrentElementValue((uint)VSConstants.VSSELELEMID.SEID_DocumentFrame, out frameObj)); 176 | 177 | IVsWindowFrame frame = frameObj as IVsWindowFrame; 178 | if (frame == null) 179 | { 180 | return null; 181 | } 182 | 183 | return GetActiveView(frame); 184 | } 185 | 186 | private static IVsTextView GetActiveView(IVsWindowFrame windowFrame) 187 | { 188 | if (windowFrame == null) 189 | { 190 | throw new ArgumentException("windowFrame"); 191 | } 192 | 193 | object pvar; 194 | ErrorHandler.ThrowOnFailure(windowFrame.GetProperty((int)__VSFPROPID.VSFPROPID_DocView, out pvar)); 195 | 196 | IVsTextView textView = pvar as IVsTextView; 197 | if (textView == null) 198 | { 199 | IVsCodeWindow codeWin = pvar as IVsCodeWindow; 200 | if (codeWin != null) 201 | { 202 | ErrorHandler.ThrowOnFailure(codeWin.GetLastActiveView(out textView)); 203 | } 204 | } 205 | return textView; 206 | } 207 | 208 | private static IWpfTextView GetTextViewFromVsTextView(IVsTextView view) 209 | { 210 | if (view == null) 211 | { 212 | throw new ArgumentNullException("view"); 213 | } 214 | 215 | IVsUserData userData = view as IVsUserData; 216 | if (userData == null) 217 | { 218 | throw new InvalidOperationException(); 219 | } 220 | 221 | object objTextViewHost; 222 | if (VSConstants.S_OK != userData.GetData(Microsoft.VisualStudio.Editor.DefGuidList.guidIWpfTextViewHost, out objTextViewHost)) 223 | { 224 | throw new InvalidOperationException(); 225 | } 226 | 227 | IWpfTextViewHost textViewHost = objTextViewHost as IWpfTextViewHost; 228 | if (textViewHost == null) 229 | { 230 | throw new InvalidOperationException(); 231 | } 232 | 233 | return textViewHost.TextView; 234 | } 235 | 236 | /// 237 | /// Given an IWpfTextView, find the position of the caret and report its column number 238 | /// The column number is 0-based 239 | /// 240 | /// The text view containing the caret 241 | /// The column number of the caret's position. When the caret is at the leftmost column, the return value is zero. 242 | private static int GetCaretColumn(IWpfTextView textView) 243 | { 244 | // This is the code the editor uses to populate the status bar. Thanks, Jack! 245 | Microsoft.VisualStudio.Text.Formatting.ITextViewLine caretViewLine = textView.Caret.ContainingTextViewLine; 246 | double columnWidth = textView.FormattedLineSource.ColumnWidth; 247 | return (int)(Math.Round((textView.Caret.Left - caretViewLine.Left) / columnWidth)); 248 | } 249 | 250 | private int GetCurrentEditorColumn() 251 | { 252 | IVsTextView view = GetActiveTextView(); 253 | if (view == null) 254 | { 255 | return -1; 256 | } 257 | 258 | try 259 | { 260 | IWpfTextView textView = GetTextViewFromVsTextView(view); 261 | int column = GetCaretColumn(textView); 262 | 263 | // Note: GetCaretColumn returns 0-based positions. Guidelines are 1-based positions. 264 | // However, do not subtract one here since the caret is positioned to the left of 265 | // the given column and the guidelines are positioned to the right. We want the 266 | // guideline to line up with the current caret position. e.g. When the caret is 267 | // at position 1 (zero-based), the status bar says column 2. We want to add a 268 | // guideline for column 1 since that will place the guideline where the caret is. 269 | return column; 270 | } 271 | catch (InvalidOperationException) 272 | { 273 | return -1; 274 | } 275 | } 276 | } 277 | } 278 | --------------------------------------------------------------------------------