├── .github
├── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── issue.md
├── pull_request_template.md
├── stale.yml
└── workflows
│ └── quality.yml
├── .gitignore
├── LICENSE
├── build.ps1
├── docs
├── Debug-LaunchBuilder-Paths.png
├── GeneratingKeys.md
├── ScreenShot-MenuItems.png
├── ScreenShot-ToolsOptions-v1.9.png
├── ScreenShot-ToolsOptions.png
├── Screenshot-Cmd-GenKey.png
├── Screenshot-Cmd-ifconfig.png
├── Screenshot-Output-FailedToConnect.png
├── Screenshot-VS-ExtensionMngr.png
├── Screenshot-VS-Output-Logging.png
├── Snips.md
├── TuxDebug.png
└── sample.launch.json.md
├── marketplace.md
├── readme.md
├── release-notes.md
├── sandbox
├── .editorconfig
├── ConsoleNet6.sln
├── ConsoleNet6
│ ├── ConsoleNet6.csproj
│ └── Program.cs
├── GuiNet6.sln
├── GuiNet6
│ ├── App.axaml
│ ├── App.axaml.cs
│ ├── Assets
│ │ └── avalonia-logo.ico
│ ├── GuiNet6.csproj
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── RegionNames.cs
│ ├── ViewModels
│ │ ├── DashboardViewModel.cs
│ │ ├── SettingsViewModel.cs
│ │ ├── ShellWindowViewModel.cs
│ │ ├── SidebarViewModel.cs
│ │ └── ViewModelBase.cs
│ ├── Views
│ │ ├── DashboardView.axaml
│ │ ├── DashboardView.axaml.cs
│ │ ├── SettingsView.axaml
│ │ ├── SettingsView.axaml.cs
│ │ ├── ShellWindow.axaml
│ │ ├── ShellWindow.axaml.cs
│ │ ├── SidebarView.axaml
│ │ └── SidebarView.axaml.cs
│ └── nuget.config
├── LaunchJson-Notes.md
└── launch.json
└── src
├── .editorconfig
├── CodeMaid.config
├── LinuxDebugger.sln
├── StyleCop.Analyzers.ruleset
├── VsLinuxDebugger
├── Commands.Impl.cs
├── Commands.cs
├── Core
│ ├── BuildOptions.cs
│ ├── Constants.cs
│ ├── LaunchBuilder.cs
│ ├── LinuxPath.cs
│ ├── Remote
│ │ ├── Configuration.cs
│ │ ├── Launch.cs
│ │ └── PipeTransport.cs
│ ├── RemoteDebugger.cs
│ ├── SshConnectionInfo.cs
│ ├── SshTool.cs
│ └── UserOptions.cs
├── DebuggerPackage.cs
├── DebuggerPackage.vsct
├── Extensions
│ ├── JsonExtension.cs
│ ├── ProcessExtension.cs
│ └── SolutionExtension.cs
├── Helpers.cs
├── LICENSE.txt
├── Logger.cs
├── OptionsPages
│ ├── OptionsPage.DotNet.cs
│ ├── OptionsPage.Local.cs
│ └── OptionsPage.Ssh.cs
├── Properties
│ └── AssemblyInfo.cs
├── Resources
│ ├── BuildIcon.png
│ ├── BuildSelection_16x.png
│ ├── DebugOnly_16x.png
│ ├── DebuggerPackage.ico
│ ├── DeployAndDebug_16x.png
│ ├── DeployOnly_16x.png
│ ├── Process_16x.png
│ ├── Settings_16x.png
│ ├── ShowLog_16x.png
│ ├── SshDebugCommand.png
│ └── TuxDebug.png
├── Services
│ ├── ConnectionService.cs
│ └── IConnectionService.cs
├── VsLinuxDebugger.csproj
└── source.extension.vsixmanifest
└── stylecop.json
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature Request
3 | about: Suggest an idea for this project
4 | title: "[Enhancement] "
5 | labels: suggestion, not-reviewed
6 | assignees: ''
7 |
8 | ---
9 |
10 | ## Description
11 | A clear and concise description of what you want to happen. Why is this feature important?
12 |
13 | ## Context
14 | Add any other context, examples, or screenshots about the feature request here.
15 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/issue.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Issue
3 | about: Create a report to help us improve
4 | title: "[Issue] "
5 | labels: not-reviewed
6 | assignees: ''
7 |
8 | ---
9 |
10 | ## Description
11 | A clear and concise description of what the bug is.
12 |
13 | ## Severity (1-5)
14 | 1=Low (_annoyance_), 5=High (_crashes visual studio_)
15 |
16 | ## Steps To Reproduce
17 | Steps to reproduce the behavior:
18 | 1. Go to '...'
19 | 2. Click on '...'
20 | 3. Scroll down to '...'
21 | 4. Error message '...'
22 |
23 | ## Expected Behavior
24 | A clear and concise description of what you expected to happen.
25 |
26 | ## Screenshots
27 | If applicable, add screenshots to help explain your problem.
28 |
29 | ## Linux Distribution:
30 | * **OS:** [e.g. Ubuntu, Debian, ...]
31 | * **Version:** [e.g. 20.04 LTS, 22.04 LTS, ...]
32 |
33 | ## Additional Context
34 | Add any other context about the problem here.
35 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## Description of Change
2 |
3 | Describe the changes here
4 |
5 | ## Related Work Items
6 |
7 | * _List related #DiscussionId or #PR here or remove this section_
8 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 11
3 |
4 | # Number of days of inactivity before a stale issue is closed
5 | daysUntilClose: 5
6 |
7 | # Issues with these labels will never be considered stale
8 | exemptLabels:
9 | - pinned
10 | - security
11 | - enhancement
12 | - vNext
13 | # Label to use when marking an issue as stale
14 | staleLabel: stale
15 | # Comment to post when marking an issue as stale. Set to `false` to disable
16 | markComment: >
17 | This issue has been automatically marked as stale because it has not had
18 | recent activity. It will be closed if no further activity occurs. Thank you
19 | for your contributions.
20 | # Comment to post when closing a stale issue. Set to `false` to disable
21 | closeComment: false
22 |
--------------------------------------------------------------------------------
/.github/workflows/quality.yml:
--------------------------------------------------------------------------------
1 | # https://github.com/actions/stale
2 | # https://docs.github.com/en/actions/managing-issues-and-pull-requests/closing-inactive-issues
3 | name: Close Stale Issues
4 | on:
5 | schedule:
6 | - cron: "30 * * * *"
7 | # - cron: "30 1 * * *"
8 | # - cron: "30 1,13 * * *"
9 |
10 | jobs:
11 | close-issues:
12 | runs-on: ubuntu-latest
13 | permissions:
14 | issues: write
15 | pull-requests: write
16 | steps:
17 | - uses: actions/stale@v5
18 | with:
19 | days-before-issue-stale: 11
20 | days-before-issue-close: 5
21 | stale-issue-label: "stale"
22 | # stale-issue-message: "This issue is stale because it has been open for 12 days with no activity."
23 | # close-issue-message: "This issue was closed because it has been inactive for 5 days since being marked as stale."
24 | stale-issue-message: >
25 | This issue has been automatically marked as stale because it has not had
26 | recent activity. It will be closed if no further activity occurs. Thank you
27 | for your contributions.
28 | days-before-pr-stale: -1
29 | days-before-pr-close: -1
30 | repo-token: ${{ secrets.GITHUB_TOKEN }}
31 | exempt-issue-labels: pinned, security, enhancement
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Copyright 2023 Xeno Innovations, Inc
2 |
3 | # Generic Visual Studio files
4 | *.bak
5 | *.csproj.user
6 | *.suo
7 | *.vshost.exe
8 | *.vshost.exe.manifest
9 | *.exe.config
10 | *.exp
11 | *.lib
12 | *.pdb
13 | *.pfx
14 | *.user
15 | *.vbw
16 | *.scc
17 | *.oca
18 | *.userprefs
19 | *.userosscache
20 | *.sln.docstates
21 | *.autorecover
22 | *.coverage
23 |
24 | # SQLite datbases
25 | *.db3
26 |
27 | # Xamarin.Android Resource.Designer.cs files
28 | **/*.Android/**/[Rr]esource.[Dd]esigner.cs
29 | **/*.Droid/**/[Rr]esource.[Dd]esigner.cs
30 | **/Android/**/[Rr]esource.[Dd]esigner.cs
31 | **/Droid/**/[Rr]esource.[Dd]esigner.cs
32 |
33 | # Build results
34 | [Oo]utput/
35 | [Dd]ebug/
36 | [Dd]ebugPublic/
37 | [Rr]elease/
38 | x64/
39 | x86/
40 | build/
41 | bld/
42 | [Bb]in/
43 | [Oo]bj/
44 | [Ll]og/
45 | .vs/
46 |
47 | # NuGet Packages
48 | *.nupkg
49 | ## The packages folder can be ignored because of Package Restore
50 | **/packages/*
51 | ## Except build/, which is used as an MSBuild target.
52 | !**/packages/build/
53 | ## Uncomment if necessary however generally it will be regenerated when needed
54 | !**/packages/repositories.config
55 | ## NuGet v3's project.json files produces more ignorable files
56 | *.nuget.props
57 | *.nuget.targets
58 |
59 | # NUnit
60 | *.VisualState.xml
61 | TestResult.xml
62 |
63 | # Installer Folder
64 | /installer/*.exe
65 | !/installer/autorun.exe
66 |
67 | # Lib folder
68 | /lib/*
69 | !/lib/readme.txt
70 | !/lib/readme.md
71 |
72 | # Output
73 | !/[Oo]utput/readme.txt
74 | !/[Oo]utput/readme.md
75 |
76 | ## USER DEFINED
77 | /[Dd]ocs/*.csv
78 | /[Dd]ocs/backup
79 | /[Tt]ests
80 | /[Tt]ools
81 | /src/VsLinuxDebugger/plink.exe
82 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Xeno Innovations, Inc.
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 |
--------------------------------------------------------------------------------
/build.ps1:
--------------------------------------------------------------------------------
1 | # Linux Debugger
2 | # Build script for generating release package
3 |
4 | if (Test-Path -Path "bin")
5 | {
6 | Remove-Item bin\* -Recurse -Force
7 | }
8 |
9 | $VCToolsInstallDir = . "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -Latest -requires Microsoft.Component.MSBuild -property InstallationPath
10 | Write-Host "VCToolsInstallDir: $VCToolsInstallDir"
11 |
12 | $msBuildPath = "$VCToolsInstallDir\MSBuild\Current\Bin\msbuild.exe"
13 | Write-Host "msBuildPath: $msBuildPath"
14 |
15 | Write-Host "Cleaning..."
16 | & $msBuildPath -t:Clean src/LinuxDebugger.sln
17 |
18 | Write-Host "Building..."
19 | & $msBuildPath /restore `
20 | src/LinuxDebugger.sln `
21 | /p:Configuration=Release
22 |
23 | # TODO:
24 | ### https://github.com/madskristensen/VsctIntellisense/blob/master/appveyor.yml
25 | # (new-object Net.WebClient).DownloadString("https://raw.github.com/madskristensen/ExtensionScripts/master/AppVeyor/vsix.ps1") | iex
26 | # Vsix-IncrementVsixVersion .\src\VsctCompletion2019\source.extension.vsixmanifest | Vsix-UpdateBuildVersion
27 | # Vsix-IncrementVsixVersion .\src\VsctCompletion2022\source.extension.vsixmanifest
28 | # Vsix-TokenReplacement src\VsctCompletion2019\source.extension.cs 'Version = "([0-9\\.]+)"' 'Version = "{version}"'
29 | # Vsix-TokenReplacement src\VsctCompletion2022\source.extension.cs 'Version = "([0-9\\.]+)"' 'Version = "{version}"'
30 | # nuget restore -Verbosity quiet
31 | # msbuild /p:configuration=Release /p:DeployExtension=false /p:ZipPackageCompressionLevel=normal /v:m
32 |
33 | ## TODO: Build project and set version to 1.2.3.4
34 | # dotnet build -p:Version=1.2.3.4
35 |
--------------------------------------------------------------------------------
/docs/Debug-LaunchBuilder-Paths.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/docs/Debug-LaunchBuilder-Paths.png
--------------------------------------------------------------------------------
/docs/GeneratingKeys.md:
--------------------------------------------------------------------------------
1 | # Generating an SSH Key (Windows 10)
2 |
3 | ## Steps
4 |
5 | The following steps are options if you wish to use an SSH Private Key. These steps were written for Windows 10, however, on Linux the steps are similar.
6 |
7 | 1. Open PowerShell:
8 | 2. **Generate key** (_with old PEM format_)
9 | 1. `ssh-keygen -m PEM -t rsa -b 4096`
10 | 2. In the future, we'll be able to use `ssh-keygen`.. just not yet.
11 | 3. Set output name (_default is okay for basic setups_)
12 | 4. Input a passphrase for the key _(OPTIONAL)_
13 | 5. Windows will now generate your RSA public/private key pair.
14 | 1. Default location: `%UserProfile%\.ssh` (WINOWS)
15 | 2. The public key will be stored as `id_rsa.pub` in the directory
16 | 6. **Upload the public key** to your remote machine
17 | 1. Navigate to folder, `~/.ssh/` on Linux device
18 | 2. If `~/.ssh/authorized_keys` exists, append the contents of `id_rsa.pub` to the next line.
19 | 3. If it does not exist, simply upload `id_rsa.pub` and rename it to, `authorized_keys`
20 | 7. Test your connection using SSH on Windows via `ssh user@hostname`
21 |
22 | ## Convert Key to PEM format
23 |
24 | SSH.Net still has some issues with ssh-rsa. To overcome this, you'll need to convert keyfile to PEM.
25 |
26 | ```powershell
27 | ssh-keygen -p -P "OLD_PASSPHRASE" -N "NEW_PASSPHRASE" -m pem -f "%UserProfile%\.ssh\id_rsa"
28 | ```
29 |
30 | ## Sample output
31 |
32 | ```cmd
33 | C:\workXXXXXX> ssh-keygen -m PEM -t rsa -b 4096
34 | Generating public/private rsa key pair.
35 | Enter file in which to save the key (C:\Users\XXXXX/.ssh/id_rsa):
36 | Enter passphrase (empty for no passphrase):
37 | Enter same passphrase again:
38 | Your identification has been saved in C:\Users\XXXXXX/.ssh/id_rsa.
39 | Your public key has been saved in C:\Users\XXXXX/.ssh/id_rsa.pub.
40 | The key fingerprint is:
41 | SHA256:ETNWXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXcms YYYYYYY\XXXXX@ZZZZZZZZ
42 | The key's randomart image is:
43 | +---[RSA 3072]----+
44 | | oO=o |
45 | | XXXXXXXXXXXX |
46 | | XXXXXXXXXXXX |
47 | | XXXXXXXXXXXX |
48 | | XXXXXXXXXXXX |
49 | |+XXXXXXXXXXXX |
50 | |.XXXXXXXXXXXX |
51 | |oXXXXXXXXXXXX |
52 | |o+.. |
53 | +----[SHA256]-----+
54 | ```
55 |
56 | ## Reference
57 |
58 | * [https://www.onmsft.com/how-to/how-to-generate-an-ssh-key-in-windows-10]
59 |
--------------------------------------------------------------------------------
/docs/ScreenShot-MenuItems.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/docs/ScreenShot-MenuItems.png
--------------------------------------------------------------------------------
/docs/ScreenShot-ToolsOptions-v1.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/docs/ScreenShot-ToolsOptions-v1.9.png
--------------------------------------------------------------------------------
/docs/ScreenShot-ToolsOptions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/docs/ScreenShot-ToolsOptions.png
--------------------------------------------------------------------------------
/docs/Screenshot-Cmd-GenKey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/docs/Screenshot-Cmd-GenKey.png
--------------------------------------------------------------------------------
/docs/Screenshot-Cmd-ifconfig.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/docs/Screenshot-Cmd-ifconfig.png
--------------------------------------------------------------------------------
/docs/Screenshot-Output-FailedToConnect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/docs/Screenshot-Output-FailedToConnect.png
--------------------------------------------------------------------------------
/docs/Screenshot-VS-ExtensionMngr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/docs/Screenshot-VS-ExtensionMngr.png
--------------------------------------------------------------------------------
/docs/Screenshot-VS-Output-Logging.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/docs/Screenshot-VS-Output-Logging.png
--------------------------------------------------------------------------------
/docs/Snips.md:
--------------------------------------------------------------------------------
1 | # Code Snips
2 |
3 | ## RemoteDebugger.cs
4 |
5 | ```cs
6 | /*
7 | * Borrowed from VSMonoDebugger
8 | *
9 | public async Task BuildStartupProjectAsync()
10 | {
11 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
12 |
13 | var failedBuilds = BuildStartupProject();
14 | if (failedBuilds > 0)
15 | {
16 | Window window = _dte.Windows.Item("{34E76E81-EE4A-11D0-AE2E-00A0C90FFFC3}");//EnvDTE.Constants.vsWindowKindOutput
17 | OutputWindow outputWindow = (OutputWindow)window.Object;
18 | outputWindow.ActivePane.Activate();
19 | outputWindow.ActivePane.OutputString($"{failedBuilds} project(s) failed to build. See error and output window!");
20 |
21 | //// _errorListProvider.Show();
22 |
23 | throw new Exception($"{failedBuilds} project(s) failed to build. See error and output window!");
24 | }
25 | }
26 |
27 | private int BuildStartupProject()
28 | {
29 | ThreadHelper.ThrowIfNotOnUIThread();
30 |
31 | //// var dte = (DTE)Package.GetGlobalService(typeof(DTE));
32 | var sb = (SolutionBuild2)_dte.Solution.SolutionBuild;
33 |
34 | try
35 | {
36 | var startProject = GetStartupProject();
37 | var activeConfiguration = _dte.Solution.SolutionBuild.ActiveConfiguration as SolutionConfiguration2;
38 | var activeConfigurationName = activeConfiguration.Name;
39 | var activeConfigurationPlatform = activeConfiguration.PlatformName;
40 | var startProjectName = startProject.FullName;
41 |
42 | sb.BuildProject($"{activeConfigurationName}|{activeConfigurationPlatform}", startProject.FullName, true);
43 | }
44 | catch (Exception ex)
45 | {
46 | // Build complete solution (fallback solution)
47 | return BuildSolution();
48 | }
49 |
50 | return sb.LastBuildInfo;
51 | }
52 |
53 | private int BuildSolution()
54 | {
55 | ThreadHelper.ThrowIfNotOnUIThread();
56 |
57 | var sb = (SolutionBuild2)_dte.Solution.SolutionBuild;
58 | sb.Build(true);
59 | return sb.LastBuildInfo;
60 | }
61 | */
62 | ```
--------------------------------------------------------------------------------
/docs/TuxDebug.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/docs/TuxDebug.png
--------------------------------------------------------------------------------
/docs/sample.launch.json.md:
--------------------------------------------------------------------------------
1 | # Samples
2 |
3 | Documentation assistance for `launch.json` file.
4 |
5 | ## Sample from VSMonoDebugger
6 |
7 | ```json
8 | {
9 | "version": "0.2.0",
10 | "adapter": "$(PLINK_EXE_PATH)",
11 | "adapterArgs": "$(PLINK_SSH_CONNECTION_ARGS) -batch -T vsdbg --interpreter=vscode",
12 | "configurations": [
13 | {
14 | "name": ".NET Core Launch (console)",
15 | "type": "coreclr",
16 | "request": "launch",
17 | "preLaunchTask": "build",
18 | "program": "dotnet",
19 | "args": [
20 | "$(TARGET_EXE_FILENAME)",
21 | "$(START_ARGUMENTS)"
22 | ],
23 | "cwd": "$(DEPLOYMENT_PATH)",
24 | "console": "internalConsole",
25 | "stopAtEntry": true
26 | }
27 | ]
28 | }
29 | ```
--------------------------------------------------------------------------------
/marketplace.md:
--------------------------------------------------------------------------------
1 | # [VS .NET Linux Debugger](https://github.com/SuessLabs/VsLinuxDebug)
2 |
3 |
4 |
5 | Remotely deploy and debug your .NET C# apps via SSH to Linux using Visual Studio 2022.
6 |
7 | Get it on the [VS MarketPlace](https://marketplace.visualstudio.com/items?itemName=SuessLabs.VSLinuxDebugger)!
8 |
9 | Visual Studio's "attach to process via SSH" is cute, but it lacks deployment and automatic attaching. This project allows you to do just that on your Linux VM or Raspberry Pi over the network!
10 |
11 | Suess Labs consulting is sponsored by _Xeno Innovations, Inc._
12 |
13 | ## Overview
14 |
15 | Now developers can build, deploy and debug projects on their remote Linux (Ubuntu, Raspberry PI, etc) devices! Customize your SSH connection to use either a _password_ or a _private key_.
16 |
17 | If you enjoy using the extension, please give it a ★★★★★ rating on the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=SuessLabs.VSLinuxDebugger).
18 |
19 | ### Usage
20 |
21 | 
22 |
23 | * Build and upload to remote devices
24 | * Remote debugging*
25 | * _This is still in the experimental stages. Please use VS' Attach to Process if you have issues_
26 | * VS Linux Debugger will automatically detect and install `vsdbg` for you!
27 |
28 | For GUI app debugging, you can use the _Build and Deploy_ feature, however, you must manually _Attach to Process_ via SSH using Visual Studio at this time.
29 |
30 | ### Getting Started
31 |
32 | **Linux**, we'll need **SSH** and **cURL** for access and downloading any missing tools:
33 |
34 | ```bash
35 | sudo apt install openssh-server
36 | sudo apt install curl
37 | ```
38 |
39 | **Windows**:
40 |
41 | 1. Open Visual Studio (VS) > Tools > Options > **Linux Debugger**
42 | 2. **Input:** Remote Host IP address
43 | 3. **Input:** Remote's User Name and Password
44 | 4. VS > Extensions > Linux Debugger > **Build, Deploy, Debug**
45 |
46 | 
47 |
48 | ### Manually Attaching (for GUI apps)
49 |
50 | For GUI projects, you can use **Build and Deploy** and then manually attach to the process via SSH by using Visual Studio's built-in tool
51 |
52 | 1. Deploy to remote machine via
53 | 1. Extensions > Linux Debugger > **"Build and Deploy"**
54 | 2. Run GUI app on remote machine
55 | 1. `dotnet MyGuiApp.dll`
56 | 3. Debug > **"Attach to Process.."**
57 | 4. Connection Type: **SSH**
58 | 5. Connection Target: **(Remote machine's IP)**
59 | 6. (Select process)
60 | 7. Click, **Attach**
61 | 8. Check, **"Managed (.NET Core for Unix)"**
62 | 9. Click, **OK**
63 |
64 | This will save you 1.5 minutes on every build of manual uploading and updating rights via `chown -R`.
65 |
66 | ## Developers Wanted
67 |
68 | Contributors and Q/A are welcomed!
69 |
70 | To contribute, please pick off an item from the project or issue page. We'd love to hear your enhancement ideas as well.
71 |
72 | ## Revision History
73 |
74 | This document contains the release information for the project.
75 |
76 | ### 2.1
77 |
78 | * Update: Code cleanup
79 | * Update: Moved RSA SHA256 classes into their own files for separation of responsibility
80 | * Update: Enabled `SSH` for remote debugging by default instead of using `PLink` (_still experimental_)
81 | * Removed: Out of date files (.NET 5 sample applications, UpgradeLog.htm, LinuxDebugger-2019.sln)
82 |
83 | ### 2.0.3.2 (Preview)
84 |
85 | Contributor: [ZeuSWarE GmbH](https://github.com/zeusware) - PR: #62
86 |
87 | * Added: The RSA-SHA2-256 for SSH.Net for interpreting private keys made by `ssh-keygen -m PEM -t rsa -b 4096` allowing connecting directly.
88 | * Ubuntu 22.04 LTS OpenSSH package does not support the ssh algo: `ssh-rsa` you intended being generating via `ssh-keygen -m PEM -t rsa -b 4096` by default anymore
89 | * Added: Using a ppk via plink is obsolete if u gen the key via PowerShell `ssh-keygen`, so u have now the option to use the system integrated ssh.exe (PS 6 integrate that by default)
90 |
91 | ### 2.0.3
92 |
93 | Contributor: [Claas Hilbrecht](https://github.com/clahil-linum) - PR: #59
94 |
95 | * Fixed: As per #32 add `vsdbg` constant to debugger full path.
96 | * Update: Options descriptions are now more clear about the .NET executable. It's not a path but the dotnet executable.
97 |
98 | ### 2.0.2
99 |
100 | * Fixed: As per #53, cleaned up exponential Build status messages.
101 | * Added: Submenu item to "Options" to quickly access to Linux Debugger's Options dialog
102 | * Update: Refactored options mechanism in prep for custom profiles.
103 |
104 | ### 2.0.1 (Prev-1)
105 |
106 | * Added: Option to set output window focus to Linux Debugger, default=`false`. (PR #46)
107 | * `Tools > Options > Linux Debugger > "Switch to LinuxDbg Output on Build"`
108 | * Added: Async BASH and SFTP operations to not lock up Visual Studio (PR #40)
109 | * Added: "Experimental" tag to menu items for Alpha/Beta items. (PR# 41)
110 | * `Build, Deploy, and Debug`
111 | * `Build, Deploy, and Launch` - _Temp disabled in Preview-1_
112 | * Added: Deploy and Launch (**ALPHA Feature**) (PR #36)
113 | * Added: BashSudo (PR #36)
114 | * Update: Default VSDBG path to match Visual Studio 2022's deployed path (`~/.vs-debugger/vs2022/`). (PR #36, #47)
115 | * Update: Sample's NuGet package for Prism.Avalonia
116 | * Fixed: Typo, "Build was notsuccessful" (PR #43) `User Contribution` :rocket:
117 | * Fixed: Auto-install cURL (PR #36)
118 | * Fixed: Reduced duplicate output messages (PR #40)
119 | * Removed: Publish (PR #36)
120 | * Removed: Redundant sample project
121 |
122 | ### 1.9.0
123 |
124 | * Added: Now comes with PLink embedded. You can still manually set this if you prefer.
125 | * Removed: Option to enable/disable PLink
126 |
127 | ### 1.8.1
128 |
129 | * Fixed: Remote folder pre-cleanup.
130 | * Added: Upload files async to reduce locking of Visual Studio
131 | * Added: Removal of `launch.json` if it previously existed
132 | * Added: More output logging.
133 | * Update: Enhanced Output
134 | * Updated: Output Window drop-down title to "Linux Debugger" from "Remote Debugger"
135 |
136 | ### 1.8.0
137 |
138 | * Added: Logging to Output window under "_Remote Debugging_" dropdown
139 | * Update: Do not include `launch.json` in the uploaded `.tar.gz` package
140 | * Update: Readme notes
141 | * Update: Code cleanup
142 |
143 | ### 1.7.0
144 |
145 | * Fixed: Remote debugging for PLink
146 | * Fixed: VSDBG default path
147 | * Update: DeleteLaunchJsonAfterBuild set to false by default
148 | * Update: Separated LaunchJson class into separate objects
149 | * Updated: SSH parameters to include `-o` (option) for `StrictHostKeyChecking = NO`.
150 | * Added: Additional internal logging
151 | * Added: documentation to Launch and Configure classes
152 |
153 | ### 1.6.0
154 |
155 | * Added: Ability to use SSH KeyFile with or without a passphrase.
156 | * Added: Directions for creating and configuring local and remote devices
157 | * Added: Additional directions in the Docs folder
158 |
159 | ### 1.2.0
160 |
161 | * Removed: Publish option
162 | * Updated Options page defaults
163 | * Update: Remote output folder is now the assembly name
164 | * Update: Remote output folder only clears intended target sub-folder
165 | * Added: Remote Debugging (_still in preview stages.._)
166 |
167 | ### 1.1.1
168 |
169 | * Updated: Branding name
170 | * Removed: Temp disabled remote debugger
171 |
172 | ### 1.0.1
173 |
174 | * Update: Remote output folder now creates subfolders with the same name as your project.
175 | * Updated: project icon
176 |
177 | ### 1.0.0
178 |
179 | * Initial release
180 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # [VS .NET Linux Debugger](https://github.com/SuessLabs/VsLinuxDebug)
2 |
3 |
4 |
5 | Remotely deploy and debug your .NET C# apps via SSH to Linux using Visual Studio 2022.
6 |
7 | Get it on the [VS MarketPlace](https://marketplace.visualstudio.com/items?itemName=SuessLabs.VSLinuxDebugger)!
8 |
9 | Visual Studio's "attach to process via SSH" is cute, but it lacks deployment and automatic attaching. This project allows you to do just that on your Linux VM or Raspberry Pi over the network!
10 |
11 | Suess Labs consulting is sponsored by _Xeno Innovations, Inc._
12 |
13 | ## Overview
14 |
15 | Now developers can build, deploy and debug projects on their remote Linux (Ubuntu, Raspberry PI, etc) devices! Customize your SSH connection to use either a _password_ or a _private key_.
16 |
17 | If you enjoy using the extension, please give it a ★★★★★ rating on the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=SuessLabs.VSLinuxDebugger).
18 |
19 | ### Supported Remote OS
20 |
21 | The following Linux distrobutions have been validated and are supported.
22 |
23 | * Ubuntu (20.04 LTS, 22.04 LTS)
24 | * Raspberry PI OS
25 |
26 | ### Usage
27 |
28 | 
29 |
30 | * Build and upload to remote devices
31 | * Remote debugging*
32 | * _This is still in the experimental stages. Please use VS' Attach to Process if you have issues_
33 | * VS Linux Debugger will automatically detect and install `vsdbg` for you!
34 |
35 | For GUI app debugging, you can use the _Build and Deploy_ feature, however, you must manually _Attach to Process_ via SSH using Visual Studio at this time.
36 |
37 | ### Getting Started
38 |
39 | **Linux**, we'll need **SSH** and **cURL** for access and downloading any missing tools:
40 |
41 | ```bash
42 | sudo apt install openssh-server
43 | sudo apt install curl
44 | ```
45 |
46 | **Windows**:
47 |
48 | 1. Open Visual Studio (VS) > Tools > Options > **Linux Debugger**
49 | 2. **Input:** Remote Host IP address
50 | 3. **Input:** Remote's User Name and Password
51 | 4. VS > Extensions > Linux Debugger > **Build, Deploy, Debug**
52 |
53 | 
54 |
55 | ### Manually Attaching (for GUI apps)
56 |
57 | For GUI projects, you can use **Build and Deploy** and then manually attach to the process via SSH by using Visual Studio's built-in tool
58 |
59 | 1. Deploy to remote machine via
60 | 1. Extensions > Linux Debugger > **"Build and Deploy"**
61 | 2. Run GUI app on remote machine
62 | 1. `dotnet MyGuiApp.dll`
63 | 3. Debug > **"Attach to Process.."**
64 | 4. Connection Type: **SSH**
65 | 5. Connection Target: **(Remote machine's IP)**
66 | 6. (Select process)
67 | 7. Click, **Attach**
68 | 8. Check, **"Managed (.NET Core for Unix)"**
69 | 9. Click, **OK**
70 |
71 | This will save you 1.5 minutes on every build of manual uploading and updating rights via `chown -R`.
72 |
73 | ### Manually Attaching (for Command line apps)
74 |
75 | For CLI projects, you can use **Build and Deploy** and then manually attach to the process via SSH by using Visual Studio's built-in tool (similar to above).
76 |
77 | You may have to manually interrupt your app via `Console.ReadLine();` high-up in your entry-point (i.e. `main()`).
78 |
79 | 1. Deploy to remote machine via
80 | 1. Extensions > Linux Debugger > **"Build and Deploy"**
81 | 2. Run your CLI app on remote machine
82 | 1. `dotnet MyCliApp.dll`
83 | 3. Debug > **"Attach to Process.."**
84 | 1. Connection Type: **SSH**
85 | 2. Connection Target: **(Remote machine's IP)**
86 | 3. (Select process)
87 | 4. Click, **Attach**
88 | 5. Check, **"Managed (.NET Core for Unix)"**
89 | 6. Click, **OK**
90 | 4. Continue your application, if using a manual interrupt (i.e. `Console.ReadLine();`)
91 |
92 | This will save you 1.5 minutes on every build of manual uploading and updating rights via `chown -R`.
93 |
94 | ## How To Generate Private Key (optional)
95 |
96 | The following steps are options if you wish to use an SSH Private Key. These steps were written for Windows 10, however, on Linux the steps are similar.
97 |
98 | 1. Open PowerShell:
99 | 2. **Generate key** (_with old PEM format_)
100 | 1. `ssh-keygen -m PEM -t rsa -b 4096`
101 | 2. In the future, we'll be able to use `ssh-keygen`.. just not yet.
102 | 3. Set output name (_default is okay for basic setups_)
103 | 4. Input a passphrase for the key _(OPTIONAL)_
104 | 5. Windows will now generate your RSA public/private key pair.
105 | 1. Default location: `%UserProfile%\.ssh` (WINOWS)
106 | 2. The public key will be stored as `id_rsa.pub` in the directory
107 | 6. **Upload the public key** to your remote machine
108 | 1. Navigate to folder, `~/.ssh/` on Linux device
109 | 2. If `~/.ssh/authorized_keys` exists, append the contents of `id_rsa.pub` to the next line.
110 | 3. If it does not exist, simply upload `id_rsa.pub` and rename it to, `authorized_keys`
111 | 7. DONE!
112 |
113 | ## Used By
114 |
115 | * [SuessLabs](https://suesslabs.com) and [Xeno Innovations](https://xenoinc.com)
116 | * [Wilderness Labs](https://github.com/WildernessLabs)
117 | * [Omnicell, Inc.](https://omnicell.com)
118 |
119 | _Want your name added? Reach out to us_
120 |
121 | ## Future Features
122 |
123 | * [ ] **Debugging:** Launching of GUI apps for remote debugging
124 | * [ ] **Debugging:** PLink using PPK instead of manual password
125 | * [ ] **Options Window:** Multiple remote profile management
126 | * [ ] **Options Window:** SSH PPK generator assistant tool
127 |
128 | ## Developers Wanted
129 |
130 | Contributors and Q/A are welcomed!
131 |
132 | To contribute, please pick off an item from the project or issue page. We'd love to hear your enhancement ideas as well.
133 |
134 | ## References
135 |
136 | * [PuTTY PLink](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html)
137 | * [Extension Docs](https://docs.microsoft.com/en-us/visualstudio/extensibility/creating-a-settings-category?view=vs-2022)
138 | * [Extension Sample](https://github.com/microsoft/VSSDK-Extensibility-Samples/tree/master/Options)
139 | * [Offroad Debugging](https://github.com/Microsoft/MIEngine/wiki/Offroad-Debugging-of-.NET-Core-on-Linux---OSX-from-Visual-Studio)
140 |
141 |
142 | _Copyright 2024 Xeno Innovations, Inc._
143 |
--------------------------------------------------------------------------------
/release-notes.md:
--------------------------------------------------------------------------------
1 | # Release Notes
2 |
3 | ## Revision History
4 |
5 | This document contains the release information for the project.
6 |
7 | ### 2.3.0 - 2024-12-02
8 |
9 | Early patch release before Christmas break. Effort moving forward will be for the upcoming 3.0 release.
10 |
11 | * Fixed: Fix connection error when using SSH private key (PR #95, Issue #82)
12 | * Added: Setting that allows to change the remote X11 display number (PR #94)
13 | * Update: Bump System.Text.Json from 6.0.2 to 6.0.10 (PR #92)
14 | * Update: Documentation (PR #91)
15 |
16 | ### 2.2.0 - 2024-03-08
17 |
18 | * Fixed: Support VS v17.9 (#79)
19 | * Update: SSH.NET to v2023.0.1
20 |
21 | ### 2.1.1
22 |
23 | * Update: Version 2.1 and Code Housekeeping by @DamianSuess in #65
24 | * Update: GUI .NET 6 Sample NuGet packages by @DamianSuess in #68
25 | * Update: PR Template by @DamianSuess in #69
26 | * Update: Release build generator script by @DamianSuess in #70
27 | * Update: Quality control by @DamianSuess in #74
28 | * Update: Upgrade github actions by @DamianSuess in #75
29 |
30 | ### 2.1
31 |
32 | * Update: Code cleanup
33 | * Update: Moved RSA SHA256 classes into their own files for separation of responsibility
34 | * Update: Enabled `SSH` for remote debugging by default instead of using `PLink` (_still experimental_)
35 | * Removed: Out of date files (.NET 5 sample applications, UpgradeLog.htm, LinuxDebugger-2019.sln)
36 |
37 | ### 2.0.3.2 (Preview)
38 |
39 | Contributor: [ZeuSWarE GmbH](https://github.com/zeusware) - PR: #62
40 |
41 | * Added: The RSA-SHA2-256 for SSH.Net for interpreting private keys made by `ssh-keygen -m PEM -t rsa -b 4096` allowing connecting directly.
42 | * Ubuntu 22.04 LTS OpenSSH package does not support the ssh algo: `ssh-rsa` you intended being generating via `ssh-keygen -m PEM -t rsa -b 4096` by default anymore
43 | * Added: Using a ppk via plink is obsolete if u gen the key via PowerShell `ssh-keygen`, so u have now the option to use the system integrated ssh.exe (PS 6 integrate that by default)
44 |
45 | ### 2.0.3
46 |
47 | Contributor: [Claas Hilbrecht](https://github.com/clahil-linum) - PR: #59
48 |
49 | * Fixed: As per #32 add `vsdbg` constant to debugger full path.
50 | * Update: Options descriptions are now more clear about the .NET executable. It's not a path but the dotnet executable.
51 |
52 | ### 2.0.2
53 |
54 | * Fixed: As per #53, cleaned up exponential Build status messages.
55 | * Added: Submenu item to "Options" to quickly access to Linux Debugger's Options dialog
56 | * Update: Refactored options mechanism in prep for custom profiles.
57 |
58 | ### 2.0.1 (Prev-1)
59 |
60 | * Added: Option to set output window focus to Linux Debugger, default=`false`. (PR #46)
61 | * `Tools > Options > Linux Debugger > "Switch to LinuxDbg Output on Build"`
62 | * Added: Async BASH and SFTP operations to not lock up Visual Studio (PR #40)
63 | * Added: "Experimental" tag to menu items for Alpha/Beta items. (PR# 41)
64 | * `Build, Deploy, and Debug`
65 | * `Build, Deploy, and Launch` - _Temp disabled in Preview-1_
66 | * Added: Deploy and Launch (**ALPHA Feature**) (PR #36)
67 | * Added: BashSudo (PR #36)
68 | * Update: Default VSDBG path to match Visual Studio 2022's deployed path (`~/.vs-debugger/vs2022/`). (PR #36, #47)
69 | * Update: Sample's NuGet package for Prism.Avalonia (#54)
70 | * Fixed: Typo, "Build was notsuccessful" (PR #43) `User Contribution` :rocket:
71 | * Fixed: Auto-install cURL (PR #36)
72 | * Fixed: Reduced duplicate output messages (PR #40)
73 | * Removed: Publish (PR #36)
74 | * Removed: Redundant sample project
75 |
76 | ### 1.9.0
77 |
78 | * Added: Now comes with PLink embedded. You can still manually set this if you prefer.
79 | * Removed: Option to enable/disable PLink
80 |
81 | ### 1.8.1
82 |
83 | * Fixed: Remote folder pre-cleanup.
84 | * Added: Upload files async to reduce locking of Visual Studio
85 | * Added: Removal of `launch.json` if it previously existed
86 | * Added: More output logging.
87 | * Update: Enhanced Output
88 | * Updated: Output Window drop-down title to "Linux Debugger" from "Remote Debugger"
89 |
90 | ### 1.8.0
91 |
92 | * Added: Logging to Output window under "_Remote Debugging_" dropdown
93 | * Update: Do not include `launch.json` in the uploaded `.tar.gz` package
94 | * Update: Readme notes
95 | * Update: Code cleanup
96 |
97 | ### 1.7.0
98 |
99 | * Fixed: Remote debugging for PLink
100 | * Fixed: VSDBG default path
101 | * Update: DeleteLaunchJsonAfterBuild set to false by default
102 | * Update: Separated LaunchJson class into separate objects
103 | * Updated: SSH parameters to include `-o` (option) for `StrictHostKeyChecking = NO`.
104 | * Added: Additional internal logging
105 | * Added: documentation to Launch and Configure classes
106 |
107 | ### 1.6.0
108 |
109 | * Added: Ability to use SSH KeyFile with or without a passphrase.
110 | * Added: Directions for creating and configuring local and remote devices
111 | * Added: Additional directions in the Docs folder
112 |
113 | ### 1.2.0
114 |
115 | * Removed: Publish option
116 | * Updated Options page defaults
117 | * Update: Remote output folder is now the assembly name
118 | * Update: Remote output folder only clears intended target sub-folder
119 | * Added: Remote Debugging (_still in preview stages.._)
120 |
121 | ### 1.1.1
122 |
123 | * Updated: Branding name
124 | * Removed: Temp disabled remote debugger
125 |
126 | ### 1.0.1
127 |
128 | * Update: Remote output folder now creates subfolders with the same name as your project.
129 | * Updated: project icon
130 |
131 | ### 1.0.0
132 |
133 | * Initial release
134 |
--------------------------------------------------------------------------------
/sandbox/.editorconfig:
--------------------------------------------------------------------------------
1 | # To learn more about .editorconfig see https://aka.ms/editorconfigdocs
2 | ###############################
3 | # Core EditorConfig Options #
4 | ###############################
5 | root = true
6 | # All files
7 | [*]
8 | indent_style = space
9 |
10 | # XML project files
11 | [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
12 | indent_size = 2
13 |
14 | # XML config files
15 | [*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
16 | indent_size = 2
17 |
18 | # Code files
19 | [*.{cs,csx,vb,vbx}]
20 | indent_size = 2
21 | insert_final_newline = true
22 | charset = utf-8-bom
23 | ###############################
24 | # .NET Coding Conventions #
25 | ###############################
26 | [*.{cs,vb}]
27 | # Organize usings
28 | dotnet_sort_system_directives_first = true
29 | # this. preferences
30 | dotnet_style_qualification_for_field = false:silent
31 | dotnet_style_qualification_for_property = false:silent
32 | dotnet_style_qualification_for_method = false:silent
33 | dotnet_style_qualification_for_event = false:silent
34 | # Language keywords vs BCL types preferences
35 | dotnet_style_predefined_type_for_locals_parameters_members = true:silent
36 | dotnet_style_predefined_type_for_member_access = true:silent
37 | # Parentheses preferences
38 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
39 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
40 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
41 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
42 | # Modifier preferences
43 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
44 | dotnet_style_readonly_field = true:suggestion
45 | # Expression-level preferences
46 | dotnet_style_object_initializer = true:suggestion
47 | dotnet_style_collection_initializer = true:suggestion
48 | dotnet_style_explicit_tuple_names = true:suggestion
49 | dotnet_style_null_propagation = true:suggestion
50 | dotnet_style_coalesce_expression = true:suggestion
51 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
52 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
53 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
54 | dotnet_style_prefer_auto_properties = true:silent
55 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent
56 | dotnet_style_prefer_conditional_expression_over_return = true:silent
57 | ###############################
58 | # Naming Conventions #
59 | ###############################
60 | # Style Definitions
61 | dotnet_naming_style.pascal_case_style.capitalization = pascal_case
62 | # Use PascalCase for constant fields
63 | dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
64 | dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
65 | dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
66 | dotnet_naming_symbols.constant_fields.applicable_kinds = field
67 | dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
68 | dotnet_naming_symbols.constant_fields.required_modifiers = const
69 | dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
70 | dotnet_style_prefer_compound_assignment = true:suggestion
71 | dotnet_style_operator_placement_when_wrapping = beginning_of_line
72 | tab_width = 2
73 | end_of_line = crlf
74 | ###############################
75 | # C# Coding Conventions #
76 | ###############################
77 | [*.cs]
78 | # var preferences
79 | csharp_style_var_for_built_in_types = true:silent
80 | csharp_style_var_when_type_is_apparent = true:silent
81 | csharp_style_var_elsewhere = true:silent
82 | # Expression-bodied members
83 | csharp_style_expression_bodied_methods = false:silent
84 | csharp_style_expression_bodied_constructors = false:silent
85 | csharp_style_expression_bodied_operators = false:silent
86 | csharp_style_expression_bodied_properties = true:silent
87 | csharp_style_expression_bodied_indexers = true:silent
88 | csharp_style_expression_bodied_accessors = true:silent
89 | # Pattern matching preferences
90 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
91 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
92 | # Null-checking preferences
93 | csharp_style_throw_expression = true:suggestion
94 | csharp_style_conditional_delegate_call = true:suggestion
95 | # Modifier preferences
96 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
97 | # Expression-level preferences
98 | csharp_prefer_braces = true:silent
99 | csharp_style_deconstructed_variable_declaration = true:suggestion
100 | csharp_prefer_simple_default_expression = true:suggestion
101 | csharp_style_pattern_local_over_anonymous_function = true:suggestion
102 | csharp_style_inlined_variable_declaration = true:suggestion
103 | ###############################
104 | # C# Formatting Rules #
105 | ###############################
106 | # New line preferences
107 | csharp_new_line_before_open_brace = all
108 | csharp_new_line_before_else = true
109 | csharp_new_line_before_catch = true
110 | csharp_new_line_before_finally = true
111 | csharp_new_line_before_members_in_object_initializers = true
112 | csharp_new_line_before_members_in_anonymous_types = true
113 | csharp_new_line_between_query_expression_clauses = true
114 | # Indentation preferences
115 | csharp_indent_case_contents = true
116 | csharp_indent_switch_labels = true
117 | csharp_indent_labels = flush_left
118 | # Space preferences
119 | csharp_space_after_cast = false
120 | csharp_space_after_keywords_in_control_flow_statements = true
121 | csharp_space_between_method_call_parameter_list_parentheses = false
122 | csharp_space_between_method_declaration_parameter_list_parentheses = false
123 | csharp_space_between_parentheses = false
124 | csharp_space_before_colon_in_inheritance_clause = true
125 | csharp_space_after_colon_in_inheritance_clause = true
126 | csharp_space_around_binary_operators = before_and_after
127 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
128 | csharp_space_between_method_call_name_and_opening_parenthesis = false
129 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
130 | # Wrapping preferences
131 | csharp_preserve_single_line_statements = true
132 | csharp_preserve_single_line_blocks = true
133 | csharp_using_directive_placement = outside_namespace:silent
134 | csharp_prefer_simple_using_statement = true:suggestion
135 | csharp_style_namespace_declarations = block_scoped:silent
136 | csharp_style_prefer_method_group_conversion = true:silent
137 | csharp_style_expression_bodied_lambdas = true:silent
138 | csharp_style_expression_bodied_local_functions = false:silent
139 | ###############################
140 | # VB Coding Conventions #
141 | ###############################
142 | [*.vb]
143 | # Modifier preferences
144 | visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion
145 |
--------------------------------------------------------------------------------
/sandbox/ConsoleNet6.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.2.32317.152
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleNet6", "ConsoleNet6\ConsoleNet6.csproj", "{F181E39A-CCE1-45C1-B196-0CE759BB86AB}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {F181E39A-CCE1-45C1-B196-0CE759BB86AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {F181E39A-CCE1-45C1-B196-0CE759BB86AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {F181E39A-CCE1-45C1-B196-0CE759BB86AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {F181E39A-CCE1-45C1-B196-0CE759BB86AB}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {FD190C6A-36C9-416E-8BB1-E1C7421B6661}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/sandbox/ConsoleNet6/ConsoleNet6.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 | enable
7 | enable
8 | false
9 |
10 |
11 | portable
12 | true
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/sandbox/ConsoleNet6/Program.cs:
--------------------------------------------------------------------------------
1 | // See https://aka.ms/new-console-template for more information
2 | Console.WriteLine("Hello .NET 6, VS Linux Debugger!");
3 |
4 | #if DEBUG
5 | // Console.ReadLine();
6 | #endif
7 |
8 | Console.WriteLine("Apply breakpoint here!");
9 | System.Diagnostics.Debugger.Break();
10 |
11 | Console.WriteLine("All done!");
12 |
13 | //Console.WriteLine("Press any key to exit..");
14 | //var x = Console.ReadLine();
15 |
--------------------------------------------------------------------------------
/sandbox/GuiNet6.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.3.32804.467
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GuiNet6", "GuiNet6\GuiNet6.csproj", "{98F164D1-51CF-4BC1-8F9D-8DFB541E57F5}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {98F164D1-51CF-4BC1-8F9D-8DFB541E57F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {98F164D1-51CF-4BC1-8F9D-8DFB541E57F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {98F164D1-51CF-4BC1-8F9D-8DFB541E57F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {98F164D1-51CF-4BC1-8F9D-8DFB541E57F5}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {BD83A76E-61CC-4C41-86F5-501FBE71FCCC}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/sandbox/GuiNet6/App.axaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/sandbox/GuiNet6/App.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Markup.Xaml;
3 | using GuiNet6.ViewModels;
4 | using GuiNet6.Views;
5 | using Prism.DryIoc;
6 | using Prism.Ioc;
7 | using Prism.Modularity;
8 | using Prism.Regions;
9 |
10 | namespace GuiNet6;
11 |
12 | public class App : PrismApplication
13 | {
14 | /// App entry point.
15 | public override void Initialize()
16 | {
17 | AvaloniaXamlLoader.Load(this);
18 | base.Initialize();
19 | }
20 |
21 | /// Prism Module Registration.
22 | ///
23 | /// Module Catalog.
24 | protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
25 | {
26 | base.ConfigureModuleCatalog(moduleCatalog);
27 |
28 | // Wire-up modules for Region Manager
29 | //// moduleCatalog.AddModule();
30 | }
31 |
32 | /// User interface entry point, called after Register and ConfigureModules.
33 | /// Startup View.
34 | protected override IAvaloniaObject CreateShell()
35 | {
36 | return this.Container.Resolve();
37 | }
38 |
39 | /// Called after Initialize.
40 | protected override void OnInitialized()
41 | {
42 | // Register Views to Region it will appear in. Don't register them in the ViewModel.
43 | var regionManager = Container.Resolve();
44 | regionManager.RegisterViewWithRegion(RegionNames.ContentRegion, typeof(DashboardView));
45 | regionManager.RegisterViewWithRegion(RegionNames.SidebarRegion, typeof(SidebarView));
46 | }
47 |
48 | /// Register views and Services.
49 | /// IOC Container.
50 | protected override void RegisterTypes(IContainerRegistry containerRegistry)
51 | {
52 | // Services
53 | // ...
54 |
55 | // Views - Generic views
56 | containerRegistry.Register();
57 | containerRegistry.Register();
58 |
59 | // Views - Region Navigation
60 | containerRegistry.RegisterForNavigation();
61 | containerRegistry.RegisterForNavigation();
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/sandbox/GuiNet6/Assets/avalonia-logo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/sandbox/GuiNet6/Assets/avalonia-logo.ico
--------------------------------------------------------------------------------
/sandbox/GuiNet6/GuiNet6.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | WinExe
4 | net6.0
5 | enable
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | ShellWindow.axaml
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/sandbox/GuiNet6/Program.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using Avalonia;
3 | using Avalonia.ReactiveUI;
4 |
5 | namespace GuiNet6;
6 |
7 | internal class Program
8 | {
9 | // Avalonia configuration, don't remove; also used by visual designer.
10 | public static AppBuilder BuildAvaloniaApp() => AppBuilder
11 | .Configure()
12 | .UsePlatformDetect()
13 | .With(new X11PlatformOptions
14 | {
15 | EnableMultiTouch = true,
16 | UseDBusMenu = false,
17 | })
18 | .With(new Win32PlatformOptions
19 | {
20 | EnableMultitouch = true,
21 | AllowEglInitialization = true,
22 | })
23 | .UseSkia()
24 | .UseReactiveUI()
25 | .LogToTrace();
26 |
27 | // Initialization code. Don't use any Avalonia, third-party APIs or any
28 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
29 | // yet and stuff might break.
30 | [ExcludeFromCodeCoverage]
31 | public static void Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
32 | }
33 |
--------------------------------------------------------------------------------
/sandbox/GuiNet6/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "GuiNet6": {
4 | "commandName": "Project"
5 | },
6 | "WSL": {
7 | "commandName": "Project",
8 | "environmentVariables": {
9 | "DISPLAY": ":0"
10 | },
11 | "distributionName": ""
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/sandbox/GuiNet6/RegionNames.cs:
--------------------------------------------------------------------------------
1 | namespace GuiNet6;
2 |
3 | public static class RegionNames
4 | {
5 | /// Main Window's content region
6 | public const string ContentRegion = "ContentRegion";
7 |
8 | /// Main Window's Footer Status Bar.
9 | public const string FooterRegion = "FooterRegion";
10 |
11 | /// Main Window's right side bar.
12 | public const string RightSidebarRegion = "RightSidebarRegion";
13 |
14 | /// Main Window's side bar.
15 | public const string SidebarRegion = "SidebarRegion";
16 | }
17 |
--------------------------------------------------------------------------------
/sandbox/GuiNet6/ViewModels/DashboardViewModel.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using Prism.Commands;
3 |
4 | namespace GuiNet6.ViewModels;
5 |
6 | public class DashboardViewModel : ViewModelBase
7 | {
8 | public DashboardViewModel()
9 | {
10 | Title = "Dashboard View!";
11 | }
12 |
13 | public DelegateCommand CmdBreakPoint => new(() =>
14 | {
15 | // Force a breakpoint
16 | System.Diagnostics.Debug.WriteLine("Breakpoint triggering");
17 | System.Diagnostics.Debugger.Break();
18 | });
19 | }
20 |
--------------------------------------------------------------------------------
/sandbox/GuiNet6/ViewModels/SettingsViewModel.cs:
--------------------------------------------------------------------------------
1 | namespace GuiNet6.ViewModels;
2 |
3 | public class SettingsViewModel : ViewModelBase
4 | {
5 | public SettingsViewModel()
6 | {
7 | Title = "Settings View!";
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/sandbox/GuiNet6/ViewModels/ShellWindowViewModel.cs:
--------------------------------------------------------------------------------
1 | namespace GuiNet6.ViewModels;
2 |
3 | public class ShellWindowViewModel : ViewModelBase
4 | {
5 | public ShellWindowViewModel()
6 | {
7 | Title = "Sample Prism.Avalonia - Navigation";
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/sandbox/GuiNet6/ViewModels/SidebarViewModel.cs:
--------------------------------------------------------------------------------
1 | using GuiNet6.Views;
2 | using Prism.Commands;
3 | using Prism.Events;
4 | using Prism.Regions;
5 |
6 | namespace GuiNet6.ViewModels;
7 |
8 | public class SidebarViewModel : ViewModelBase
9 | {
10 | private IEventAggregator _eventAggregator;
11 | private IRegionManager _regionManager;
12 |
13 | public SidebarViewModel(IRegionManager regionManager, IEventAggregator ea)
14 | {
15 | _regionManager = regionManager;
16 | _eventAggregator = ea;
17 |
18 | Title = "Navigation";
19 | }
20 |
21 | public DelegateCommand CmdDashboard => new(() =>
22 | {
23 | _regionManager.RequestNavigate(RegionNames.ContentRegion, nameof(DashboardView));
24 | });
25 |
26 | public DelegateCommand CmdSettings => new(() =>
27 | {
28 | _regionManager.RequestNavigate(RegionNames.ContentRegion, nameof(SettingsView));
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/sandbox/GuiNet6/ViewModels/ViewModelBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq.Expressions;
3 | using Prism.Mvvm;
4 | using Prism.Regions;
5 |
6 | namespace GuiNet6.ViewModels;
7 |
8 | public class ViewModelBase : BindableBase, INavigationAware
9 | {
10 | private string _title = string.Empty;
11 |
12 | /// Gets or sets the title of the View.
13 | public string Title
14 | {
15 | get => _title;
16 | set => SetProperty(ref _title, value);
17 | }
18 |
19 | ///
20 | /// Called to determine if this instance can handle the navigation request.
21 | /// Don't call this directly, use .
22 | ///
23 | /// The navigation context.
24 | /// if this instance accepts the navigation request; otherwise, .
25 | public virtual bool IsNavigationTarget(NavigationContext navigationContext)
26 | {
27 | // Auto-allow navigation
28 | return OnNavigatingTo(navigationContext);
29 | }
30 |
31 | /// Called when the implementer is being navigated away from.
32 | /// The navigation context.
33 | public virtual void OnNavigatedFrom(NavigationContext navigationContext)
34 | {
35 | }
36 |
37 | /// Called when the implementer has been navigated to.
38 | /// The navigation context.
39 | public virtual void OnNavigatedTo(NavigationContext navigationContext)
40 | {
41 | }
42 |
43 | /// Navigation validation checker.
44 | /// Override for Prism 7.2's IsNavigationTarget.
45 | /// The navigation context.
46 | /// if this instance accepts the navigation request; otherwise, .
47 | public virtual bool OnNavigatingTo(NavigationContext navigationContext)
48 | {
49 | return true;
50 | }
51 |
52 | // Note from Prism.Mvvm.BindableBase
53 | [Obsolete("Please use RaisePropertyChanged(nameof(PropertyName)) from Prism.Mvvm.BindableBase instead. Expressions are slower, and the new nameof feature eliminates the magic strings.")]
54 | protected void RaisePropertyChanged(Expression> propertyExpression)
55 | {
56 | var propertyName = PropertySupport.ExtractPropertyName(propertyExpression);
57 | RaisePropertyChanged(propertyName);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/sandbox/GuiNet6/Views/DashboardView.axaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/sandbox/GuiNet6/Views/DashboardView.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Markup.Xaml;
4 |
5 | namespace GuiNet6.Views;
6 |
7 | public partial class DashboardView : UserControl
8 | {
9 | public DashboardView()
10 | {
11 | InitializeComponent();
12 | }
13 |
14 | private void InitializeComponent()
15 | {
16 | AvaloniaXamlLoader.Load(this);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/sandbox/GuiNet6/Views/SettingsView.axaml:
--------------------------------------------------------------------------------
1 |
9 | >
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/sandbox/GuiNet6/Views/SettingsView.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Markup.Xaml;
4 |
5 | namespace GuiNet6.Views;
6 |
7 | public partial class SettingsView : UserControl
8 | {
9 | public SettingsView()
10 | {
11 | InitializeComponent();
12 | }
13 |
14 | private void InitializeComponent()
15 | {
16 | AvaloniaXamlLoader.Load(this);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/sandbox/GuiNet6/Views/ShellWindow.axaml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
38 |
39 |
40 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/sandbox/GuiNet6/Views/ShellWindow.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Markup.Xaml;
4 |
5 | namespace GuiNet6.Views;
6 |
7 | public partial class ShellWindow : Window
8 | {
9 | public ShellWindow()
10 | {
11 | InitializeComponent();
12 | #if DEBUG
13 | this.AttachDevTools();
14 | #endif
15 | }
16 |
17 | private void InitializeComponent()
18 | {
19 | AvaloniaXamlLoader.Load(this);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sandbox/GuiNet6/Views/SidebarView.axaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
15 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/sandbox/GuiNet6/Views/SidebarView.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Markup.Xaml;
4 |
5 | namespace GuiNet6.Views;
6 |
7 | public partial class SidebarView : UserControl
8 | {
9 | public SidebarView()
10 | {
11 | InitializeComponent();
12 | }
13 |
14 | private void InitializeComponent()
15 | {
16 | AvaloniaXamlLoader.Load(this);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/sandbox/GuiNet6/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sandbox/LaunchJson-Notes.md:
--------------------------------------------------------------------------------
1 | # Launch.JSON Notes
2 |
3 | ## Launch for Debugging Example
4 |
5 | ```json
6 | {
7 | "version": "0.2.0",
8 | "adapter": "C:\\work\\tools\\PuTTY\\plink.exe",
9 | "adapterArgs": "-pw XXXX USERNAME@IPADDRESS -batch -T ~/.vsdbg/vsdbg --interpreter=vscode",
10 | "configurations": [
11 | {
12 | "name": ".NET Core Launch",
13 | "type": "coreclr",
14 | "request": "launch",
15 | "program": "dotnet",
16 | "args": ["ConsoleApp1.dll"],
17 | "cwd": "./VSLinuxDbg/",
18 | "console": "internalConsole",
19 | "stopAtEntry": true
20 | }
21 | ]
22 | }
23 | ```
24 |
25 | ## Attach and Launch Example
26 |
27 | ```json
28 | {
29 | "version": "0.2.0",
30 | "adapter": "c:\\work\\tools\\putty\\plink.exe",
31 |
32 | // NOTE: Must include ~/vsdbg
33 | "adapterArgs": "-pw XXXX USERNAME@IPADDRESS -batch -T ~/vsdbg/vsdbg",
34 | "configurations": [
35 | {
36 | "name": ".NET Launch",
37 | "type": "coreclr",
38 | "request": "launch",
39 | "program": "dotnet",
40 | "args": [ "LaunchTester.dll" ],
41 | // MUST USE "~/VSLinuxDbg/..."
42 | "cwd": "./VSLinuxDbg/LaunchTester",
43 | "console": "internalConsole"
44 | },
45 | {
46 | "name": ".NET Attach",
47 | "type": "coreclr",
48 | "request": "attach",
49 | "program": "dotnet",
50 | "args": [ "LaunchTester.dll" ],
51 | "cwd": "./VSLinuxDbg/LaunchTester",
52 | "console": "internalConsole"
53 | }
54 | ]
55 | }
56 | ```
57 |
58 | ## Notes
59 |
60 | ```json
61 | {
62 | "version": "0.2.0",
63 |
64 | // WORKS!
65 | "adapter": "c:\\work\\tools\\putty\\plink.exe",
66 | "adapterArgs": "-pw XXXXXXX USERNAME@IPADDRESS -batch -T ~/vsdbg/vsdbg",
67 |
68 | // Port Number causes issues
69 | // "adapter": "ssh.exe",
70 | // "adapterArgs": "-i C:\\Users\\USERNAME\\.ssh\\id_rsa USERNAME@IPADDRESS:22 vsdbg --interpreter=vscode --engineLogging=./VSLinuxDbg/LaunchTester/_vsdbg.log",
71 |
72 | // Connects and hangs.. needs password
73 | // "adapter": "ssh.exe",
74 | // "adapterArgs": "-i C:\\Users\\USERNAME\\.ssh\\id_rsa USERNAME@IPADDRESS ~/vsdbg/vsdbg --engineLogging=./VSLinuxDbg/LaunchTester/_vsdbg.log",
75 |
76 | // Fails
77 | // "adapter": "ssh.exe",
78 | // "adapterArgs": "-pw XXXXXXXX USERNAME@IPADDRESS ~/vsdbg/vsdbg --engineLogging=./VSLinuxDbg/LaunchTester/_vsdbg.log",
79 |
80 |
81 | "configurations": [
82 | {
83 | "name": ".NET Launch",
84 | "type": "coreclr",
85 | "request": "launch",
86 | "program": "dotnet",
87 | "args": [ "LaunchTester.dll" ],
88 | "cwd": "~/VSLinuxDbg/LaunchTester"
89 | },
90 | {
91 | "name": ".NET Attach",
92 | "type": "coreclr",
93 | "request": "attach",
94 | "program": "dotnet",
95 | "args": [
96 | "LaunchTester.dll"
97 | ],
98 | "cwd": "./VSLinuxDbg/LaunchTester",
99 | "console": "internalConsole"
100 | }
101 | ]
102 | }
103 | ```
104 |
--------------------------------------------------------------------------------
/sandbox/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "adapter": "C:\\work\\tools\\PuTTY\\plink.exe",
4 | "adapterArgs": "-pw XXXX USERNAME@IPADDRESS -batch -T ~/.vsdbg/vsdbg --interpreter=vscode",
5 | "configurations": [
6 | {
7 | "name": ".NET Core Launch",
8 | "type": "coreclr",
9 | "request": "launch",
10 | "program": "dotnet",
11 | "args": ["ConsoleApp1.dll"],
12 | "cwd": "./VSLinuxDbg/",
13 | "console": "internalConsole",
14 | "stopAtEntry": true
15 | }
16 | ]
17 | }
--------------------------------------------------------------------------------
/src/.editorconfig:
--------------------------------------------------------------------------------
1 | # Copyright 2022 Xeno Innovations, Inc.
2 | # https://github.com/xenoinc/CodeDevOps
3 | # Revision: 6.2
4 | #
5 | # This EditorConfig file provides consistant coding styles and formatting
6 | # structures for your team's projects while preserving your personal defaults.
7 | #
8 | # Revision Log
9 | # 6.1 2022-01-21 - Updated rules to include StyleCopAnalyzers. Added Static Readonly PascalCase from _camelCase.
10 | # 6.0 2022-01-10 - Included defaults from Microsoft to override custom settings
11 | # 5.2 2021-10-11 - Uniform C# spacing rules and labeled code formatting rules
12 | # 5.1 2021-09-14 - Added PowerShell and Markdown rules
13 | # 5 2021-08-26 - C# StyleCop rules
14 | # 4a 2021-01-17 - C# StyleCop rules
15 | # 4 2020-05-10 - C# coding standards
16 | # 3c 2020-04-18 - Split file filters into their own sections
17 | # 3b 2019-03-24 - Included additional rules
18 | # 3 2017-07-31 - Basic
19 | #
20 | # References:
21 | # - https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/code-style-rule-options
22 | # - https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/
23 | # - https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/formatting-rules
24 | # - https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/language-rules
25 | # - https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/naming-rules
26 | # - https://github.com/dotnet/roslyn/blob/main/.editorconfig
27 | # - https://github.com/microsoft/microsoft-ui-xaml/blob/master/.editorconfig
28 | #
29 |
30 | # Top-most EditorConfig file
31 | root = true
32 |
33 | # All generic files should use MSDOS style endings, not Unix (lf)
34 | [*]
35 | end_of_line = crlf
36 | indent_style = space
37 |
38 | [*.{cs,csx}]
39 | indent_style = space
40 | indent_size = 2
41 | tab_width = 2
42 | charset = utf-8-bom
43 | trim_trailing_whitespace = true
44 | insert_final_newline = true
45 |
46 | [*.{c,cpp,h}]
47 | indent_style = space
48 | indent_size = 2
49 | trim_trailing_whitespace = true
50 |
51 | cpp_indent_case_contents = true
52 | cpp_indent_case_contents_when_block = true
53 | cpp_indent_case_labels = true
54 |
55 | [*.sql]
56 | indent_style = space
57 | indent_size = 2
58 | trim_trailing_whitespace = true
59 |
60 | [*.{xml,xaml,axml,axaml}]
61 | indent_style = space
62 | indent_size = 2
63 | charset = utf-8-bom
64 | trim_trailing_whitespace = true
65 |
66 | [*.json]
67 | indent_style = space
68 | indent_size = 2
69 | trim_trailing_whitespace = true
70 |
71 | [*.sln]
72 | indent_size = 2
73 |
74 | # Xml project files
75 | [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
76 | indent_style = space
77 | indent_size = 2
78 | trim_trailing_whitespace = true
79 |
80 | # Xml config files
81 | [*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
82 | indent_style = space
83 | indent_size = 2
84 | trim_trailing_whitespace = true
85 |
86 | # PList Files
87 | [*.plist]
88 | indent_style = space
89 | indent_size = 2
90 | trim_trailing_whitespace = true
91 |
92 | # YAML files
93 | [*.{yaml,yml}]
94 | indent_style = space
95 | indent_size = 2
96 | trim_trailing_whitespace = true
97 |
98 | # Shell script files
99 | [*.sh]
100 | end_of_line = lf
101 | indent_style = space
102 | indent_size = 2
103 |
104 | # Powershell
105 | [*.{ps1,psd1,psm1}]
106 | indent_style = space
107 | indent_size = 2
108 | trim_trailing_whitespace = true
109 |
110 | [*.md]
111 | indent_style = space
112 | indent_size = 2
113 | insert_final_newline = true
114 | trim_trailing_whitespace = true
115 |
116 | # C# Ruleset
117 | [*.{cs,csx}]
118 | # Indentation preferences
119 | csharp_indent_block_contents = true
120 | csharp_indent_braces = false
121 | csharp_indent_case_contents = true
122 | csharp_indent_case_contents_when_block = false
123 | csharp_indent_labels = one_less_than_current
124 | csharp_indent_switch_labels = true
125 |
126 | ## Formatting - new line options
127 | ### Require braces to be on a new line for (also known as "Allman" style)
128 | ### accessors, methods, object_collection, control_blocks, types, properties, lambdas
129 | csharp_new_line_before_open_brace = all
130 | csharp_new_line_before_catch = true
131 | csharp_new_line_before_else = true
132 | csharp_new_line_before_finally = true
133 | csharp_new_line_before_members_in_object_initializers = true
134 | csharp_new_line_before_members_in_anonymous_types = true
135 | csharp_new_line_between_query_expression_clauses = true
136 |
137 | ## Spaces
138 | csharp_space_after_cast = false
139 | csharp_space_after_colon_in_inheritance_clause = true
140 | csharp_space_after_keywords_in_control_flow_statements = true
141 | csharp_space_before_colon_in_inheritance_clause = true
142 | csharp_space_before_comma = false
143 | csharp_space_before_dot = false
144 | csharp_space_before_open_square_brackets = false
145 | csharp_space_before_semicolon_in_for_statement = false
146 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
147 | csharp_space_between_method_call_name_and_opening_parenthesis = false
148 | csharp_space_between_method_call_parameter_list_parentheses = false
149 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
150 | csharp_space_between_method_declaration_parameter_list_parentheses = false
151 |
152 | csharp_using_directive_placement = outside_namespace:silent
153 | csharp_prefer_simple_using_statement = true:suggestion
154 |
155 | # Modifier preferences
156 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
157 |
158 | # Organize Usings
159 | dotnet_separate_import_directive_groups = false
160 | dotnet_sort_system_directives_first = true
161 | file_header_template = unset
162 | # file_header_template = Copyright Xeno Innovations, Inc. 2022\nSee the LICENSE file in the project root for more information.
163 |
164 | # this. and Me. preferences
165 | dotnet_style_qualification_for_event = false
166 | dotnet_style_qualification_for_field = false
167 | dotnet_style_qualification_for_method = false
168 | dotnet_style_qualification_for_property = false
169 |
170 | # Language keywords vs BCL types preferences
171 | dotnet_style_predefined_type_for_locals_parameters_members = true
172 | dotnet_style_predefined_type_for_member_access = true
173 |
174 | # Parentheses preferences
175 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
176 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
177 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary
178 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
179 |
180 | # Modifier preferences
181 | dotnet_style_predefined_type_for_locals_parameters_members = true
182 | dotnet_style_require_accessibility_modifiers = for_non_interface_members
183 | dotnet_style_readonly_field = true
184 |
185 | # Expression-level preferences
186 | dotnet_style_coalesce_expression = true
187 | dotnet_style_collection_initializer = true
188 | dotnet_style_explicit_tuple_names = true
189 | dotnet_style_namespace_match_folder = true
190 | dotnet_style_null_propagation = true
191 | dotnet_style_object_initializer = true
192 | dotnet_style_operator_placement_when_wrapping = beginning_of_line
193 | dotnet_style_prefer_auto_properties = true
194 | dotnet_style_prefer_compound_assignment = true
195 | dotnet_style_prefer_conditional_expression_over_assignment = true
196 | dotnet_style_prefer_conditional_expression_over_return = true
197 | dotnet_style_prefer_inferred_anonymous_type_member_names = true
198 | dotnet_style_prefer_inferred_tuple_names = true
199 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true
200 | dotnet_style_prefer_simplified_boolean_expressions = true
201 | dotnet_style_prefer_simplified_interpolation = true
202 |
203 | # Parameter preferences
204 | dotnet_code_quality_unused_parameters = all
205 |
206 | # Suppression preferences
207 | dotnet_remove_unnecessary_suppression_exclusions = none
208 |
209 | # New line preferences
210 | #dotnet_diagnostic.IDE2000.severity = warning
211 | dotnet_style_allow_multiple_blank_lines_experimental = false:error
212 |
213 | # dotnet_diagnostic.IDE2001.severity = none
214 | csharp_style_allow_embedded_statements_on_same_line_experimental = false
215 |
216 | # dotnet_diagnostic.IDE2002.severity = warning
217 | csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false
218 |
219 | # dotnet_diagnostic.IDE2003.severity = error
220 | dotnet_style_allow_statement_immediately_after_block_experimental = false:error
221 |
222 | # Naming Conventions
223 | ## Async methods must use "Async" suffix
224 | dotnet_naming_rule.async_methods_end_in_async.symbols = any_async_methods
225 | dotnet_naming_rule.async_methods_end_in_async.style = end_in_async
226 | dotnet_naming_rule.async_methods_end_in_async.severity = error
227 | dotnet_naming_symbols.any_async_methods.applicable_kinds = method
228 | dotnet_naming_symbols.any_async_methods.applicable_accessibilities = *
229 | dotnet_naming_symbols.any_async_methods.required_modifiers = async
230 | dotnet_naming_style.end_in_async.capitalization = pascal_case
231 | dotnet_naming_style.end_in_async.required_prefix =
232 | dotnet_naming_style.end_in_async.required_suffix = Async
233 | dotnet_naming_style.end_in_async.word_separator =
234 |
235 | ## private fields must prefix with an underscore
236 | dotnet_naming_rule.private_members_with_underscore.symbols = private_fields
237 | dotnet_naming_rule.private_members_with_underscore.style = prefix_underscore
238 | dotnet_naming_rule.private_members_with_underscore.severity = error
239 | dotnet_naming_symbols.private_fields.applicable_kinds = field
240 | dotnet_naming_symbols.private_fields.applicable_accessibilities = private
241 | dotnet_naming_style.prefix_underscore.capitalization = camel_case
242 | dotnet_naming_style.prefix_underscore.required_prefix = _
243 |
244 | ## private static fields must use PascalCase (overrides '_' based on SA1311)
245 | dotnet_naming_rule.private_static_field_naming.symbols = private_static_field_naming
246 | dotnet_naming_rule.private_static_field_naming.style = pascal_case_style
247 | dotnet_naming_rule.private_static_field_naming.severity = error
248 | dotnet_naming_symbols.private_static_field_naming.applicable_kinds = field
249 | dotnet_naming_symbols.private_static_field_naming.applicable_accessibilities = private
250 | dotnet_naming_symbols.private_static_field_naming.required_modifiers = static, readonly
251 | dotnet_naming_style.pascal_case_style.capitalization = pascal_case
252 |
253 | ## Constant fields must use PascalCase
254 | dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
255 | dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = error
256 | dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
257 | dotnet_naming_symbols.constant_fields.applicable_kinds = field
258 | dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
259 | dotnet_naming_symbols.constant_fields.required_modifiers = const
260 | dotnet_naming_style.pascal_case_style.capitalization = pascal_case
261 |
262 | ## Interfaces must have an I suffix
263 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
264 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = error
265 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
266 | dotnet_naming_symbols.interface.applicable_kinds = interface
267 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
268 | dotnet_naming_symbols.interface.required_modifiers =
269 | dotnet_naming_style.begins_with_i.capitalization = pascal_case
270 | dotnet_naming_style.begins_with_i.required_prefix = I
271 | dotnet_naming_style.begins_with_i.required_suffix =
272 | dotnet_naming_style.begins_with_i.word_separator =
273 |
274 | ## Types and Non-Field Members must be PascalCase
275 | dotnet_naming_rule.types_should_be_pascal_case.severity = error
276 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types
277 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
278 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
279 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
280 | dotnet_naming_symbols.types.required_modifiers =
281 |
282 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = error
283 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
284 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
285 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
286 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
287 | dotnet_naming_symbols.non_field_members.required_modifiers =
288 |
289 | ## Code Style Rules
290 | # IDE1005: Use conditional delegate call
291 | csharp_style_conditional_delegate_call = true
292 | # IDE1005: Delegate invocation can be simplified.
293 | dotnet_diagnostic.IDE1005.severity = warning
294 | # IDE1006: Naming Styles
295 | dotnet_diagnostic.IDE1006.severity = error
296 | # IDEOOO8: Use of var
297 | dotnet_diagnostic.IDE0008.severity = none
298 | # IDE0010: Add missing cases
299 | dotnet_diagnostic.IDE0010.severity = none
300 | # IDE0011: Add braces
301 | csharp_prefer_braces = when_multiline
302 | # IDE0025: Use expression body for properties
303 | csharp_style_expression_bodied_properties = true
304 | # IDE0026: Use expression body for indexers
305 | csharp_style_expression_bodied_indexers = true
306 | # IDE0027: Use expression body for accessors
307 | csharp_style_expression_bodied_accessors = true
308 | # IDE0046: Convert to conditional expression
309 | dotnet_diagnostic.IDE0046.severity = none
310 | # IDE0058: Expression value is never used
311 | # csharp_style_unused_value_expression_statement_preference = discard_variable
312 | dotnet_diagnostic.IDE0058.severity = none
313 | csharp_style_namespace_declarations = block_scoped:silent
314 | csharp_style_prefer_method_group_conversion = true:silent
315 | csharp_style_expression_bodied_methods = false:silent
316 | csharp_style_expression_bodied_constructors = false:silent
317 | csharp_style_expression_bodied_operators = false:silent
318 | csharp_style_expression_bodied_lambdas = true:silent
319 | csharp_style_expression_bodied_local_functions = false:silent
320 |
321 | ## Code Quality Rules
322 | # CA1031: Do not catch general exception types
323 | dotnet_diagnostic.CA1031.severity = none
324 | # CA1822: Mark members as static
325 | ##dotnet_diagnostic.CA1822.severity = none
326 | # CA1507: Use nameof to express symbol names
327 | dotnet_diagnostic.CA1507.severity = error
328 |
329 | ## Compiler
330 | # CS0618: Type or member is obsolete
331 | dotnet_diagnostic.CS0618.severity = error
332 | ##dotnet_diagnostic.CS0618.severity = warning
333 | # CS1591: Missing XML comment for publicly visible type or member
334 | dotnet_diagnostic.CS1591.severity = silent
335 |
336 | ## StyleCop.Analyzers
337 | # SA1000: Keywords should be spaced correctly
338 | dotnet_diagnostic.SA1000.severity = error
339 | # SA1005: Single line comments should begin with single space
340 | dotnet_diagnostic.SA1005.severity = error
341 | # SA1101: PrefixLocalCallsWithThis
342 | dotnet_diagnostic.SA1101.severity = none
343 | # SA1116: Split parameters should start on line after declaration
344 | dotnet_diagnostic.SA1116.severity = none
345 | # SA1118: Parameter should not span multiple lines
346 | dotnet_diagnostic.SA1118.severity = warning
347 | # SA1137: Elements should have the same indentation
348 | dotnet_diagnostic.SA1137.severity = error
349 | # SA1124: Do not use regions
350 | dotnet_diagnostic.SA1124.severity = error
351 | # SA1200: UsingDirectivesMustBePlacedWithinNamespace
352 | dotnet_diagnostic.SA1200.severity = none
353 | # SA1201: Elements should appear in the correct order
354 | dotnet_diagnostic.SA1201.severity = error
355 | # SA1202: Elements should be ordered by access
356 | dotnet_diagnostic.SA1202.severity = error
357 | # SA1203: Constants should appear before fields
358 | dotnet_diagnostic.SA1203.severity = error
359 | # SA1204: Static elements should appear before instance elements
360 | dotnet_diagnostic.SA1204.severity = error
361 | # SA1306: Field names should begin with lower-case letter
362 | dotnet_diagnostic.SA1306.severity = error
363 | # SA1309: FieldNamesMustNotBeginWithUnderscore
364 | dotnet_diagnostic.SA1309.severity = none
365 | # SA1313: Parameter names should begin with lower-case letter
366 | dotnet_diagnostic.SA1313.severity = error
367 | # SA1414: Tuple types in signatures should have element names
368 | dotnet_diagnostic.SA1414.severity = silent
369 | # SA1503: Braces should not be omitted
370 | dotnet_diagnostic.SA1503.severity = none
371 | # SA1505: Opening braces should not be floowed by a blank line
372 | dotnet_diagnostic.SA1505.severity= error
373 | # SA1507: Code should not contain multiple blank lines in a row
374 | dotnet_diagnostic.SA1507.severity = error
375 | # SA1508: Closing brac should not be preceded by a blank line
376 | dotnet_diagnostic.SA1508.severity= error
377 | # SA1513: Closing brace should be followed by blank line
378 | dotnet_diagnostic.SA1513.severity = error
379 | # SA1514: Element documentation header should be preceded by blank line
380 | dotnet_diagnostic.SA1514.severity = error
381 | # SA1515: Single-line comment should be preceded by blank line
382 | dotnet_diagnostic.SA1515.severity = error
383 | # SA1516: Elements should be separated by blank line
384 | dotnet_diagnostic.SA1516.severity = error
385 | # SA1600: Elements should be documented
386 | dotnet_diagnostic.SA1600.severity = none
387 | # SA1602: Enumeration items should not be documented
388 | dotnet_diagnostic.SA1602.severity = none
389 | # SA1623: Property summary documentation should match accessors
390 | dotnet_diagnostic.SA1623.severity = warning
391 | # SA1633: FileMustHaveHeader
392 | dotnet_diagnostic.SA1633.severity = silent
393 | # SA1636: File header copyright text should match
394 | dotnet_diagnostic.SA1636.severity = none
395 |
396 | # Default severity for analyzer diagnostics with category 'StyleCop.CSharp.SpacingRules' SA1028: Code should not contain trailing whitespace
397 | dotnet_analyzer_diagnostic.category-StyleCop.CSharp.SpacingRules.severity = error
398 |
--------------------------------------------------------------------------------
/src/CodeMaid.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | 2
12 |
13 |
14 | False
15 |
16 |
17 | 1
18 |
19 |
20 | True
21 |
22 |
24 | True
25 |
26 |
27 | True
28 |
29 |
30 | False
31 |
32 |
33 | False
34 |
35 |
36 | False
37 |
38 |
39 | False
40 |
41 |
43 | 1
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/LinuxDebugger.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.2.32314.265
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VsLinuxDebugger", "VsLinuxDebugger\VsLinuxDebugger.csproj", "{C204143E-FDE0-4F29-976B-A0C8AC798BAD}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A24E66EE-CAA8-48EE-8BA4-2532CD35ABB3}"
9 | ProjectSection(SolutionItems) = preProject
10 | .editorconfig = .editorconfig
11 | ..\readme.md = ..\readme.md
12 | EndProjectSection
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{42E06A3F-6EF1-49EC-910E-A89C15BC6E91}"
15 | EndProject
16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GuiNet6", "..\sandbox\GuiNet6\GuiNet6.csproj", "{2F01BAAF-DF1E-406C-A46D-127896C203F1}"
17 | EndProject
18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleNet6", "..\sandbox\ConsoleNet6\ConsoleNet6.csproj", "{D108A8EB-E4CD-4A84-A92B-DAD4DB6CA983}"
19 | EndProject
20 | Global
21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
22 | Debug|Any CPU = Debug|Any CPU
23 | Debug|x86 = Debug|x86
24 | Release|Any CPU = Release|Any CPU
25 | Release|x86 = Release|x86
26 | EndGlobalSection
27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
28 | {C204143E-FDE0-4F29-976B-A0C8AC798BAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {C204143E-FDE0-4F29-976B-A0C8AC798BAD}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {C204143E-FDE0-4F29-976B-A0C8AC798BAD}.Debug|x86.ActiveCfg = Debug|x86
31 | {C204143E-FDE0-4F29-976B-A0C8AC798BAD}.Debug|x86.Build.0 = Debug|x86
32 | {C204143E-FDE0-4F29-976B-A0C8AC798BAD}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {C204143E-FDE0-4F29-976B-A0C8AC798BAD}.Release|Any CPU.Build.0 = Release|Any CPU
34 | {C204143E-FDE0-4F29-976B-A0C8AC798BAD}.Release|x86.ActiveCfg = Release|x86
35 | {C204143E-FDE0-4F29-976B-A0C8AC798BAD}.Release|x86.Build.0 = Release|x86
36 | {2F01BAAF-DF1E-406C-A46D-127896C203F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {2F01BAAF-DF1E-406C-A46D-127896C203F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {2F01BAAF-DF1E-406C-A46D-127896C203F1}.Debug|x86.ActiveCfg = Debug|Any CPU
39 | {2F01BAAF-DF1E-406C-A46D-127896C203F1}.Debug|x86.Build.0 = Debug|Any CPU
40 | {2F01BAAF-DF1E-406C-A46D-127896C203F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
41 | {2F01BAAF-DF1E-406C-A46D-127896C203F1}.Release|Any CPU.Build.0 = Release|Any CPU
42 | {2F01BAAF-DF1E-406C-A46D-127896C203F1}.Release|x86.ActiveCfg = Release|Any CPU
43 | {2F01BAAF-DF1E-406C-A46D-127896C203F1}.Release|x86.Build.0 = Release|Any CPU
44 | {D108A8EB-E4CD-4A84-A92B-DAD4DB6CA983}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 | {D108A8EB-E4CD-4A84-A92B-DAD4DB6CA983}.Debug|Any CPU.Build.0 = Debug|Any CPU
46 | {D108A8EB-E4CD-4A84-A92B-DAD4DB6CA983}.Debug|x86.ActiveCfg = Debug|Any CPU
47 | {D108A8EB-E4CD-4A84-A92B-DAD4DB6CA983}.Debug|x86.Build.0 = Debug|Any CPU
48 | {D108A8EB-E4CD-4A84-A92B-DAD4DB6CA983}.Release|Any CPU.ActiveCfg = Release|Any CPU
49 | {D108A8EB-E4CD-4A84-A92B-DAD4DB6CA983}.Release|Any CPU.Build.0 = Release|Any CPU
50 | {D108A8EB-E4CD-4A84-A92B-DAD4DB6CA983}.Release|x86.ActiveCfg = Release|Any CPU
51 | {D108A8EB-E4CD-4A84-A92B-DAD4DB6CA983}.Release|x86.Build.0 = Release|Any CPU
52 | EndGlobalSection
53 | GlobalSection(SolutionProperties) = preSolution
54 | HideSolutionNode = FALSE
55 | EndGlobalSection
56 | GlobalSection(NestedProjects) = preSolution
57 | {2F01BAAF-DF1E-406C-A46D-127896C203F1} = {42E06A3F-6EF1-49EC-910E-A89C15BC6E91}
58 | {D108A8EB-E4CD-4A84-A92B-DAD4DB6CA983} = {42E06A3F-6EF1-49EC-910E-A89C15BC6E91}
59 | EndGlobalSection
60 | GlobalSection(ExtensibilityGlobals) = postSolution
61 | SolutionGuid = {5F242569-D0C3-4062-B813-48028E458962}
62 | EndGlobalSection
63 | EndGlobal
64 |
--------------------------------------------------------------------------------
/src/StyleCop.Analyzers.ruleset:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
--------------------------------------------------------------------------------
/src/VsLinuxDebugger/Commands.Impl.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Microsoft.VisualStudio.Shell;
4 | using Microsoft.VisualStudio.Shell.Interop;
5 | using VsLinuxDebugger.Core;
6 | using Xeno.VsLinuxDebug.OptionsPages;
7 |
8 | namespace VsLinuxDebugger
9 | {
10 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "Its annoying")]
11 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD200:Avoid async void methods", Justification = "Its annoying")]
12 | internal sealed partial class Commands
13 | {
14 | /// VS Menu Command IDs. This must be insync with .vsct values.
15 | private sealed class CommandIds
16 | {
17 | public const int CmdBuildDeployOnly = 0x1001;
18 | public const int CmdBuildDeployDebug = 0x1002;
19 | public const int CmdBuildDeployLaunch = 0x1006;
20 |
21 | public const int CmdDebugOnly = 0x1003;
22 | ////public const int CmdPublishOnly = 0x1006;
23 | ////public const int CmdPublishDebug= 0x1007;
24 |
25 | public const int CmdShowLog = 0x1004;
26 | public const int CmdShowSettings = 0x1005;
27 |
28 | public const int LinuxRemoteMainMenu = 0x1000;
29 | public const int RemoteMainMenuGroupLevel1 = 0x1100;
30 | public const int RemoteMainMenuGroupLevel2 = 0x1200;
31 | }
32 |
33 | /////// Override standard button text with.
34 | /////// Command Id.
35 | /////// Text to display.
36 | ////public string GetMenuText(int commandId)
37 | ////{
38 | //// switch (commandId)
39 | //// {
40 | //// case CommandIds.CmdBuildDeployOnly: return "Build and Deploy";
41 | //// case CommandIds.CmdBuildDeployDebug: return "Build, Deploy and Debug";
42 | ////
43 | //// case CommandIds.CmdBuildDeployLaunch: return "Build, Deploy and Launch";
44 | //// case CommandIds.CmdDebugOnly: return "Debug Only";
45 | //// ////case CommandIds.CmdPublishOnly: return "Publish Only";
46 | //// ////case CommandIds.CmdPublishDebug: return "Publish and Debug";
47 | //// case CommandIds.CmdShowLog: return "Show Log";
48 | //// case CommandIds.CmdShowSettings: return "Settings";
49 | //// default: return $"Unknown CommandId ({commandId})";
50 | //// }
51 | ////}
52 |
53 | /// Wire-up menu item to event handlers
54 | /// See `DebuggerPackage.vsct` for menu item builder.
55 | /// Command invoked by user.
56 | private void CreateVsMenu(OleMenuCommandService cmd)
57 | {
58 | AddMenuItem(cmd, CommandIds.CmdBuildDeployOnly, SetMenuTextAndVisibility, OnBuildDeployAsync);
59 | AddMenuItem(cmd, CommandIds.CmdBuildDeployDebug, SetMenuTextAndVisibility, OnBuildDeployDebugAsync);
60 | AddMenuItem(cmd, CommandIds.CmdBuildDeployLaunch, SetMenuTextAndVisibility, OnBuildDeployLaunchAsync);
61 |
62 | ////AddMenuItem(cmd, CommandIds.CmdPublishDebug, SetMenuTextAndVisibility, OnPublishDebugAsyc);
63 | AddMenuItem(cmd, CommandIds.CmdDebugOnly, SetMenuTextAndVisibility, OnDebugOnlyAsync);
64 |
65 | AddMenuItem(cmd, CommandIds.CmdShowLog, SetMenuTextAndVisibility, OnShowLog);
66 | AddMenuItem(cmd, CommandIds.CmdShowSettings, SetMenuTextAndVisibility, OnShowSettingsAsync);
67 | }
68 |
69 | private async Task ExecuteBuildAsync(BuildOptions buildOptions)
70 | {
71 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
72 |
73 | var success = true;
74 |
75 | var options = ToUserOptions();
76 | using (var dbg = new RemoteDebugger(options))
77 | {
78 | if (!dbg.IsProjectValid())
79 | {
80 | Logger.Output("No C# startup project/solution loaded.");
81 | success = false;
82 | }
83 |
84 | if (success && !await dbg.BeginAsync(buildOptions))
85 | {
86 | Logger.Output("Failed to perform actions.");
87 | success = false;
88 | }
89 | }
90 |
91 | return success;
92 | }
93 |
94 | private async void OnBuildDeployAsync(object sender, EventArgs e)
95 | {
96 | await ExecuteBuildAsync(BuildOptions.Build | BuildOptions.Deploy);
97 | }
98 |
99 | private async void OnBuildDeployDebugAsync(object sender, EventArgs e)
100 | {
101 | await ExecuteBuildAsync(BuildOptions.Build | BuildOptions.Deploy | BuildOptions.Debug);
102 | }
103 |
104 | private async void OnBuildDeployLaunchAsync(object sender, EventArgs e)
105 | {
106 | await ExecuteBuildAsync(BuildOptions.Build | BuildOptions.Deploy | BuildOptions.Launch);
107 | }
108 |
109 | private async void OnDebugOnlyAsync(object sender, EventArgs e)
110 | {
111 | await ExecuteBuildAsync(BuildOptions.Build | BuildOptions.Debug);
112 | }
113 |
114 | private void OnShowLog(object sender, EventArgs e)
115 | {
116 | // Not implemented yet
117 | if (sender is OleMenuCommand cmd)
118 | cmd.Enabled = false;
119 |
120 | MessageBox("Not implemented");
121 | }
122 |
123 | private async void OnShowSettingsAsync(object sender, EventArgs e)
124 | {
125 | // Not implemented yet
126 | if (sender is OleMenuCommand cmd)
127 | cmd.Enabled = false;
128 |
129 | await Task.Yield();
130 |
131 | Instance._package.ShowOptionPage(typeof(OptionsPage));
132 | }
133 |
134 | private void SetMenuTextAndVisibility(object sender, EventArgs e)
135 | {
136 | ThreadHelper.ThrowIfNotOnUIThread();
137 |
138 | if (sender is OleMenuCommand cmd)
139 | {
140 | // TODO: Enhance by displaying IP Address
141 | ////var settings = SettingsManager.Instance.Load();
142 | //// cmd.Text = $"{GetMenuText(cmd.CommandID.ID)} ({settings.HostIp})";
143 | //// cmd.Enabled = _extension.IsStartupProjectAvailable();
144 |
145 | if (cmd.CommandID.ID == CommandIds.CmdShowLog
146 | || cmd.CommandID.ID == CommandIds.CmdDebugOnly
147 | ////|| cmd.CommandID.ID == CommandIds.CmdShowSettings
148 | || cmd.CommandID.ID == CommandIds.CmdBuildDeployLaunch)
149 | {
150 | cmd.Enabled = false;
151 | }
152 | else
153 | {
154 | cmd.Enabled = true;
155 | }
156 | }
157 | }
158 |
159 | private UserOptions ToUserOptions()
160 | {
161 | DebuggerPackage VsixPackage = _package as DebuggerPackage;
162 |
163 | return new UserOptions
164 | {
165 | DeleteLaunchJsonAfterBuild = VsixPackage.VsixOptions.DeleteLaunchJsonAfterBuild,
166 |
167 | HostIp = VsixPackage.VsixOptions.HostIp,
168 | HostPort = VsixPackage.VsixOptions.HostPort,
169 |
170 | LocalPLinkPath = VsixPackage.VsixOptions.PLinkPath,
171 | LocalSwitchLinuxDbgOutput = VsixPackage.VsixOptions.SwitchLinuxDbgOutput,
172 |
173 | RemoteDebugDisplayGui = VsixPackage.VsixOptions.RemoteDebugDisplayGui,
174 | RemoteDebugDisplayNumber = VsixPackage.VsixOptions.RemoteDebugDisplayNumber,
175 | RemoteDeployBasePath = VsixPackage.VsixOptions.RemoteDeployBasePath,
176 | RemoteDotNetPath = VsixPackage.VsixOptions.RemoteDotNetPath,
177 | RemoteVsDbgBasePath = VsixPackage.VsixOptions.RemoteVsDbgRootPath,
178 |
179 | UseCommandLineArgs = VsixPackage.VsixOptions.UseCommandLineArgs,
180 | //// UsePublish = Settings.UsePublish,
181 |
182 | UserPrivateKeyEnabled = VsixPackage.VsixOptions.UserPrivateKeyEnabled,
183 | UserPrivateKeyPath = VsixPackage.VsixOptions.UserPrivateKeyPath,
184 | UserPrivateKeyPassword = VsixPackage.VsixOptions.UserPrivateKeyPassword,
185 | UserName = VsixPackage.VsixOptions.UserName,
186 | UserPass = VsixPackage.VsixOptions.UserPass,
187 | UserGroupName = VsixPackage.VsixOptions.UserGroupName,
188 | UseSSHExeEnabled = VsixPackage.VsixOptions.UseSSHExeEnabled
189 | };
190 | }
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/src/VsLinuxDebugger/Commands.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.Design;
3 | using Microsoft.VisualStudio.Shell;
4 | using Microsoft.VisualStudio.Shell.Interop;
5 | using Task = System.Threading.Tasks.Task;
6 |
7 | namespace VsLinuxDebugger
8 | {
9 | ///
10 | /// Command handler
11 | ///
12 | internal sealed partial class Commands
13 | {
14 | /// Command ID.
15 | public const int CommandId = 0x0100;
16 |
17 | /// Command menu group (command set GUID).
18 | public static readonly Guid CommandSet = new Guid("da478db6-b5f9-4b11-ab42-4e08c5d1db07");
19 |
20 | /// VS Package that provides this command, not null.
21 | private readonly AsyncPackage _package;
22 |
23 | ///
24 | /// Initializes a new instance of the class.
25 | /// Adds our command handlers for menu (commands must exist in the command table file)
26 | ///
27 | /// Owner package, not null.
28 | /// Command service to add command to, not null.
29 | private Commands(AsyncPackage package, OleMenuCommandService commandService)
30 | {
31 | _package = package ?? throw new ArgumentNullException(nameof(package));
32 | //// _vsExtension = IVsPackageExtensionProvider ?? throw new ArgumentNullException(nameof(vsExtension));
33 | commandService = commandService ?? throw new ArgumentNullException(nameof(commandService));
34 |
35 | CreateVsMenu(commandService);
36 | }
37 |
38 | /// Gets the instance of the command.
39 | public static Commands Instance { get; private set; }
40 |
41 | /// Gets the service provider from the owner package.
42 | private Microsoft.VisualStudio.Shell.IAsyncServiceProvider ServiceProvider => this._package;
43 |
44 | /// Initializes the singleton instance of the command.
45 | /// Owner package, not null.
46 | public static async Task InitializeAsync(AsyncPackage package)
47 | {
48 | // Switch to the main thread - the call to AddCommand in SshDebugCommand's constructor requires
49 | // the UI thread.
50 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);
51 |
52 | OleMenuCommandService commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService;
53 | Instance = new Commands(package, commandService);
54 | }
55 |
56 | private OleMenuCommand AddMenuItem(OleMenuCommandService mcs, int cmdCode, EventHandler check, EventHandler action)
57 | {
58 | var commandID = new CommandID(CommandSet, cmdCode);
59 | var menuCommand = new OleMenuCommand(action, commandID);
60 | if (check != null)
61 | menuCommand.BeforeQueryStatus += check;
62 |
63 | mcs.AddCommand(menuCommand);
64 |
65 | return menuCommand;
66 | }
67 |
68 | private void MessageBox(string message, string title = "Error") => VsShellUtilities.ShowMessageBox(
69 | _package,
70 | message,
71 | title,
72 | OLEMSGICON.OLEMSGICON_INFO,
73 | OLEMSGBUTTON.OLEMSGBUTTON_OK,
74 | OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/VsLinuxDebugger/Core/BuildOptions.cs:
--------------------------------------------------------------------------------
1 | namespace VsLinuxDebugger.Core
2 | {
3 | public enum BuildOptions
4 | {
5 | Build,
6 | Deploy,
7 | Publish,
8 | Debug,
9 | /// Launches application with `DISPLAY:=0`
10 | Launch,
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/VsLinuxDebugger/Core/Constants.cs:
--------------------------------------------------------------------------------
1 | namespace VsLinuxDebugger.Core
2 | {
3 | public static class Constants
4 | {
5 | /// Filename of Visual Studio Debugger.
6 | public const string AppVSDbg = "vsdbg";
7 |
8 | public const string DefaultDotNetPath = "dotnet";
9 | public const string VS2022 = "vs2022";
10 | public const string DefaultVsdbgBasePath = "~/.vs-debugger";
11 | public const string LaunchJson = "launch.json";
12 |
13 | public const string PackageTarGz = "vsldBuildContents.tar.gz";
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/VsLinuxDebugger/Core/LaunchBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text.Json;
4 | using EnvDTE;
5 | using EnvDTE80;
6 | using Microsoft.VisualStudio.Shell;
7 | using VsLinuxDebugger.Core.Remote;
8 |
9 | namespace VsLinuxDebugger.Core
10 | {
11 | /// LaunchBuilder class for serialization.
12 | public class LaunchBuilder
13 | {
14 | public const string AdapterFileName = "launch.json";
15 |
16 | private UserOptions _opts;
17 |
18 | public LaunchBuilder(DTE2 dte, Project dteProject, UserOptions userOptions)
19 | {
20 | ThreadHelper.ThrowIfNotOnUIThread();
21 |
22 | _opts = userOptions;
23 |
24 | AssemblyName = dteProject.Properties.Item("AssemblyName").Value.ToString();
25 | ProjectConfigName = dteProject.ConfigurationManager.ActiveConfiguration.ConfigurationName;
26 | ProjectFileFullPath = dteProject.FullName;
27 | ProjectName = dteProject.Name;
28 | SolutionFileFullPath = dte.Solution.FullName;
29 | SolutionDirPath = Path.GetDirectoryName(dte.Solution.FullName);
30 | OutputDirName = dteProject.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath").Value.ToString();
31 | OutputDirFullPath = Path.Combine(Path.GetDirectoryName(dteProject.FullName), OutputDirName);
32 | }
33 |
34 | /// Project assembly name. I.E. "ConsoleApp1"
35 | public string AssemblyName { get; set; }
36 |
37 | public string CommandLineArgs { get; set; } = string.Empty;
38 |
39 | /// Remove the `launch.json` file after building. Keep it around for debugging.
40 | public bool DeleteLaunchJsonAfterBuild => _opts.DeleteLaunchJsonAfterBuild;
41 |
42 | /// Full output folder path. I.E. "C:\\path\\Repos\\Porj\\bin\\Debug\\net6.0\\".
43 | public string OutputDirFullPath { get; set; }
44 |
45 | /// Partial path to the output directory. I.E. "bin\\Debug\\net6.0".
46 | public string OutputDirName { get; set; }
47 |
48 | /// Configuration build type. I.E. "Debug".
49 | public string ProjectConfigName { get; set; }
50 |
51 | /// Full project output path. I.E. "C:\\path\\Repos\\Proj\\ConsoleApp1.csproj"
52 | public string ProjectFileFullPath { get; set; }
53 |
54 | /// Project name (not always the same as AssemblyName). I.E. "Console App1"
55 | public string ProjectName { get; set; }
56 |
57 | /// Full path to the remote assembly. (i.e. `/home/USER/VLSDbg/Proj/ConsoleApp1.dll`)
58 | public string RemoteDeployAssemblyFilePath => LinuxPath.Combine(RemoteDeployProjectFolder, $"{AssemblyName}.dll");
59 |
60 | /// Folder of our remote assembly. (i.e. `/home/USER/VLSDbg/Proj`)
61 | public string RemoteDeployProjectFolder => LinuxPath.Combine(_opts.RemoteDeployBasePath, ProjectName);
62 |
63 | public string RemoteDotNetPath => _opts.RemoteDotNetPath;
64 |
65 | public string RemoteHostIp => _opts.HostIp;
66 |
67 | public int RemoteHostPort => _opts.HostPort;
68 |
69 | public string RemoteUserName => _opts.UserName;
70 |
71 | public string RemoteUserPass => _opts.UserPass;
72 |
73 | /// Solution folder path. I.E. "C:\\path\Repos\"
74 | public string SolutionDirPath { get; set; }
75 |
76 | /// Full solution output path. I.E. "C:\\path\Repos\Proj.sln"
77 | public string SolutionFileFullPath { get; set; }
78 |
79 | /// Generates the project's `launch.json` file.
80 | /// Returns the local path to the file.
81 | public string GenerateLaunchJson(bool vsdbgLogging = false)
82 | {
83 | string adapter, adapterArgs;
84 |
85 | (adapter, adapterArgs) = GetAdapter(vsdbgLogging);
86 |
87 | var obj = new Launch(
88 | RemoteDotNetPath,
89 | $"{AssemblyName}.dll", /// RemoteDeployAppPath,
90 | RemoteDeployProjectFolder,
91 | default,
92 | false)
93 | {
94 | Adapter = adapter,
95 | AdapterArgs = adapterArgs,
96 | };
97 |
98 | var json = JsonSerializer.Serialize(obj, new JsonSerializerOptions
99 | {
100 | PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
101 | WriteIndented = true,
102 | });
103 |
104 | // Create out file
105 | var outputPath = Path.Combine(OutputDirFullPath, "launch.json");
106 |
107 | try
108 | {
109 | File.WriteAllText(outputPath, json);
110 | }
111 | catch (Exception ex)
112 | {
113 | Logger.Output($"Error writing 'launch.json' to path, '{outputPath}'!\n{ex.Message}");
114 | outputPath = string.Empty;
115 | }
116 |
117 | return outputPath;
118 | }
119 |
120 | private (string adapterPath, string adapterArgs) GetAdapter(bool vsdbgLogging = false)
121 | {
122 | // NOTE: Removed ":{RemoteHostPort}" because it failed to launch with PLink
123 | // var sshEndpoint = $"{_opts.UserName}@{_opts.HostIp}:{_opts.HostPort}";
124 | var sshEndpoint = $"{RemoteUserName}@{RemoteHostIp}";
125 |
126 | var vsdbgLogPath = "";
127 | if (vsdbgLogging)
128 | vsdbgLogPath = $" --engineLogging={LinuxPath.Combine(RemoteDeployProjectFolder, "_vsdbg.log")}";
129 |
130 | ////if (!_opts.LocalPlinkEnabled)
131 | ////{
132 | //// adapter = "ssh.exe";
133 | //// adapterArgs = $"{sshPassword} {sshEndpoint} {_opts.RemoteVsDbgPath} --interpreter=vscode {vsdbgLogPath}";
134 | ////}
135 | ////else
136 | ////{
137 |
138 | string plinkPath = string.Empty;
139 |
140 | if (_opts.UseSSHExeEnabled)
141 | {
142 | plinkPath = "ssh.exe";
143 | }
144 | else
145 | {
146 | // Adapter Path:
147 | // PLink.exe - Use manual path or embedded
148 | if (!string.IsNullOrEmpty(_opts.LocalPLinkPath) && File.Exists(_opts.LocalPLinkPath))
149 | {
150 | plinkPath = _opts.LocalPLinkPath;
151 | }
152 | else
153 | {
154 | plinkPath = Path.Combine(GetExtensionDirectory(), "plink.exe").Trim('"');
155 | }
156 | }
157 |
158 | // Adapter Arguments:
159 | // NOTE:
160 | // 1. SSH Private Key ("-i PPK") fails with PLINK. Must use manual password until this is resolved.
161 | // 2. Strict Host Key Checking is disabled by default; this doesn't need set.
162 | //
163 | // REF: https://linuxhint.com/ssh-stricthostkeychecking/
164 | // $"-i \"{_opts.UserPrivateKeyPath}\" -o \"StrictHostKeyChecking no\" {RemoteUserName}@{RemoteHostIp} {_opts.RemoteVsDbgPath} --interpreter=vscode {vsdbgLogPath}")
165 | //
166 | //// var strictKeyChecking = " -o \"StrictHostKeyChecking no\"";
167 | ////
168 | ////var sshPassword = !_opts.UserPrivateKeyEnabled
169 | //// ? $"-pw {RemoteUserPass}"
170 | //// : $"-i \"{_opts.UserPrivateKeyPath}{strictKeyChecking}\"";
171 | string sshPassword = "";
172 |
173 | if(_opts.UseSSHExeEnabled)
174 | {
175 | sshPassword = ""; //nothing to do, we assume that c:\users\[user]\.ssh\id_rsa exists
176 | }
177 | else
178 | {
179 | sshPassword = $"-pw {RemoteUserPass}";
180 | }
181 |
182 | // TODO: Figure out why "-i " isn't working.
183 | if (string.IsNullOrEmpty(RemoteUserPass))
184 | Logger.Output("You must provide a User Password to debug.");
185 |
186 | string adapter = plinkPath;
187 | string adapterArgs = "";
188 | string displayAdapter = "";
189 |
190 | if(_opts.RemoteDebugDisplayGui)
191 | {
192 | displayAdapter = !string.IsNullOrWhiteSpace(_opts.RemoteDebugDisplayNumber) ?
193 | $"DISPLAY={_opts.RemoteDebugDisplayNumber}" :
194 | "DISPLAY=:0";
195 | }
196 |
197 | if (_opts.UseSSHExeEnabled)
198 | {
199 | adapterArgs = $"{sshPassword} {sshEndpoint} -T {displayAdapter} {_opts.RemoteVsDbgFullPath} {vsdbgLogPath}";
200 | //// adapterArgs = $"-ssh {sshPassword} {sshEndpoint} -batch -T {RemoteVsDbgFullPath} --interpreter=vscode {vsdbgLogPath}";
201 | }
202 | else
203 | {
204 | adapterArgs= $"-ssh {sshPassword} {sshEndpoint} -T {displayAdapter} {_opts.RemoteVsDbgFullPath} {vsdbgLogPath}";
205 | }
206 |
207 | return (adapter, adapterArgs);
208 | }
209 |
210 | /// Attempt to get the extension's local directory.
211 | /// Path of this VSIX or empty string.
212 | private string GetExtensionDirectory()
213 | {
214 | var path = string.Empty;
215 | try
216 | {
217 | var uri = new Uri(typeof(LaunchBuilder).Assembly.CodeBase, UriKind.Absolute);
218 | path = Path.GetDirectoryName(uri.LocalPath);
219 | }
220 | catch (Exception)
221 | {
222 | }
223 |
224 | return path;
225 | }
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/src/VsLinuxDebugger/Core/LinuxPath.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------------
2 | // FILE: LinuxPath.cs
3 | // CONTRIBUTOR: Jeff Lill
4 | // COPYRIGHT: Copyright (c) 2005-2022 by neonFORGE LLC. All rights reserved.
5 | //
6 | // Licensed under the Apache License, Version 2.0 (the "License");
7 | // you may not use this file except in compliance with the License.
8 | // You may obtain a copy of the License at
9 | //
10 | // http://www.apache.org/licenses/LICENSE-2.0
11 | //
12 | // Unless required by applicable law or agreed to in writing, software
13 | // distributed under the License is distributed on an "AS IS" BASIS,
14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | // See the License for the specific language governing permissions and
16 | // limitations under the License.
17 | //
18 | // References:
19 | // - https://github.com/nforgeio/neonKUBE/blob/master/Lib/Neon.Common/IO/LinuxPath.cs
20 | // - https://github.com/nforgeio/neonKUBE/blob/master/Lib/Neon.Common/Diagnostics/Covenant.cs
21 |
22 | using System;
23 | using System.IO;
24 | using System.Runtime.InteropServices;
25 |
26 | namespace VsLinuxDebugger.Core
27 | {
28 | ///
29 | /// Implements functionality much like , except for
30 | /// this class is oriented towards handling Linux-style paths on
31 | /// a remote (possibly a Windows) machine.
32 | ///
33 | public static class LinuxPath
34 | {
35 | ///
36 | /// Changes the file extension.
37 | ///
38 | /// The file path.
39 | /// The new extension.
40 | /// The modified path.
41 | public static string ChangeExtension(string path, string extension)
42 | {
43 | Covenant.Requires(!string.IsNullOrEmpty(path), nameof(path));
44 | Covenant.Requires(!string.IsNullOrEmpty(extension), nameof(extension));
45 |
46 | return Path.ChangeExtension(Normalize(path), extension).ToLinux();
47 | }
48 |
49 | ///
50 | /// Combines an array of strings into a path.
51 | ///
52 | /// The paths.
53 | /// The combined paths.
54 | public static string Combine(params string[] paths)
55 | {
56 | Covenant.Requires(paths != null, nameof(paths));
57 |
58 | return Path.Combine(paths).ToLinux();
59 | }
60 |
61 | ///
62 | /// Extracts the directory portion of a path.
63 | ///
64 | /// The path.
65 | /// The directory portion.
66 | public static string GetDirectoryName(string path)
67 | {
68 | Covenant.Requires(!string.IsNullOrEmpty(path), nameof(path));
69 |
70 | return Path.GetDirectoryName(Normalize(path)).ToLinux();
71 | }
72 |
73 | ///
74 | /// Returns the file extension from a path.
75 | ///
76 | /// The path.
77 | /// The file extension.
78 | public static string GetExtension(string path)
79 | {
80 | Covenant.Requires(!string.IsNullOrEmpty(path), nameof(path));
81 |
82 | return Path.GetExtension(Normalize(path));
83 | }
84 |
85 | ///
86 | /// Returns the file name and extension from a path.
87 | ///
88 | /// The path.
89 | /// The file name and extension.
90 | public static string GetFileName(string path)
91 | {
92 | Covenant.Requires(!string.IsNullOrEmpty(path), nameof(path));
93 |
94 | return Path.GetFileName(Normalize(path));
95 | }
96 |
97 | ///
98 | /// Returns the file name from a path without the extension.
99 | ///
100 | /// The path.
101 | /// The file name without the extension.
102 | public static string GetFileNameWithoutExtension(string path)
103 | {
104 | Covenant.Requires(!string.IsNullOrEmpty(path), nameof(path));
105 |
106 | return Path.GetFileNameWithoutExtension(Normalize(path));
107 | }
108 |
109 | ///
110 | /// Determines whether a path has a file extension.
111 | ///
112 | /// The path.
113 | /// true if the path has an extension.
114 | public static bool HasExtension(string path)
115 | {
116 | Covenant.Requires(!string.IsNullOrEmpty(path), nameof(path));
117 |
118 | return Path.HasExtension(Normalize(path));
119 | }
120 |
121 | ///
122 | /// Determines whether the path is rooted.
123 | ///
124 | /// The path.
125 | /// true ifc the path is rooted.
126 | public static bool IsPathRooted(string path)
127 | {
128 | Covenant.Requires(!string.IsNullOrEmpty(path), nameof(path));
129 |
130 | return Normalize(path).ToLinux().StartsWith("/");
131 | }
132 |
133 | ///
134 | /// Ensures that the path passed is suitable for non-Windows platforms
135 | /// by conmverting any backslashes to forward slashes.
136 | ///
137 | /// The input path (or null).
138 | /// The normalized path.
139 | private static string Normalize(string path)
140 | {
141 | if (path == null || RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
142 | {
143 | return path;
144 | }
145 |
146 | return path.Replace('\\', '/');
147 | }
148 |
149 | ///
150 | /// Converts a Windows style path to Linux.
151 | ///
152 | /// The source path.
153 | /// The converted path.
154 | private static string ToLinux(this string path)
155 | {
156 | return path.Replace('\\', '/');
157 | }
158 |
159 | /// A simple, lightweight, and partial implementation of the Microsoft Dev Labs Contract class.
160 | ///
161 | ///
162 | /// This class is intended to be a drop-in replacement for code contract assertions by simply
163 | /// searching and replacing "Contract." with "." in all source code.
164 | /// In my experience, code contracts slow down build times too much and often obsfucate
165 | /// async methods such that they cannot be debugged effectively using the debugger.
166 | /// Code Contracts are also somewhat of a pain to configure as project propoerties.
167 | ///
168 | ///
169 | /// This class includes the ,
170 | /// and methods that can be used to capture validation
171 | /// requirements in code, but these methods don't currently generate any code.
172 | ///
173 | ///
174 | private class Covenant
175 | {
176 | private static Type[] _oneStringArg = new Type[] { typeof(string) };
177 | private static Type[] _twoStringArgs = new Type[] { typeof(string), typeof(string) };
178 |
179 | ///
180 | /// Verifies a method pre-condition throwing a custom exception.
181 | ///
182 | /// The exception to be thrown if the condition is false.
183 | /// The condition to be tested.
184 | /// The first optional string argument to the exception constructor.
185 | /// The second optional string argument to the exception constructor.
186 | ///
187 | ///
188 | /// This method throws a instance when
189 | /// is false. Up to two string arguments may be passed to the exception constructor when an
190 | /// appropriate constructor exists, otherwise these arguments will be ignored.
191 | ///
192 | ///
193 | public static void Requires(bool condition, string arg1 = null, string arg2 = null)
194 | where TException : Exception, new()
195 | {
196 | if (condition)
197 | {
198 | return;
199 | }
200 |
201 | var exceptionType = typeof(TException);
202 |
203 | // Look for a constructor with two string parameters.
204 |
205 | var constructor = exceptionType.GetConstructor(_twoStringArgs);
206 |
207 | if (constructor != null)
208 | {
209 | throw (Exception)constructor.Invoke(new object[] { arg1, arg2 });
210 | }
211 |
212 | // Look for a constructor with one string parameter.
213 |
214 | constructor = exceptionType.GetConstructor(_oneStringArg);
215 |
216 | if (constructor != null)
217 | {
218 | throw (Exception)constructor.Invoke(new object[] { arg1 });
219 | }
220 |
221 | // Fall back to the default constructor.
222 |
223 | throw new TException();
224 | }
225 | }
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/src/VsLinuxDebugger/Core/Remote/Configuration.cs:
--------------------------------------------------------------------------------
1 | // The type of debugger to use for this launch configuration. (i.e. 'coreclr' for .NET 3.1/5/6, 'clr' for .NET Framework).
16 | public string Type { get; set; } = "coreclr";
17 |
18 | /// The request type of this launch configuration. Currently, launch and attach are supported.
19 | public string Request { get; set; } = "launch";
20 |
21 | //// OPTIONAL ATTRIBUTES ----------------
22 |
23 | /// Executable or file to run when launching the debugger.
24 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
25 | public string Program { get; set; }
26 |
27 | /// Arugments passed to the program to debug.
28 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
29 | public string[] Args { get; set; }
30 |
31 | /// Current working directory for finding dependencies and other files.
32 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
33 | public string Cwd { get; set; }
34 |
35 | /// Break immediately when the program launches.
36 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
37 | public bool StopAtEntry { get; set; }
38 |
39 | /// What kind of console to use. For example, 'internalConsole', 'integratedTerminal', or 'externalTerminal'.
40 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
41 | public string Console => "integratedTerminal"; // "internalConsole";
42 |
43 | /// Environment variables (the value null can be used to "undefine" a variable).
44 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
45 | public string Env { get; set; }
46 |
47 | //// ---- TESTING PHASE BELOW ----
48 | ////
49 | //// /// Program name to debug (i.e. 'TestApp.exe').
50 | //// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
51 | //// public string ProcessName { get; set; }
52 | ////
53 | //// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
54 | //// public PipeTransport PipeTransport { get; set; }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/VsLinuxDebugger/Core/Remote/Launch.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | // Remote machine DotNet path
16 | /// Name of app(.dll) or full path on remote machine.
17 | /// Working directory (CWD) where app resides.
18 | /// Custom environment variables.
19 | public Launch(string dotNetPath, string remoteAppFileName, string remoteOutputFolder, string envVariables = default, bool stopAtEntry = false)
20 | {
21 | ////string appPath = LinuxPath.Combine(remoteDebugFolder, $"{appName}.dll");
22 | string appToLaunch = remoteAppFileName;
23 |
24 | System.IO.Path.Combine("");
25 | _remoteDotNetPath = dotNetPath;
26 | _args = new string[] { appToLaunch };
27 | _remoteOutputFolder = remoteOutputFolder;
28 | _environmentVariables = envVariables;
29 | _stopAtEntry = stopAtEntry;
30 |
31 | Configurations = new List
32 | {
33 | new Configuration
34 | {
35 | Program = _remoteDotNetPath,
36 | Args = _args,
37 | Cwd = _remoteOutputFolder,
38 | StopAtEntry = _stopAtEntry,
39 | Env = _environmentVariables,
40 | }
41 | };
42 | }
43 |
44 | public string Version => "0.2.0";
45 |
46 | public string Adapter { get; set; }
47 |
48 | public string AdapterArgs { get; set; }
49 |
50 | public List Configurations { get; set; }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/VsLinuxDebugger/Core/Remote/PipeTransport.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | // _buildTask = null;
22 | private DTE _dte;
23 | private LaunchBuilder _launchBuilder;
24 | private string _launchJsonPath = string.Empty;
25 | private UserOptions _options;
26 | //// private SshTool _ssh;
27 |
28 | public RemoteDebugger(UserOptions options)
29 | {
30 | ThreadHelper.ThrowIfNotOnUIThread();
31 |
32 | _options = options;
33 | _dte = (DTE)Package.GetGlobalService(typeof(DTE));
34 | _dte.Events.BuildEvents.OnBuildProjConfigDone += BuildEvents_OnBuildProjConfigDone;
35 | _dte.Events.BuildEvents.OnBuildDone += BuildEvents_OnBuildDone;
36 | }
37 |
38 | public static BuildEvents BuildEvents { get; set; }
39 |
40 | /// Perform operation.
41 | /// Build options.
42 | /// True on success.
43 | public async Task BeginAsync(BuildOptions buildOptions)
44 | {
45 | try
46 | {
47 | await Task.Yield();
48 |
49 | if (!Initialize())
50 | {
51 | return false;
52 | }
53 |
54 | if (buildOptions.HasFlag(BuildOptions.Build))
55 | {
56 | BuildBegin();
57 |
58 | await _buildTask.Task;
59 |
60 | // Work completed
61 | if (!_buildSuccessful)
62 | {
63 | Logger.Output("Build was not successful.");
64 | return false;
65 | }
66 | }
67 |
68 | var remoteInfo = GetRemoteConnectionInfo();
69 |
70 | using (var ssh = new SshTool(remoteInfo))
71 | {
72 | var success = await ssh.ConnectAsync();
73 | if (!success)
74 | {
75 | Logger.Output("Could not connect to remote device.");
76 | return false;
77 | }
78 |
79 | var vsDbgFolder = LinuxPath.Combine(_options.RemoteVsDbgBasePath, Constants.VS2022);
80 |
81 | await ssh.TryInstallVsDbgAsync(vsDbgFolder);
82 | await ssh.MakeDeploymentFolderAsync(_options.RemoteDeployBasePath);
83 | await ssh.CleanFolderAsync(_launchBuilder.RemoteDeployProjectFolder);
84 |
85 | if (buildOptions.HasFlag(BuildOptions.Deploy))
86 | {
87 | await ssh.UploadFilesAsync(_launchBuilder.OutputDirFullPath, _launchBuilder.RemoteDeployProjectFolder);
88 | }
89 | ////else if (buildOptions.HasFlag(BuildOptions.Publish))
90 | ////{
91 | //// // This is PUBLISH not our 'deployer'
92 | ////}
93 |
94 | // The following replaces -->> if (_options.RemoteDebugDisplayGui)
95 | if (buildOptions.HasFlag(BuildOptions.Launch))
96 | {
97 | var cmd = $"DISPLAY=:0 dotnet \"{_launchBuilder.RemoteDeployAssemblyFilePath}\" &";
98 | //// var retPid = ssh.Bash(cmd);
99 |
100 | // RET: "[1] 31974"
101 | var retPid = ssh.BashStream(cmd, "[");
102 | Logger.Output($"Launch command returned: {retPid}");
103 |
104 | //ssh.BashStream("export DISPLAY=:0");
105 | //ssh.BashStream($"dotnet \"{_launchBuilder.RemoteDeployAssemblyFilePath}\"");
106 | }
107 |
108 | if (buildOptions.HasFlag(BuildOptions.Debug) && buildOptions.HasFlag(BuildOptions.Launch))
109 | {
110 | ; // TODO: Find ProcId and set Launch.json to `Attach`
111 | }
112 | else if (buildOptions.HasFlag(BuildOptions.Debug))
113 | {
114 | BuildDebugAttacher();
115 | }
116 | }
117 |
118 | BuildCleanup();
119 | }
120 | catch (Exception ex)
121 | {
122 | Logger.Output($"An error occurred during the build process. {ex.Message}");
123 | return false;
124 | }
125 |
126 | return true;
127 | }
128 |
129 | public void Dispose()
130 | {
131 | try
132 | {
133 | ThreadHelper.ThrowIfNotOnUIThread();
134 | _dte.Events.BuildEvents.OnBuildProjConfigDone -= BuildEvents_OnBuildProjConfigDone;
135 | _dte.Events.BuildEvents.OnBuildDone -= BuildEvents_OnBuildDone;
136 | }
137 | catch { }
138 | }
139 |
140 | /// Validate if we have a startup project and that it's for C#.
141 | /// True if valid.
142 | public bool IsProjectValid()
143 | {
144 | ThreadHelper.ThrowIfNotOnUIThread();
145 |
146 | //// var dte = (DTE)Package.GetGlobalService(typeof(DTE));
147 | var sb = (SolutionBuild2)_dte.Solution.SolutionBuild;
148 | return sb.StartupProjects != null && ((Array)sb.StartupProjects).Cast().Count() > 0;
149 | }
150 |
151 | private void BuildBegin()
152 | {
153 | // TODO: Disable the menu buttons.
154 | ThreadHelper.ThrowIfNotOnUIThread();
155 | BuildEvents = _dte.Events.BuildEvents;
156 |
157 | _buildTask = new TaskCompletionSource();
158 |
159 | // For some reason, cleanup isn't actually always ran when there has been an error.
160 | // This removes the fact that if you run a debug attempt, get a file error, that you don't get 2 message boxes, 3 message boxes, etc for each attempt.
161 | ////BuildEvents.OnBuildDone -= BuildEvents_OnBuildDoneAsync;
162 | ////BuildEvents.OnBuildDone += BuildEvents_OnBuildDoneAsync;
163 | ////BuildEvents.OnBuildProjConfigDone -= BuildEvents_OnBuildProjConfigDone;
164 | ////BuildEvents.OnBuildProjConfigDone += BuildEvents_OnBuildProjConfigDone;
165 |
166 | _dte.SuppressUI = false;
167 | _dte.Solution.SolutionBuild.BuildProject(_launchBuilder.ProjectConfigName, _launchBuilder.ProjectFileFullPath);
168 | }
169 |
170 | private void BuildCleanup()
171 | {
172 | // Not really needed
173 | if (_launchBuilder.DeleteLaunchJsonAfterBuild && File.Exists(_launchJsonPath))
174 | File.Delete(_launchJsonPath);
175 |
176 | //// BuildEvents.OnBuildDone -= BuildEvents_OnBuildDoneAsync;
177 | //// BuildEvents.OnBuildProjConfigDone -= BuildEvents_OnBuildProjConfigDone;
178 | }
179 |
180 | ///
181 | /// Start debugging using the remote visual studio server adapter
182 | ///
183 | private void BuildDebugAttacher()
184 | {
185 | ////_launchJsonPath = _launchBuilder.GenerateLaunchJson();
186 |
187 | _launchJsonPath = _launchBuilder.GenerateLaunchJson(vsdbgLogging: true);
188 | if (string.IsNullOrEmpty(_launchJsonPath))
189 | {
190 | Logger.Output("Could not generate 'launch.json'. Potential folder creation permissions in project's output directory.");
191 | }
192 |
193 | Logger.Output("Debugger launching...");
194 | Logger.Output($"- launch.json path: '{_launchJsonPath}'");
195 | Logger.Output($"- DebugAdapterHost.Launch /LaunchJson:\"{_launchJsonPath}\"");
196 |
197 | DTE2 dte2 = (DTE2)Package.GetGlobalService(typeof(SDTE));
198 | //Enable Logging for the Debugger output
199 | dte2.ExecuteCommand(DebugAdapterHostLogging, $"{DebugAdapterHostLoggingOnOutputWindow}");
200 |
201 | dte2.ExecuteCommand(DebugAdapterHost, $"{DebugAdapterLaunchJson}\"{_launchJsonPath}\"");
202 |
203 | // launchConfigName = "Debug on Linux";
204 | // DebugAdapterHost.Launch /LaunchJson:LaunchTester\Properties\launch.json /ConfigurationName:"{launchConfigName}"
205 |
206 | Logger.Output("Debug session complete.");
207 | }
208 |
209 | private void BuildEvents_OnBuildDone(vsBuildScope Scope, vsBuildAction Action)
210 | {
211 | // TODO: Re-enable the menu buttons.
212 | // Inform system that the task is complete
213 | _buildTask?.TrySetResult(true);
214 |
215 | var not = !_buildSuccessful ? "not " : "";
216 | Logger.Output($"Build was {not}successful");
217 | }
218 |
219 | private void BuildEvents_OnBuildProjConfigDone(string project, string projectConfig, string platform, string solutionConfig, bool success)
220 | {
221 | Logger.Output($"Project [success={success}]: {project}");
222 |
223 | if (!success)
224 | BuildCleanup();
225 |
226 | _buildSuccessful = Path.GetFileName(project) == $"{_launchBuilder.ProjectName}.csproj" && success;
227 | }
228 |
229 | private bool Initialize()
230 | {
231 | ThreadHelper.ThrowIfNotOnUIThread();
232 | var dte = (DTE2)Package.GetGlobalService(typeof(SDTE));
233 | var project = dte.Solution.GetStartupProject();
234 |
235 | if (project == null)
236 | return false;
237 |
238 | _launchBuilder = new LaunchBuilder(dte, project, _options);
239 |
240 | // TODO: Commandline Args
241 | //// if (_options.UseCommandLineArgs)
242 | //// _launchBuilder.CommandLineArgs = ... extract from localSettings.json
243 |
244 | return true;
245 | }
246 |
247 | private SshConnectionInfo GetRemoteConnectionInfo()
248 | {
249 | return new SshConnectionInfo
250 | {
251 | Host = _options.HostIp,
252 | Port = _options.HostPort,
253 | UserGroup = _options.UserGroupName,
254 | UserName = _options.UserName,
255 | UserPass = _options.UserPass,
256 | PrivateKeyEnabled = _options.UserPrivateKeyEnabled,
257 | PrivateKeyPath = _options.UserPrivateKeyPath,
258 | PrivateKeyPassword = _options.UserPrivateKeyPassword,
259 | };
260 | }
261 |
262 | private bool IsCSharpProject(Project vsProject)
263 | {
264 | ThreadHelper.ThrowIfNotOnUIThread();
265 |
266 | try
267 | {
268 | return vsProject.CodeModel.Language == CodeModelLanguageConstants.vsCMLanguageCSharp;
269 | }
270 | catch (Exception ex)
271 | {
272 | Logger.Output($"Only C# projects are supported at this time. {ex.Message}");
273 | return false;
274 | }
275 | }
276 | }
277 | }
278 |
--------------------------------------------------------------------------------
/src/VsLinuxDebugger/Core/SshConnectionInfo.cs:
--------------------------------------------------------------------------------
1 | namespace VsLinuxDebugger.Core
2 | {
3 | public struct SshConnectionInfo
4 | {
5 | public string Host { get; set; }
6 |
7 | public int Port { get; set; }
8 |
9 | /// User's Group (if applicable).
10 | public string UserGroup { get; set; }
11 |
12 | public string UserName { get; set; }
13 |
14 | public string UserPass { get; set; }
15 |
16 | public bool PrivateKeyEnabled { get; set; }
17 |
18 | public string PrivateKeyPath { get; set; }
19 |
20 | public string PrivateKeyPassword { get; set; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/VsLinuxDebugger/Core/UserOptions.cs:
--------------------------------------------------------------------------------
1 | namespace VsLinuxDebugger.Core
2 | {
3 | public class UserOptions
4 | {
5 | public bool DeleteLaunchJsonAfterBuild { get; set; }
6 |
7 | public string HostIp { get; set; }
8 | public int HostPort { get; set; }
9 |
10 | public bool LocalPlinkEnabled { get; set; }
11 | public string LocalPLinkPath { get; set; }
12 | public bool LocalSwitchLinuxDbgOutput { get; set; }
13 |
14 | public bool RemoteDebugDisplayGui { get; set; }
15 | public string RemoteDebugDisplayNumber { get; set; }
16 | public string RemoteDeployBasePath { get; set; }
17 | /// Full path to `dotnet` executable.
18 | public string RemoteDotNetPath { get; set; }
19 | /// Base path to VSDBG (i.e. `~/.vsdbg`).
20 | public string RemoteVsDbgBasePath { get; set; }
21 | /// Full path to VS Debugger.
22 | public string RemoteVsDbgFullPath => LinuxPath.Combine(RemoteVsDbgBasePath, Constants.VS2022, Constants.AppVSDbg);
23 |
24 | public bool UseCommandLineArgs { get; set; }
25 | public bool UsePublish { get; set; }
26 |
27 | public string UserGroupName { get; set; }
28 | public string UserName { get; set; }
29 | public string UserPass { get; set; }
30 | public bool UserPrivateKeyEnabled { get; set; }
31 | public string UserPrivateKeyPath { get; set; }
32 | public string UserPrivateKeyPassword { get; set; }
33 |
34 | public bool UseSSHExeEnabled { get; set; } = false;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/VsLinuxDebugger/DebuggerPackage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 | using System.Threading;
4 | using Microsoft.VisualStudio.Shell;
5 | using Xeno.VsLinuxDebug.OptionsPages;
6 | using Task = System.Threading.Tasks.Task;
7 |
8 | namespace VsLinuxDebugger
9 | {
10 | /// This is the class that implements the package exposed by this assembly.
11 | ///
12 | ///
13 | /// The minimum requirement for a class to be considered a valid package for Visual Studio
14 | /// is to implement the IVsPackage interface and register itself with the shell.
15 | /// This package uses the helper classes defined inside the Managed Package Framework (MPF)
16 | /// to do it: it derives from the Package class that provides the implementation of the
17 | /// IVsPackage interface and uses the registration attributes defined in the framework to
18 | /// register itself and its components with the shell. These attributes tell the pkgdef creation
19 | /// utility what data to put into .pkgdef file.
20 | ///
21 | ///
22 | /// To get loaded into VS, the package must be referred by <Asset Type="Microsoft.VisualStudio.VsPackage" ...> in .vsixmanifest file.
23 | ///
24 | ///
25 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
26 | [Guid(DebuggerPackage.PackageGuidString)]
27 | [ProvideMenuResource("Menus.ctmenu", 1)]
28 | [ProvideOptionPage(typeof(OptionsPage), "Linux Debugger", "General", 0, 0, true)]
29 | public sealed partial class DebuggerPackage : AsyncPackage
30 | {
31 | /// Package GUID string.
32 | public const string PackageGuidString = "19f87f23-7a2c-4279-ac7c-c9267776bbf9";
33 |
34 | public OptionsPage VsixOptions => (OptionsPage)GetDialogPage(typeof(OptionsPage));
35 |
36 | ///
37 | /// Initialization of the package; this method is called right after the package is sited, so this is the place
38 | /// where you can put all the initialization code that rely on services provided by VisualStudio.
39 | ///
40 | /// A cancellation token to monitor for initialization cancellation, which can occur when VS is shutting down.
41 | /// A provider for progress updates.
42 | /// A task representing the async work of package initialization, or an already completed task if there is none. Do not return null from this method.
43 | protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress)
44 | {
45 | // When initialized asynchronously, the current thread may be a background thread at this point.
46 | // Do any initialization that requires the UI thread after switching to the UI thread.
47 | await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
48 | await Commands.InitializeAsync(this);
49 |
50 | Logger.Init(this, OutputWindowType.Custom, VsixOptions.SwitchLinuxDbgOutput);
51 | Logger.Output("InitializeAsync");
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/VsLinuxDebugger/DebuggerPackage.vsct:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
25 |
26 |
33 |
34 |
35 |
45 |
46 |
47 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
64 |
65 |
74 |
75 |
84 |
85 |
95 |
96 |
107 |
108 |
120 |
121 |
124 |
125 |
137 |
138 |
147 |
148 |
149 |
150 |
151 |
158 |
159 |
160 |
161 |
162 |
163 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
223 |
224 |
225 |
--------------------------------------------------------------------------------
/src/VsLinuxDebugger/Extensions/JsonExtension.cs:
--------------------------------------------------------------------------------
1 | /*
2 | using System;
3 | using System.Collections;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Reflection;
7 | using System.Text;
8 | using System.Text.Json;
9 | */
10 | namespace VsLinuxDebugger.Extensions
11 | {
12 | /*
13 | ///
14 | /// System.Text.Json extension.
15 | /// License: MIT
16 | /// https://github.com/andrewjpoole/jsonelement.extensions/blob/master/AJP.JsonElementExtensions/JsonElementExtensions.cs
17 | ///
18 | public static class JsonExtension
19 | {
20 | ///
21 | /// Method which recreates a new JsonElement from an existing one, with an extra null valued property added to the start of the list of properties.
22 | /// If you care about where the new property should appear in the list of properties, use InsertNullProperty(), although its a slightly more expensive operation.
23 | /// If you have multiple changes to make to the JsonElement, please consider/test using ParseAsJsonStringAndMutate()
24 | /// so that all changes can be done together, with only one roudtrip process.
25 | ///
26 | /// A string containing the name of the property to add
27 | /// The json serializer options to respect.
28 | /// A new JsonElement containing the old properties plus the new property
29 | public static JsonElement AddNullProperty(this JsonElement jElement, string name, JsonSerializerOptions options = null) =>
30 | jElement.ParseAsJsonStringAndMutate((utf8JsonWriter, _) => HandleNull(utf8JsonWriter, name, options));
31 |
32 | ///
33 | /// Method which recreates a new JsonElement from an existing one, with an extra property added to the start of the list of properties.
34 | /// If you care about where the new property should appear in the list of properties, use InsertProperty(), although its a slightly more expensive operation.
35 | /// If you have multiple changes to make to the JsonElement, please consider/test using ParseAsJsonStringAndMutate()
36 | /// so that all changes can be done together, with only one roudtrip process.
37 | ///
38 | /// A string containing the name of the property to add
39 | /// The value of the property to add, primitives and simple objects are supported.
40 | /// The serializer options to respect when converting values.
41 | /// A new JsonElement containing the old properties plus the new property
42 | public static JsonElement AddProperty(this JsonElement jElement, string name, object value, JsonSerializerOptions options = null) =>
43 | jElement.ParseAsJsonStringAndMutate((utf8JsonWriter, _) =>
44 | {
45 | if (value is null)
46 | {
47 | HandleNull(utf8JsonWriter, name, options);
48 | return;
49 | }
50 |
51 | utf8JsonWriter.WritePropertyName(name);
52 | RenderValue(utf8JsonWriter, value, options);
53 | });
54 |
55 | ///
56 | /// Method which attempts to convert a given JsonElement into the specified type
57 | ///
58 | /// The JsonElement to convert
59 | /// JsonSerializerOptions to use
60 | /// The specified type
61 | ///
62 | public static T ConvertToObject(this JsonElement jElement, JsonSerializerOptions options = null)
63 | {
64 | var arrayBufferWriter = new ArrayBufferWriter();
65 | using (var writer = new Utf8JsonWriter(arrayBufferWriter))
66 | jElement.WriteTo(writer);
67 |
68 | return JsonSerializer.Deserialize(arrayBufferWriter.WrittenSpan, options);
69 | }
70 |
71 | ///
72 | /// Method which attempts to convert a given JsonDocument into the specified type
73 | ///
74 | /// The JsonDocument to convert
75 | /// JsonSerializerOptions to use
76 | /// The specified type
77 | /// An instance of the specified type from the supplied JsonDocument
78 | /// Thrown if the JsonDocument cannot be dserialised into the specified type
79 | public static T ConvertToObject(this JsonDocument jDocument, JsonSerializerOptions options = null)
80 | {
81 | if (jDocument == null)
82 | throw new ArgumentNullException(nameof(jDocument));
83 |
84 | return jDocument.RootElement.ConvertToObject(options);
85 | }
86 |
87 | ///
88 | /// Method which returns a list of property name and value, from a given object
89 | ///
90 | public static IEnumerable<(string Name, object Value)> GetProperties(this object source)
91 | {
92 | if (source is IDictionary dictionary)
93 | {
94 | return dictionary.Select(x => (x.Key, x.Value));
95 | }
96 |
97 | return source.GetType()
98 | .GetProperties(BindingFlags.Public | BindingFlags.Instance)
99 | .Where(p => !p.GetGetMethod().GetParameters().Any())
100 | .Select(x => (x.Name, x.GetValue(source)));
101 | }
102 |
103 | ///
104 | /// Method which recreates a new JsonElement from an existing one, with an extra null property inserted at a specified position in the list of properties.
105 | /// If you don't care about where the new property should appear in the list of properties, or if you care more about performance,
106 | /// use AddNullProperty(), which is a slightly less expensive operation.
107 | ///
108 | ///
109 | ///
110 | ///
111 | ///
112 | public static JsonElement InsertNullProperty(this JsonElement jElement, string name, int insertAt, JsonSerializerOptions options = null) =>
113 | jElement.ParseAsJsonStringAndMutatePreservingOrder(props => props.Insert(name, null, insertAt), options);
114 |
115 | ///
116 | /// Method which recreates a new JsonElement from an existing one, with an extra property inserted at a specified position in the list of properties.
117 | /// If you don't care about where the new property should appear in the list of properties, or if you care more about performance,
118 | /// use AddProperty(), which is a slightly less expensive operation.
119 | ///
120 | ///
121 | ///
122 | ///
123 | ///
124 | ///
125 | public static JsonElement InsertProperty(this JsonElement jElement, string name, object value, int insertAt, JsonSerializerOptions options = null) =>
126 | jElement.ParseAsJsonStringAndMutatePreservingOrder(props => props.Insert(name, value, insertAt), options);
127 |
128 | ///
129 | /// Method which recreates a new JsonElement from an existing one, with the opportunity to add new properties to the start of the object
130 | /// and remove existing properties. New properties will be added to the beginning of the list, if you care about the order of the properties,
131 | /// use ParseAsJsonStringAndMutatePreservingOrder() however that method is slightly more expensive in terms of time and allocation.
132 | ///
133 | /// An Action of Utf8JsonWriter and List of strings.
134 | /// The Utf8JsonWriter allows the calling code to write additional properties, its possible to add highly complex nested structures,
135 | /// the list of strings is a list names of any existing properties to be removed from the resulting JsonElement
136 | /// A new JsonElement
137 | public static JsonElement ParseAsJsonStringAndMutate(this JsonElement jElement, Action> mutate)
138 | {
139 | if (jElement.ValueKind != JsonValueKind.Object)
140 | throw new Exception("Only able to add properties to json objects (i.e. jElement.ValueKind == JsonValueKind.Object)");
141 |
142 | var arrayBufferWriter = new ArrayBufferWriter();
143 | using (var jsonWriter = new Utf8JsonWriter(arrayBufferWriter))
144 | {
145 | jsonWriter.WriteStartObject();
146 |
147 | var namesOfPropertiesToRemove = new List();
148 |
149 | mutate?.Invoke(jsonWriter, namesOfPropertiesToRemove);
150 |
151 | foreach (var jProp in jElement.EnumerateObject())
152 | {
153 | if (!(namesOfPropertiesToRemove.Contains(jProp.Name)))
154 | {
155 | jProp.WriteTo(jsonWriter);
156 | }
157 | }
158 | jsonWriter.WriteEndObject();
159 | }
160 | var resultJson = Encoding.UTF8.GetString(arrayBufferWriter.WrittenSpan);
161 | return JsonDocument.Parse(resultJson).RootElement;
162 | }
163 |
164 | ///
165 | /// Method which recreates a new JsonElement from an existing one,
166 | /// with the opportunity to add, remove and change properties while preserving the order of the properties.
167 | /// This method is slightly more expensive in terms of time and allocation, than ParseAsJsonStringAndMutate()
168 | ///
169 | /// An Action on a list of Name/Value.
170 | /// This list contains the properties from the JsonElement in order, items can be added, removed or updated.
171 | /// The resulting JsonElement will be built from the mutated list of properties.
172 | ///
173 | /// JsonSerializerOptions that will be respected when a value is rendered.
174 | ///
175 | public static JsonElement ParseAsJsonStringAndMutatePreservingOrder(this JsonElement jElement, Action mutateProps, JsonSerializerOptions options)
176 | {
177 | if (jElement.ValueKind != JsonValueKind.Object)
178 | throw new Exception("Only able to add properties to json objects (i.e. jElement.ValueKind == JsonValueKind.Object)");
179 |
180 | var props = new PropertyList();
181 | foreach (var prop in jElement.EnumerateObject())
182 | {
183 | props.Add(prop.Name, prop.Value);
184 | }
185 |
186 | mutateProps.Invoke(props);
187 |
188 | var arrayBufferWriter = new ArrayBufferWriter();
189 | using (var jsonWriter = new Utf8JsonWriter(arrayBufferWriter))
190 | {
191 | jsonWriter.WriteStartObject();
192 |
193 | foreach (Property prop in props)
194 | {
195 | if (prop.Value is null)
196 | {
197 | HandleNull(jsonWriter, prop.Name, options);
198 | }
199 | else
200 | {
201 | jsonWriter.WritePropertyName(options?.PropertyNamingPolicy?.ConvertName(prop.Name) ?? prop.Name);
202 | if (prop.Value is JsonElement jProp)
203 | {
204 | jProp.WriteTo(jsonWriter);
205 | }
206 | else
207 | {
208 | RenderValue(jsonWriter, prop.Value, options);
209 | }
210 | }
211 | }
212 |
213 | jsonWriter.WriteEndObject();
214 | }
215 |
216 | var resultJson = Encoding.UTF8.GetString(arrayBufferWriter.WrittenSpan);
217 | return JsonDocument.Parse(resultJson).RootElement;
218 | }
219 |
220 | ///
221 | /// Method which recreates a new JsonElement from an existing one, but without some of the exiting properties
222 | ///
223 | /// A list of names of the properties to remove
224 | /// A new JsonElement without the properties listed
225 | public static JsonElement RemoveProperties(this JsonElement jElement, IEnumerable propertyNamesToRemove) =>
226 | jElement.ParseAsJsonStringAndMutate((writer, namesOfPropertiesToRemove) => namesOfPropertiesToRemove.AddRange(propertyNamesToRemove));
227 |
228 | ///
229 | /// Method which recreates a new JsonElement from an existing one, but without one of the exiting properties
230 | ///
231 | /// A string containing the name of the property to remove
232 | /// A new JsonElement containing the old properties apart from the named property to remove
233 | public static JsonElement RemoveProperty(this JsonElement jElement, string nameOfPropertyToRemove) =>
234 | jElement.ParseAsJsonStringAndMutate((writer, namesOfPropertiesToRemove) => namesOfPropertiesToRemove.Add(nameOfPropertyToRemove));
235 |
236 | ///
237 | /// Method which recreates a new JsonElement from an existing one, with a property updated, preserving the order of the list of properties.
238 | /// If you don't care about preserving the order of the list of properties, or if you care more about performance,
239 | /// you could use ParseAsJsonStringAndMutate() which is a slightly less expensive operation.
240 | ///
241 | ///
242 | ///
243 | ///
244 | ///
245 | public static JsonElement UpdateProperty(this JsonElement jElement, string nameOfPropertyToUpdate, object newValue, JsonSerializerOptions options = null)
246 | => jElement.ParseAsJsonStringAndMutatePreservingOrder(props =>
247 | {
248 | var propToUpdate = props.FirstOrDefault(p => p.Name == nameOfPropertyToUpdate);
249 | if (propToUpdate is null)
250 | throw new ArgumentException($"Could not find a property named {nameOfPropertyToUpdate} in the list.");
251 | propToUpdate.Value = newValue;
252 | }, options ?? new JsonSerializerOptions());
253 |
254 | private static void HandleNull(Utf8JsonWriter writer, string propName, JsonSerializerOptions options = null)
255 | {
256 | if (options?.IgnoreNullValues == true)
257 | return;
258 |
259 | writer.WriteNull(options?.PropertyNamingPolicy?.ConvertName(propName) ?? propName);
260 | }
261 |
262 | private static void RenderValue(this Utf8JsonWriter writer, object value, JsonSerializerOptions options = null)
263 | {
264 | // The value is not a primitive.
265 | if (Convert.GetTypeCode(value) == TypeCode.Object && !(value is IEnumerable) && !(value is JsonElement))
266 | {
267 | writer.WriteStartObject();
268 | foreach (var (propName, propValue) in value.GetProperties())
269 | {
270 | if (propValue is null)
271 | {
272 | HandleNull(writer, propName, options);
273 | continue;
274 | }
275 |
276 | writer.WritePropertyName(options?.PropertyNamingPolicy.ConvertName(propName) ?? propName);
277 | writer.RenderValue(propValue);
278 | }
279 |
280 | writer.WriteEndObject();
281 | return;
282 | }
283 |
284 | switch (value)
285 | {
286 | case string v:
287 | writer.WriteStringValue(v);
288 | break;
289 |
290 | case bool v:
291 | writer.WriteBooleanValue(v);
292 | break;
293 |
294 | case decimal v:
295 | writer.WriteNumberValue(v);
296 | break;
297 |
298 | case int v:
299 | writer.WriteNumberValue(v);
300 | break;
301 |
302 | case double v:
303 | writer.WriteNumberValue(v);
304 | break;
305 |
306 | case float v:
307 | writer.WriteNumberValue(v);
308 | break;
309 |
310 | case DateTime v:
311 | writer.WriteStringValue(v);
312 | break;
313 |
314 | case Guid v:
315 | writer.WriteStringValue(v);
316 | break;
317 |
318 | case JsonElement v:
319 | writer.WriteStartObject();
320 | foreach (var jProp in v.EnumerateObject())
321 | {
322 | jProp.WriteTo(writer);
323 | }
324 |
325 | writer.WriteEndObject();
326 | break;
327 |
328 | case IEnumerable