├── .editorconfig
├── .gitattributes
├── .github
└── workflows
│ └── dotnet.yml
├── .gitignore
├── BrightExtensions.sln
├── BrightGit.Extensibility
├── .vsextension
│ └── string-resources.json
├── BrightGit.Extensibility.csproj
├── Commands
│ ├── EFGitMigrationDownCommand.cs
│ ├── EFGitMigrationHookAddCommand.cs
│ ├── EFGitMigrationHookCheckCommand.cs
│ ├── EFGitMigrationHookRemoveCommand.cs
│ ├── EFGitTest.cs
│ ├── TabsRestoreCommand.cs
│ ├── TabsSaveCommand.cs
│ └── TabsSortCommand.cs
├── ExtensionEntrypoint.cs
├── Helpers
│ ├── LibGit2SharpHelper.cs
│ └── VSHelper.cs
├── Listeners
│ ├── GitSharpHookListener.cs
│ ├── SolutionSubscriptionObserver.cs
│ └── SolutionTrackerObserver.cs
├── Meta.cs
├── Models
│ ├── TabDocumentInfo.cs
│ └── TabsInfo.cs
├── Services
│ ├── DialogService.cs
│ ├── EFCoreManagerService.cs
│ ├── GitFileWatcherService.cs
│ ├── GitSharpHookService.cs
│ ├── IDialogService.cs
│ ├── SettingsData.cs
│ ├── SettingsEFCoreData.cs
│ ├── SettingsService.cs
│ ├── SettingsTabsData.cs
│ ├── TabManagerService.cs
│ ├── TabsStorageData.cs
│ └── TabsStorageService.cs
└── Windows
│ ├── SettingsWindow.cs
│ ├── SettingsWindowCommand.cs
│ ├── SettingsWindowContent.cs
│ ├── SettingsWindowContent.xaml
│ ├── SettingsWindowViewModel.cs
│ ├── TabsWindow.cs
│ ├── TabsWindowCommand.cs
│ ├── TabsWindowContent.cs
│ ├── TabsWindowContent.xaml
│ └── TabsWindowViewModel.cs
├── BrightGit.SharpAutoMigrator
├── BrightGit.SharpAutoMigrator.csproj
├── DotnetHelper.cs
├── Program.cs
├── Properties
│ └── PublishProfiles
│ │ └── FolderProfile.pubxml
├── Readme.txt
└── Resources
│ └── post-checkout
├── BrightGit.SharpCommon
├── BrightGit.SharpCommon.csproj
├── DotnetHelper.cs
├── Helpers
│ ├── GitHelper.cs
│ └── MigratorHelper.cs
├── Models
│ ├── GitHookType.cs
│ ├── RunData.cs
│ └── RunType.cs
├── SharpHookHelper.cs
└── SharpRunHelper.cs
├── BrightGit.SharpHook
├── BrightGit.SharpHook.csproj
├── Program.cs
└── Properties
│ ├── PublishProfiles
│ └── FolderProfile.pubxml
│ └── launchSettings.json
├── BrightGit.SharpRun
├── BrightGit.SharpRun.csproj
└── Program.cs
├── BrightXaml.Extensibility
├── .vsextension
│ └── string-resources.json
├── BrightXaml.Extensibility.csproj
├── Commands
│ ├── CleanBinAndObjCommand.cs
│ ├── DevOpenSolutionCommand.cs
│ ├── ExtractClassesInUseCommand.cs
│ ├── ExtractFolderCommand.cs
│ ├── ExtractViewCommand.cs
│ ├── FormatXamlAllCommand.cs
│ ├── FormatXamlCommand.cs
│ ├── HelloWorldCommand.cs
│ ├── KillXamlDesignerCommand.cs
│ ├── PropertyToINPCAllCommand.cs
│ ├── PropertyToINPCCommand.cs
│ ├── ShowCodeBehindCommand.cs
│ ├── ShowDefinitionCommand.cs
│ ├── ShowViewCommand.cs
│ ├── ShowViewModelCommand.cs
│ └── ToggleViewModelCommand.cs
├── ExtensionEntrypoint.cs
├── Helpers
│ ├── ClipboardHelper.cs
│ └── VSHelper.cs
├── Listeners
│ └── ShowDefinitionListener.cs
├── Meta.cs
├── Models
│ ├── BindingDefinitionOffsets.cs
│ ├── ComboIntData.cs
│ └── CommandInfo.cs
├── Properties
│ └── launchSettings.json
├── Services
│ ├── DialogService.cs
│ ├── IDialogService.cs
│ ├── SettingsData.cs
│ ├── SettingsFormatXamlData.cs
│ ├── SettingsGoToBindingData.cs
│ ├── SettingsPropInpcData.cs
│ └── SettingsService.cs
├── Utilities
│ ├── ExportFilesHelper.cs
│ ├── PropToInpcHelper.cs
│ ├── PropertyLineData.cs
│ ├── ShowDefinitionHelper.cs
│ ├── ViewModelHelper.cs
│ └── XamlFormatter.cs
└── Windows
│ ├── ChooseItemWindow.cs
│ ├── ChooseItemWindowCommand.cs
│ ├── ChooseItemWindowContent.cs
│ ├── ChooseItemWindowContent.xaml
│ ├── ChooseItemWindowViewModel.cs
│ ├── HelpWindow.cs
│ ├── HelpWindowCommand.cs
│ ├── HelpWindowContent.cs
│ ├── HelpWindowContent.xaml
│ ├── HelpWindowViewModel.cs
│ ├── ProgressWindow.cs
│ ├── ProgressWindowCommand.cs
│ ├── ProgressWindowContent.cs
│ ├── ProgressWindowContent.xaml
│ ├── ProgressWindowViewModel.cs
│ ├── SettingsWindow.cs
│ ├── SettingsWindowCommand.cs
│ ├── SettingsWindowContent.cs
│ ├── SettingsWindowContent.xaml
│ └── SettingsWindowViewModel.cs
├── BrightXaml.ExtensibilityTests
├── BrightXaml.ExtensibilityTests.csproj
├── MockEntrypoint.cs
├── Resources
│ ├── FullDocument01_Bad.xml
│ ├── FullDocument01_Good.xml
│ ├── FullDocument02_Bad.xml
│ ├── FullDocument02_Good.xml
│ ├── FullDocument03_Bad.xml
│ ├── FullDocument03_Good.xml
│ ├── FullDocument04_Bad.xml
│ ├── FullDocument04_Good.xml
│ ├── MultipleLine01_Bad.xml
│ ├── MultipleLine01_Good.xml
│ ├── MultipleLine02_Bad.xml
│ ├── MultipleLine02_Good.xml
│ ├── MultipleLine03_Bad.xml
│ ├── MultipleLine03_Good.xml
│ ├── MultipleLine04_Bad.xml
│ ├── MultipleLine04_Good.xml
│ ├── MultipleLine05_Bad.xml
│ ├── MultipleLine05_Good.xml
│ ├── MultipleLine06_Bad.xml
│ ├── MultipleLine06_Good.xml
│ ├── MultipleLine07_Bad.xml
│ ├── MultipleLine07_Good.xml
│ ├── MultipleLine08_Bad.xml
│ ├── MultipleLine08_Good.xml
│ ├── RemoveEmpty_MultipleLine01_Bad.xml
│ ├── RemoveEmpty_MultipleLine01_Good.xml
│ ├── RemoveEmpty_MultipleLine02_Bad.xml
│ ├── RemoveEmpty_MultipleLine02_Good.xml
│ ├── SingleLine01_Bad.xml
│ ├── SingleLine01_Good.xml
│ ├── SingleLine02_Bad.xml
│ ├── SingleLine02_Good.xml
│ ├── SingleLine03_Bad.xml
│ ├── SingleLine03_Good.xml
│ ├── SingleLine04_Bad.xml
│ ├── SingleLine04_Good.xml
│ ├── SingleLine05_Bad.xml
│ ├── SingleLine05_Good.xml
│ ├── SingleLineComment01_Bad.xml
│ ├── SingleLineComment01_Good.xml
│ ├── SingleLineComment02_Bad.xml
│ ├── SingleLineComment02_Good.xml
│ ├── SplitTagsPerLine01_Bad.xml
│ ├── SplitTagsPerLine01_Good.xml
│ ├── SplitTagsPerLine02_Bad.xml
│ └── SplitTagsPerLine02_Good.xml
├── TestHelper.cs
└── Utilities
│ ├── PropToInpcHelperTests.cs
│ ├── ShowDefinitionHelperTests.cs
│ ├── ViewModelHelperTests.cs
│ └── XamlFormatterTests.cs
├── LICENSE
└── README.md
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 |
3 | # CEE0027: String not localized
4 | dotnet_diagnostic.CEE0027.severity = silent
5 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.github/workflows/dotnet.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a .NET project
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
3 |
4 | name: .NET
5 |
6 | on:
7 | push:
8 | branches: [ "main" ]
9 | pull_request:
10 | branches: [ "main" ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: windows-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v4
19 |
20 | - name: Setup .NET
21 | uses: actions/setup-dotnet@v4
22 | with:
23 | dotnet-version: 8.0.x
24 |
25 | - name: Restore dependencies
26 | run: dotnet restore
27 |
28 | - name: Build
29 | run: dotnet build --no-restore --configuration Release
30 |
31 | - name: Test
32 | run: dotnet test --no-build --configuration Release --verbosity normal
33 |
34 | - name: Upload VSIX artifact (BrightXaml)
35 | uses: actions/upload-artifact@v4
36 | with:
37 | name: vsix-artifact
38 | path: '**/BrightXaml*.vsix'
--------------------------------------------------------------------------------
/BrightGit.Extensibility/.vsextension/string-resources.json:
--------------------------------------------------------------------------------
1 | {
2 | "BrightGit.Extensibility.Command1.DisplayName": "Sample Remote Command"
3 | }
4 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/BrightGit.Extensibility.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0-windows
4 | enable
5 | disable
6 |
7 |
8 |
9 | win-x64
10 |
11 | 1.0.0.0
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | MSBuild:Compile
20 |
21 |
22 | MSBuild:Compile
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Commands/EFGitMigrationHookAddCommand.cs:
--------------------------------------------------------------------------------
1 | namespace BrightGit.Extensibility.Commands;
2 |
3 | using BrightGit.Extensibility.Helpers;
4 | using BrightGit.SharpCommon;
5 | using Microsoft;
6 | using Microsoft.VisualStudio.Extensibility;
7 | using Microsoft.VisualStudio.Extensibility.Commands;
8 | using Microsoft.VisualStudio.Extensibility.Shell;
9 | using System.Diagnostics;
10 | using System.Threading;
11 | using System.Threading.Tasks;
12 |
13 | [VisualStudioContribution]
14 | internal class EFGitMigrationHookAddCommand : Command
15 | {
16 | private readonly TraceSource logger;
17 |
18 | public EFGitMigrationHookAddCommand(TraceSource traceSource)
19 | {
20 | this.logger = Requires.NotNull(traceSource, nameof(traceSource));
21 | }
22 |
23 | ///
24 | public override CommandConfiguration CommandConfiguration => new(displayName: "EF Auto Migrator - Add Hook")
25 | {
26 | //Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
27 | Icon = new(ImageMoniker.KnownValues.AddLink, IconSettings.IconAndText),
28 | };
29 |
30 | ///
31 | public override Task InitializeAsync(CancellationToken cancellationToken)
32 | {
33 | // Use InitializeAsync for any one-time setup or initialization.
34 | return base.InitializeAsync(cancellationToken);
35 | }
36 |
37 | ///
38 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
39 | {
40 | try
41 | {
42 | var solutionDirectoryPath = await VSHelper.GetSolutionDirectoryAsync(Extensibility.Workspaces(), cancellationToken);
43 | if (string.IsNullOrWhiteSpace(solutionDirectoryPath))
44 | {
45 | await Extensibility.Shell().ShowPromptAsync("Please open a solution before adding the hook.", PromptOptions.OK, cancellationToken);
46 | return;
47 | }
48 |
49 | SharpHookHelper.AddAutoMigratorHook(solutionDirectoryPath);
50 | await Extensibility.Shell().ShowPromptAsync($"Auto Migration hook added to repo {Path.GetDirectoryName(solutionDirectoryPath)}.", PromptOptions.OK, cancellationToken);
51 | }
52 | catch (Exception ex)
53 | {
54 | logger.TraceEvent(TraceEventType.Error, 0, ex.Message);
55 | await Extensibility.Shell().ShowPromptAsync(ex.Message, PromptOptions.OK, cancellationToken);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Commands/EFGitMigrationHookCheckCommand.cs:
--------------------------------------------------------------------------------
1 | namespace BrightGit.Extensibility.Commands;
2 |
3 | using BrightGit.Extensibility.Helpers;
4 | using BrightGit.SharpCommon;
5 | using Microsoft;
6 | using Microsoft.VisualStudio.Extensibility;
7 | using Microsoft.VisualStudio.Extensibility.Commands;
8 | using Microsoft.VisualStudio.Extensibility.Shell;
9 | using System.Diagnostics;
10 | using System.Threading;
11 | using System.Threading.Tasks;
12 |
13 | [VisualStudioContribution]
14 | internal class EFGitMigrationHookCheckCommand : Command
15 | {
16 | private readonly TraceSource logger;
17 |
18 | public EFGitMigrationHookCheckCommand(TraceSource traceSource)
19 | {
20 | this.logger = Requires.NotNull(traceSource, nameof(traceSource));
21 | }
22 |
23 | ///
24 | public override CommandConfiguration CommandConfiguration => new(displayName: "EF Auto Migrator - Check Hook State")
25 | {
26 | //Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
27 | Icon = new(ImageMoniker.KnownValues.LinkAlert, IconSettings.IconAndText),
28 | };
29 |
30 | ///
31 | public override Task InitializeAsync(CancellationToken cancellationToken)
32 | {
33 | // Use InitializeAsync for any one-time setup or initialization.
34 | return base.InitializeAsync(cancellationToken);
35 | }
36 |
37 | ///
38 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
39 | {
40 | try
41 | {
42 | var solutionDirectoryPath = await VSHelper.GetSolutionDirectoryAsync(Extensibility.Workspaces(), cancellationToken);
43 | if (string.IsNullOrWhiteSpace(solutionDirectoryPath))
44 | {
45 | await Extensibility.Shell().ShowPromptAsync("Please open a solution before checking the hook state.", PromptOptions.OK, cancellationToken);
46 | return;
47 | }
48 |
49 | var isHookActive = SharpHookHelper.CheckAutoMigratorHook(solutionDirectoryPath);
50 | var msg = isHookActive ? "Auto Migration hook is active for this repo." : "Auto Migration hook is NOT active for this repo.";
51 | await Extensibility.Shell().ShowPromptAsync(msg, PromptOptions.OK, cancellationToken);
52 | }
53 | catch (Exception ex)
54 | {
55 | logger.TraceEvent(TraceEventType.Error, 0, ex.Message);
56 | await Extensibility.Shell().ShowPromptAsync(ex.Message, PromptOptions.OK, cancellationToken);
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Commands/EFGitMigrationHookRemoveCommand.cs:
--------------------------------------------------------------------------------
1 | namespace BrightGit.Extensibility.Commands;
2 |
3 | using BrightGit.Extensibility.Helpers;
4 | using BrightGit.SharpCommon;
5 | using Microsoft;
6 | using Microsoft.VisualStudio.Extensibility;
7 | using Microsoft.VisualStudio.Extensibility.Commands;
8 | using Microsoft.VisualStudio.Extensibility.Shell;
9 | using System.Diagnostics;
10 | using System.Threading;
11 | using System.Threading.Tasks;
12 |
13 | [VisualStudioContribution]
14 | internal class EFGitMigrationHookRemoveCommand : Command
15 | {
16 | private readonly TraceSource logger;
17 |
18 | public EFGitMigrationHookRemoveCommand(TraceSource traceSource)
19 | {
20 | this.logger = Requires.NotNull(traceSource, nameof(traceSource));
21 | }
22 |
23 | ///
24 | public override CommandConfiguration CommandConfiguration => new(displayName: "EF Auto Migrator - Remove Hook")
25 | {
26 | //Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
27 | Icon = new(ImageMoniker.KnownValues.RemoveLink, IconSettings.IconAndText),
28 | };
29 |
30 | ///
31 | public override Task InitializeAsync(CancellationToken cancellationToken)
32 | {
33 | // Use InitializeAsync for any one-time setup or initialization.
34 | return base.InitializeAsync(cancellationToken);
35 | }
36 |
37 | ///
38 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
39 | {
40 | try
41 | {
42 | var solutionDirectoryPath = await VSHelper.GetSolutionDirectoryAsync(Extensibility.Workspaces(), cancellationToken);
43 | if (string.IsNullOrWhiteSpace(solutionDirectoryPath))
44 | {
45 | await Extensibility.Shell().ShowPromptAsync("Please open a solution before removing the hook.", PromptOptions.OK, cancellationToken);
46 | return;
47 | }
48 |
49 | SharpHookHelper.RemoveAutoMigratorHook(solutionDirectoryPath);
50 | await Extensibility.Shell().ShowPromptAsync($"Auto Migration hook removed from {Path.GetDirectoryName(solutionDirectoryPath)}.", PromptOptions.OK, cancellationToken);
51 | }
52 | catch (Exception ex)
53 | {
54 | logger.TraceEvent(TraceEventType.Error, 0, ex.Message);
55 | await Extensibility.Shell().ShowPromptAsync(ex.Message, PromptOptions.OK, cancellationToken);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Commands/EFGitTest.cs:
--------------------------------------------------------------------------------
1 | namespace BrightGit.Extensibility.Commands;
2 | using LibGit2Sharp;
3 | using Microsoft;
4 | using Microsoft.VisualStudio.Extensibility;
5 | using Microsoft.VisualStudio.Extensibility.Commands;
6 | using Microsoft.VisualStudio.Extensibility.Shell;
7 | using System.Diagnostics;
8 | using System.Reflection;
9 | using System.Threading;
10 | using System.Threading.Tasks;
11 |
12 | [VisualStudioContribution]
13 | internal class EFGitTest : Command
14 | {
15 | private readonly TraceSource logger;
16 |
17 | public EFGitTest(TraceSource traceSource)
18 | {
19 | this.logger = Requires.NotNull(traceSource, nameof(traceSource));
20 | }
21 |
22 | ///
23 | public override CommandConfiguration CommandConfiguration => new(displayName: "(Dev) EF Core - Test Git Library")
24 | {
25 | // Use in debug only for testing, might be deleted later.
26 | #if DEBUG
27 | Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
28 | #endif
29 | Icon = new(ImageMoniker.KnownValues.Extension, IconSettings.IconAndText),
30 | };
31 |
32 | ///
33 | public override Task InitializeAsync(CancellationToken cancellationToken)
34 | {
35 | // Use InitializeAsync for any one-time setup or initialization.
36 | return base.InitializeAsync(cancellationToken);
37 | }
38 |
39 | ///
40 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
41 | {
42 | try
43 | {
44 | // Set the native library path for LibGit2Sharp when inside VS extension (VSIX).
45 | string assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
46 | assemblyFolder = assemblyFolder[..^2];
47 | GlobalSettings.NativeLibraryPath = Path.Combine(assemblyFolder, "runtimes", "win-x64", "native");
48 |
49 | var repo = new Repository("");
50 | await Extensibility.Shell().ShowPromptAsync("Success", PromptOptions.OK, cancellationToken);
51 | }
52 | catch (Exception ex)
53 | {
54 | logger.TraceEvent(TraceEventType.Error, 0, ex.Message);
55 | await Extensibility.Shell().ShowPromptAsync(ex.Message, PromptOptions.OK, cancellationToken);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Commands/TabsRestoreCommand.cs:
--------------------------------------------------------------------------------
1 | namespace BrightGit.Extensibility.Commands;
2 |
3 | using BrightGit.Extensibility.Helpers;
4 | using BrightGit.Extensibility.Services;
5 | using Microsoft;
6 | using Microsoft.VisualStudio.Extensibility;
7 | using Microsoft.VisualStudio.Extensibility.Commands;
8 | using Microsoft.VisualStudio.Extensibility.Shell;
9 | using System.Diagnostics;
10 | using System.Threading;
11 | using System.Threading.Tasks;
12 |
13 | [VisualStudioContribution]
14 | internal class TabsRestoreCommand : Command
15 | {
16 | private readonly TraceSource logger;
17 | private readonly TabsStorageService tabsStorageService;
18 | private readonly TabManagerService tabManagerService;
19 |
20 | public TabsRestoreCommand(TraceSource traceSource, TabsStorageService tabsStorageService, TabManagerService tabManagerService)
21 | {
22 | this.logger = Requires.NotNull(traceSource, nameof(traceSource));
23 | this.tabsStorageService = tabsStorageService;
24 | this.tabManagerService = tabManagerService;
25 | }
26 |
27 | ///
28 | public override CommandConfiguration CommandConfiguration => new(displayName: "(WIP) Restore Tabs")
29 | {
30 | //Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
31 | Icon = new(ImageMoniker.KnownValues.RestoreDefaultView, IconSettings.IconAndText),
32 | };
33 |
34 | ///
35 | public override Task InitializeAsync(CancellationToken cancellationToken)
36 | {
37 | // Use InitializeAsync for any one-time setup or initialization.
38 | return base.InitializeAsync(cancellationToken);
39 | }
40 |
41 | ///
42 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
43 | {
44 | var shell = Extensibility.Shell();
45 | var documents = Extensibility.Documents();
46 | var configuration = Extensibility.Configuration();
47 | var workspaces = Extensibility.Workspaces();
48 |
49 | try
50 | {
51 | var sw = Stopwatch.StartNew();
52 |
53 | // Check if we are in a solution.
54 | var solutionName = await VSHelper.GetSolutionNameAsync(workspaces, cancellationToken);
55 | if (string.IsNullOrWhiteSpace(solutionName))
56 | {
57 | await shell.ShowPromptAsync("Please open a solution before restoring tabs", PromptOptions.OK, cancellationToken);
58 | return;
59 | }
60 |
61 | // Format the branch name.
62 | string gitHeadPath = Path.Combine(await VSHelper.GetSolutionDirectoryAsync(workspaces, cancellationToken), ".git", "HEAD");
63 | string gitBranchName = string.Empty;
64 | if (File.Exists(gitHeadPath))
65 | {
66 | gitBranchName = File.ReadAllText(gitHeadPath).Split('/').LastOrDefault().TrimEnd('\n');
67 | gitBranchName = $"{gitBranchName}";
68 | }
69 |
70 | var tabsRestored = await tabManagerService.RestoreTabsAsync(true, gitBranchName, shell, documents, workspaces, cancellationToken);
71 |
72 | sw.Stop();
73 | Debug.WriteLine($"Restored {tabsRestored?.Tabs.Count} tabs for {solutionName}.{gitBranchName} ({sw.ElapsedMilliseconds}ms)");
74 | await shell.ShowPromptAsync($"Restored {tabsRestored?.Tabs.Count} tabs for {solutionName}.{gitBranchName} ({sw.ElapsedMilliseconds}ms)", PromptOptions.OK, cancellationToken);
75 | }
76 | catch (Exception ex)
77 | {
78 | Debug.WriteLine(ex);
79 | await shell.ShowPromptAsync($"Error restoring tabs: {ex.Message}", PromptOptions.OK, cancellationToken);
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Commands/TabsSaveCommand.cs:
--------------------------------------------------------------------------------
1 | namespace BrightGit.Extensibility.Commands;
2 |
3 | using BrightGit.Extensibility.Helpers;
4 | using BrightGit.Extensibility.Services;
5 | using Microsoft;
6 | using Microsoft.VisualStudio.Extensibility;
7 | using Microsoft.VisualStudio.Extensibility.Commands;
8 | using Microsoft.VisualStudio.Extensibility.Shell;
9 | using System.Diagnostics;
10 | using System.Threading;
11 | using System.Threading.Tasks;
12 |
13 | [VisualStudioContribution]
14 | internal class TabsSaveCommand : Command
15 | {
16 | private readonly TraceSource logger;
17 | private readonly SettingsService settingsService;
18 | private readonly TabsStorageService tabsStorageService;
19 | private readonly TabManagerService tabManagerService;
20 |
21 | public TabsSaveCommand(TraceSource traceSource,
22 | SettingsService settingsService,
23 | TabsStorageService tabsStorageService,
24 | TabManagerService tabManagerService)
25 | {
26 | this.logger = Requires.NotNull(traceSource, nameof(traceSource));
27 | this.settingsService = settingsService;
28 | this.tabsStorageService = tabsStorageService;
29 | this.tabManagerService = tabManagerService;
30 | }
31 |
32 | ///
33 | public override CommandConfiguration CommandConfiguration => new(displayName: "(WIP) Save Tabs")
34 | {
35 | //Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
36 | Icon = new(ImageMoniker.KnownValues.SaveFileDialog, IconSettings.IconAndText),
37 | };
38 |
39 | ///
40 | public override Task InitializeAsync(CancellationToken cancellationToken)
41 | {
42 | // Use InitializeAsync for any one-time setup or initialization.
43 | return base.InitializeAsync(cancellationToken);
44 | }
45 |
46 | ///
47 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
48 | {
49 | var shell = Extensibility.Shell();
50 | var documents = Extensibility.Documents();
51 | var configuration = Extensibility.Configuration();
52 | var workspaces = Extensibility.Workspaces();
53 |
54 | try
55 | {
56 | var sw = Stopwatch.StartNew();
57 |
58 | // Check if we are in a solution.
59 | var solutionName = await VSHelper.GetSolutionNameAsync(workspaces, cancellationToken);
60 | if (string.IsNullOrWhiteSpace(solutionName))
61 | {
62 | await shell.ShowPromptAsync("Please open a solution before saving tabs", PromptOptions.OK, cancellationToken);
63 | return;
64 | }
65 |
66 | // Format the branch name.
67 | string gitHeadPath = Path.Combine(await VSHelper.GetSolutionDirectoryAsync(workspaces, cancellationToken), ".git", "HEAD");
68 | string gitBranchName = string.Empty;
69 | if (File.Exists(gitHeadPath))
70 | {
71 | gitBranchName = File.ReadAllText(gitHeadPath).Split('/').LastOrDefault().TrimEnd('\n');
72 | gitBranchName = $"{gitBranchName}";
73 | }
74 |
75 | // Debug only.
76 | var openedDocuments = await documents.GetOpenDocumentsAsync(cancellationToken);
77 | Debug.WriteLine($"TavsSaveCommand - openedDocuments: {openedDocuments.Count}");
78 |
79 | // Save tabs.
80 | var tabsSaved = await tabManagerService.SaveTabsAsync(true, gitBranchName, shell, documents, workspaces, cancellationToken);
81 |
82 | sw.Stop();
83 | Debug.WriteLine($"Saved tabs {tabsSaved?.Tabs?.Count ?? 0} for {tabsSaved?.SolutionName}.{gitBranchName} ({sw.ElapsedMilliseconds}ms)");
84 | await shell.ShowPromptAsync($"Saved {tabsSaved?.Tabs.Count ?? 0} tabs for {tabsSaved?.SolutionName}.{gitBranchName} ({sw.ElapsedMilliseconds}ms)", PromptOptions.OK, cancellationToken);
85 | }
86 | catch (Exception ex)
87 | {
88 | Debug.WriteLine(ex);
89 | await shell.ShowPromptAsync($"Error saving tabs: {ex.Message}", PromptOptions.OK, cancellationToken);
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Commands/TabsSortCommand.cs:
--------------------------------------------------------------------------------
1 | namespace BrightGit.Extensibility.Commands;
2 |
3 | using Microsoft;
4 | using Microsoft.VisualStudio.Extensibility;
5 | using Microsoft.VisualStudio.Extensibility.Commands;
6 | using Microsoft.VisualStudio.Extensibility.Shell;
7 | using Microsoft.VisualStudio.RpcContracts.Documents;
8 | using System.Diagnostics;
9 | using System.Threading;
10 | using System.Threading.Tasks;
11 |
12 | [VisualStudioContribution]
13 | internal class TabsSortCommand : Command
14 | {
15 | private readonly TraceSource logger;
16 |
17 | public TabsSortCommand(TraceSource traceSource)
18 | {
19 | this.logger = Requires.NotNull(traceSource, nameof(traceSource));
20 | }
21 |
22 | ///
23 | public override CommandConfiguration CommandConfiguration => new(displayName: "(WIP) Sort Tabs")
24 | {
25 | //Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
26 | Icon = new(ImageMoniker.KnownValues.SaveFileDialog, IconSettings.IconAndText),
27 | };
28 |
29 | ///
30 | public override Task InitializeAsync(CancellationToken cancellationToken)
31 | {
32 | // Use InitializeAsync for any one-time setup or initialization.
33 | return base.InitializeAsync(cancellationToken);
34 | }
35 |
36 | ///
37 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
38 | {
39 | try
40 | {
41 | var sw = Stopwatch.StartNew();
42 |
43 | var openedDocuments = await Extensibility.Documents().GetOpenDocumentsAsync(cancellationToken);
44 | if (openedDocuments.Any())
45 | {
46 | var sortedDocuments = openedDocuments.OrderBy(d => Path.GetFileName(d.Moniker.LocalPath)).ToList();
47 |
48 | // TODO: This is a terrible way to do this... I couldn't find a better way to simply reorder the tabs with the API.
49 |
50 | // Close all documents.
51 | //await Task.WhenAll(openedDocuments.Select(document => document.CloseAsync(SaveDocumentOption.PromptSave, Extensibility, cancellationToken)));
52 | await Task.WhenAll(openedDocuments.Select(document => Extensibility.Documents().CloseDocumentAsync(document.Moniker, SaveDocumentOption.PromptSave, cancellationToken)));
53 |
54 | // Open documents in sorted order.
55 | await Task.WhenAll(sortedDocuments.Select(document => Extensibility.Documents().OpenDocumentAsync(document.Moniker, cancellationToken)));
56 |
57 | sw.Stop();
58 | Debug.WriteLine($"Sorted {openedDocuments.Count} tabs in {sw.ElapsedMilliseconds}ms");
59 | await Extensibility.Shell().ShowPromptAsync($"Sorted {openedDocuments.Count} tabs in {sw.ElapsedMilliseconds}ms", PromptOptions.OK, cancellationToken);
60 | }
61 | else
62 | {
63 | await Extensibility.Shell().ShowPromptAsync("No tabs to sort", PromptOptions.OK, cancellationToken);
64 | return;
65 | }
66 | }
67 | catch (Exception ex)
68 | {
69 | Debug.WriteLine(ex);
70 | await Extensibility.Shell().ShowPromptAsync("An error occurred while sorting tabs", PromptOptions.OK, cancellationToken);
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/ExtensionEntrypoint.cs:
--------------------------------------------------------------------------------
1 | namespace BrightGit.Extensibility;
2 |
3 | using BrightGit.Extensibility.Commands;
4 | using BrightGit.Extensibility.Listeners;
5 | using BrightGit.Extensibility.Services;
6 | using BrightGit.Extensibility.Windows;
7 | using Microsoft.Extensions.DependencyInjection;
8 | using Microsoft.VisualStudio.Extensibility;
9 | using Microsoft.VisualStudio.Extensibility.Commands;
10 | using Microsoft.VisualStudio.ProjectSystem.Query;
11 | using System.Threading;
12 | using System.Threading.Tasks;
13 |
14 | ///
15 | /// Extension entrypoint for the VisualStudio.Extensibility extension.
16 | ///
17 | [VisualStudioContribution]
18 | internal class ExtensionEntrypoint : Extension
19 | {
20 | ///
21 | public override ExtensionConfiguration ExtensionConfiguration => new()
22 | {
23 | Metadata = new(
24 | id: "BrightGit.14722f3b-45e7-4e62-bfde-b25896550871",
25 | version: this.ExtensionAssemblyVersion,
26 | publisherName: "Luis Henrique Goll",
27 | displayName: "Bright Git Extension",
28 | description: "Bright Commands and Automations with C# developers using git source control in mind!"),
29 | LoadedWhen = ActivationConstraint.SolutionState(SolutionState.FullyLoaded),
30 | //LoadedWhen = ActivationConstraint.SolutionState(SolutionState.Exists)
31 | };
32 |
33 | ///
34 | protected override void InitializeServices(IServiceCollection serviceCollection)
35 | {
36 | base.InitializeServices(serviceCollection);
37 |
38 | // You can configure dependency injection here by adding services to the serviceCollection.
39 | serviceCollection.AddTransient(provider => new DialogService());
40 | serviceCollection.AddSingleton();
41 | serviceCollection.AddSingleton();
42 | serviceCollection.AddSingleton();
43 | serviceCollection.AddSingleton();
44 | serviceCollection.AddSingleton();
45 | serviceCollection.AddSingleton();
46 | }
47 |
48 | protected override async Task OnInitializedAsync(VisualStudioExtensibility extensibility, CancellationToken cancellationToken)
49 | {
50 | // Start monitoring Git hooks.
51 | //base.ServiceProvider.GetRequiredService().Extensibility = extensibility;
52 | //_ = base.ServiceProvider.GetRequiredService().StartMonitoringAsync();
53 |
54 | // Start monitoring Git HEAD.
55 | base.ServiceProvider.GetRequiredService().Extensibility = extensibility;
56 | _ = base.ServiceProvider.GetRequiredService().StartMonitoringAsync();
57 |
58 | // Subscribe to solution changes.
59 | var solutions = await extensibility.Workspaces().QuerySolutionAsync(solution => solution.With(p => p.Path), cancellationToken);
60 | var firstSolution = solutions.FirstOrDefault();
61 | if (firstSolution != null)
62 | {
63 | var observer = new SolutionSubscriptionObserver();
64 | var subscribe = firstSolution.Projects.With(p => p.Path).SubscribeAsync(observer, CancellationToken.None);
65 | }
66 |
67 | // Carry on with the initialization.
68 | await base.OnInitializedAsync(extensibility, cancellationToken);
69 | }
70 |
71 | [VisualStudioContribution]
72 | //public static MenuConfiguration MyMenu => new("%MyMenu.DisplayName%")
73 | public static MenuConfiguration MyMenu => new("Bright Git")
74 | {
75 | Placements = new[]
76 | {
77 | CommandPlacement.KnownPlacements.ExtensionsMenu
78 | },
79 | Children = new[]
80 | {
81 | MenuChild.Command(),
82 | MenuChild.Separator,
83 | MenuChild.Command(),
84 | MenuChild.Command(),
85 | MenuChild.Command(),
86 |
87 | MenuChild.Separator,
88 | MenuChild.Command(),
89 | MenuChild.Command(),
90 | #if DEBUG
91 | MenuChild.Separator,
92 | MenuChild.Command(),
93 | MenuChild.Command(),
94 | #endif
95 | MenuChild.Separator,
96 | MenuChild.Command(),
97 | #if DEBUG
98 | MenuChild.Command(),
99 | #endif
100 | //MenuChild.Command(),
101 | },
102 | };
103 | }
104 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Helpers/LibGit2SharpHelper.cs:
--------------------------------------------------------------------------------
1 | using LibGit2Sharp;
2 | using System.Reflection;
3 |
4 | namespace BrightGit.Extensibility.Helpers;
5 | internal static class LibGit2SharpHelper
6 | {
7 | public static void RegisterNativePath(string path)
8 | {
9 | // We check first because if we overwrite after in use, it will throw an exception.
10 | if (GlobalSettings.NativeLibraryPath == null)
11 | {
12 | // Set the native library path for LibGit2Sharp when inside VS extension (VSIX).
13 | string assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
14 | assemblyFolder = assemblyFolder[..^2];
15 | GlobalSettings.NativeLibraryPath = Path.Combine(assemblyFolder, "runtimes", "win-x64", "native");
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Helpers/VSHelper.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.Extensibility;
2 | using Microsoft.VisualStudio.ProjectSystem.Query;
3 |
4 | namespace BrightGit.Extensibility.Helpers;
5 | public static class VSHelper
6 | {
7 | public static async Task GetSolutionPathAsync(WorkspacesExtensibility workspace, CancellationToken cancellationToken)
8 | {
9 | // Get the solution path.
10 | var solutionPath = (await workspace.QuerySolutionAsync(solution => solution.With(p => p.Path), cancellationToken)).FirstOrDefault()?.Path;
11 | return solutionPath;
12 | }
13 |
14 | public static async Task GetSolutionNameAsync(WorkspacesExtensibility workspace, CancellationToken cancellationToken)
15 | {
16 | return Path.GetFileNameWithoutExtension(await GetSolutionPathAsync(workspace, cancellationToken));
17 | }
18 |
19 | public static async Task GetSolutionDirectoryAsync(WorkspacesExtensibility workspace, CancellationToken cancellationToken)
20 | {
21 | // Get the solution directory.
22 | var solutionDirectory = (await workspace.QuerySolutionAsync(solution => solution.With(p => p.Directory), cancellationToken)).FirstOrDefault()?.Directory;
23 | return solutionDirectory;
24 | }
25 |
26 | public static async Task> GetProjectsDirectoryAsync(WorkspacesExtensibility workspace, CancellationToken cancellationToken)
27 | {
28 | // Get the directories from all active projects in the solution.
29 | var projectsDirs = await workspace.QuerySolutionAsync(solution => solution.Get(p => p.Projects).With(p => p.Path), cancellationToken);
30 | return projectsDirs.Select(p => p.Path).ToList();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Listeners/GitSharpHookListener.cs:
--------------------------------------------------------------------------------
1 | using BrightGit.Extensibility.Services;
2 | using Microsoft.VisualStudio.Extensibility;
3 | using Microsoft.VisualStudio.Extensibility.Editor;
4 | using System.Diagnostics;
5 |
6 | namespace BrightGit.Extensibility.Listeners;
7 | [VisualStudioContribution]
8 | public class GitSharpHookListener : ExtensionPart, ITextViewOpenClosedListener, ITextViewChangedListener
9 | {
10 | public TextViewExtensionConfiguration TextViewExtensionConfiguration => new()
11 | {
12 | AppliesTo =
13 | [
14 | DocumentFilter.FromGlobPattern("**/*", true),
15 | ],
16 | };
17 |
18 | public GitSharpHookListener(TraceSource traceSource, SettingsService settingsService, GitSharpHookService gitSharpHookService)
19 | {
20 | // If any of the features are enabled, we need to listen for Git hooks.
21 | if (settingsService.Data.Tabs.IsEnabled || settingsService.Data.EFCore.IsEnabled)
22 | {
23 | // TODO:
24 | // Disabled at the moment as I'm experimenting with FileWatcherService.
25 | //_ = gitSharpHookService.StartMonitoringAsync();
26 | }
27 | }
28 |
29 | // We inherit from Listeners just to trigger our constructor and start monitoring git events.
30 | public Task TextViewChangedAsync(TextViewChangedArgs args, CancellationToken cancellationToken)
31 | {
32 | return Task.CompletedTask;
33 | }
34 |
35 | public Task TextViewClosedAsync(ITextViewSnapshot textView, CancellationToken cancellationToken)
36 | {
37 | return Task.CompletedTask;
38 | }
39 |
40 | public Task TextViewOpenedAsync(ITextViewSnapshot textView, CancellationToken cancellationToken)
41 | {
42 | return Task.CompletedTask;
43 | }
44 | }
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Listeners/SolutionSubscriptionObserver.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.ProjectSystem.Query;
2 | using System.Diagnostics;
3 |
4 | namespace BrightGit.Extensibility.Listeners;
5 | public class SolutionSubscriptionObserver : IObserver>
6 | {
7 | public void OnCompleted()
8 | {
9 | Debug.WriteLine("SolutionSubscriptionObserver.OnCompleted");
10 | }
11 |
12 | public void OnError(Exception error)
13 | { }
14 |
15 | public void OnNext(IQueryResults value)
16 | {
17 | Debug.WriteLine("SolutionSubscriptionObserver.OnNext");
18 | }
19 | }
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Listeners/SolutionTrackerObserver.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.ProjectSystem.Query;
2 |
3 | namespace BrightGit.Extensibility.Listeners;
4 |
5 | // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
6 | #pragma warning disable VSEXTPREVIEW_PROJECTQUERY_TRACKING
7 |
8 | public class SolutionTrackerObserver : IObserver>
9 | {
10 | public void OnCompleted()
11 | { }
12 |
13 | public void OnError(Exception error)
14 | { }
15 |
16 | public void OnNext(IQueryTrackUpdates value)
17 | { }
18 | }
19 |
20 | // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
21 | #pragma warning restore VSEXTPREVIEW_PROJECTQUERY_TRACKING
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Meta.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | namespace BrightGit.Extensibility;
4 | internal static class Meta
5 | {
6 | public static Version Version { get; } = Assembly.GetExecutingAssembly().GetName().Version;
7 |
8 | public static bool IsDebug { get; } =
9 | #if DEBUG
10 | true;
11 | #else
12 | false;
13 | #endif
14 | }
15 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Models/TabDocumentInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.Serialization;
2 |
3 | namespace BrightGit.Extensibility.Models;
4 | [DataContract]
5 | public class TabDocumentInfo
6 | {
7 | [DataMember]
8 | public string FilePath { get; set; }
9 |
10 | [DataMember]
11 | public int Index { get; set; }
12 |
13 | [DataMember]
14 | public bool IsPinned { get; set; }
15 |
16 | [DataMember]
17 | public string FileName => (!string.IsNullOrWhiteSpace(FilePath)) ? Path.GetFileName(FilePath) : string.Empty;
18 | }
19 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Models/TabsInfo.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.PlatformUI;
2 | using System.Runtime.Serialization;
3 |
4 | namespace BrightGit.Extensibility.Models;
5 | [DataContract]
6 | public class TabsInfo : ObservableObject
7 | {
8 | [DataMember]
9 | public string Id { get; set; }
10 |
11 | [DataMember]
12 | public string SolutionName { get; set; }
13 |
14 | [DataMember]
15 | public string BranchName { get; set; }
16 |
17 | [DataMember]
18 | public string Name { get => name; set => SetProperty(ref name, value); }
19 | private string name;
20 |
21 | [DataMember]
22 | public DateTime DateSaved { get => dateSaved; set => SetProperty(ref dateSaved, value); }
23 | private DateTime dateSaved;
24 |
25 | [DataMember]
26 | public List Tabs { get; set; } = new();
27 | }
28 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Services/DialogService.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.Extensibility.Shell;
2 |
3 | namespace BrightGit.Extensibility.Services;
4 | public class DialogService : IDialogService
5 | {
6 | public ShellExtensibility Shell { get; set; }
7 |
8 | public async Task ShowPromptOptionsAsync(string message, List items, CancellationToken cancellationToken)
9 | {
10 | if (message == null)
11 | message = "Choose:";
12 |
13 | // Remove duplicates and sort the items.
14 | items = items.Distinct().ToList();
15 |
16 | // Create a dictionary to map items to unique integers.
17 | Dictionary itemMap = new();
18 | for (int i = 0; i < items.Count; i++)
19 | {
20 | itemMap[i] = items[i];
21 | }
22 |
23 | // Create PromptOptions and populate it with the item map.
24 | var promptOptions = new PromptOptions
25 | {
26 | DismissedReturns = -1,
27 | DefaultChoiceIndex = 0,
28 | };
29 |
30 | foreach (var kvp in itemMap)
31 | {
32 | promptOptions.Choices.Add(kvp.Value, kvp.Key);
33 | }
34 |
35 | // Show the prompt and get the result.
36 | var result = await Shell.ShowPromptAsync(
37 | message,
38 | promptOptions,
39 | cancellationToken);
40 |
41 | // Map the selected integer back to the corresponding item.
42 | if (itemMap.ContainsKey(result))
43 | {
44 | return itemMap[result];
45 | }
46 |
47 | // Return null or handle the case when no valid choice is made.
48 | return null;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Services/EFCoreManagerService.cs:
--------------------------------------------------------------------------------
1 | namespace BrightGit.Extensibility.Services;
2 | public class EFCoreManagerService
3 | {
4 | }
5 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Services/IDialogService.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.Extensibility.Shell;
2 |
3 | namespace BrightGit.Extensibility.Services;
4 | public interface IDialogService
5 | {
6 | ShellExtensibility Shell { get; set; }
7 |
8 | ///
9 | /// Only use this for a maximum of 5 items... It displays horizontally only.
10 | ///
11 | Task ShowPromptOptionsAsync(string title, List items, CancellationToken cancellationToken);
12 | }
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Services/SettingsData.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.PlatformUI;
2 | using System.Runtime.Serialization;
3 |
4 | namespace BrightGit.Extensibility.Services;
5 | [DataContract]
6 | public class SettingsData : ObservableObject
7 | {
8 | [DataMember]
9 | public SettingsEFCoreData EFCore { get => efcore; set => SetProperty(ref efcore, value); }
10 | private SettingsEFCoreData efcore;
11 |
12 | [DataMember]
13 | public SettingsTabsData Tabs { get => tabs; set => SetProperty(ref tabs, value); }
14 | private SettingsTabsData tabs;
15 |
16 | public SettingsData()
17 | {
18 | Tabs = new SettingsTabsData();
19 | EFCore = new SettingsEFCoreData();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Services/SettingsEFCoreData.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.PlatformUI;
2 | using System.Runtime.Serialization;
3 |
4 | namespace BrightGit.Extensibility.Services;
5 | [DataContract]
6 | public class SettingsEFCoreData : ObservableObject
7 | {
8 | [DataMember]
9 | public bool IsEnabled { get => isEnabled; set => SetProperty(ref isEnabled, value); }
10 | private bool isEnabled;
11 | }
12 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Services/SettingsService.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.Text.Json;
3 |
4 | namespace BrightGit.Extensibility.Services;
5 | public class SettingsService
6 | {
7 | private const string fileName = "BrightGitSettings.json";
8 | private readonly string directory;
9 | private readonly string filePath;
10 |
11 | public SettingsData Data { get; set; } = new();
12 |
13 | public SettingsService()
14 | {
15 | directory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "BrightExtensions");
16 | filePath = Path.Combine(directory, fileName);
17 | Load();
18 | }
19 |
20 | public void Load()
21 | {
22 | try
23 | {
24 | // Check if directory exists.
25 | if (!Directory.Exists(directory))
26 | Directory.CreateDirectory(directory);
27 |
28 | // Load settings.
29 | if (File.Exists(filePath))
30 | {
31 | var json = File.ReadAllText(filePath);
32 | Data = JsonSerializer.Deserialize(json) ?? new SettingsData();
33 | }
34 | }
35 | catch (Exception ex)
36 | {
37 | Debug.WriteLine(ex);
38 |
39 | // Use default settings.
40 | Data = new SettingsData();
41 | }
42 | }
43 |
44 | public void Save()
45 | {
46 | try
47 | {
48 | var json = JsonSerializer.Serialize(Data, new JsonSerializerOptions { WriteIndented = true });
49 | File.WriteAllText(filePath, json);
50 | }
51 | catch (Exception ex)
52 | {
53 | // Add log?
54 | Debug.WriteLine(ex);
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Services/SettingsTabsData.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.PlatformUI;
2 | using System.Runtime.Serialization;
3 |
4 | namespace BrightGit.Extensibility.Services;
5 | [DataContract]
6 | public class SettingsTabsData : ObservableObject
7 | {
8 | [DataMember]
9 | public bool IsEnabled { get => isEnabled; set => SetProperty(ref isEnabled, value); }
10 | private bool isEnabled;
11 |
12 | [DataMember]
13 | public bool CloseTabsOnSave { get => closeTabsOnSave; set => SetProperty(ref closeTabsOnSave, value); }
14 | private bool closeTabsOnSave;
15 |
16 | [DataMember]
17 | public bool CloseTabsOnBranchChange { get => closeTabsOnBranchChange; set => SetProperty(ref closeTabsOnBranchChange, value); }
18 | private bool closeTabsOnBranchChange = true;
19 | }
20 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Services/TabsStorageData.cs:
--------------------------------------------------------------------------------
1 | using BrightGit.Extensibility.Models;
2 | using Microsoft.VisualStudio.PlatformUI;
3 | using System.Runtime.Serialization;
4 |
5 | namespace BrightGit.Extensibility.Services;
6 | [DataContract]
7 | public class TabsStorageData : ObservableObject
8 | {
9 | [DataMember]
10 | public List TabsBranch { get => tabsBranch; set => SetProperty(ref tabsBranch, value); }
11 | private List tabsBranch = new();
12 |
13 | [DataMember]
14 | public List TabsCustom { get => tabsCustom; set => SetProperty(ref tabsCustom, value); }
15 | private List tabsCustom = new();
16 | }
17 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Services/TabsStorageService.cs:
--------------------------------------------------------------------------------
1 | using BrightGit.Extensibility.Models;
2 | using System.Diagnostics;
3 | using System.Text.Json;
4 |
5 | namespace BrightGit.Extensibility.Services;
6 | public class TabsStorageService
7 | {
8 | private const string fileName = "BrightTabs.json";
9 | private readonly string fileDirectory;
10 | private readonly string filePath;
11 |
12 | public TabsStorageData Data { get; set; } = new();
13 |
14 | public TabsStorageService()
15 | {
16 | fileDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "BrightExtensions");
17 | filePath = Path.Combine(fileDirectory, fileName);
18 | Load();
19 | }
20 |
21 | public void Load()
22 | {
23 | try
24 | {
25 | // Check if directory exists.
26 | if (!Directory.Exists(fileDirectory))
27 | Directory.CreateDirectory(fileDirectory);
28 |
29 | // Load settings.
30 | if (File.Exists(filePath))
31 | {
32 | var json = File.ReadAllText(filePath);
33 | Data = JsonSerializer.Deserialize(json) ?? new TabsStorageData();
34 | }
35 | }
36 | catch (Exception ex)
37 | {
38 | Debug.WriteLine(ex);
39 |
40 | // Use default settings.
41 | Data = new TabsStorageData();
42 | }
43 | }
44 |
45 | public void Save()
46 | {
47 | try
48 | {
49 | var json = JsonSerializer.Serialize(Data, new JsonSerializerOptions { WriteIndented = true });
50 | File.WriteAllText(filePath, json);
51 | }
52 | catch (Exception ex)
53 | {
54 | // Add log?
55 | Debug.WriteLine(ex);
56 | }
57 | }
58 |
59 | public void AddTabsCustom(TabsInfo tabsInfo)
60 | {
61 | AddTabs(tabsInfo, Data.TabsCustom);
62 | }
63 |
64 | public void AddTabsBranch(TabsInfo tabsInfo)
65 | {
66 | AddTabs(tabsInfo, Data.TabsBranch);
67 | }
68 |
69 | private void AddTabs(TabsInfo tabsInfo, List tabs)
70 | {
71 | if (tabsInfo != null)
72 | {
73 | // Remove existing tabs info if any.
74 | var existingTabsInfo = tabs.FirstOrDefault(x => x.Id == tabsInfo.Id);
75 | if (existingTabsInfo != null)
76 | tabs.Remove(existingTabsInfo);
77 |
78 | // Add new tabs info.
79 | tabs.Add(tabsInfo);
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Windows/SettingsWindow.cs:
--------------------------------------------------------------------------------
1 | namespace BrightGit.Extensibility.Windows;
2 |
3 | using BrightGit.Extensibility.Services;
4 | using Microsoft.VisualStudio.Extensibility;
5 | using Microsoft.VisualStudio.Extensibility.ToolWindows;
6 | using Microsoft.VisualStudio.RpcContracts.RemoteUI;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | ///
11 | /// A sample tool window.
12 | ///
13 | [VisualStudioContribution]
14 | public class SettingsWindow : ToolWindow
15 | {
16 | private readonly SettingsWindowContent content;
17 |
18 | ///
19 | /// Initializes a new instance of the class.
20 | ///
21 | public SettingsWindow(SettingsService settingsService)
22 | {
23 | this.Title = "Bright Git - Settings";
24 | this.content = new SettingsWindowContent(settingsService);
25 | content.ViewModel.CloseWindow = (cancellationToken) => { _ = HideAsync(cancellationToken); };
26 | }
27 |
28 | ///
29 | public override ToolWindowConfiguration ToolWindowConfiguration => new()
30 | {
31 | // Use this object initializer to set optional parameters for the tool window.
32 | Placement = ToolWindowPlacement.Floating,
33 | };
34 |
35 | ///
36 | public override Task InitializeAsync(CancellationToken cancellationToken)
37 | {
38 | // Use InitializeAsync for any one-time setup or initialization.
39 | return Task.CompletedTask;
40 | }
41 |
42 | ///
43 | public override Task GetContentAsync(CancellationToken cancellationToken)
44 | {
45 | return Task.FromResult(content);
46 | }
47 |
48 | public override Task OnShowAsync(CancellationToken cancellationToken)
49 | {
50 | return base.OnShowAsync(cancellationToken);
51 | }
52 |
53 | public override Task OnHideAsync(CancellationToken cancellationToken)
54 | {
55 | // Auto save when closing?
56 | //content.ViewModel.SaveSettings();
57 | return base.OnHideAsync(cancellationToken);
58 | }
59 |
60 | ///
61 | protected override void Dispose(bool disposing)
62 | {
63 | if (disposing)
64 | content.Dispose();
65 |
66 | base.Dispose(disposing);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Windows/SettingsWindowCommand.cs:
--------------------------------------------------------------------------------
1 | namespace BrightGit.Extensibility.Windows;
2 |
3 | using Microsoft.VisualStudio.Extensibility;
4 | using Microsoft.VisualStudio.Extensibility.Commands;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | [VisualStudioContribution]
9 | public class SettingsWindowCommand : Command
10 | {
11 | ///
12 | public override CommandConfiguration CommandConfiguration => new(displayName: "Settings")
13 | {
14 | //Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
15 | Icon = new(ImageMoniker.KnownValues.Settings, IconSettings.IconAndText),
16 | };
17 |
18 | ///
19 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
20 | {
21 | await this.Extensibility.Shell().ShowToolWindowAsync(activate: true, cancellationToken);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Windows/SettingsWindowContent.cs:
--------------------------------------------------------------------------------
1 | namespace BrightGit.Extensibility.Windows;
2 |
3 | using BrightGit.Extensibility.Services;
4 | using Microsoft.VisualStudio.Extensibility.UI;
5 |
6 | ///
7 | /// A remote user control to use as tool window UI content.
8 | ///
9 | internal class SettingsWindowContent : RemoteUserControl
10 | {
11 | public SettingsWindowViewModel ViewModel => base.DataContext as SettingsWindowViewModel;
12 |
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | public SettingsWindowContent(SettingsService settingsService)
17 | : base(dataContext: new SettingsWindowViewModel(settingsService))
18 | {
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Windows/SettingsWindowContent.xaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
14 |
15 |
18 |
22 |
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 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Windows/SettingsWindowViewModel.cs:
--------------------------------------------------------------------------------
1 | namespace BrightGit.Extensibility.Windows;
2 |
3 | using BrightGit.Extensibility.Services;
4 | using Microsoft.VisualStudio.Extensibility.UI;
5 | using System.Runtime.Serialization;
6 | using System.Text.Json;
7 |
8 | [DataContract]
9 | internal class SettingsWindowViewModel : NotifyPropertyChangedObject
10 | {
11 | public Action CloseWindow { get; set; }
12 |
13 | public SettingsService SettingsService { get; }
14 |
15 | [DataMember]
16 | public SettingsData SettingsData { get => settingsData; private set => SetProperty(ref settingsData, value); }
17 | private SettingsData settingsData;
18 |
19 | [DataMember]
20 | public AsyncCommand OKCommand { get; }
21 |
22 | [DataMember]
23 | public AsyncCommand CancelCommand { get; }
24 |
25 | [DataMember]
26 | public string Version { get; } = Meta.Version.ToString();
27 |
28 | public SettingsWindowViewModel(SettingsService settingsService)
29 | {
30 | SettingsService = settingsService;
31 | SettingsData = CloneSettings(settingsService.Data);
32 |
33 | OKCommand = new AsyncCommand((parameter, clientContext, cancellationToken) =>
34 | {
35 | // Save settings.
36 | SaveSettings();
37 |
38 | // Close window.
39 | CloseWindow?.Invoke(cancellationToken);
40 | return Task.CompletedTask;
41 | });
42 | CancelCommand = new AsyncCommand((parameter, clientContext, cancellationToken) =>
43 | {
44 | // Reset settings.
45 | SettingsData = CloneSettings(settingsService.Data);
46 |
47 | // Close window.
48 | CloseWindow?.Invoke(cancellationToken);
49 | return Task.CompletedTask;
50 | });
51 | }
52 |
53 | public void SaveSettings()
54 | {
55 | SettingsService.Data = SettingsData;
56 | SettingsService.Save();
57 | }
58 |
59 | public SettingsData CloneSettings(SettingsData data)
60 | {
61 | return JsonSerializer.Deserialize(JsonSerializer.Serialize(data));
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Windows/TabsWindow.cs:
--------------------------------------------------------------------------------
1 | namespace BrightGit.Extensibility.Windows;
2 |
3 | using BrightGit.Extensibility.Services;
4 | using Microsoft.VisualStudio.Extensibility;
5 | using Microsoft.VisualStudio.Extensibility.ToolWindows;
6 | using Microsoft.VisualStudio.RpcContracts.RemoteUI;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | ///
11 | /// A sample tool window.
12 | ///
13 | [VisualStudioContribution]
14 | public class TabsWindow : ToolWindow
15 | {
16 | private readonly TabsWindowContent content;
17 |
18 | ///
19 | /// Initializes a new instance of the class.
20 | ///
21 | public TabsWindow(TabsStorageService tabsStorageService)
22 | {
23 | this.Title = "Bright Git - Tabs";
24 | this.content = new TabsWindowContent(tabsStorageService);
25 | content.ViewModel.CloseWindow = (cancellationToken) => { _ = HideAsync(cancellationToken); };
26 | }
27 |
28 | ///
29 | public override ToolWindowConfiguration ToolWindowConfiguration => new()
30 | {
31 | // Use this object initializer to set optional parameters for the tool window.
32 | Placement = ToolWindowPlacement.Floating,
33 | };
34 |
35 | ///
36 | public override Task InitializeAsync(CancellationToken cancellationToken)
37 | {
38 | // Use InitializeAsync for any one-time setup or initialization.
39 | return Task.CompletedTask;
40 | }
41 |
42 | ///
43 | public override Task GetContentAsync(CancellationToken cancellationToken)
44 | {
45 | return Task.FromResult(content);
46 | }
47 |
48 | ///
49 | protected override void Dispose(bool disposing)
50 | {
51 | if (disposing)
52 | content.Dispose();
53 |
54 | base.Dispose(disposing);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Windows/TabsWindowCommand.cs:
--------------------------------------------------------------------------------
1 | namespace BrightGit.Extensibility.Windows;
2 |
3 | using Microsoft.VisualStudio.Extensibility;
4 | using Microsoft.VisualStudio.Extensibility.Commands;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | [VisualStudioContribution]
9 | public class TabsWindowCommand : Command
10 | {
11 | ///
12 | public override CommandConfiguration CommandConfiguration => new(displayName: "View Saved Tabs")
13 | {
14 | //Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
15 | Icon = new(ImageMoniker.KnownValues.ProcedureSettings, IconSettings.IconAndText),
16 | };
17 |
18 | ///
19 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
20 | {
21 | await this.Extensibility.Shell().ShowToolWindowAsync(activate: true, cancellationToken);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Windows/TabsWindowContent.cs:
--------------------------------------------------------------------------------
1 | namespace BrightGit.Extensibility.Windows;
2 |
3 | using BrightGit.Extensibility.Services;
4 | using Microsoft.VisualStudio.Extensibility.UI;
5 |
6 | ///
7 | /// A remote user control to use as tool window UI content.
8 | ///
9 | internal class TabsWindowContent : RemoteUserControl
10 | {
11 | public TabsWindowViewModel ViewModel => base.DataContext as TabsWindowViewModel;
12 |
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | public TabsWindowContent(TabsStorageService tabsStorageService)
17 | : base(dataContext: new TabsWindowViewModel(tabsStorageService))
18 | {
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/BrightGit.Extensibility/Windows/TabsWindowViewModel.cs:
--------------------------------------------------------------------------------
1 | namespace BrightGit.Extensibility.Windows;
2 |
3 | using BrightGit.Extensibility.Services;
4 | using Microsoft.VisualStudio.Extensibility.UI;
5 | using System.Runtime.Serialization;
6 | using System.Text.Json;
7 |
8 | [DataContract]
9 | internal class TabsWindowViewModel : NotifyPropertyChangedObject
10 | {
11 | public Action CloseWindow { get; set; }
12 |
13 | [DataMember]
14 | public TabsStorageData TabsStorageData { get => tabsStorageData; private set => SetProperty(ref tabsStorageData, value); }
15 | private TabsStorageData tabsStorageData;
16 |
17 | [DataMember]
18 | public AsyncCommand OKCommand { get; }
19 |
20 | [DataMember]
21 | public AsyncCommand CancelCommand { get; }
22 |
23 | [DataMember]
24 | public string Version { get; } = Meta.Version.ToString();
25 |
26 | private readonly TabsStorageService tabsStorageService;
27 |
28 | public TabsWindowViewModel(TabsStorageService tabsStorageService)
29 | {
30 | TabsStorageData = CloneTabsStorage(tabsStorageService.Data);
31 |
32 | OKCommand = new AsyncCommand((parameter, clientContext, cancellationToken) =>
33 | {
34 | // Save TabsStorage.
35 | SaveTabsStorage();
36 |
37 | // Close window.
38 | CloseWindow?.Invoke(cancellationToken);
39 | return Task.CompletedTask;
40 | });
41 | CancelCommand = new AsyncCommand((parameter, clientContext, cancellationToken) =>
42 | {
43 | // Reset TabsStorage.
44 | TabsStorageData = CloneTabsStorage(tabsStorageService.Data);
45 |
46 | // Close window.
47 | CloseWindow?.Invoke(cancellationToken);
48 | return Task.CompletedTask;
49 | });
50 |
51 | this.tabsStorageService = tabsStorageService;
52 | }
53 |
54 | public void SaveTabsStorage()
55 | {
56 | tabsStorageService.Data = TabsStorageData;
57 | tabsStorageService.Save();
58 | }
59 |
60 | public TabsStorageData CloneTabsStorage(TabsStorageData data)
61 | {
62 | return JsonSerializer.Deserialize(JsonSerializer.Serialize(data));
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/BrightGit.SharpAutoMigrator/BrightGit.SharpAutoMigrator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net8.0-windows
5 | enable
6 | disable
7 | 1.0.0
8 |
9 | false
10 | false
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/BrightGit.SharpAutoMigrator/DotnetHelper.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 |
3 | namespace EFCoreGitAutoMigration;
4 | public static class DotnetHelper
5 | {
6 | public static Task UpdateDatabaseEFCoreAsync(string projectDir, string migrationName)
7 | {
8 | return RunDotnetCommandAsync(projectDir, $"ef database update {migrationName}");
9 | }
10 |
11 | public static async Task RunDotnetCommandAsync(string projectDir, string arguments)
12 | {
13 | ProcessStartInfo startInfo = new ProcessStartInfo
14 | {
15 | FileName = "dotnet",
16 | Arguments = arguments,
17 | RedirectStandardOutput = true,
18 | RedirectStandardError = true,
19 | UseShellExecute = false,
20 | CreateNoWindow = true,
21 | WorkingDirectory = projectDir
22 | };
23 |
24 | using (Process process = new Process { StartInfo = startInfo })
25 | {
26 | process.OutputDataReceived += (sender, e) => Console.WriteLine(e.Data);
27 | process.ErrorDataReceived += (sender, e) => Console.WriteLine(e.Data);
28 |
29 | process.Start();
30 | process.BeginOutputReadLine();
31 | process.BeginErrorReadLine();
32 | await process.WaitForExitAsync();
33 |
34 | return process.ExitCode == 0;
35 | }
36 | }
37 |
38 | public static async Task CheckDotnetToolInstalledAsync(string toolName)
39 | {
40 | var startInfo = new ProcessStartInfo
41 | {
42 | FileName = "dotnet",
43 | Arguments = "tool list --global",
44 | RedirectStandardOutput = true,
45 | UseShellExecute = false,
46 | CreateNoWindow = true
47 | };
48 |
49 | using (Process process = new Process { StartInfo = startInfo })
50 | {
51 | process.Start();
52 | string output = process.StandardOutput.ReadToEnd();
53 | await process.WaitForExitAsync();
54 |
55 | // Check if the toolName is in the output
56 | return output.Contains(toolName, StringComparison.OrdinalIgnoreCase);
57 | }
58 | }
59 |
60 | public static Task CheckDotnetT4Installed()
61 | {
62 | return CheckDotnetToolInstalledAsync("dotnet-t4");
63 | }
64 |
65 | public static Task CheckDotnetEFInstalled()
66 | {
67 | return CheckDotnetToolInstalledAsync("dotnet-ef");
68 | }
69 |
70 | public static void BuildSolutionEFCore(string solutionPath)
71 | {
72 | var startInfo = new ProcessStartInfo
73 | {
74 | FileName = "dotnet",
75 | Arguments = "build",
76 | RedirectStandardOutput = true,
77 | UseShellExecute = false,
78 | CreateNoWindow = true,
79 | };
80 |
81 | using (Process process = new Process { StartInfo = startInfo })
82 | {
83 | process.Start();
84 | process.WaitForExit();
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/BrightGit.SharpAutoMigrator/Properties/PublishProfiles/FolderProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | bin\Release\publish\
10 | FileSystem
11 | <_TargetId>Folder
12 | net8.0-windows
13 | win-x64
14 | false
15 | true
16 | true
17 | true
18 | false
19 |
20 |
--------------------------------------------------------------------------------
/BrightGit.SharpAutoMigrator/Readme.txt:
--------------------------------------------------------------------------------
1 | - Publish Options - Console
2 | Self-Contained
3 | 35MB
4 |
5 | Self Contained and EnableCompressionInSingleFile and PublishTrimmed
6 | 11.5MB
7 |
8 | Not self contained (requires .NET)
9 | 2.3MB
--------------------------------------------------------------------------------
/BrightGit.SharpAutoMigrator/Resources/post-checkout:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #dotnet-script '.git\hooks\post-checkout.csx' -- $1 $2 $3
3 | #exec .\.git\hooks\EFCoreGitHelper.exe "$1" "$2" "$3"
4 | exec "D:\Projects Visual Studio\Bright Soft Projects\BrightExtensions\EFCoreGitHelper\bin\Debug\EFCoreGitHelper.exe" "$1" "$2" "$3"
--------------------------------------------------------------------------------
/BrightGit.SharpCommon/BrightGit.SharpCommon.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0-windows
4 | enable
5 | disable
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/BrightGit.SharpCommon/DotnetHelper.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 |
3 | namespace BrightGit.SharpCommon;
4 | public static class DotnetHelper
5 | {
6 | public static Task UpdateDatabaseEFCoreAsync(string projectDir, string migrationName)
7 | {
8 | return RunDotnetCommandAsync(projectDir, $"ef database update {migrationName}");
9 | }
10 |
11 | public static async Task RunDotnetCommandAsync(string projectDir, string arguments)
12 | {
13 | ProcessStartInfo startInfo = new ProcessStartInfo
14 | {
15 | FileName = "dotnet",
16 | Arguments = arguments,
17 | RedirectStandardOutput = true,
18 | RedirectStandardError = true,
19 | UseShellExecute = false,
20 | CreateNoWindow = true,
21 | WorkingDirectory = projectDir
22 | };
23 |
24 | using (Process process = new Process { StartInfo = startInfo })
25 | {
26 | process.OutputDataReceived += (sender, e) => { Console.WriteLine(e.Data); Debug.WriteLine(e.Data); };
27 | process.ErrorDataReceived += (sender, e) => { Console.WriteLine(e.Data); Debug.WriteLine(e.Data); };
28 |
29 | process.Start();
30 | process.BeginOutputReadLine();
31 | process.BeginErrorReadLine();
32 | await process.WaitForExitAsync();
33 |
34 | return process.ExitCode == 0;
35 | }
36 | }
37 |
38 | public static async Task CheckDotnetToolInstalledAsync(string toolName)
39 | {
40 | var startInfo = new ProcessStartInfo
41 | {
42 | FileName = "dotnet",
43 | Arguments = "tool list --global",
44 | RedirectStandardOutput = true,
45 | UseShellExecute = false,
46 | CreateNoWindow = true
47 | };
48 |
49 | using (Process process = new Process { StartInfo = startInfo })
50 | {
51 | process.Start();
52 | string output = process.StandardOutput.ReadToEnd();
53 | await process.WaitForExitAsync();
54 |
55 | // Check if the toolName is in the output
56 | return output.Contains(toolName, StringComparison.OrdinalIgnoreCase);
57 | }
58 | }
59 |
60 | public static Task CheckDotnetT4Installed()
61 | {
62 | return CheckDotnetToolInstalledAsync("dotnet-t4");
63 | }
64 |
65 | public static Task CheckDotnetEFInstalled()
66 | {
67 | return CheckDotnetToolInstalledAsync("dotnet-ef");
68 | }
69 |
70 | public static void BuildSolutionEFCore(string solutionPath)
71 | {
72 | var startInfo = new ProcessStartInfo
73 | {
74 | FileName = "dotnet",
75 | Arguments = "build",
76 | RedirectStandardOutput = true,
77 | UseShellExecute = false,
78 | CreateNoWindow = true,
79 | };
80 |
81 | using (Process process = new Process { StartInfo = startInfo })
82 | {
83 | process.Start();
84 | process.WaitForExit();
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/BrightGit.SharpCommon/Helpers/MigratorHelper.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 |
3 | namespace BrightGit.SharpCommon.Helpers;
4 | public static class MigratorHelper
5 | {
6 | public static List FindMigrationsInDir(string migrationDir)
7 | {
8 | // Get all .cs files in the Migrations directory (excluding .designer.cs).
9 | var migrations = Directory.GetFiles(migrationDir)
10 | .Where(p => p.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) &&
11 | !p.EndsWith(".designer.cs", StringComparison.OrdinalIgnoreCase))
12 | .OrderBy(p => p)
13 | .ToList();
14 |
15 | // Check only for .cs files containing this specific date format (a real migration).
16 | string dateformat = "yyyyMMddHHmmss";
17 | migrations.RemoveAll(migration => !DateTime.TryParseExact(Path.GetFileName(migration)[..14], dateformat, CultureInfo.InvariantCulture, DateTimeStyles.None, out _));
18 |
19 | return migrations;
20 | }
21 |
22 | public static string GetLatestCommonNameBetweenTwoLists(List list1, List list2)
23 | {
24 | // Get the latest common name between two lists.
25 | return list1.Intersect(list2).LastOrDefault();
26 | }
27 |
28 | public static string GetMigrationsDirectory(string repoDir)
29 | {
30 | // Check if the repoDir exists.
31 | if (!Directory.Exists(repoDir))
32 | return null;
33 |
34 | // Search for the Migrations directory.
35 | var directories = Directory.GetDirectories(repoDir, "Migrations", SearchOption.AllDirectories);
36 |
37 | // Return the first found Migrations directory, or null if not found.
38 | return directories.Length > 0 ? directories[0] : null;
39 | }
40 |
41 | public static string GetProjectFilePathFromInsideOut(string childrDir)
42 | {
43 | string currentDir = childrDir;
44 |
45 | while (currentDir != null)
46 | {
47 | var projectFile = Directory.GetFiles(currentDir, "*.csproj").FirstOrDefault();
48 | if (projectFile != null)
49 | return projectFile;
50 |
51 | currentDir = Directory.GetParent(currentDir)?.FullName;
52 | }
53 |
54 | return null;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/BrightGit.SharpCommon/Models/GitHookType.cs:
--------------------------------------------------------------------------------
1 | namespace BrightGit.SharpCommon.Models;
2 | public enum GitHookType
3 | {
4 | None,
5 | ApplypatchMsg,
6 | CommitMsg,
7 | PostUpdate,
8 | PreApplypatch,
9 | PreCommit,
10 | PreMergeCommit,
11 | PrepareCommitMsg,
12 | PrePush,
13 | PreRebase,
14 | PreReceive,
15 | PushToCheckout,
16 | PostCheckout,
17 | Update
18 | }
19 |
--------------------------------------------------------------------------------
/BrightGit.SharpCommon/Models/RunData.cs:
--------------------------------------------------------------------------------
1 | namespace BrightGit.SharpCommon.Models;
2 | public class RunData
3 | {
4 | public RunType RunType { get; set; }
5 | public string RepoDir { get; set; }
6 | public string[] Parameters { get; set; }
7 | }
8 |
--------------------------------------------------------------------------------
/BrightGit.SharpCommon/Models/RunType.cs:
--------------------------------------------------------------------------------
1 | namespace BrightGit.SharpCommon.Models;
2 | public enum RunType
3 | {
4 | EFMigrationDown,
5 | EFMigrationUp,
6 | GitUndoChanges,
7 | GitStashChanges,
8 | GitStashPop,
9 | GitStashApply,
10 | }
11 |
--------------------------------------------------------------------------------
/BrightGit.SharpCommon/SharpHookHelper.cs:
--------------------------------------------------------------------------------
1 | using BrightGit.SharpCommon.Models;
2 | using System.Text;
3 |
4 | namespace BrightGit.SharpCommon;
5 | public class SharpHookHelper
6 | {
7 | private const string baseHook = "#!/bin/sh";
8 | private const string sharpHookFileName = "BrightGit.SharpHook.exe";
9 | private const string sharpHookCommand = $"exec ./.git/hooks/{sharpHookFileName} PostCheckout \"$(git rev-parse --show-toplevel)\" \"$1\" \"$2\" \"$3\"";
10 | private const string sharpMigratorFileName = "BrightGit.SharpAutoMigrator.exe";
11 | private const string sharpMigratorCommand = $"exec ./.git/hooks/{sharpMigratorFileName} \"$1\" \"$2\" \"$3\"";
12 |
13 | ///
14 | /// Check if a hook file already exists.
15 | /// If it doesn't, generate a new hook file.
16 | /// If it does, append the new hook to the existing hook file.
17 | ///
18 | public static void AddSharpHook(string repoDir, GitHookType hookType = GitHookType.PostCheckout)
19 | {
20 | //string hookFilePath = Path.Combine(repoDir, ".git", "hooks", hookType.ToString());
21 | string hookFilePath = Path.Combine(repoDir, ".git", "hooks", "post-checkout");
22 |
23 | // If the hook file doesn't exist, create a new hook file.
24 | if (!File.Exists(hookFilePath))
25 | {
26 | var sb = new StringBuilder();
27 | sb.AppendLine(baseHook);
28 | sb.AppendLine(sharpMigratorCommand);
29 |
30 | File.WriteAllText(hookFilePath, sb.ToString());
31 | }
32 | // If the hook file exists, append the sharp command to the existing hook file.
33 | else
34 | {
35 | string hookContent = File.ReadAllText(hookFilePath);
36 | if (!hookContent.Contains(sharpHookCommand))
37 | {
38 | if (!hookContent.EndsWith(Environment.NewLine))
39 | hookContent += Environment.NewLine;
40 |
41 | hookContent += sharpHookCommand;
42 | File.WriteAllText(hookFilePath, hookContent);
43 | }
44 | }
45 | }
46 |
47 | public static bool CheckAutoMigratorHook(string repoDir)
48 | {
49 | // Check if the post-checkout hook file exists.
50 | string hookFilePath = Path.Combine(repoDir, ".git", "hooks", "post-checkout");
51 | if (File.Exists(hookFilePath))
52 | {
53 | string hookContent = File.ReadAllText(hookFilePath);
54 | if (hookContent.Contains(sharpMigratorCommand))
55 | {
56 | return true;
57 | }
58 | }
59 |
60 | return false;
61 | }
62 |
63 | ///
64 | /// Add a post-checkout hook to the .git/hooks directory.
65 | ///
66 | public static void AddAutoMigratorHook(string repoDir)
67 | {
68 | string hookFilePath = Path.Combine(repoDir, ".git", "hooks", "post-checkout");
69 |
70 | // If the hook file doesn't exist, create a new hook file.
71 | if (!File.Exists(hookFilePath))
72 | {
73 | var sb = new StringBuilder();
74 | sb.AppendLine(baseHook);
75 | sb.AppendLine(sharpMigratorCommand);
76 |
77 | File.WriteAllText(hookFilePath, sb.ToString());
78 | }
79 | // If the hook file exists, append the migrator command to the existing hook file.
80 | else
81 | {
82 | string hookContent = File.ReadAllText(hookFilePath);
83 | if (!hookContent.Contains(sharpMigratorCommand))
84 | {
85 | if (!hookContent.EndsWith(Environment.NewLine))
86 | hookContent += Environment.NewLine;
87 |
88 | hookContent += sharpMigratorCommand;
89 | File.WriteAllText(hookFilePath, hookContent);
90 | }
91 | }
92 |
93 | // TODO: For now we are relying that the exe will already be in the hooks directory.
94 | //// Extract migrator exe to the .git/hooks directory.
95 | //string migratorDestination = Path.Combine(hooksDirectory, migratorFileName);
96 | //File.Copy("AutoMigration.exe", migratorDestination, true);
97 | }
98 |
99 | ///
100 | /// Open the post-checkout hook file and remove the migrator command.
101 | ///
102 | public static void RemoveAutoMigratorHook(string repoDir)
103 | {
104 | string hookFilePath = Path.Combine(repoDir, ".git", "hooks", "post-checkout");
105 | if (File.Exists(hookFilePath))
106 | {
107 | // Remove the entire line containing the migrator command.
108 | string hookContent = File.ReadAllText(hookFilePath);
109 | hookContent = hookContent.Replace(sharpMigratorCommand, string.Empty);
110 |
111 | // Save the updated hook content.
112 | File.WriteAllText(hookFilePath, hookContent);
113 | }
114 | }
115 |
116 | // TODO: Create events and listen to the pipe and fire events.
117 | }
118 |
--------------------------------------------------------------------------------
/BrightGit.SharpCommon/SharpRunHelper.cs:
--------------------------------------------------------------------------------
1 | using BrightGit.SharpCommon.Models;
2 | using System.Diagnostics;
3 |
4 | namespace BrightGit.SharpCommon;
5 | public class SharpRunHelper
6 | {
7 | public Action ActionMessageReceived { get; set; }
8 |
9 | private const string sharpRunFileName = "BrightGit.SharpRun.exe";
10 |
11 | public void ExecuteGitAction(RunData data)
12 | {
13 | var processStartInfo = new ProcessStartInfo
14 | {
15 | FileName = sharpRunFileName,
16 | Arguments = $"{data.RunType} {data.RepoDir} {string.Join(" ", data.Parameters)}",
17 | RedirectStandardOutput = true,
18 | UseShellExecute = false,
19 | CreateNoWindow = true
20 | };
21 |
22 | using (var process = Process.Start(processStartInfo))
23 | {
24 | using (var reader = process.StandardOutput)
25 | {
26 | string result = reader.ReadToEnd();
27 |
28 | // Raise event to notify the caller.
29 | ActionMessageReceived?.Invoke(result);
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/BrightGit.SharpHook/BrightGit.SharpHook.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net8.0-windows
5 | enable
6 | disable
7 | 1.0.0
8 |
9 | false
10 | false
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/BrightGit.SharpHook/Program.cs:
--------------------------------------------------------------------------------
1 | using System.IO.Pipes;
2 |
3 | namespace BrightGit.SharpHook;
4 | public class Program
5 | {
6 | static void Main(string[] args)
7 | {
8 | // The timeout for the connection to the pipe.
9 | const int timeout = 10000;
10 |
11 | // Skip the git hook to avoid entering an infinite loop.
12 | var skipVariable = Environment.GetEnvironmentVariable("BRIGHT_GITEFCORE_SKIP_GIT_HOOK");
13 | if (skipVariable == "1")
14 | return;
15 |
16 | // First argument is the hook name, the rest are the arguments.
17 | if (args.Length < 1)
18 | {
19 | Console.WriteLine("Error, at least one argument is required (Example: `BrightGit.SharpHook.exe `.");
20 | return;
21 | }
22 |
23 | string eventName = args[0];
24 | string arguments = string.Join("|", args, 1, args.Length - 1);
25 |
26 | using (var pipeClient = new NamedPipeClientStream(".", "BrightSharpHook", PipeDirection.Out))
27 | {
28 | try
29 | {
30 | pipeClient.Connect(timeout);
31 | using (var writer = new StreamWriter(pipeClient))
32 | {
33 | writer.AutoFlush = true;
34 | writer.WriteLine($"{eventName}|{arguments}");
35 | }
36 | }
37 | catch (Exception ex)
38 | {
39 | Console.WriteLine($"Failed to send message: {ex.Message}");
40 | }
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/BrightGit.SharpHook/Properties/PublishProfiles/FolderProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | bin\Release\publish\
10 | FileSystem
11 | <_TargetId>Folder
12 | net8.0-windows
13 | win-x64
14 | false
15 | true
16 | true
17 | true
18 | false
19 |
20 |
--------------------------------------------------------------------------------
/BrightGit.SharpHook/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "BrightGit.ConsoleHooks": {
4 | "commandName": "Project",
5 | "commandLineArgs": "arg1\r\narg2\r\narg3"
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/BrightGit.SharpRun/BrightGit.SharpRun.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net8.0-windows
5 | enable
6 | disable
7 | 1.0.0
8 |
9 | false
10 | false
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/.vsextension/string-resources.json:
--------------------------------------------------------------------------------
1 | {
2 | "BrightXaml.Extensibility.HelloWorldCommand.DisplayName": "Hello World Command"
3 | }
4 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/BrightXaml.Extensibility.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0-windows
4 | enable
5 | disable
6 |
7 | 1.1.0.1
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | MSBuild:Compile
18 |
19 |
20 | MSBuild:Compile
21 |
22 |
23 | MSBuild:Compile
24 |
25 |
26 | MSBuild:Compile
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Commands/CleanBinAndObjCommand.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Commands;
2 |
3 | using Microsoft;
4 | using Microsoft.VisualStudio.Extensibility;
5 | using Microsoft.VisualStudio.Extensibility.Commands;
6 | using Microsoft.VisualStudio.Extensibility.Shell;
7 | using Microsoft.VisualStudio.ProjectSystem.Query;
8 | using System.Diagnostics;
9 | using System.Text;
10 | using System.Threading;
11 | using System.Threading.Tasks;
12 |
13 | [VisualStudioContribution]
14 | internal class CleanBinAndObjCommand : Command
15 | {
16 | private readonly TraceSource logger;
17 |
18 | public CleanBinAndObjCommand(TraceSource traceSource)
19 | {
20 | this.logger = Requires.NotNull(traceSource, nameof(traceSource));
21 | }
22 |
23 | ///
24 | public override CommandConfiguration CommandConfiguration => new(displayName: "Clean Solution Bin and Obj")
25 | {
26 | //Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
27 | Icon = new(ImageMoniker.KnownValues.CleanData, IconSettings.IconAndText),
28 | TooltipText = "Cleans bin and obj directories for all projects in the solution"
29 | };
30 |
31 | ///
32 | public override Task InitializeAsync(CancellationToken cancellationToken)
33 | {
34 | // Use InitializeAsync for any one-time setup or initialization.
35 | return base.InitializeAsync(cancellationToken);
36 | }
37 |
38 | ///
39 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
40 | {
41 | var sw = Stopwatch.StartNew();
42 | var shell = Extensibility.Shell();
43 | var workspace = Extensibility.Workspaces();
44 | var documents = Extensibility.Documents();
45 |
46 | // Get the directories from all active projects in the solution.
47 | var projectsDirs = (await workspace.QuerySolutionAsync(solution => solution.Get(p => p.Projects).With(p => p.Path), cancellationToken)).ToList();
48 | var projectsCleaned = new StringBuilder();
49 | foreach (var project in projectsDirs)
50 | {
51 | var projectDirectory = Path.GetDirectoryName(project.Path);
52 | Debug.WriteLine($"Cleaning bin and obj directories in {projectDirectory}");
53 | if (!string.IsNullOrWhiteSpace(projectDirectory))
54 | {
55 | await CleanDirectoryAsync(projectDirectory, cancellationToken);
56 | projectsCleaned.AppendLine(Path.GetFileName(projectDirectory));
57 | }
58 | }
59 |
60 | sw.Stop();
61 | await shell.ShowPromptAsync($"Cleaned bin and obj directories for {projectsDirs.Count} projects:" +
62 | $"\n{projectsCleaned}" +
63 | $"\nTime Elapsed: {sw.Elapsed.TotalMilliseconds:N0}ms",
64 | PromptOptions.OK, cancellationToken);
65 | }
66 |
67 | private async Task CleanDirectoryAsync(string directoryPath, CancellationToken cancellationToken)
68 | {
69 | if (directoryPath == null)
70 | return;
71 |
72 | var optionsTargetSubdirectories = new string[] { "bin", "obj" };
73 |
74 | try
75 | {
76 | foreach (var di in optionsTargetSubdirectories.Select(x => Path.Combine(directoryPath, x))
77 | .Where(Directory.Exists)
78 | .Select(x => new DirectoryInfo(x)))
79 | {
80 | foreach (var file in di.EnumerateFiles())
81 | {
82 | file.Delete();
83 | }
84 |
85 | foreach (var dir in di.EnumerateDirectories())
86 | {
87 | dir.Delete(true);
88 | }
89 | }
90 | }
91 | catch (Exception ex)
92 | {
93 | Debug.WriteLine(ex.Message);
94 | logger.TraceEvent(TraceEventType.Error, 0, ex.Message);
95 | await Extensibility.Shell().ShowPromptAsync($"Error while cleaning directory {directoryPath}: {ex.Message}", PromptOptions.OK, cancellationToken);
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Commands/DevOpenSolutionCommand.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Commands;
2 |
3 | using Microsoft;
4 | using Microsoft.VisualStudio.Extensibility;
5 | using Microsoft.VisualStudio.Extensibility.Commands;
6 | using System.Diagnostics;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | [VisualStudioContribution]
11 | internal class DevOpenSolutionCommand : Command
12 | {
13 | private readonly TraceSource logger;
14 |
15 | public DevOpenSolutionCommand(TraceSource traceSource)
16 | {
17 | this.logger = Requires.NotNull(traceSource, nameof(traceSource));
18 | }
19 |
20 | ///
21 | public override CommandConfiguration CommandConfiguration => new(displayName: "Dev Open Test Solution")
22 | {
23 | #if DEBUG
24 | //Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
25 | //Shortcuts = [new CommandShortcutConfiguration(ModifierKey.Control, Key.E, ModifierKey.Control, Key.T)],
26 | #endif
27 | Icon = new(ImageMoniker.KnownValues.DebugTemplate, IconSettings.IconAndText),
28 | };
29 |
30 | ///
31 | public override Task InitializeAsync(CancellationToken cancellationToken)
32 | {
33 | // Use InitializeAsync for any one-time setup or initialization.
34 | return base.InitializeAsync(cancellationToken);
35 | }
36 |
37 | ///
38 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
39 | {
40 | // TODO: Open a solution for testing.
41 | // TODO: This opens the file, not the solution itself...
42 | await this.Extensibility.Documents().OpenDocumentAsync(new Uri(@"D:\Projects Visual Studio\Test Projects\TestWPFNETCore\TestWPFNETCore.sln"), cancellationToken);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Commands/ExtractClassesInUseCommand.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Commands;
2 |
3 | using Microsoft;
4 | using Microsoft.VisualStudio.Extensibility;
5 | using Microsoft.VisualStudio.Extensibility.Commands;
6 | using Microsoft.VisualStudio.Extensibility.Shell;
7 | using System.Diagnostics;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 |
11 | [VisualStudioContribution]
12 | internal class ExtractClassesInUseCommand : Command
13 | {
14 | private readonly TraceSource logger;
15 |
16 | public ExtractClassesInUseCommand(TraceSource traceSource)
17 | {
18 | logger = Requires.NotNull(traceSource, nameof(traceSource));
19 | }
20 |
21 | ///
22 | public override CommandConfiguration CommandConfiguration => new(displayName: "[WIP] Export Referenced Classes")
23 | {
24 | //Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
25 | Icon = new(ImageMoniker.KnownValues.ExportData, IconSettings.IconAndText),
26 | //Shortcuts = [new CommandShortcutConfiguration(ModifierKey.Control, Key.E, ModifierKey.Control, Key.H)],
27 | EnabledWhen = ActivationConstraint.ClientContext(ClientContextKey.Shell.ActiveEditorFileName, @"\.(cs)$"),
28 | TooltipText = "Copies to clipboard all classes being used in the current document",
29 | };
30 |
31 | ///
32 | public override Task InitializeAsync(CancellationToken cancellationToken)
33 | {
34 | // Use InitializeAsync for any one-time setup or initialization.
35 | return base.InitializeAsync(cancellationToken);
36 | }
37 |
38 | ///
39 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
40 | {
41 | var shell = Extensibility.Shell();
42 | var workspace = Extensibility.Workspaces();
43 | var documents = Extensibility.Documents();
44 |
45 | await Extensibility.Shell().ShowPromptAsync("Work in Progress!", PromptOptions.OK, cancellationToken);
46 | }
47 |
48 | public void CopyClassesToClipboard(string text)
49 | {
50 | if (!string.IsNullOrWhiteSpace(text))
51 | {
52 |
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Commands/ExtractViewCommand.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Commands;
2 |
3 | using Microsoft;
4 | using Microsoft.VisualStudio.Extensibility;
5 | using Microsoft.VisualStudio.Extensibility.Commands;
6 | using Microsoft.VisualStudio.Extensibility.Shell;
7 | using System.Diagnostics;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 |
11 | [VisualStudioContribution]
12 | internal class ExtractViewCommand : Command
13 | {
14 | private readonly TraceSource logger;
15 |
16 | public ExtractViewCommand(TraceSource traceSource)
17 | {
18 | this.logger = Requires.NotNull(traceSource, nameof(traceSource));
19 | }
20 |
21 | ///
22 | public override CommandConfiguration CommandConfiguration => new(displayName: "Extract View + CodeBehind + ViewModel")
23 | {
24 | //Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
25 | Shortcuts = [new CommandShortcutConfiguration(ModifierKey.Control, Key.E, ModifierKey.Control, Key.G)],
26 | Icon = new(ImageMoniker.KnownValues.ExportData, IconSettings.IconAndText),
27 | };
28 |
29 | ///
30 | public override Task InitializeAsync(CancellationToken cancellationToken)
31 | {
32 | // Use InitializeAsync for any one-time setup or initialization.
33 | return base.InitializeAsync(cancellationToken);
34 | }
35 |
36 | ///
37 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
38 | {
39 | await this.Extensibility.Shell().ShowPromptAsync("Hello from Luis ;)", PromptOptions.OK, cancellationToken);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Commands/FormatXamlCommand.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Commands;
2 |
3 | using BrightXaml.Extensibility.Services;
4 | using BrightXaml.Extensibility.Utilities;
5 | using Microsoft;
6 | using Microsoft.VisualStudio.Extensibility;
7 | using Microsoft.VisualStudio.Extensibility.Commands;
8 | using Microsoft.VisualStudio.Extensibility.Shell;
9 | using System.Diagnostics;
10 | using System.Threading;
11 | using System.Threading.Tasks;
12 |
13 | [VisualStudioContribution]
14 | internal class FormatXamlCommand : Command
15 | {
16 | private readonly TraceSource logger;
17 | private readonly SettingsService settingsService;
18 |
19 | public FormatXamlCommand(TraceSource traceSource, SettingsService settingsService)
20 | {
21 | logger = Requires.NotNull(traceSource, nameof(traceSource));
22 | this.settingsService = settingsService;
23 | }
24 |
25 | ///
26 | public override CommandConfiguration CommandConfiguration => new(displayName: "Format Xaml (Ctrl+E+F)")
27 | {
28 | //Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
29 | Icon = new(ImageMoniker.KnownValues.FormatDocument, IconSettings.IconAndText),
30 | Shortcuts = [new CommandShortcutConfiguration(ModifierKey.Control, Key.E, ModifierKey.Control, Key.F)],
31 | EnabledWhen = ActivationConstraint.ClientContext(ClientContextKey.Shell.ActiveEditorFileName, @"\.(xaml)$"),
32 | TooltipText = "Formats a XAML file",
33 | };
34 |
35 | ///
36 | public override Task InitializeAsync(CancellationToken cancellationToken)
37 | {
38 | // Use InitializeAsync for any one-time setup or initialization.
39 | return base.InitializeAsync(cancellationToken);
40 | }
41 |
42 | ///
43 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
44 | {
45 | var sw = Stopwatch.StartNew();
46 | var shell = Extensibility.Shell();
47 | var workspace = Extensibility.Workspaces();
48 | var documents = Extensibility.Documents();
49 | var editor = Extensibility.Editor();
50 |
51 | // Get active document editor.
52 | var textView = await editor.GetActiveTextViewAsync(context, cancellationToken);
53 | if (textView != null)
54 | {
55 | var textViewFilePath = textView?.FilePath;
56 | var textContent = new string(textView.Document.Text.ToArray());
57 | var originalCaret = textView.Selection.Extent.Start;
58 |
59 | try
60 | {
61 | // Format XAML.
62 | var formattedXaml = XamlFormatter.FormatXaml(textContent,
63 | settingsService.Data.FormatXaml.EndingTagSpaces,
64 | settingsService.Data.FormatXaml.ClosingTagSpaces);
65 |
66 | // Apply formatted XAML to the active document.
67 | await editor.EditAsync(
68 | batch =>
69 | {
70 | textView.Document.AsEditable(batch).Replace(textView.Document.Text, formattedXaml);
71 |
72 | // TODO: Tries to restore the caret position after formatting.
73 | //var caret = new TextPosition(textView.Document, 5);
74 | //textView.AsEditable(batch).SetSelections([new Selection(activePosition: caret, anchorPosition: caret, insertionPosition: caret)]);
75 | },
76 | cancellationToken);
77 |
78 | sw.Stop();
79 | Debug.WriteLine($"FormatXamlCommand.ExecuteCommandAsync: {sw.ElapsedMilliseconds}ms");
80 | }
81 | catch (Exception ex)
82 | {
83 | logger.TraceEvent(TraceEventType.Error, 0, ex.ToString());
84 | await shell.ShowPromptAsync($"Error formatting XAML.\n{ex.Message}", PromptOptions.OK, cancellationToken);
85 | }
86 | }
87 | else
88 | {
89 | logger.TraceEvent(TraceEventType.Warning, 0, "No active text view found.");
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Commands/HelloWorldCommand.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Commands;
2 |
3 | using Microsoft;
4 | using Microsoft.VisualStudio.Extensibility;
5 | using Microsoft.VisualStudio.Extensibility.Commands;
6 | using Microsoft.VisualStudio.Extensibility.Shell;
7 | using System.Diagnostics;
8 |
9 | ///
10 | /// Command1 handler.
11 | ///
12 | [VisualStudioContribution]
13 | internal class HelloWorldCommand : Command
14 | {
15 | private readonly TraceSource logger;
16 |
17 | ///
18 | /// Initializes a new instance of the class.
19 | ///
20 | /// Trace source instance to utilize.
21 | public HelloWorldCommand(TraceSource traceSource)
22 | {
23 | // This optional TraceSource can be used for logging in the command. You can use dependency injection to access
24 | // other services here as well.
25 | logger = Requires.NotNull(traceSource, nameof(traceSource));
26 | }
27 |
28 | ///
29 | public override CommandConfiguration CommandConfiguration => new("%BrightXaml.Extensibility.HelloWorldCommand.DisplayName%")
30 | {
31 | // Use this object initializer to set optional parameters for the command. The required parameter,
32 | // displayName, is set above. DisplayName is localized and references an entry in .vsextension\string-resources.json.
33 | //Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
34 | Icon = new(ImageMoniker.KnownValues.Extension, IconSettings.IconAndText),
35 | TooltipText = "Shows a prompt with a hello message (Debug/Test Only)",
36 | };
37 |
38 | ///
39 | public override Task InitializeAsync(CancellationToken cancellationToken)
40 | {
41 | // Use InitializeAsync for any one-time setup or initialization.
42 | return base.InitializeAsync(cancellationToken);
43 | }
44 |
45 | ///
46 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
47 | {
48 | await Extensibility.Shell().ShowPromptAsync("Hello from Luis' extension!", PromptOptions.OK, cancellationToken);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Commands/KillXamlDesignerCommand.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Commands;
2 |
3 | using Microsoft;
4 | using Microsoft.VisualStudio.Extensibility;
5 | using Microsoft.VisualStudio.Extensibility.Commands;
6 | using System.Diagnostics;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | [VisualStudioContribution]
11 | internal class KillXamlDesignerCommand : Command
12 | {
13 | private readonly TraceSource logger;
14 |
15 | public KillXamlDesignerCommand(TraceSource traceSource)
16 | {
17 | this.logger = Requires.NotNull(traceSource, nameof(traceSource));
18 | }
19 |
20 | ///
21 | public override CommandConfiguration CommandConfiguration => new(displayName: "Kill XAML Designer Process")
22 | {
23 | //Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
24 | Icon = new(ImageMoniker.KnownValues.TerminateProcess, IconSettings.IconAndText),
25 | };
26 |
27 | ///
28 | public override Task InitializeAsync(CancellationToken cancellationToken)
29 | {
30 | // Use InitializeAsync for any one-time setup or initialization.
31 | return base.InitializeAsync(cancellationToken);
32 | }
33 |
34 | ///
35 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
36 | {
37 | KillAllDesigners();
38 | await Task.CompletedTask;
39 | }
40 |
41 | private static void KillAllDesigners()
42 | {
43 | // VS 2022.
44 | var designers = Process.GetProcessesByName("WpfSurface");
45 | foreach (var designer in designers)
46 | {
47 | designer.Kill();
48 | }
49 |
50 | // Usually up to VS 2019.
51 | //var designers = Process.GetProcessesByName("XDesProc");
52 | //foreach (var designer in designers)
53 | //{
54 | // designer.Kill();
55 | //}
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Commands/ShowCodeBehindCommand.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Commands;
2 |
3 | using Microsoft;
4 | using Microsoft.VisualStudio.Extensibility;
5 | using Microsoft.VisualStudio.Extensibility.Commands;
6 | using Microsoft.VisualStudio.Extensibility.Shell;
7 | using System.Diagnostics;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 |
11 | [VisualStudioContribution]
12 | internal class ShowCodeBehindCommand : Command
13 | {
14 | private readonly TraceSource logger;
15 |
16 | public ShowCodeBehindCommand(TraceSource traceSource)
17 | {
18 | this.logger = Requires.NotNull(traceSource, nameof(traceSource));
19 | }
20 |
21 | ///
22 | public override CommandConfiguration CommandConfiguration => new(displayName: "Show CodeBehind (Ctrl+E+3)")
23 | {
24 | //Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
25 | Shortcuts = [new CommandShortcutConfiguration(ModifierKey.Control, Key.E, ModifierKey.Control, Key.VK_NUMPAD3)],
26 | Icon = new(ImageMoniker.KnownValues.Extension, IconSettings.IconAndText),
27 | EnabledWhen = ActivationConstraint.ClientContext(ClientContextKey.Shell.ActiveEditorContentType, ".+"),
28 | TooltipText = "Shows the CodeBehind of the current View or ViewModel",
29 | };
30 |
31 | ///
32 | public override Task InitializeAsync(CancellationToken cancellationToken)
33 | {
34 | // Use InitializeAsync for any one-time setup or initialization.
35 | return base.InitializeAsync(cancellationToken);
36 | }
37 |
38 | ///
39 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
40 | {
41 | await this.Extensibility.Shell().ShowPromptAsync("Hello from Luis :)", PromptOptions.OK, cancellationToken);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Commands/ShowViewCommand.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Commands;
2 |
3 | using Microsoft;
4 | using Microsoft.VisualStudio.Extensibility;
5 | using Microsoft.VisualStudio.Extensibility.Commands;
6 | using Microsoft.VisualStudio.Extensibility.Shell;
7 | using System.Diagnostics;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 |
11 | [VisualStudioContribution]
12 | internal class ShowViewCommand : Command
13 | {
14 | private readonly TraceSource logger;
15 |
16 | public ShowViewCommand(TraceSource traceSource)
17 | {
18 | logger = Requires.NotNull(traceSource, nameof(traceSource));
19 | }
20 |
21 | ///
22 | public override CommandConfiguration CommandConfiguration => new(displayName: "Show View (Ctrl+E+1)")
23 | {
24 | //Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
25 | Shortcuts = [new CommandShortcutConfiguration(ModifierKey.Control, Key.E, ModifierKey.Control, Key.VK_NUMPAD1)],
26 | Icon = new(ImageMoniker.KnownValues.View, IconSettings.IconAndText),
27 | TooltipText = "Shows the View of the current ViewModel",
28 | };
29 |
30 | ///
31 | public override Task InitializeAsync(CancellationToken cancellationToken)
32 | {
33 | // Use InitializeAsync for any one-time setup or initialization.
34 | return base.InitializeAsync(cancellationToken);
35 | }
36 |
37 | ///
38 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
39 | {
40 | await Extensibility.Shell().ShowPromptAsync("Hello from an extension!", PromptOptions.OK, cancellationToken);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/ExtensionEntrypoint.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility;
2 |
3 | using BrightXaml.Extensibility.Commands;
4 | using BrightXaml.Extensibility.Services;
5 | using BrightXaml.Extensibility.Windows;
6 | using Microsoft.Extensions.DependencyInjection;
7 | using Microsoft.VisualStudio.Extensibility;
8 | using Microsoft.VisualStudio.Extensibility.Commands;
9 |
10 | ///
11 | /// Extension entrypoint for the VisualStudio.Extensibility extension.
12 | ///
13 | [VisualStudioContribution]
14 | internal class ExtensionEntrypoint : Extension
15 | {
16 | ///
17 | public override ExtensionConfiguration ExtensionConfiguration => new()
18 | {
19 | Metadata = new(
20 | id: "BrightXaml.c6b5c335-85e8-4243-bdf1-2b6468178ad1",
21 | version: this.ExtensionAssemblyVersion,
22 | publisherName: "Luis Henrique Goll",
23 | displayName: "Bright Xaml Extension",
24 | description: "Bright Commands and Automations made with Xaml Developers in mind! (WPF, MAUI, WinUI)"),
25 | };
26 |
27 | [VisualStudioContribution]
28 | //public static MenuConfiguration MyMenu => new("%MyMenu.DisplayName%")
29 | public static MenuConfiguration MyMenu => new("Bright Xaml")
30 | {
31 | Placements = new CommandPlacement[]
32 | {
33 | CommandPlacement.KnownPlacements.ExtensionsMenu
34 | },
35 | Children = new[]
36 | {
37 | MenuChild.Command(),
38 | //MenuChild.Command(),
39 | //MenuChild.Command(),
40 | //MenuChild.Command(),
41 | MenuChild.Separator,
42 | MenuChild.Command(),
43 | MenuChild.Command(),
44 | MenuChild.Separator,
45 | MenuChild.Command(),
46 | MenuChild.Command(),
47 | //MenuChild.Separator,
48 | MenuChild.Command(),
49 | #if DEBUG
50 | MenuChild.Command(),
51 | MenuChild.Separator,
52 | MenuChild.Command(),
53 | MenuChild.Separator,
54 | MenuChild.Command(),
55 | MenuChild.Command(),
56 | #endif
57 | MenuChild.Separator,
58 | MenuChild.Command(),
59 | MenuChild.Command(),
60 | },
61 | };
62 |
63 | ///
64 | protected override void InitializeServices(IServiceCollection serviceCollection)
65 | {
66 | base.InitializeServices(serviceCollection);
67 |
68 | // You can configure dependency injection here by adding services to the serviceCollection.
69 | serviceCollection.AddTransient(provider => new DialogService());
70 | serviceCollection.AddSingleton();
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Helpers/ClipboardHelper.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.Windows;
3 |
4 | namespace BrightXaml.Extensibility.Helpers;
5 | public static class ClipboardHelper
6 | {
7 | ///
8 | /// Most reliable and fastest way to copy text to clipboard.
9 | ///
10 | public static void SetClipboard(string text)
11 | {
12 | if (text == null)
13 | throw new ArgumentNullException("Attempt to set clipboard with null");
14 |
15 | var clipboardExecutable = new Process();
16 | clipboardExecutable.StartInfo = new ProcessStartInfo
17 | {
18 | RedirectStandardInput = true,
19 | FileName = @"clip",
20 | UseShellExecute = false,
21 | CreateNoWindow = true,
22 | };
23 | clipboardExecutable.Start();
24 |
25 | // CLIP uses STDIN as input.
26 | clipboardExecutable.StandardInput.Write(text);
27 |
28 | // When we are done writing all the string, close it so clip doesn't wait and get stuck.
29 | clipboardExecutable.StandardInput.Close();
30 |
31 | return;
32 | }
33 |
34 | ///
35 | /// Really slow and unreliable.
36 | ///
37 | public static void SetTextClipboard(string text)
38 | {
39 | if (!string.IsNullOrWhiteSpace(text))
40 | {
41 | var thread = new Thread(() =>
42 | {
43 | int retryCount = 10;
44 | bool success = false;
45 |
46 | while (!success && retryCount-- > 0)
47 | {
48 | try
49 | {
50 | Clipboard.SetText(text);
51 | success = true;
52 | }
53 | catch (System.Runtime.InteropServices.COMException)
54 | {
55 | // Wait and retry.
56 | Thread.Sleep(100);
57 | }
58 | }
59 |
60 | if (!success)
61 | {
62 | Debug.WriteLine("Failed to copy text to clipboard. Please try again.");
63 | }
64 | });
65 |
66 | thread.SetApartmentState(ApartmentState.STA);
67 | thread.Start();
68 | thread.Join();
69 | }
70 | }
71 |
72 | ///
73 | /// [WIP] It works cross-platform.
74 | /// Add:
75 | /// using Windows.ApplicationModel.DataTransfer;
76 | ///
77 | public static void SetContentClipboardPackage(string text)
78 | {
79 | //DataPackage package = new DataPackage();
80 | //package.SetText("text to copy");
81 | //Clipboard.SetContent(package);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Helpers/VSHelper.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.Extensibility;
2 | using Microsoft.VisualStudio.ProjectSystem.Query;
3 |
4 | namespace BrightXaml.Extensibility.Helpers;
5 | public static class VSHelper
6 | {
7 | public static async Task GetSolutionPathAsync(WorkspacesExtensibility workspace, CancellationToken cancellationToken)
8 | {
9 | // Get the solution path.
10 | var solutionPath = (await workspace.QuerySolutionAsync(solution => solution.With(p => p.Path), cancellationToken)).FirstOrDefault()?.Path;
11 | return solutionPath;
12 | }
13 |
14 | public static async Task GetSolutionNameAsync(WorkspacesExtensibility workspace, CancellationToken cancellationToken)
15 | {
16 | return Path.GetFileNameWithoutExtension(await GetSolutionPathAsync(workspace, cancellationToken));
17 | }
18 |
19 | public static async Task GetSolutionDirectoryAsync(WorkspacesExtensibility workspace, CancellationToken cancellationToken)
20 | {
21 | // Get the solution directory.
22 | var solutionDirectory = (await workspace.QuerySolutionAsync(solution => solution.With(p => p.Directory), cancellationToken)).FirstOrDefault()?.Directory;
23 | return solutionDirectory;
24 | }
25 |
26 | public static async Task> GetProjectsDirectoryAsync(WorkspacesExtensibility workspace, CancellationToken cancellationToken)
27 | {
28 | // Get the directories from all active projects in the solution.
29 | var projectsDirs = await workspace.QuerySolutionAsync(solution => solution.Get(p => p.Projects).With(p => p.Path), cancellationToken);
30 | return projectsDirs.Select(p => p.Path).ToList();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Meta.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | namespace BrightXaml.Extensibility;
4 | internal static class Meta
5 | {
6 | public static Version Version { get; } = Assembly.GetExecutingAssembly().GetName().Version;
7 |
8 | public static bool IsDebug { get; } =
9 | #if DEBUG
10 | true;
11 | #else
12 | false;
13 | #endif
14 | }
15 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Models/BindingDefinitionOffsets.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Models;
2 | public class BindingDefinitionOffsets
3 | {
4 | public string BindingWord { get; set; }
5 |
6 | public int Line { get; set; }
7 | public int OffsetFromFile { get; set; }
8 | public int OffsetFromLine { get; set; }
9 |
10 | //public int AttributeLine { get; set; }
11 | //public int TargetLine { get; set; }
12 | }
13 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Models/ComboIntData.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.PlatformUI;
2 | using System.Runtime.Serialization;
3 |
4 | namespace BrightXaml.Extensibility.Models;
5 | [DataContract]
6 | public class ComboIntData : ObservableObject
7 | {
8 | [DataMember]
9 | public int Id { get; set; }
10 |
11 | [DataMember]
12 | public string Text { get; set; }
13 | }
14 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Models/CommandInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.Serialization;
2 |
3 | namespace BrightXaml.Extensibility.Models;
4 | [DataContract]
5 | internal class CommandInfo
6 | {
7 | [DataMember]
8 | public string Name { get; set; }
9 |
10 | [DataMember]
11 | public string Description { get; set; }
12 | }
13 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "BrightXaml.Extensibility": {
4 | "commandName": "Project"
5 | },
6 | "BrightXaml.Extensibility Debug": {
7 | "commandName": "Project",
8 | "commandLineArgs": "\"D:\\Projects Visual Studio\\Test Projects\\TestWPFNETCore\\TestWPFNETCore.sln\""
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Services/DialogService.cs:
--------------------------------------------------------------------------------
1 | using BrightXaml.Extensibility.Windows;
2 | using Microsoft.VisualStudio.Extensibility.Shell;
3 | using Microsoft.VisualStudio.RpcContracts.Notifications;
4 |
5 | namespace BrightXaml.Extensibility.Services;
6 | public class DialogService : IDialogService
7 | {
8 | public ShellExtensibility Shell { get; set; }
9 |
10 | public Task ShowPromptOKAsync(string message, CancellationToken cancellationToken)
11 | {
12 | return Shell.ShowPromptAsync(message, PromptOptions.OK, cancellationToken);
13 | }
14 |
15 | public Task ShowPromptOKCancelAsync(string message, CancellationToken cancellationToken)
16 | {
17 | return Shell.ShowPromptAsync(message, PromptOptions.OKCancel, cancellationToken);
18 | }
19 |
20 | public Task ShowPromptRetryCancelAsync(string message, CancellationToken cancellationToken)
21 | {
22 | return Shell.ShowPromptAsync(message, PromptOptions.RetryCancel, cancellationToken);
23 | }
24 |
25 | // TODO: Progress doesn't update.
26 | public Task ShowDialogProgressAsync(string message, out Action progress, CancellationToken cancellationToken)
27 | {
28 | if (message == null)
29 | message = "Processing...";
30 |
31 | // Show the dialog asynchronously.
32 | var dialogControl = new ProgressWindowContent();
33 | dialogControl.ViewModel.ProgressText = message;
34 | dialogControl.ViewModel.ProgressValue = 0;
35 |
36 | // Return the task so that it can be awaited later.
37 | var dialogResult = Shell.ShowDialogAsync(dialogControl, message, DialogOption.Close, cancellationToken);
38 |
39 | // Try using this one instead... the issue is that we will need a service to be registered and used accross.
40 | //var dialogResult = await Shell.ShowToolWindowAsync(dialogControl, title, DialogOption.OKCancel, cancellationToken);
41 |
42 | progress = (value) =>
43 | {
44 | if (dialogControl != null && dialogControl.ViewModel != null)
45 | dialogControl.ViewModel.ProgressValue = value;
46 | //dialogControl.ViewModel.ProgressText = $"{value}% completed";
47 | };
48 |
49 | return dialogResult;
50 | }
51 |
52 | public async Task ShowDialogOptionsAsync(string title, string label, List items, CancellationToken cancellationToken)
53 | {
54 | if (title == null)
55 | title = "Choose an item";
56 |
57 | // Remove duplicates and sort the items.
58 | items = items.Distinct().OrderBy(i => i).ToList();
59 |
60 | // Create a ChooseItemWindowContent instance and populate it with the items.
61 | var dialogControl = new ChooseItemWindowContent();
62 | dialogControl.ViewModel.Items = new System.Collections.ObjectModel.ObservableCollection(items);
63 | dialogControl.ViewModel.SelectedItem = dialogControl.ViewModel.Items.FirstOrDefault();
64 | dialogControl.ViewModel.LabelText = label;
65 |
66 | // Show the dialog and get the result.
67 | var dialogResult = await Shell.ShowDialogAsync(dialogControl, title, DialogOption.OKCancel, cancellationToken);
68 |
69 | if (dialogResult == DialogResult.OK)
70 | {
71 | return dialogControl.ViewModel.SelectedItem;
72 | }
73 |
74 | return null;
75 | }
76 |
77 | public async Task ShowPromptOptionsAsync(string message, List items, CancellationToken cancellationToken)
78 | {
79 | if (message == null)
80 | message = "Choose:";
81 |
82 | // Remove duplicates and sort the items.
83 | items = items.Distinct().ToList();
84 |
85 | // Create a dictionary to map items to unique integers.
86 | Dictionary itemMap = new();
87 | for (int i = 0; i < items.Count; i++)
88 | {
89 | itemMap[i] = items[i];
90 | }
91 |
92 | // Create PromptOptions and populate it with the item map.
93 | var promptOptions = new PromptOptions
94 | {
95 | DismissedReturns = -1,
96 | DefaultChoiceIndex = 0,
97 | };
98 |
99 | foreach (var kvp in itemMap)
100 | {
101 | promptOptions.Choices.Add(kvp.Value, kvp.Key);
102 | }
103 |
104 | // Show the prompt and get the result.
105 | var result = await Shell.ShowPromptAsync(
106 | message,
107 | promptOptions,
108 | cancellationToken);
109 |
110 | // Map the selected integer back to the corresponding item.
111 | if (itemMap.ContainsKey(result))
112 | {
113 | return itemMap[result];
114 | }
115 |
116 | // Return null or handle the case when no valid choice is made.
117 | return null;
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Services/IDialogService.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.Extensibility.Shell;
2 | using Microsoft.VisualStudio.RpcContracts.Notifications;
3 |
4 | namespace BrightXaml.Extensibility.Services;
5 | public interface IDialogService
6 | {
7 | ShellExtensibility Shell { get; set; }
8 |
9 | Task ShowDialogOptionsAsync(string title, string label, List items, CancellationToken cancellationToken);
10 |
11 | Task ShowPromptOKAsync(string message, CancellationToken cancellationToken);
12 | Task ShowPromptOKCancelAsync(string message, CancellationToken cancellationToken);
13 | Task ShowPromptRetryCancelAsync(string message, CancellationToken cancellationToken);
14 |
15 | ///
16 | /// Only use this for a maximum of 5 items... It displays horizontally only.
17 | ///
18 | Task ShowPromptOptionsAsync(string title, List items, CancellationToken cancellationToken);
19 |
20 | Task ShowDialogProgressAsync(string message, out Action progress, CancellationToken cancellationToken);
21 | }
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Services/SettingsData.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.PlatformUI;
2 | using System.Runtime.Serialization;
3 |
4 | namespace BrightXaml.Extensibility.Services;
5 | [DataContract]
6 | public class SettingsData : ObservableObject
7 | {
8 | [DataMember]
9 | public SettingsPropInpcData PropInpc { get => propInpc; set => SetProperty(ref propInpc, value); }
10 | private SettingsPropInpcData propInpc;
11 |
12 | [DataMember]
13 | public SettingsFormatXamlData FormatXaml { get => formatXaml; set => SetProperty(ref formatXaml, value); }
14 | private SettingsFormatXamlData formatXaml;
15 |
16 | [DataMember]
17 | public SettingsGoToBindingData GoToBinding { get => goToBinding; set => SetProperty(ref goToBinding, value); }
18 | private SettingsGoToBindingData goToBinding;
19 |
20 | public SettingsData()
21 | {
22 | PropInpc = new SettingsPropInpcData();
23 | FormatXaml = new SettingsFormatXamlData();
24 | GoToBinding = new SettingsGoToBindingData();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Services/SettingsFormatXamlData.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.PlatformUI;
2 | using System.Runtime.Serialization;
3 |
4 | namespace BrightXaml.Extensibility.Services;
5 | [DataContract]
6 | public class SettingsFormatXamlData : ObservableObject
7 | {
8 | [DataMember]
9 | public bool RemoveEmptyLines { get => removeEmptyLines; set => SetProperty(ref removeEmptyLines, value); }
10 | private bool removeEmptyLines = true;
11 |
12 | [DataMember]
13 | public int ClosingTagSpaces { get => closingTagSpaces; set => SetProperty(ref closingTagSpaces, value); }
14 | private int closingTagSpaces = -1;
15 |
16 | [DataMember]
17 | public int EndingTagSpaces { get => endingTagSpaces; set => SetProperty(ref endingTagSpaces, value); }
18 | private int endingTagSpaces = -1;
19 |
20 | [DataMember]
21 | public bool? IndentWithHeader { get => indentWithHeader; set => SetProperty(ref indentWithHeader, value); }
22 | private bool? indentWithHeader = null;
23 | }
24 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Services/SettingsGoToBindingData.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.PlatformUI;
2 | using System.Runtime.Serialization;
3 |
4 | namespace BrightXaml.Extensibility.Services;
5 | [DataContract]
6 | public class SettingsGoToBindingData : ObservableObject
7 | {
8 | [DataMember]
9 | public bool IsEnabled { get => isEnabled; set => SetProperty(ref isEnabled, value); }
10 | private bool isEnabled = true;
11 | }
12 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Services/SettingsPropInpcData.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.PlatformUI;
2 | using System.Runtime.Serialization;
3 |
4 | namespace BrightXaml.Extensibility.Services;
5 | [DataContract]
6 | public class SettingsPropInpcData : ObservableObject
7 | {
8 | [DataMember]
9 | public bool IsEnabled { get => isEnabled; set => SetProperty(ref isEnabled, value); }
10 | private bool isEnabled = true;
11 |
12 | [DataMember]
13 | public bool AddFieldAbove { get => addFieldAbove; set => SetProperty(ref addFieldAbove, value); }
14 | private bool addFieldAbove = false;
15 |
16 | [DataMember]
17 | public bool AddFieldUnderscore { get => addFieldUnderscore; set => SetProperty(ref addFieldUnderscore, value); }
18 | private bool addFieldUnderscore = false;
19 |
20 | [DataMember]
21 | public string SetMethodName { get => setMethodName; set => SetProperty(ref setMethodName, value); }
22 | private string setMethodName = "SetProperty";
23 |
24 | [DataMember]
25 | public bool PreserveDefaultValue { get => preserveDefaultValue; set => SetProperty(ref preserveDefaultValue, value); }
26 | private bool preserveDefaultValue = true;
27 |
28 | [DataMember]
29 | public bool SetTryAutoMethodName { get => setTryAutoMethodName; set => SetProperty(ref setTryAutoMethodName, value); }
30 | private bool setTryAutoMethodName = true;
31 | }
32 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Services/SettingsService.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.Text.Json;
3 |
4 | namespace BrightXaml.Extensibility.Services;
5 | public class SettingsService
6 | {
7 | private const string settingsFileName = "BrightXamlSettings.json";
8 | private readonly string settingsDirectory;
9 | private readonly string settingsFilePath;
10 |
11 | public SettingsData Data { get; set; } = new();
12 |
13 | public SettingsService()
14 | {
15 | settingsDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "BrightExtensions");
16 | settingsFilePath = Path.Combine(settingsDirectory, settingsFileName);
17 | Load();
18 | }
19 |
20 | public void Load()
21 | {
22 | try
23 | {
24 | // Check if directory exists.
25 | if (!Directory.Exists(settingsDirectory))
26 | Directory.CreateDirectory(settingsDirectory);
27 |
28 | // Load settings.
29 | if (File.Exists(settingsFilePath))
30 | {
31 | var json = File.ReadAllText(settingsFilePath);
32 | Data = JsonSerializer.Deserialize(json) ?? new SettingsData();
33 | }
34 | }
35 | catch (Exception ex)
36 | {
37 | Debug.WriteLine(ex);
38 |
39 | // Use default settings.
40 | Data = new SettingsData();
41 | }
42 | }
43 |
44 | public void Save()
45 | {
46 | try
47 | {
48 | var json = JsonSerializer.Serialize(Data, new JsonSerializerOptions { WriteIndented = true });
49 | File.WriteAllText(settingsFilePath, json);
50 | }
51 | catch (Exception ex)
52 | {
53 | // Add log?
54 | Debug.WriteLine(ex);
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Utilities/ExportFilesHelper.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 |
3 | namespace BrightXaml.Extensibility.Utilities;
4 | public static class ExportFilesHelper
5 | {
6 | public static List GetFilesFromDir(string directoryPath, bool includeSubDirs, List fileExtensions)
7 | {
8 | var files = new List();
9 | // Ensure the directory exists.
10 | if (!Directory.Exists(directoryPath))
11 | {
12 | throw new DirectoryNotFoundException($"The directory '{directoryPath}' does not exist.");
13 | }
14 |
15 | // Iterate over each file extension and get the files.
16 | foreach (var extension in fileExtensions)
17 | {
18 | var matchedFiles = Directory.GetFiles(directoryPath, $"*{extension}", includeSubDirs ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
19 | files.AddRange(matchedFiles);
20 | }
21 |
22 | // Sort the files to ensure related files are together.
23 | files.Sort();
24 |
25 | return files;
26 | }
27 |
28 | public static async Task ReadFilesContentAsync(List filenames, bool useMarkdown, bool includeFileName)
29 | {
30 | // StringBuilder to accumulate file contents.
31 | var allContents = new StringBuilder(filenames.Count * 256);
32 |
33 | foreach (string file in filenames)
34 | {
35 | string extension = Path.GetExtension(file).ToLowerInvariant();
36 | string language = GetMarkdownLanguage(extension);
37 | string commentStart, commentEnd;
38 |
39 | // Determine the comment syntax based on file extension
40 | GetCommentSyntax(extension, out commentStart, out commentEnd);
41 |
42 | // Start of Markdown code block for the specific file type.
43 | if (useMarkdown && !string.IsNullOrEmpty(language))
44 | allContents.AppendLine($"```{language}");
45 |
46 | // Read and append each file's content.
47 | string content = await File.ReadAllTextAsync(file);
48 | if (!string.IsNullOrEmpty(commentStart))
49 | {
50 | if (includeFileName)
51 | {
52 | allContents.AppendLine($"{commentStart} File: {Path.GetFileName(file)} {commentEnd}");
53 | }
54 | }
55 | allContents.AppendLine(content);
56 |
57 | // End of Markdown code block.
58 | if (useMarkdown && !string.IsNullOrEmpty(language))
59 | allContents.AppendLine("```");
60 |
61 | allContents.AppendLine();
62 | }
63 |
64 | return allContents.ToString();
65 | }
66 |
67 | private static string GetMarkdownLanguage(string extension)
68 | {
69 | return extension switch
70 | {
71 | ".cs" => "csharp",
72 | ".xaml" => "xml",
73 | ".xml" => "xml",
74 | ".html" => "html",
75 | ".css" => "css",
76 | ".js" => "javascript",
77 | _ => string.Empty,
78 | };
79 | }
80 |
81 | private static void GetCommentSyntax(string extension, out string commentStart, out string commentEnd)
82 | {
83 | switch (extension)
84 | {
85 | case ".cs":
86 | case ".js":
87 | commentStart = "//";
88 | commentEnd = "";
89 | break;
90 | case ".xaml":
91 | case ".xml":
92 | case ".html":
93 | commentStart = "";
95 | break;
96 | case ".css":
97 | commentStart = "/*";
98 | commentEnd = " */";
99 | break;
100 | default:
101 | commentStart = "//";
102 | commentEnd = "";
103 | break;
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Utilities/PropertyLineData.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Utilities;
2 | public class PropertyLineData
3 | {
4 | public string Indentation { get; set; }
5 | public string Access { get; set; }
6 | public string Type { get; set; }
7 | public string Name { get; set; }
8 | public string GetAccess { get; set; }
9 | public string SetAccess { get; set; }
10 | public string DefaultValue { get; set; }
11 | }
12 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Utilities/ViewModelHelper.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Utilities;
2 | public static class ViewModelHelper
3 | {
4 | public static List GetViewModelNamePossibilities(string xamlName)
5 | {
6 | const string suffix = "ViewModel.cs";
7 | string treatedName = xamlName;
8 | var possibilities = new List();
9 |
10 | // Remove the "Page" or "View" suffix from the XAML file name.
11 | if (xamlName.EndsWith("page", StringComparison.InvariantCultureIgnoreCase) ||
12 | xamlName.EndsWith("view", StringComparison.InvariantCultureIgnoreCase))
13 | {
14 | treatedName = treatedName.Substring(0, xamlName.Length - 4);
15 | }
16 | // Remove the "Window" suffix from the XAML file name.
17 | else if (xamlName.EndsWith("window", StringComparison.InvariantCultureIgnoreCase))
18 | {
19 | treatedName = treatedName.Substring(0, xamlName.Length - 6);
20 | }
21 | // Remove the "Content" suffix from the XAML file name (only VS Extensibility uses this).
22 | else if (xamlName.EndsWith("content", StringComparison.InvariantCultureIgnoreCase))
23 | {
24 | treatedName = treatedName.Substring(0, xamlName.Length - 7);
25 | }
26 |
27 | // Return the list of possibilities.
28 | possibilities.Add(treatedName + suffix);
29 | possibilities.Add(xamlName + suffix);
30 |
31 | return possibilities;
32 | }
33 |
34 | public static List GetViewNamePossibilities(string viewModelName)
35 | {
36 | const string suffixXaml = ".xaml";
37 | string treatedName = viewModelName.Replace("ViewModel", string.Empty, StringComparison.InvariantCultureIgnoreCase);
38 | var possibilities = new List
39 | {
40 | // Add different suffixes to generate possible view names.
41 | treatedName + "View" + suffixXaml,
42 | treatedName + "Page" + suffixXaml,
43 | treatedName + "Window" + suffixXaml,
44 | treatedName + "Content" + suffixXaml, // (only VS Extensibility uses this).
45 | treatedName + suffixXaml
46 | };
47 |
48 | return possibilities;
49 | }
50 |
51 | public static List RemoveCommonPath(List filePaths)
52 | {
53 | if (filePaths == null || filePaths.Count == 0)
54 | {
55 | return new List();
56 | }
57 |
58 | string commonPath = FindCommonPath(filePaths);
59 |
60 | return filePaths.Select(path => path.Replace(commonPath, string.Empty)).ToList();
61 | }
62 |
63 | private static string FindCommonPath(List paths)
64 | {
65 | if (paths.Count == 1)
66 | {
67 | return paths[0];
68 | }
69 |
70 | string[] separatedPaths = paths[0].Split(new[] { '/', '\\' });
71 |
72 | for (int pathIndex = 1; pathIndex < paths.Count; pathIndex++)
73 | {
74 | string[] nextPath = paths[pathIndex].Split(new[] { '/', '\\' });
75 |
76 | int minLength = Math.Min(separatedPaths.Length, nextPath.Length);
77 |
78 | for (int i = 0; i < minLength; i++)
79 | {
80 | if (!separatedPaths[i].Equals(nextPath[i], StringComparison.OrdinalIgnoreCase))
81 | {
82 | separatedPaths = separatedPaths.Take(i).ToArray();
83 | break;
84 | }
85 | }
86 | }
87 |
88 | return string.Join("/", separatedPaths) + "/";
89 | }
90 |
91 | public static string GetProperDirectoryCapitalization(DirectoryInfo dirInfo)
92 | {
93 | DirectoryInfo parentDirInfo = dirInfo.Parent;
94 | if (null == parentDirInfo)
95 | return dirInfo.Name;
96 |
97 | return Path.Combine(GetProperDirectoryCapitalization(parentDirInfo),
98 | parentDirInfo.GetDirectories(dirInfo.Name)[0].Name);
99 | }
100 |
101 | public static string GetProperFilePathCapitalization(string filename)
102 | {
103 | FileInfo fileInfo = new FileInfo(filename);
104 | DirectoryInfo dirInfo = fileInfo.Directory;
105 |
106 | return Path.Combine(GetProperDirectoryCapitalization(dirInfo),
107 | dirInfo.GetFiles(fileInfo.Name)[0].Name);
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Windows/ChooseItemWindow.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Windows;
2 |
3 | using Microsoft.VisualStudio.Extensibility;
4 | using Microsoft.VisualStudio.Extensibility.ToolWindows;
5 | using Microsoft.VisualStudio.RpcContracts.RemoteUI;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | ///
10 | /// A sample tool window.
11 | ///
12 | [VisualStudioContribution]
13 | public class ChooseItemWindow : ToolWindow
14 | {
15 | private readonly ChooseItemWindowContent content = new();
16 |
17 | ///
18 | /// Initializes a new instance of the class.
19 | ///
20 | public ChooseItemWindow()
21 | {
22 | this.Title = "My Tool Window";
23 | }
24 |
25 | ///
26 | public override ToolWindowConfiguration ToolWindowConfiguration => new()
27 | {
28 | // Use this object initializer to set optional parameters for the tool window.
29 | Placement = ToolWindowPlacement.Floating,
30 | };
31 |
32 | ///
33 | public override Task InitializeAsync(CancellationToken cancellationToken)
34 | {
35 | // Use InitializeAsync for any one-time setup or initialization.
36 | return Task.CompletedTask;
37 | }
38 |
39 | ///
40 | public override Task GetContentAsync(CancellationToken cancellationToken)
41 | {
42 | return Task.FromResult(content);
43 | }
44 |
45 | ///
46 | protected override void Dispose(bool disposing)
47 | {
48 | if (disposing)
49 | content.Dispose();
50 |
51 | base.Dispose(disposing);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Windows/ChooseItemWindowCommand.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Windows;
2 |
3 | using Microsoft.VisualStudio.Extensibility;
4 | using Microsoft.VisualStudio.Extensibility.Commands;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | ///
9 | /// A command for showing a tool window.
10 | ///
11 | [VisualStudioContribution]
12 | public class ChooseItemWindowCommand : Command
13 | {
14 | ///
15 | public override CommandConfiguration CommandConfiguration => new(displayName: "Show Item Chooser")
16 | {
17 | //Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
18 | Icon = new(ImageMoniker.KnownValues.Extension, IconSettings.IconAndText),
19 | };
20 |
21 | ///
22 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
23 | {
24 | await this.Extensibility.Shell().ShowToolWindowAsync(activate: true, cancellationToken);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Windows/ChooseItemWindowContent.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Windows;
2 |
3 | using Microsoft.VisualStudio.Extensibility.UI;
4 |
5 | ///
6 | /// A remote user control to use as tool window UI content.
7 | ///
8 | internal class ChooseItemWindowContent : RemoteUserControl
9 | {
10 | public ChooseItemWindowViewModel ViewModel => base.DataContext as ChooseItemWindowViewModel;
11 |
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | public ChooseItemWindowContent()
16 | : base(dataContext: new ChooseItemWindowViewModel())
17 | {
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Windows/ChooseItemWindowContent.xaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Windows/ChooseItemWindowViewModel.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Windows;
2 |
3 | using Microsoft.VisualStudio.Extensibility.UI;
4 | using System.Collections.ObjectModel;
5 | using System.Runtime.Serialization;
6 |
7 | [DataContract]
8 | internal class ChooseItemWindowViewModel : NotifyPropertyChangedObject
9 | {
10 | [DataMember]
11 | public ObservableCollection Items { get => _items; set => SetProperty(ref _items, value); }
12 | private ObservableCollection _items = new ObservableCollection();
13 |
14 | [DataMember]
15 | public string SelectedItem { get => _selectedItem; set => SetProperty(ref _selectedItem, value); }
16 | private string _selectedItem;
17 |
18 | [DataMember]
19 | public string LabelText { get => _labelText; set => SetProperty(ref _labelText, value); }
20 | private string _labelText;
21 |
22 | public ChooseItemWindowViewModel()
23 | { }
24 | }
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Windows/HelpWindow.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Windows;
2 |
3 | using Microsoft.VisualStudio.Extensibility;
4 | using Microsoft.VisualStudio.Extensibility.ToolWindows;
5 | using Microsoft.VisualStudio.RpcContracts.RemoteUI;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | ///
10 | /// A sample tool window.
11 | ///
12 | [VisualStudioContribution]
13 | public class HelpWindow : ToolWindow
14 | {
15 | private readonly HelpWindowContent content = new();
16 |
17 | ///
18 | /// Initializes a new instance of the class.
19 | ///
20 | public HelpWindow()
21 | {
22 | this.Title = "Bright Xaml - Help";
23 | }
24 |
25 | ///
26 | public override ToolWindowConfiguration ToolWindowConfiguration => new()
27 | {
28 | // Use this object initializer to set optional parameters for the tool window.
29 | Placement = ToolWindowPlacement.Floating,
30 | };
31 |
32 | ///
33 | public override Task InitializeAsync(CancellationToken cancellationToken)
34 | {
35 | // Use InitializeAsync for any one-time setup or initialization.
36 | return Task.CompletedTask;
37 | }
38 |
39 | ///
40 | public override Task GetContentAsync(CancellationToken cancellationToken)
41 | {
42 | return Task.FromResult(content);
43 | }
44 |
45 | ///
46 | protected override void Dispose(bool disposing)
47 | {
48 | if (disposing)
49 | content.Dispose();
50 |
51 | base.Dispose(disposing);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Windows/HelpWindowCommand.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Windows;
2 |
3 | using Microsoft.VisualStudio.Extensibility;
4 | using Microsoft.VisualStudio.Extensibility.Commands;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | [VisualStudioContribution]
9 | public class HelpWindowCommand : Command
10 | {
11 | ///
12 | public override CommandConfiguration CommandConfiguration => new(displayName: "Help")
13 | {
14 | //Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
15 | Icon = new(ImageMoniker.KnownValues.QuestionMark, IconSettings.IconAndText),
16 | };
17 |
18 | ///
19 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
20 | {
21 | await this.Extensibility.Shell().ShowToolWindowAsync(activate: true, cancellationToken);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Windows/HelpWindowContent.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Windows;
2 |
3 | using Microsoft.VisualStudio.Extensibility.UI;
4 |
5 | ///
6 | /// A remote user control to use as tool window UI content.
7 | ///
8 | internal class HelpWindowContent : RemoteUserControl
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | public HelpWindowContent()
14 | : base(dataContext: new HelpWindowViewModel())
15 | {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Windows/HelpWindowContent.xaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
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 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Windows/HelpWindowViewModel.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Windows;
2 |
3 | using BrightXaml.Extensibility.Models;
4 | using Microsoft.VisualStudio.Extensibility.UI;
5 | using System.Collections.ObjectModel;
6 | using System.Runtime.Serialization;
7 |
8 | [DataContract]
9 | internal class HelpWindowViewModel : NotifyPropertyChangedObject
10 | {
11 | [DataMember]
12 | public string Text { get => _text; set => SetProperty(ref _text, value); }
13 | private string _text = string.Empty;
14 |
15 | [DataMember]
16 | public ObservableCollection CommandsInfo { get => _commandsInfo; set => SetProperty(ref _commandsInfo, value); }
17 | private ObservableCollection _commandsInfo;
18 |
19 | [DataMember]
20 | public AsyncCommand RefreshCommand { get; }
21 |
22 | public HelpWindowViewModel()
23 | {
24 | RefreshCommand = new AsyncCommand((parameter, clientContext, cancellationToken) =>
25 | {
26 | InitializeCommandsInfo();
27 | return Task.CompletedTask;
28 | });
29 | RefreshCommand.CanExecute = Meta.IsDebug;
30 |
31 | InitializeCommandsInfo();
32 | }
33 |
34 | private void InitializeCommandsInfo()
35 | {
36 | CommandsInfo = new ObservableCollection
37 | {
38 | new() { Name = "Clean Solution Bin and Obj", Description = "Clean 'bin' and 'obj' folders from all projects in the solution" },
39 | new() { Name = "Kill XAML Designer Process", Description = "Kills the WpfSurface.exe process, VS can auto reload it later" },
40 | //new() { Name = "Extract Classes Being Used", Description = "Copy to the clipboard all C# classes being referenced by the current class" },
41 | //new() { Name = "Extract Directory", Description = "Copy to the clipboard all coding files from the current directory" },
42 | new() { Name = "Format Xaml", Description = "Format the current XAML file (preserves tags position and indentation)" },
43 | new() { Name = "Property To INPC", Description = "Convert regular properties to INotifyPropertyChanged properties" },
44 | new() { Name = "Automatically Go To Binding Definition", Description = "When pressing F12, if a SourceGenerator file is opened by VS,\nautomatically open the actual command in the ViewModel" },
45 | new() { Name = "Show View/ViewModel", Description = "Display the view of the current ViewModel and vice-versa" },
46 | //new() { Name = "Show View", Description = "Display the view of the current ViewModel" },
47 | //new() { Name = "Show View Model", Description = "Display the ViewModel of the current View (.xaml | .xaml.cs)" }
48 | };
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Windows/ProgressWindow.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Windows;
2 |
3 | using Microsoft.VisualStudio.Extensibility;
4 | using Microsoft.VisualStudio.Extensibility.ToolWindows;
5 | using Microsoft.VisualStudio.RpcContracts.RemoteUI;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | ///
10 | /// A sample tool window.
11 | ///
12 | [VisualStudioContribution]
13 | public class ProgressWindow : ToolWindow
14 | {
15 | private readonly ProgressWindowContent content = new();
16 |
17 | ///
18 | /// Initializes a new instance of the class.
19 | ///
20 | public ProgressWindow()
21 | {
22 | this.Title = "Progress Window";
23 | }
24 |
25 | ///
26 | public override ToolWindowConfiguration ToolWindowConfiguration => new()
27 | {
28 | // Use this object initializer to set optional parameters for the tool window.
29 | Placement = ToolWindowPlacement.Floating,
30 | };
31 |
32 | ///
33 | public override Task InitializeAsync(CancellationToken cancellationToken)
34 | {
35 | // Use InitializeAsync for any one-time setup or initialization.
36 | return Task.CompletedTask;
37 | }
38 |
39 | ///
40 | public override Task GetContentAsync(CancellationToken cancellationToken)
41 | {
42 | return Task.FromResult(content);
43 | }
44 |
45 | ///
46 | protected override void Dispose(bool disposing)
47 | {
48 | if (disposing)
49 | content.Dispose();
50 |
51 | base.Dispose(disposing);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Windows/ProgressWindowCommand.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Windows;
2 |
3 | using Microsoft.VisualStudio.Extensibility;
4 | using Microsoft.VisualStudio.Extensibility.Commands;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | ///
9 | /// A command for showing a tool window.
10 | ///
11 | [VisualStudioContribution]
12 | public class ProgressWindowCommand : Command
13 | {
14 | ///
15 | public override CommandConfiguration CommandConfiguration => new(displayName: "Progress Window")
16 | {
17 | // Use this object initializer to set optional parameters for the command. The required parameter,
18 | // displayName, is set above. To localize the displayName, add an entry in .vsextension\string-resources.json
19 | // and reference it here by passing "%BrightXaml.Extensibility.Windows.ProgressWindowCommand.DisplayName%" as a constructor parameter.
20 | //Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
21 | Icon = new(ImageMoniker.KnownValues.Extension, IconSettings.IconAndText),
22 | };
23 |
24 | ///
25 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
26 | {
27 | await this.Extensibility.Shell().ShowToolWindowAsync(activate: true, cancellationToken);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Windows/ProgressWindowContent.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Windows;
2 |
3 | using Microsoft.VisualStudio.Extensibility.UI;
4 |
5 | ///
6 | /// A remote user control to use as tool window UI content.
7 | ///
8 | internal class ProgressWindowContent : RemoteUserControl
9 | {
10 | public ProgressWindowViewModel ViewModel => base.DataContext as ProgressWindowViewModel;
11 |
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | public ProgressWindowContent()
16 | : base(dataContext: new ProgressWindowViewModel())
17 | {
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Windows/ProgressWindowContent.xaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Windows/ProgressWindowViewModel.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Windows;
2 |
3 | using Microsoft.VisualStudio.Extensibility.UI;
4 | using System.Runtime.Serialization;
5 |
6 | [DataContract]
7 | internal class ProgressWindowViewModel : NotifyPropertyChangedObject
8 | {
9 | [DataMember]
10 | public int ProgressValue { get => _progressValue; set => SetProperty(ref _progressValue, value); }
11 | private int _progressValue;
12 |
13 | [DataMember]
14 | public string ProgressText { get => _progressText; set => SetProperty(ref _progressText, value); }
15 | private string _progressText;
16 |
17 | public ProgressWindowViewModel()
18 | {
19 | ProgressValue = 0;
20 | ProgressText = "Starting...";
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Windows/SettingsWindow.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Windows;
2 |
3 | using BrightXaml.Extensibility.Services;
4 | using Microsoft.VisualStudio.Extensibility;
5 | using Microsoft.VisualStudio.Extensibility.ToolWindows;
6 | using Microsoft.VisualStudio.RpcContracts.RemoteUI;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | ///
11 | /// A sample tool window.
12 | ///
13 | [VisualStudioContribution]
14 | public class SettingsWindow : ToolWindow
15 | {
16 | private readonly SettingsWindowContent content;
17 |
18 | ///
19 | /// Initializes a new instance of the class.
20 | ///
21 | public SettingsWindow(SettingsService settingsService)
22 | {
23 | this.Title = "Bright Xaml - Settings";
24 | this.content = new SettingsWindowContent(settingsService);
25 | content.ViewModel.CloseWindow = (cancellationToken) => { _ = HideAsync(cancellationToken); };
26 | }
27 |
28 | ///
29 | public override ToolWindowConfiguration ToolWindowConfiguration => new()
30 | {
31 | // Use this object initializer to set optional parameters for the tool window.
32 | Placement = ToolWindowPlacement.Floating,
33 | };
34 |
35 | ///
36 | public override Task InitializeAsync(CancellationToken cancellationToken)
37 | {
38 | // Use InitializeAsync for any one-time setup or initialization.
39 | return Task.CompletedTask;
40 | }
41 |
42 | ///
43 | public override Task GetContentAsync(CancellationToken cancellationToken)
44 | {
45 | return Task.FromResult(content);
46 | }
47 |
48 | public override Task OnShowAsync(CancellationToken cancellationToken)
49 | {
50 | return base.OnShowAsync(cancellationToken);
51 | }
52 |
53 | public override Task OnHideAsync(CancellationToken cancellationToken)
54 | {
55 | // Auto save when closing?
56 | //content.ViewModel.SaveSettings();
57 | return base.OnHideAsync(cancellationToken);
58 | }
59 |
60 | ///
61 | protected override void Dispose(bool disposing)
62 | {
63 | if (disposing)
64 | content.Dispose();
65 |
66 | base.Dispose(disposing);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Windows/SettingsWindowCommand.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Windows;
2 |
3 | using Microsoft.VisualStudio.Extensibility;
4 | using Microsoft.VisualStudio.Extensibility.Commands;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | [VisualStudioContribution]
9 | public class SettingsWindowCommand : Command
10 | {
11 | ///
12 | public override CommandConfiguration CommandConfiguration => new(displayName: "Settings")
13 | {
14 | //Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
15 | Icon = new(ImageMoniker.KnownValues.Settings, IconSettings.IconAndText),
16 | };
17 |
18 | ///
19 | public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
20 | {
21 | await this.Extensibility.Shell().ShowToolWindowAsync(activate: true, cancellationToken);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Windows/SettingsWindowContent.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Windows;
2 |
3 | using BrightXaml.Extensibility.Services;
4 | using Microsoft.VisualStudio.Extensibility.UI;
5 |
6 | ///
7 | /// A remote user control to use as tool window UI content.
8 | ///
9 | internal class SettingsWindowContent : RemoteUserControl
10 | {
11 | public SettingsWindowViewModel ViewModel => base.DataContext as SettingsWindowViewModel;
12 |
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | public SettingsWindowContent(SettingsService settingsService)
17 | : base(dataContext: new SettingsWindowViewModel(settingsService))
18 | {
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/BrightXaml.Extensibility/Windows/SettingsWindowViewModel.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Windows;
2 |
3 | using BrightXaml.Extensibility.Models;
4 | using BrightXaml.Extensibility.Services;
5 | using BrightXaml.Extensibility.Utilities;
6 | using Microsoft.VisualStudio.Extensibility.UI;
7 | using System.Runtime.Serialization;
8 | using System.Text.Json;
9 |
10 | [DataContract]
11 | internal class SettingsWindowViewModel : NotifyPropertyChangedObject
12 | {
13 | public Action CloseWindow { get; set; }
14 |
15 | public SettingsService SettingsService { get; }
16 |
17 | [DataMember]
18 | public SettingsData SettingsData { get => settingsData; private set => SetProperty(ref settingsData, value); }
19 | private SettingsData settingsData;
20 |
21 | [DataMember]
22 | public string PreviewInpcInput { get => previewInpcInput; set => SetProperty(ref previewInpcInput, value); }
23 | private string previewInpcInput;
24 |
25 | [DataMember]
26 | public string PreviewInpcOutput { get => previewInpcOutput; set => SetProperty(ref previewInpcOutput, value); }
27 | private string previewInpcOutput;
28 |
29 | [DataMember]
30 | public List TagSpacesOptions { get => tagSpacesOptions; set => SetProperty(ref tagSpacesOptions, value); }
31 | private List tagSpacesOptions;
32 |
33 | [DataMember]
34 | public AsyncCommand OKCommand { get; }
35 |
36 | [DataMember]
37 | public AsyncCommand CancelCommand { get; }
38 |
39 | [DataMember]
40 | public string Version { get; } = Meta.Version.ToString();
41 |
42 | public SettingsWindowViewModel(SettingsService settingsService)
43 | {
44 | SettingsService = settingsService;
45 | SettingsData = CloneSettings(settingsService.Data);
46 |
47 | TagSpacesOptions = new List() {
48 | new (){ Id = -1, Text = "Preserve spaces (0 or 1)" },
49 | new (){ Id = 0, Text = "Always 0 spaces" },
50 | new (){ Id = 1, Text = "Always 1 space" },
51 | };
52 |
53 | OKCommand = new AsyncCommand((parameter, clientContext, cancellationToken) =>
54 | {
55 | // Save settings.
56 | SaveSettings();
57 |
58 | // Close window.
59 | CloseWindow?.Invoke(cancellationToken);
60 | return Task.CompletedTask;
61 | });
62 | CancelCommand = new AsyncCommand((parameter, clientContext, cancellationToken) =>
63 | {
64 | // Reset settings.
65 | SettingsData = CloneSettings(settingsService.Data);
66 | SettingsData.PropInpc.PropertyChanged += SettingsWindowViewModel_PropertyChanged;
67 |
68 | // Close window.
69 | CloseWindow?.Invoke(cancellationToken);
70 | return Task.CompletedTask;
71 | });
72 |
73 | // Initialize preview data.
74 | PreviewInpcInput = "public int Age { get; set; }";
75 | CalculatePreviewInpc();
76 |
77 | // Subscribe to property changes.
78 | PropertyChanged += SettingsWindowViewModel_PropertyChanged;
79 | SettingsData.PropInpc.PropertyChanged += SettingsWindowViewModel_PropertyChanged;
80 | }
81 |
82 | private void SettingsWindowViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
83 | {
84 | if (e.PropertyName == nameof(SettingsData.PropInpc.AddFieldAbove) ||
85 | e.PropertyName == nameof(SettingsData.PropInpc.AddFieldUnderscore) ||
86 | e.PropertyName == nameof(SettingsData.PropInpc.SetMethodName))
87 | {
88 | CalculatePreviewInpc();
89 | }
90 | }
91 |
92 | private void CalculatePreviewInpc()
93 | {
94 | var propData = PropToInpcHelper.GetPropertyLineData(PreviewInpcInput);
95 | PreviewInpcOutput = PropToInpcHelper.GenerateInpcPropertySet(propData,
96 | SettingsData.PropInpc.AddFieldAbove,
97 | SettingsData.PropInpc.AddFieldUnderscore,
98 | true,
99 | SettingsData.PropInpc.SetMethodName);
100 | }
101 |
102 | public void SaveSettings()
103 | {
104 | SettingsService.Data = SettingsData;
105 | SettingsService.Save();
106 | }
107 |
108 | public SettingsData CloneSettings(SettingsData data)
109 | {
110 | return JsonSerializer.Deserialize(JsonSerializer.Serialize(data));
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/MockEntrypoint.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.Extensibility;
2 |
3 | namespace BrightXaml.ExtensibilityTests;
4 | [VisualStudioContribution]
5 | internal class MockEntrypoint : Extension
6 | {
7 | public override ExtensionConfiguration ExtensionConfiguration => null;
8 | }
9 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/FullDocument01_Bad.xml:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
17 |
18 | 120
19 | 80
20 | 20
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
68 |
69 |
70 |
72 |
73 |
74 |
75 |
77 |
78 |
79 |
81 |
82 |
83 |
84 |
85 |
88 |
89 |
90 |
91 |
92 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/FullDocument01_Good.xml:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
17 |
18 | 120
19 | 80
20 | 20
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
68 |
69 |
70 |
72 |
73 |
74 |
75 |
77 |
78 |
79 |
81 |
82 |
83 |
84 |
85 |
88 |
89 |
90 |
91 |
92 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/FullDocument02_Bad.xml:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
17 |
18 | 120
19 | 80
20 | 20
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
70 |
71 |
72 |
74 |
75 |
76 |
77 |
78 |
80 |
81 |
82 |
84 |
85 |
86 |
87 |
88 |
91 |
92 |
93 |
94 |
95 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/FullDocument02_Good.xml:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
17 |
18 | 120
19 | 80
20 | 20
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
70 |
71 |
72 |
74 |
75 |
76 |
77 |
78 |
80 |
81 |
82 |
84 |
85 |
86 |
87 |
88 |
91 |
92 |
93 |
94 |
95 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/MultipleLine01_Bad.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/MultipleLine01_Good.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/MultipleLine02_Bad.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/MultipleLine02_Good.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/MultipleLine03_Bad.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/MultipleLine03_Good.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/MultipleLine04_Bad.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/MultipleLine05_Good.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/MultipleLine06_Bad.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/MultipleLine06_Good.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/MultipleLine07_Bad.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/MultipleLine07_Good.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/MultipleLine08_Bad.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/MultipleLine08_Good.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/RemoveEmpty_MultipleLine01_Bad.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/RemoveEmpty_MultipleLine01_Good.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/RemoveEmpty_MultipleLine02_Bad.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/RemoveEmpty_MultipleLine02_Good.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/SingleLine01_Bad.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/SingleLine01_Good.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/SingleLine02_Bad.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/SingleLine02_Good.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/SingleLine03_Bad.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/SingleLine03_Good.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/SingleLine04_Bad.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/SingleLine04_Good.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/SingleLine05_Bad.xml:
--------------------------------------------------------------------------------
1 | x:Class="ChatGPT.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/SingleLine05_Good.xml:
--------------------------------------------------------------------------------
1 | x:Class="ChatGPT.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/SingleLineComment01_Bad.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/SingleLineComment01_Good.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/SingleLineComment02_Bad.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/SingleLineComment02_Good.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/SplitTagsPerLine01_Bad.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/SplitTagsPerLine01_Good.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/SplitTagsPerLine02_Bad.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Resources/SplitTagsPerLine02_Good.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/TestHelper.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 |
4 | namespace BrightXaml.ExtensibilityTests;
5 | internal static class TestHelper
6 | {
7 | internal static string ReadResource(string resourceName)
8 | {
9 | // Get the assembly where the resource is embedded.
10 | var assembly = Assembly.GetExecutingAssembly();
11 |
12 | // Create the full resource name.
13 | string resourceFullName = assembly.GetName().Name + "." + resourceName;
14 |
15 | using (Stream stream = assembly.GetManifestResourceStream(resourceFullName))
16 | {
17 | if (stream == null)
18 | {
19 | throw new InvalidOperationException("Resource not found: " + resourceFullName);
20 | }
21 |
22 | using (StreamReader reader = new StreamReader(stream))
23 | {
24 | return reader.ReadToEnd();
25 | }
26 | }
27 | }
28 |
29 | internal static void WriteTestResultToFile(string text, [CallerMemberName] string callerName = null)
30 | {
31 | File.WriteAllText(callerName + ".xaml", text);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Utilities/PropToInpcHelperTests.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Utilities.Tests;
2 |
3 | [TestClass()]
4 | public class PropToInpcHelperTests
5 | {
6 | [TestMethod()]
7 | [DataRow("public string Name { get; set; }", "", "public", "string", "Name", "", "")]
8 | [DataRow(" public int Age { get; set; }", " ", "public", "int", "Age", "", "")]
9 | [DataRow(" public bool IsEnabled { get; set; }", " ", "public", "bool", "IsEnabled", "", "")]
10 | [DataRow(" uint Ticks { get; set; }", " ", "", "uint", "Ticks", "", "")]
11 | [DataRow("protected long ShortTicks { get; set; }", "", "protected", "long", "ShortTicks", "", "")]
12 | [DataRow("protected ulong PositiveShortTicks { private get; set; }", "", "protected", "ulong", "PositiveShortTicks", "private", "")]
13 | [DataRow("protected ulong PositiveShortTicks { internal get; set; }", "", "protected", "ulong", "PositiveShortTicks", "internal", "")]
14 | [DataRow("protected ulong PositiveShortTicks { protected get; set; }", "", "protected", "ulong", "PositiveShortTicks", "protected", "")]
15 | [DataRow("protected ulong PositiveShortTicks { public get; set; }", "", "protected", "ulong", "PositiveShortTicks", "public", "")]
16 | [DataRow("protected ulong PositiveShortTicks { get; private set; }", "", "protected", "ulong", "PositiveShortTicks", "", "private")]
17 | [DataRow("protected ulong PositiveShortTicks { get;internal set; }", "", "protected", "ulong", "PositiveShortTicks", "", "internal")]
18 | [DataRow("protected ulong PositiveShortTicks { get; protected set; }", "", "protected", "ulong", "PositiveShortTicks", "", "protected")]
19 | [DataRow("protected ulong PositiveShortTicks { get;public set; }", "", "protected", "ulong", "PositiveShortTicks", "", "public")]
20 | [DataRow("public string Name { get; set; } = \"John Winchester\";", "", "public", "string", "Name", "", "", "\"John Winchester\"")]
21 | [DataRow("public bool IsChecked { get; set; } = true;", "", "public", "bool", "IsChecked", "", "", "true")]
22 | public void GetPropertyLineDataTests(string line,
23 | string expectedIndent,
24 | string expectedAccess,
25 | string expectedType,
26 | string expectedName,
27 | string expectedGetAccess,
28 | string expectedSetAccess,
29 | string expectedDefaultValue = null)
30 | {
31 | // Arrange.
32 | // Act.
33 | var propertyLineData = PropToInpcHelper.GetPropertyLineData(line);
34 |
35 | // Assert.
36 | Assert.AreEqual(expectedIndent, propertyLineData.Indentation);
37 | Assert.AreEqual(expectedAccess, propertyLineData.Access);
38 | Assert.AreEqual(expectedType, propertyLineData.Type);
39 | Assert.AreEqual(expectedName, propertyLineData.Name);
40 | Assert.AreEqual(expectedGetAccess, propertyLineData.GetAccess);
41 | Assert.AreEqual(expectedSetAccess, propertyLineData.SetAccess);
42 | Assert.AreEqual(expectedDefaultValue, propertyLineData.DefaultValue);
43 | }
44 |
45 | [TestMethod()]
46 | [DataRow("public string Name { get; set; }", "private string name;", "public string Name { get => name; set => Set(ref name, value); }")]
47 | [DataRow("public int Age { get; set; }", "private int age;", "public int Age { get => age; set => Set(ref age, value); }")]
48 | [DataRow("protected bool IsEnabled { get; set; }", "private bool isEnabled;", "protected bool IsEnabled { get => isEnabled; set => Set(ref isEnabled, value); }")]
49 | [DataRow("private uint Ticks { get; set; }", "private uint ticks;", "private uint Ticks { get => ticks; set => Set(ref ticks, value); }")]
50 | [DataRow("internal long ShortTicks { get; set; }", "private long shortTicks;", "internal long ShortTicks { get => shortTicks; set => Set(ref shortTicks, value); }")]
51 | [DataRow("public string FullName { get; set; } = \"John Winchester\";", "private string fullName = \"John Winchester\";", "public string FullName { get => fullName; set => Set(ref fullName, value); }")]
52 | [DataRow("public string IsChecked { get; set; } = true;", "private string isChecked = true;", "public string IsChecked { get => isChecked; set => Set(ref isChecked, value); }")]
53 | public void GenerateInpcPropertySetTest(string propertyText, string expectedFieldLine, string expectedPropertyLine)
54 | {
55 | // Arrange.
56 | // Act.
57 | var propertyLineData = PropToInpcHelper.GetPropertyLineData(propertyText);
58 | var result = PropToInpcHelper.GenerateInpcPropertySet(propertyLineData, false, false, true, "Set");
59 |
60 | // Assert.
61 | var resultLines = result.Split(Environment.NewLine);
62 | Assert.IsTrue(resultLines.Length == 2);
63 | Assert.AreEqual(expectedPropertyLine, resultLines.FirstOrDefault());
64 | Assert.AreEqual(expectedFieldLine, resultLines.LastOrDefault());
65 | }
66 | }
--------------------------------------------------------------------------------
/BrightXaml.ExtensibilityTests/Utilities/ShowDefinitionHelperTests.cs:
--------------------------------------------------------------------------------
1 | namespace BrightXaml.Extensibility.Utilities.Tests;
2 |
3 | [TestClass()]
4 | public class ShowDefinitionHelperTests
5 | {
6 | [TestMethod()]
7 | [DataRow(" filePaths = new List
37 | {
38 | "C:/test/my/project/test.cs",
39 | "C:/test/my/project/test2.cs",
40 | "C:/test/my/project/test.cs",
41 | "C:/test/my/project2/test2.cs",
42 | "C:/test/my2/project/test.cs"
43 | };
44 |
45 | List expected = new List
46 | {
47 | "my/project/test.cs",
48 | "my/project/test2.cs",
49 | "my/project/test.cs",
50 | "my/project2/test2.cs",
51 | "my2/project/test.cs"
52 | };
53 |
54 | // Act
55 | List result = ViewModelHelper.RemoveCommonPath(filePaths);
56 |
57 | // Assert
58 | CollectionAssert.AreEqual(expected, result);
59 | }
60 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Luis Henrique Goll
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 |
--------------------------------------------------------------------------------