├── resources
├── icon.png
├── logo.png
└── extension.manifest.json
├── src
├── CodingWithCalvin.OpenInNotepadPlusPlus
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── source.extension.cs
│ ├── VSCommandTable.cs
│ ├── source.extension.vsixmanifest
│ ├── OpenInNotepadPlusPlusPackage.cs
│ ├── Dialogs
│ │ └── SettingsDialogPage.cs
│ ├── VSCommandTable.vsct
│ ├── Helpers
│ │ ├── Logger.cs
│ │ └── ProjectHelpers.cs
│ ├── Commands
│ │ └── OpenExecutableCommand.cs
│ └── CodingWithCalvin.OpenInNotepadPlusPlus.csproj
└── CodingWithCalvin.OpenInNotepadPlusPlus.sln
├── LICENSE
├── .github
└── workflows
│ ├── release_build_and_deploy.yml
│ └── publish.yml
├── .claude
└── CLAUDE.md
├── README.md
└── .gitignore
/resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodingWithCalvin/VS-OpenInNotepadPlusPlus/HEAD/resources/icon.png
--------------------------------------------------------------------------------
/resources/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodingWithCalvin/VS-OpenInNotepadPlusPlus/HEAD/resources/logo.png
--------------------------------------------------------------------------------
/resources/extension.manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/vsix-publish",
3 | "categories": [
4 | "coding",
5 | "other",
6 | "programming languages"
7 | ],
8 | "identity": {
9 | "internalName": "VS-OpenInNotepadPlusPlus"
10 | },
11 | "overview": "../README.md",
12 | "publisher": "CodingWithCalvin",
13 | "qna": false,
14 | "repo": "https://www.github.com/CodingWithCalvin/VS-OpenInNotepadPlusPlus"
15 | }
--------------------------------------------------------------------------------
/src/CodingWithCalvin.OpenInNotepadPlusPlus/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 | using CodingWithCalvin.OpenInNotepadPlusPlus;
4 |
5 | [assembly: AssemblyTitle(Vsix.Name)]
6 | [assembly: AssemblyDescription(Vsix.Description)]
7 | [assembly: AssemblyConfiguration("")]
8 | [assembly: AssemblyCompany(Vsix.Author)]
9 | [assembly: AssemblyProduct(Vsix.Name)]
10 | [assembly: AssemblyCopyright("")]
11 | [assembly: AssemblyTrademark("")]
12 | [assembly: AssemblyCulture("")]
13 | [assembly: ComVisible(false)]
14 | [assembly: AssemblyVersion(Vsix.Version)]
15 | [assembly: AssemblyFileVersion(Vsix.Version)]
16 |
--------------------------------------------------------------------------------
/src/CodingWithCalvin.OpenInNotepadPlusPlus/source.extension.cs:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------
2 | //
3 | // This file was generated by VSIX Synchronizer
4 | //
5 | // ------------------------------------------------------------------------------
6 | namespace CodingWithCalvin.OpenInNotepadPlusPlus
7 | {
8 | internal sealed partial class Vsix
9 | {
10 | public const string Id = "VS-OpenInNotepadPlusPlus";
11 | public const string Name = "Open in Notepad++";
12 | public const string Description = @"Adds a menu command that lets you open any solution, project, folder and file in Notepad++";
13 | public const string Language = "en-US";
14 | public const string Version = "4.1.0";
15 | public const string Author = "Coding With Calvin";
16 | public const string Tags = "notepad++, text, code";
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Calvin A. Allen
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.
--------------------------------------------------------------------------------
/src/CodingWithCalvin.OpenInNotepadPlusPlus/VSCommandTable.cs:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------
2 | //
3 | // This file was generated by VSIX Synchronizer
4 | //
5 | // ------------------------------------------------------------------------------
6 | namespace CodingWithCalvin.OpenInNotepadPlusPlus
7 | {
8 | using System;
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 guidPackageString = "6aeabf47-7bdc-47b3-ade7-06f5bae6d868";
16 | public static Guid guidPackage = new Guid(guidPackageString);
17 |
18 | public const string guidOpenInNppCmdSetString = "f781199d-54a8-4a18-ac9d-a91f292587db";
19 | public static Guid guidOpenInNppCmdSet = new Guid(guidOpenInNppCmdSetString);
20 |
21 | public const string guidIconsString = "3a06fde0-497f-4b1f-9150-5b1c5879e8af";
22 | public static Guid guidIcons = new Guid(guidIconsString);
23 | }
24 | ///
25 | /// Helper class that encapsulates all CommandIDs uses across VS Package.
26 | ///
27 | internal sealed partial class PackageIds
28 | {
29 | public const int OpenInNpp = 0x0100;
30 | public const int NotepadPlusPlus = 0x0001;
31 | }
32 | }
--------------------------------------------------------------------------------
/src/CodingWithCalvin.OpenInNotepadPlusPlus/source.extension.vsixmanifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Open in Notepad++
6 | Adds a menu command that lets you open any solution, project, folder and file in Notepad++
7 | https://github.com/CodingWithCalvin/VS-OpenInNotepadPlusPlus
8 | resources\LICENSE
9 | resources\logo.png
10 | notepad++, text, code
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/CodingWithCalvin.OpenInNotepadPlusPlus/OpenInNotepadPlusPlusPackage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 | using System.Threading;
4 | using CodingWithCalvin.OpenInNotepadPlusPlus.Commands;
5 | using CodingWithCalvin.OpenInNotepadPlusPlus.Dialogs;
6 | using CodingWithCalvin.OpenInNotepadPlusPlus.Helpers;
7 | using Microsoft.VisualStudio.Shell;
8 |
9 | namespace CodingWithCalvin.OpenInNotepadPlusPlus
10 | {
11 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
12 | [InstalledProductRegistration(Vsix.Name, Vsix.Description, Vsix.Version)]
13 | [ProvideOptionPage(
14 | typeof(SettingsDialogPage),
15 | Vsix.Name,
16 | "General",
17 | 101,
18 | 111,
19 | true,
20 | new string[0],
21 | ProvidesLocalizedCategoryName = false
22 | )]
23 | [ProvideMenuResource("Menus.ctmenu", 1)]
24 | [Guid(PackageGuids.guidPackageString)]
25 | public sealed class OpenInNotepadPlusPlusPackage : AsyncPackage
26 | {
27 | protected override async System.Threading.Tasks.Task InitializeAsync(
28 | CancellationToken cancellationToken,
29 | IProgress progress
30 | )
31 | {
32 | await JoinableTaskFactory.SwitchToMainThreadAsync();
33 |
34 | var settings = (SettingsDialogPage)this.GetDialogPage(typeof(SettingsDialogPage));
35 |
36 | Logger.Initialize(this, Vsix.Name);
37 | OpenExecutableCommand.Initialize(this, settings);
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/CodingWithCalvin.OpenInNotepadPlusPlus.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.9.34728.123
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodingWithCalvin.OpenInNotepadPlusPlus", "CodingWithCalvin.OpenInNotepadPlusPlus\CodingWithCalvin.OpenInNotepadPlusPlus.csproj", "{ADD4175C-AA5C-49E4-AFAF-6B9D3E832E8D}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D4EE3BDC-84CE-485E-9E33-ED9D9C2CDDD7}"
9 | ProjectSection(SolutionItems) = preProject
10 | ..\resources\extension.manifest.json = ..\resources\extension.manifest.json
11 | ..\.github\workflows\publish.yml = ..\.github\workflows\publish.yml
12 | ..\README.md = ..\README.md
13 | ..\.github\workflows\release_build_and_deploy.yml = ..\.github\workflows\release_build_and_deploy.yml
14 | EndProjectSection
15 | EndProject
16 | Global
17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
18 | Debug|x64 = Debug|x64
19 | Release|x64 = Release|x64
20 | EndGlobalSection
21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
22 | {ADD4175C-AA5C-49E4-AFAF-6B9D3E832E8D}.Debug|x64.ActiveCfg = Debug|x64
23 | {ADD4175C-AA5C-49E4-AFAF-6B9D3E832E8D}.Debug|x64.Build.0 = Debug|x64
24 | {ADD4175C-AA5C-49E4-AFAF-6B9D3E832E8D}.Release|x64.ActiveCfg = Release|x64
25 | {ADD4175C-AA5C-49E4-AFAF-6B9D3E832E8D}.Release|x64.Build.0 = Release|x64
26 | EndGlobalSection
27 | GlobalSection(SolutionProperties) = preSolution
28 | HideSolutionNode = FALSE
29 | EndGlobalSection
30 | GlobalSection(ExtensibilityGlobals) = postSolution
31 | SolutionGuid = {0E8BC91D-F5A6-4687-9E69-19535DFB44D1}
32 | EndGlobalSection
33 | EndGlobal
34 |
--------------------------------------------------------------------------------
/.github/workflows/release_build_and_deploy.yml:
--------------------------------------------------------------------------------
1 | name: 'Build and Deploy'
2 |
3 | on:
4 | workflow_dispatch:
5 | pull_request:
6 | types: [opened, reopened]
7 | push:
8 | branches:
9 | - main
10 | tags-ignore:
11 | - '[0-9]+.[0-9]+.[0-9]+.[0-9]+'
12 |
13 | jobs:
14 | Release-Build-and-Deploy:
15 | runs-on: windows-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v4
19 | - uses: microsoft/setup-msbuild@v2
20 | - uses: nuget/setup-nuget@v2
21 |
22 | - name: 1. Versioning Release
23 | id: step-version
24 | uses: CodingWithCalvin/GHA-VSVsixVersioner@v1
25 | with:
26 | extension-manifest-file: 'src/CodingWithCalvin.OpenInNotepadPlusPlus/source.extension.vsixmanifest'
27 | extension-source-file: 'src/CodingWithCalvin.OpenInNotepadPlusPlus/source.extension.cs'
28 |
29 | - name: 2. Restoring Packages
30 | run: nuget restore ./src/CodingWithCalvin.OpenInNotepadPlusPlus.sln
31 |
32 | - name: 3. Building Project
33 | run: msbuild 'src/CodingWithCalvin.OpenInNotepadPlusPlus/CodingWithCalvin.OpenInNotepadPlusPlus.csproj' /p:configuration='Release' /p:platform='x64' /p:DeployExtension=False
34 |
35 | - name: 4. Create Information File
36 | uses: jsdaniell/create-json@v1.2.3
37 | with:
38 | name: 'src/CodingWithCalvin.OpenInNotepadPlusPlus/bin/x64/Release/CodingWithCalvin.OpenInNotepadPlusPlus.info'
39 | json: '{"sha":"${{ github.sha }}", "version":"${{ steps.step-version.outputs.version }}"}'
40 |
41 | - name: 5. Publishing Build Artifact
42 | uses: actions/upload-artifact@v4
43 | with:
44 | path: |
45 | src/CodingWithCalvin.OpenInNotepadPlusPlus/bin/x64/Release/CodingWithCalvin.OpenInNotepadPlusPlus.info
46 | src/CodingWithCalvin.OpenInNotepadPlusPlus/bin/x64/Release/CodingWithCalvin.OpenInNotepadPlusPlus.vsix
47 |
--------------------------------------------------------------------------------
/src/CodingWithCalvin.OpenInNotepadPlusPlus/Dialogs/SettingsDialogPage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.IO;
4 | using System.Linq;
5 | using Microsoft.VisualStudio.Shell;
6 |
7 | namespace CodingWithCalvin.OpenInNotepadPlusPlus.Dialogs
8 | {
9 | public class SettingsDialogPage : DialogPage
10 | {
11 | [Category("General")]
12 | [DisplayName("Install path")]
13 | [Description("The absolute path to the \"notepad++.exe\" file.")]
14 | public string FolderPath { get; set; }
15 |
16 | public override void LoadSettingsFromStorage()
17 | {
18 | base.LoadSettingsFromStorage();
19 |
20 | if (!string.IsNullOrEmpty(this.FolderPath))
21 | {
22 | return;
23 | }
24 |
25 | this.FolderPath = FindNotepadPlusPlus();
26 | }
27 |
28 | private static string FindNotepadPlusPlus()
29 | {
30 | var directoryInfo = new DirectoryInfo(
31 | Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)
32 | );
33 | if (directoryInfo.Parent == null)
34 | {
35 | return null;
36 | }
37 |
38 | foreach (
39 | var directory in directoryInfo.Parent.GetDirectories(
40 | directoryInfo.Name.Replace(" (x86)", string.Empty) + "*"
41 | )
42 | )
43 | {
44 | foreach (var fileSystemInfo in directory.GetDirectories("Notepad++").Reverse())
45 | {
46 | var path = Path.Combine(fileSystemInfo.FullName, "notepad++.exe");
47 | if (File.Exists(path))
48 | {
49 | return path;
50 | }
51 | }
52 | }
53 |
54 | return null;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/CodingWithCalvin.OpenInNotepadPlusPlus/VSCommandTable.vsct:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
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 |
--------------------------------------------------------------------------------
/.claude/CLAUDE.md:
--------------------------------------------------------------------------------
1 | # CLAUDE.md
2 |
3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4 |
5 | ## Project Overview
6 |
7 | This is a Visual Studio extension that adds a right-click context menu command to open solution files, project files, or individual files in Notepad++. It targets .NET Framework 4.8 and uses the VS SDK.
8 |
9 | ## Build Commands
10 |
11 | Build the extension (requires Visual Studio with VS SDK or .NET Framework 4.8 targeting pack):
12 | ```
13 | msbuild src\CodingWithCalvin.OpenInNotepadPlusPlus.sln
14 | ```
15 |
16 | Debug by pressing F5 in Visual Studio - this launches the VS Experimental Instance (`/rootsuffix Exp`).
17 |
18 | ## Architecture
19 |
20 | The extension has a simple structure:
21 |
22 | - **OpenInNotepadPlusPlusPackage.cs** - The VS package entry point, registers the command and settings page
23 | - **Commands/OpenExecutableCommand.cs** - Handles the context menu command, gets the selected path and launches Notepad++
24 | - **Helpers/ProjectHelpers.cs** - Resolves file paths from Solution Explorer selection (handles solutions, projects, project items, and unloaded projects)
25 | - **Dialogs/SettingsDialogPage.cs** - Options page for configuring the Notepad++ executable path
26 | - **VSCommandTable.vsct** - Defines the context menu command placement
27 |
28 | ## Key Implementation Details
29 |
30 | Path resolution in `ProjectHelpers.GetSelectedPath()` handles three selection types:
31 | 1. `ProjectItem` - uses `FileNames[1]`
32 | 2. `Project` - uses `FullName` with fallback to `UniqueName` for unloaded projects
33 | 3. `Solution` - uses `FullName`
34 |
35 | The extension auto-detects Notepad++ in the default install location but allows custom paths via Tools > Options.
36 |
37 | ## Development Requirements
38 |
39 | Install the [Extensibility Essentials 2022](https://marketplace.visualstudio.com/items?itemName=MadsKristensen.ExtensibilityEssentials2022) extension for Visual Studio.
40 |
--------------------------------------------------------------------------------
/src/CodingWithCalvin.OpenInNotepadPlusPlus/Helpers/Logger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.VisualStudio.Shell;
3 | using Microsoft.VisualStudio.Shell.Interop;
4 |
5 | namespace CodingWithCalvin.OpenInNotepadPlusPlus.Helpers
6 | {
7 | public static class Logger
8 | {
9 | private static IVsOutputWindowPane _pane;
10 | private static IServiceProvider _provider;
11 | private static string _name;
12 |
13 | public static void Initialize(Package provider, string name)
14 | {
15 | _provider = provider;
16 | _name = name;
17 | }
18 |
19 | public static void Log(string message)
20 | {
21 | if (string.IsNullOrEmpty(message))
22 | {
23 | return;
24 | }
25 |
26 | try
27 | {
28 | if (!EnsurePane())
29 | {
30 | return;
31 | }
32 | ThreadHelper.ThrowIfNotOnUIThread();
33 |
34 | _pane.OutputStringThreadSafe(DateTime.Now + ": " + message + Environment.NewLine);
35 | }
36 | catch (Exception ex)
37 | {
38 | System.Diagnostics.Debug.WriteLine(ex);
39 | }
40 | }
41 |
42 | public static void Log(Exception ex)
43 | {
44 | if (ex == null)
45 | {
46 | return;
47 | }
48 |
49 | ThreadHelper.ThrowIfNotOnUIThread();
50 | Log(ex.ToString());
51 | }
52 |
53 | private static bool EnsurePane()
54 | {
55 | ThreadHelper.ThrowIfNotOnUIThread();
56 |
57 | if (_pane == null)
58 | {
59 | var guid = Guid.NewGuid();
60 | var output = _provider.GetService(typeof(SVsOutputWindow)) as IVsOutputWindow;
61 | if (output == null)
62 | {
63 | throw new ArgumentNullException(nameof(output));
64 | }
65 | output.CreatePane(ref guid, _name, 1, 1);
66 | output.GetPane(ref guid, out _pane);
67 | }
68 |
69 | return _pane != null;
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish to VS Marketplace
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | permissions:
7 | contents: write
8 | actions: read
9 |
10 | jobs:
11 | publish:
12 | runs-on: windows-latest
13 | outputs:
14 | version: ${{ steps.artifact_manifest.outputs.version }}
15 | steps:
16 | - name: Checkout
17 | uses: actions/checkout@v4
18 |
19 | - name: 1. Download artifact
20 | id: download-artifact
21 | uses: dawidd6/action-download-artifact@v6
22 | with:
23 | workflow: release_build_and_deploy.yml
24 | workflow_conclusion: success
25 |
26 | - name: 2. Parse Artifact Manifest
27 | id: artifact_manifest
28 | uses: ActionsTools/read-json-action@main
29 | with:
30 | file_path: ./artifact/CodingWithCalvin.OpenInNotepadPlusPlus.info
31 |
32 | - name: 3. Create Tag & Release
33 | uses: ncipollo/release-action@v1.14.0
34 | with:
35 | artifacts: ./artifact/CodingWithCalvin.OpenInNotepadPlusPlus.vsix
36 | generateReleaseNotes: true
37 | makeLatest: true
38 | commit: ${{ steps.artifact_manifest.outputs.sha }}
39 | tag: ${{ steps.artifact_manifest.outputs.version }}
40 |
41 | - name: 4. Publish Release to Marketplace
42 | if: success()
43 | uses: CodingWithCalvin/GHA-VSMarketplacePublisher@v1
44 | with:
45 | marketplace-pat: ${{ secrets.VS_PAT }}
46 | publish-manifest-path: ./resources/extension.manifest.json
47 | vsix-path: ./artifact/CodingWithCalvin.OpenInNotepadPlusPlus.vsix
48 |
49 | notify:
50 | needs: publish
51 | uses: CodingWithCalvin/.github/.github/workflows/bluesky-post.yml@main
52 | with:
53 | post_text: |
54 | 🚀 Open in Notepad++ v${{ needs.publish.outputs.version }} for #VisualStudio has been released!
55 |
56 | [Check out the release notes here!](https://github.com/${{ github.repository }}/releases/tag/${{ needs.publish.outputs.version }})
57 |
58 | Marketplace: https://marketplace.visualstudio.com/items?itemName=CodingWithCalvin.VS-OpenInNotepadPlusPlus
59 | secrets:
60 | BLUESKY_USERNAME: ${{ secrets.BLUESKY_USERNAME }}
61 | BLUESKY_APP_PASSWORD: ${{ secrets.BLUESKY_APP_PASSWORD }}
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Open in Notepad++
2 |
3 | A Visual Studio extension that adds a right-click context menu command that allows you to open the solution file, project file, or file in Notepad++.
4 |
5 | ## License
6 |
7 | [](https://img.shields.io/github/license/codingwithcalvin/VS-OpenInNotepadPlusPlus?style=for-the-badge)
8 |
9 | ## Build Status
10 |
11 | 
12 |
13 | ## Marketplace Status
14 |
15 | [](https://img.shields.io/visual-studio-marketplace/i/codingwithcalvin.VS-OpenInNotepadPlusPlus?style=for-the-badge) [](https://img.shields.io/visual-studio-marketplace/d/codingwithcalvin.VS-OpenInNotepadPlusPlus?style=for-the-badge)
16 | [](https://img.shields.io/visual-studio-marketplace/v/codingwithcalvin.VS-OpenInNotepadPlusPlus?style=for-the-badge) [](https://img.shields.io/visual-studio-marketplace/r/codingwithcalvin.VS-OpenInNotepadPlusPlus?style=for-the-badge)
17 |
18 | ## Prerequisite
19 |
20 | In order to use this extension, you must have Notepad++ installed.
21 |
22 | You can [download Notepad++ for free](https://notepad-plus-plus.org/).
23 |
24 | ## Settings
25 |
26 | The extension will automatically find `notepad++.exe` when it's located at in the default install directory. If it's installed in a custom location, you can easily change it in the settings dialog.
27 |
28 | ## Contributions
29 |
30 | Contributions are welcome! Issues, PRs, etc. While it may seem this extension is "done", who knows what the future may hold for it?
31 |
32 | For cloning and building this project yourself, make sure to install the [Extensibility Essentials 2022 extension](https://marketplace.visualstudio.com/items?itemName=MadsKristensen.ExtensibilityEssentials2022) for Visual Studio which enables some features used by this project.
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # something new with VS2015
5 | .vs
6 |
7 | # User-specific files
8 | *.suo
9 | *.user
10 | *.sln.docstates
11 |
12 | # Build results
13 |
14 | [Dd]ebug/
15 | [Rr]elease/
16 | x64/
17 | build/
18 | [Bb]in/
19 | [Oo]bj/
20 |
21 | # MSTest test Results
22 | [Tt]est[Rr]esult*/
23 | [Bb]uild[Ll]og.*
24 |
25 | *_i.c
26 | *_p.c
27 | *.ilk
28 | *.meta
29 | *.obj
30 | *.pch
31 | *.pdb
32 | *.pgc
33 | *.pgd
34 | *.rsp
35 | *.sbr
36 | *.tlb
37 | *.tli
38 | *.tlh
39 | *.tmp
40 | *.tmp_proj
41 | *.log
42 | *.vspscc
43 | *.vssscc
44 | .builds
45 | *.pidb
46 | *.log
47 | *.scc
48 |
49 | # Visual C++ cache files
50 | ipch/
51 | *.aps
52 | *.ncb
53 | *.opensdf
54 | *.sdf
55 | *.cachefile
56 |
57 | # Visual Studio profiler
58 | *.psess
59 | *.vsp
60 | *.vspx
61 |
62 | # Guidance Automation Toolkit
63 | *.gpState
64 |
65 | # ReSharper is a .NET coding add-in
66 | _ReSharper*/
67 | *.[Rr]e[Ss]harper
68 |
69 | # TeamCity is a build add-in
70 | _TeamCity*
71 |
72 | # DotCover is a Code Coverage Tool
73 | *.dotCover
74 |
75 | # resharper
76 | *.DotSettings
77 |
78 | # NCrunch
79 | *.ncrunch*
80 | .*crunch*.local.xml
81 |
82 | # Installshield output folder
83 | [Ee]xpress/
84 |
85 | # DocProject is a documentation generator add-in
86 | DocProject/buildhelp/
87 | DocProject/Help/*.HxT
88 | DocProject/Help/*.HxC
89 | DocProject/Help/*.hhc
90 | DocProject/Help/*.hhk
91 | DocProject/Help/*.hhp
92 | DocProject/Help/Html2
93 | DocProject/Help/html
94 |
95 | # Click-Once directory
96 | publish/
97 |
98 | # Publish Web Output
99 | *.Publish.xml
100 | *.pubxml
101 |
102 | # NuGet Packages Directory
103 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
104 | packages/
105 |
106 | # Windows Azure Build Output
107 | csx
108 | *.build.csdef
109 |
110 | # Windows Store app package directory
111 | AppPackages/
112 |
113 | # Others
114 | sql/
115 | *.Cache
116 | ClientBin/
117 | [Ss]tyle[Cc]op.*
118 | ~$*
119 | *~
120 | *.dbmdl
121 | *.[Pp]ublish.xml
122 | *.pfx
123 | *.publishsettings
124 |
125 | # RIA/Silverlight projects
126 | Generated_Code/
127 |
128 | # Backup & report files from converting an old project file to a newer
129 | # Visual Studio version. Backup files are not needed, because we have git ;-)
130 | _UpgradeReport_Files/
131 | Backup*/
132 | UpgradeLog*.XML
133 | UpgradeLog*.htm
134 |
135 | # SQL Server files
136 | App_Data/*.mdf
137 | App_Data/*.ldf
138 |
139 | # =========================
140 | # Windows detritus
141 | # =========================
142 |
143 | # Windows image file caches
144 | Thumbs.db
145 | ehthumbs.db
146 |
147 | # Folder config file
148 | Desktop.ini
149 |
150 | # Recycle Bin used on file shares
151 | $RECYCLE.BIN/
152 |
153 | # Mac crap
154 | .DS_Store
155 |
156 | # Claude Code local settings
157 | .claude/settings.local.json
--------------------------------------------------------------------------------
/src/CodingWithCalvin.OpenInNotepadPlusPlus/Commands/OpenExecutableCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.Design;
3 | using System.Diagnostics;
4 | using System.Windows.Forms;
5 | using CodingWithCalvin.OpenInNotepadPlusPlus.Dialogs;
6 | using CodingWithCalvin.OpenInNotepadPlusPlus.Helpers;
7 | using EnvDTE;
8 | using EnvDTE80;
9 | using Microsoft.VisualStudio.Shell;
10 |
11 | namespace CodingWithCalvin.OpenInNotepadPlusPlus.Commands
12 | {
13 | internal sealed class OpenExecutableCommand
14 | {
15 | private readonly Package _package;
16 | private readonly SettingsDialogPage _settings;
17 |
18 | private OpenExecutableCommand(Package package, SettingsDialogPage settings)
19 | {
20 | this._package = package;
21 | this._settings = settings;
22 |
23 | var commandService = (OleMenuCommandService)
24 | ServiceProvider.GetService(typeof(IMenuCommandService));
25 |
26 | if (commandService != null)
27 | {
28 | var menuCommandId = new CommandID(
29 | PackageGuids.guidOpenInNppCmdSet,
30 | PackageIds.OpenInNpp
31 | );
32 | var menuItem = new MenuCommand(OpenPath, menuCommandId);
33 | commandService.AddCommand(menuItem);
34 | }
35 | }
36 |
37 | public static OpenExecutableCommand Instance { get; private set; }
38 |
39 | private IServiceProvider ServiceProvider => this._package;
40 |
41 | public static void Initialize(Package package, SettingsDialogPage settings)
42 | {
43 | Instance = new OpenExecutableCommand(package, settings);
44 | }
45 |
46 | private void OpenPath(object sender, EventArgs e)
47 | {
48 | var service = (DTE2)this.ServiceProvider.GetService(typeof(DTE));
49 | try
50 | {
51 | ThreadHelper.ThrowIfNotOnUIThread();
52 | var selectedFilePath = ProjectHelpers.GetSelectedPath(service);
53 | var executablePath = _settings.FolderPath;
54 | if (
55 | !string.IsNullOrEmpty(selectedFilePath) && !string.IsNullOrEmpty(executablePath)
56 | )
57 | {
58 | OpenExecutable(executablePath, selectedFilePath);
59 | }
60 | else
61 | {
62 | MessageBox.Show("Couldn't resolve the folder");
63 | }
64 | }
65 | catch (Exception ex)
66 | {
67 | Logger.Log(ex);
68 | }
69 | }
70 |
71 | private static void OpenExecutable(string executablePath, string selectedFilePath)
72 | {
73 | var startInfo = new ProcessStartInfo
74 | {
75 | FileName = $"\"{executablePath}\"",
76 | Arguments = $"\"{selectedFilePath}\"",
77 | CreateNoWindow = true,
78 | WindowStyle = ProcessWindowStyle.Hidden
79 | };
80 |
81 | using (System.Diagnostics.Process.Start(startInfo))
82 | {
83 | //TODO : Should this be empty?
84 | }
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/CodingWithCalvin.OpenInNotepadPlusPlus/Helpers/ProjectHelpers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Runtime.InteropServices;
4 | using EnvDTE;
5 | using EnvDTE80;
6 | using Microsoft.VisualStudio;
7 | using Microsoft.VisualStudio.Shell;
8 | using Microsoft.VisualStudio.Shell.Interop;
9 |
10 | namespace CodingWithCalvin.OpenInNotepadPlusPlus.Helpers
11 | {
12 | internal static class ProjectHelpers
13 | {
14 | public static string GetSelectedPath(DTE2 dte)
15 | {
16 | ThreadHelper.ThrowIfNotOnUIThread();
17 |
18 | foreach (
19 | UIHierarchyItem selectedItem in (Array)
20 | dte.ToolWindows.SolutionExplorer.SelectedItems
21 | )
22 | {
23 | switch (selectedItem.Object)
24 | {
25 | case ProjectItem projectItem:
26 | return projectItem.FileNames[1];
27 | case Project project:
28 | return GetProjectPath(project, dte.Solution);
29 | case Solution solution:
30 | return solution.FullName;
31 | default:
32 | // Handle unloaded projects - they don't expose a Project object
33 | // but we can get the path via IVsMonitorSelection and IVsHierarchy
34 | var path = GetPathFromHierarchy();
35 | if (!string.IsNullOrEmpty(path))
36 | {
37 | return path;
38 | }
39 | break;
40 | }
41 | }
42 | return null;
43 | }
44 |
45 | private static string GetPathFromHierarchy()
46 | {
47 | ThreadHelper.ThrowIfNotOnUIThread();
48 |
49 | // Use IVsMonitorSelection to get the current selection's IVsHierarchy
50 | // This works for unloaded projects where selectedItem.Object is not a Project
51 | var monitorSelection = Package.GetGlobalService(typeof(SVsShellMonitorSelection)) as IVsMonitorSelection;
52 | if (monitorSelection == null)
53 | {
54 | return null;
55 | }
56 |
57 | var hr = monitorSelection.GetCurrentSelection(out var hierarchyPtr, out var itemId, out _, out _);
58 | if (hr != VSConstants.S_OK || hierarchyPtr == IntPtr.Zero)
59 | {
60 | return null;
61 | }
62 |
63 | try
64 | {
65 | var hierarchy = Marshal.GetObjectForIUnknown(hierarchyPtr) as IVsHierarchy;
66 | if (hierarchy == null)
67 | {
68 | return null;
69 | }
70 |
71 | // GetCanonicalName returns the full path for project files
72 | if (hierarchy.GetCanonicalName(itemId, out var canonicalName) == VSConstants.S_OK
73 | && !string.IsNullOrEmpty(canonicalName)
74 | && File.Exists(canonicalName))
75 | {
76 | return canonicalName;
77 | }
78 | }
79 | finally
80 | {
81 | Marshal.Release(hierarchyPtr);
82 | }
83 |
84 | return null;
85 | }
86 |
87 | private static string GetProjectPath(Project project, Solution solution)
88 | {
89 | ThreadHelper.ThrowIfNotOnUIThread();
90 |
91 | // Try FullName first - works for loaded projects and sometimes unloaded ones
92 | if (!string.IsNullOrEmpty(project.FullName) && File.Exists(project.FullName))
93 | {
94 | return project.FullName;
95 | }
96 |
97 | // Fallback: try UniqueName which may contain the relative or absolute path
98 | if (!string.IsNullOrEmpty(project.UniqueName))
99 | {
100 | // UniqueName might already be an absolute path
101 | if (Path.IsPathRooted(project.UniqueName) && File.Exists(project.UniqueName))
102 | {
103 | return project.UniqueName;
104 | }
105 |
106 | // Try combining with solution directory
107 | if (!string.IsNullOrEmpty(solution?.FullName))
108 | {
109 | var solutionDirectory = Path.GetDirectoryName(solution.FullName);
110 | var projectPath = Path.Combine(solutionDirectory, project.UniqueName);
111 | if (File.Exists(projectPath))
112 | {
113 | return projectPath;
114 | }
115 | }
116 | }
117 |
118 | return null;
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/CodingWithCalvin.OpenInNotepadPlusPlus/CodingWithCalvin.OpenInNotepadPlusPlus.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16.0
5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
6 | true
7 |
8 |
9 | x64
10 | bin\x64\Release\
11 | true
12 | X64
13 |
14 |
15 | true
16 | bin\x64\Debug\
17 | x64
18 | full
19 | TRACE;DEBUG;X64
20 |
21 |
22 |
23 | Debug
24 | x64
25 | 2.0
26 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
27 | {ADD4175C-AA5C-49E4-AFAF-6B9D3E832E8D}
28 | Library
29 | Properties
30 | CodingWithCalvin.OpenInNotepadPlusPlus
31 | CodingWithCalvin.OpenInNotepadPlusPlus
32 | v4.8
33 | true
34 | true
35 | true
36 | true
37 | true
38 | false
39 | Program
40 | $(DevEnvDir)devenv.exe
41 | /rootsuffix Exp
42 | v3
43 |
44 |
45 |
46 | 15.0.1
47 |
48 |
49 | 17.0.5232
50 | runtime; build; native; contentfiles; analyzers;
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | Designer
62 | VsixManifestGenerator
63 | source.extension.cs
64 |
65 |
66 |
67 |
68 | resources\icon.png
69 | true
70 |
71 |
72 | resources\LICENSE
73 | true
74 |
75 |
76 | resources\logo.png
77 | true
78 |
79 |
80 |
81 |
82 | Menus.ctmenu
83 | VsctGenerator
84 | VSCommandTable.cs
85 |
86 |
87 |
88 |
89 |
90 |
91 | Component
92 |
93 |
94 |
95 |
96 |
97 |
98 | True
99 | True
100 | source.extension.vsixmanifest
101 |
102 |
103 | True
104 | True
105 | VSCommandTable.vsct
106 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------