├── art
├── basic.png
├── info.png
├── encoding.png
├── selection.png
├── multi-selection.png
├── selected-text.png
└── encoding-tooltip.png
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── workflows
│ └── build.yaml
├── CODE_OF_CONDUCT.md
└── CONTRIBUTING.md
├── src
├── Resources
│ └── Icon.png
├── Margins
│ ├── BaseMargin.cs
│ ├── LengthMarginProvider.cs
│ ├── InfoMarginProvider.cs
│ ├── SelectionMarginProvider.cs
│ ├── LengthMargin.cs
│ ├── EncodingMarginProvider.cs
│ ├── SelectionMargin.cs
│ ├── InfoMargin.cs
│ └── EncodingMargin.cs
├── Properties
│ └── AssemblyInfo.cs
├── Commands
│ ├── CustomEncodings.cs
│ ├── EncodingMenuCommandBridge.cs
│ └── EncodingMenuCommand.cs
├── source.extension.cs
├── Telemetry.cs
├── VSCommandTable.cs
├── DocumentMarginPackage.cs
├── VSCommandTable.vsct
├── source.extension.vsixmanifest
└── DocumentMargin.csproj
├── vs-publish.json
├── DocumentMargin.sln
├── .gitattributes
├── README.md
├── .editorconfig
├── .gitignore
└── LICENSE
/art/basic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/madskristensen/DocumentMargin/master/art/basic.png
--------------------------------------------------------------------------------
/art/info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/madskristensen/DocumentMargin/master/art/info.png
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: madskristensen
4 |
--------------------------------------------------------------------------------
/art/encoding.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/madskristensen/DocumentMargin/master/art/encoding.png
--------------------------------------------------------------------------------
/art/selection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/madskristensen/DocumentMargin/master/art/selection.png
--------------------------------------------------------------------------------
/art/multi-selection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/madskristensen/DocumentMargin/master/art/multi-selection.png
--------------------------------------------------------------------------------
/art/selected-text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/madskristensen/DocumentMargin/master/art/selected-text.png
--------------------------------------------------------------------------------
/src/Resources/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/madskristensen/DocumentMargin/master/src/Resources/Icon.png
--------------------------------------------------------------------------------
/art/encoding-tooltip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/madskristensen/DocumentMargin/master/art/encoding-tooltip.png
--------------------------------------------------------------------------------
/src/Margins/BaseMargin.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Controls;
3 | using Microsoft.VisualStudio.Text.Editor;
4 |
5 | namespace DocumentMargin.Margins
6 | {
7 | internal abstract class BaseMargin : Label, IWpfTextViewMargin
8 | {
9 | public FrameworkElement VisualElement => this;
10 |
11 | public double MarginSize => ActualHeight;
12 |
13 | public bool Enabled => true;
14 |
15 | public abstract void Dispose();
16 |
17 | public ITextViewMargin GetTextViewMargin(string marginName)
18 | {
19 | return (marginName == GetType().Name) ? this : null;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Additional context**
27 | Add any other context about the problem here.
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/src/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using DocumentMargin;
2 |
3 | using System.Reflection;
4 | using System.Runtime.InteropServices;
5 |
6 | [assembly: AssemblyTitle(Vsix.Name)]
7 | [assembly: AssemblyDescription(Vsix.Description)]
8 | [assembly: AssemblyConfiguration("")]
9 | [assembly: AssemblyCompany(Vsix.Author)]
10 | [assembly: AssemblyProduct(Vsix.Name)]
11 | [assembly: AssemblyCopyright(Vsix.Author)]
12 | [assembly: AssemblyTrademark("")]
13 | [assembly: AssemblyCulture("")]
14 |
15 | [assembly: ComVisible(false)]
16 |
17 | [assembly: AssemblyVersion(Vsix.Version)]
18 | [assembly: AssemblyFileVersion(Vsix.Version)]
19 |
20 | namespace System.Runtime.CompilerServices
21 | {
22 | public class IsExternalInit { }
23 | }
--------------------------------------------------------------------------------
/src/Commands/CustomEncodings.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 |
3 | namespace DocumentMargin.Commands
4 | {
5 | internal partial class EncodingMenuCommand
6 | {
7 | public class UTF8WithBomEncoding : UTF8Encoding
8 | {
9 | public UTF8WithBomEncoding() : base(true)
10 | { }
11 |
12 | public override string EncodingName => "Unicode (UTF-8 with signature)";
13 |
14 | public override string BodyName => "UTF-8 with BOM";
15 | }
16 |
17 | public class UTF8WithoutBomEncoding : UTF8Encoding
18 | {
19 | public UTF8WithoutBomEncoding() : base(false)
20 | { }
21 |
22 | public override string EncodingName => "Unicode (UTF-8 without signature)";
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/src/source.extension.cs:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------
2 | //
3 | // This file was generated by VSIX Synchronizer
4 | //
5 | // ------------------------------------------------------------------------------
6 | namespace DocumentMargin
7 | {
8 | internal sealed partial class Vsix
9 | {
10 | public const string Id = "DocumentMargin.a5a7bd52-f250-4930-83b4-2085f8b9c7de";
11 | public const string Name = "Editor Info";
12 | public const string Description = @"Adds a margin to the editor that displays the text length, selection length, and total number of lines in the file. It also shows the encoding of the of the file and let you easily change it.";
13 | public const string Language = "en-US";
14 | public const string Version = "1.0";
15 | public const string Author = "Mads Kristensen";
16 | public const string Tags = "";
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Margins/LengthMarginProvider.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.Composition;
2 | using Microsoft.VisualStudio.Text.Editor;
3 | using Microsoft.VisualStudio.Utilities;
4 |
5 | namespace DocumentMargin.Margin
6 | {
7 | //[Export(typeof(IWpfTextViewMarginProvider))]
8 | [Name(nameof(LengthMargin))]
9 | [MarginContainer(PredefinedMarginNames.BottomRightCorner)]
10 | [Order(Before = PredefinedMarginNames.RowMargin)]
11 | [ContentType(StandardContentTypeNames.Text)]
12 | [TextViewRole(PredefinedTextViewRoles.Zoomable)]
13 | [TextViewRole(PredefinedTextViewRoles.Document)]
14 | [DeferCreation(OptionName = DefaultTextViewHostOptions.EditingStateMarginOptionName)]
15 | internal class LengthMarginProvider : IWpfTextViewMarginProvider
16 | {
17 | public IWpfTextViewMargin CreateMargin(IWpfTextViewHost wpfTextViewHost, IWpfTextViewMargin marginContainer)
18 | {
19 | return new LengthMargin(wpfTextViewHost.TextView);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Telemetry.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.Telemetry;
2 |
3 | namespace DocumentMargin
4 | {
5 | public class Telemetry
6 | {
7 | private const string _namespace = "VS/Extension/" + Vsix.Name + "/";
8 |
9 | public static TelemetryEvent CreateEvent(string name)
10 | {
11 | return new TelemetryEvent(CleanName(name));
12 | }
13 |
14 | public static void TrackEvent(TelemetryEvent telemetryEvent)
15 | {
16 | telemetryEvent.Properties["version"] = Vsix.Version;
17 | TelemetryService.DefaultSession.PostEvent(telemetryEvent);
18 | }
19 |
20 | public static void TrackUserTask(string name, TelemetryResult result = TelemetryResult.Success)
21 | {
22 | TelemetryService.DefaultSession.PostUserTask(CleanName(name), result);
23 | }
24 |
25 | private static string CleanName(string name)
26 | {
27 | return (_namespace + name).Replace(" ", "_");
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/VSCommandTable.cs:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------
2 | //
3 | // This file was generated by the free extension VSIX Synchronizer
4 | //
5 | // ------------------------------------------------------------------------------
6 | using System;
7 |
8 | namespace DocumentMargin
9 | {
10 | ///
11 | /// Helper class that exposes all GUIDs used across VS Package.
12 | ///
13 | internal sealed partial class PackageGuids
14 | {
15 | public const string DocumentMarginString = "91ef11a2-e68d-4ac8-80be-aefff53f409a";
16 | public static Guid DocumentMargin = new Guid(DocumentMarginString);
17 | }
18 | ///
19 | /// Helper class that encapsulates all CommandIDs uses across VS Package.
20 | ///
21 | internal sealed partial class PackageIds
22 | {
23 | public const int EncodingMenu = 0x0001;
24 | public const int EncodingMenuGroup = 0x0002;
25 | public const int EncodingMenuDynamicStart = 0x1001;
26 | }
27 | }
--------------------------------------------------------------------------------
/src/Margins/InfoMarginProvider.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.Composition;
2 | using Microsoft.VisualStudio.Text.Editor;
3 | using Microsoft.VisualStudio.Text.Tagging;
4 | using Microsoft.VisualStudio.Utilities;
5 |
6 | namespace DocumentMargin.Margin
7 | {
8 | [Export(typeof(IWpfTextViewMarginProvider))]
9 | [Name(nameof(InfoMargin))]
10 | [MarginContainer(PredefinedMarginNames.BottomRightCorner)]
11 | [Order(After = nameof(EncodingMargin))]
12 | [ContentType(StandardContentTypeNames.Text)]
13 | [TextViewRole(PredefinedTextViewRoles.Zoomable)]
14 | [TextViewRole(PredefinedTextViewRoles.Document)]
15 | internal class InfoMarginProvider : IWpfTextViewMarginProvider
16 | {
17 | [Import]
18 | internal IViewTagAggregatorFactoryService _tagAggregator = null;
19 |
20 | public IWpfTextViewMargin CreateMargin(IWpfTextViewHost wpfTextViewHost, IWpfTextViewMargin marginContainer)
21 | {
22 | ITagAggregator tagAggregator = _tagAggregator.CreateTagAggregator(wpfTextViewHost.TextView);
23 | return new InfoMargin(wpfTextViewHost.TextView, tagAggregator);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/vs-publish.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/vsix-publish",
3 | "categories": [ "other", "coding" ],
4 | "private": false,
5 | "identity": {
6 | "internalName": "DocumentMargin",
7 | "tags": [ "encoding", "selection", "editor", "margin" ]
8 | },
9 | "assetFiles": [
10 | {
11 | "pathOnDisk": "art/basic.png",
12 | "targetPath": "art/basic.png"
13 | },
14 | {
15 | "pathOnDisk": "art/info.png",
16 | "targetPath": "art/info.png"
17 | },
18 | {
19 | "pathOnDisk": "art/encoding.png",
20 | "targetPath": "art/encoding.png"
21 | },
22 | {
23 | "pathOnDisk": "art/encoding-tooltip.png",
24 | "targetPath": "art/encoding-tooltip.png"
25 | },
26 | {
27 | "pathOnDisk": "art/multi-selection.png",
28 | "targetPath": "art/multi-selection.png"
29 | },
30 | {
31 | "pathOnDisk": "art/selected-text.png",
32 | "targetPath": "art/selected-text.png"
33 | },
34 | {
35 | "pathOnDisk": "art/selection.png",
36 | "targetPath": "art/selection.png"
37 | }
38 | ],
39 | "overview": "README.md",
40 | "publisher": "MadsKristensen",
41 | "repo": "https://github.com/MadsKristensen/DocumentMargin"
42 | }
--------------------------------------------------------------------------------
/src/Commands/EncodingMenuCommandBridge.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.Shell.Interop;
2 | using Microsoft.VisualStudio.Text;
3 |
4 | namespace DocumentMargin.Commands
5 | {
6 | internal class EncodingMenuCommandBridge
7 | {
8 | private const uint _showOptions = (uint)(__VSSHOWCONTEXTMENUOPTS2.VSCTXMENU_PLACETOP | __VSSHOWCONTEXTMENUOPTS2.VSCTXMENU_RIGHTALIGN);
9 |
10 | public ITextDocument CurrentDocument { get; private set; }
11 |
12 | public async Task ShowAsync(ITextDocument document, int x, int y)
13 | {
14 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
15 | CurrentDocument = document;
16 |
17 | try
18 | {
19 | IVsUIShell shell = await VS.Services.GetUIShellAsync();
20 | POINTS[] locationPoints = new[] { new POINTS() { x = (short)x, y = (short)y } };
21 | _ = shell.ShowContextMenu(_showOptions, PackageGuids.DocumentMargin, PackageIds.EncodingMenu, locationPoints, pCmdTrgtActive: null);
22 |
23 | Telemetry.TrackUserTask("openencodingmenu");
24 | }
25 | finally
26 | {
27 | CurrentDocument = null;
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Margins/SelectionMarginProvider.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.Composition;
2 | using Microsoft.VisualStudio.Text.Editor;
3 | using Microsoft.VisualStudio.Utilities;
4 |
5 | namespace DocumentMargin.Margin
6 | {
7 | [Export(typeof(IWpfTextViewMarginProvider))]
8 | [Name(nameof(SelectionMargin))]
9 | [MarginContainer(PredefinedMarginNames.BottomRightCorner)]
10 | [Order(Before = nameof(LengthMargin))]
11 | [Order(Before = PredefinedMarginNames.RowMargin)]
12 | [ContentType(StandardContentTypeNames.Text)]
13 | [TextViewRole(PredefinedTextViewRoles.Zoomable)]
14 | [TextViewRole(PredefinedTextViewRoles.Document)]
15 | [DeferCreation(OptionName = DefaultTextViewHostOptions.EditingStateMarginOptionName)]
16 | internal class SelectionMarginProvider : IWpfTextViewMarginProvider
17 | {
18 | public IWpfTextViewMargin CreateMargin(IWpfTextViewHost wpfTextViewHost, IWpfTextViewMargin marginContainer)
19 | {
20 | // Disable selection (MULTI/BOX) from showing up in the editor margin
21 | wpfTextViewHost.TextView.Options.SetOptionValue(DefaultTextViewHostOptions.SelectionStateMarginOptionId, false);
22 |
23 | return new SelectionMargin(wpfTextViewHost.TextView);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/DocumentMarginPackage.cs:
--------------------------------------------------------------------------------
1 | global using System;
2 | global using Community.VisualStudio.Toolkit;
3 | global using Microsoft.VisualStudio.Shell;
4 | global using Task = System.Threading.Tasks.Task;
5 | using System.Runtime.InteropServices;
6 | using System.Threading;
7 | using DocumentMargin.Commands;
8 |
9 | namespace DocumentMargin
10 | {
11 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
12 | [InstalledProductRegistration(Vsix.Name, Vsix.Description, Vsix.Version)]
13 | [ProvideMenuResource("Menus.ctmenu", 1)]
14 | [ProvideService(typeof(EncodingMenuCommandBridge), IsAsyncQueryable = true)]
15 | [Guid(PackageGuids.DocumentMarginString)]
16 | public sealed class DocumentMarginPackage : ToolkitPackage
17 | {
18 | protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress)
19 | {
20 | AddService(
21 | typeof(EncodingMenuCommandBridge),
22 | (_, _, _) => Task.FromResult