├── .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 | ![Menu Screenshot](https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/develop/docs/ScreenShot-MenuItems.png) 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 | ![Tools Options](https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/develop/docs/ScreenShot-ToolsOptions.png) 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 | ![VS Menu](docs/ScreenShot-MenuItems.png) 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 | ![Tools Options](docs/ScreenShot-ToolsOptions.png) 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 | 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 arr: 329 | writer.WriteStartArray(); 330 | arr.ToList() 331 | .ForEach(obj => RenderValue(writer, obj)); 332 | writer.WriteEndArray(); 333 | break; 334 | 335 | default: 336 | writer.WriteStringValue(value.ToString()); 337 | break; 338 | } 339 | } 340 | 341 | public class Property 342 | { 343 | public string Name { get; set; } 344 | 345 | public object Value { get; set; } 346 | } 347 | 348 | public class PropertyList : IEnumerable 349 | { 350 | private readonly List _properties = new List(); 351 | 352 | public void Add(string name, object value) 353 | { 354 | _properties.Add(new Property { Name = name, Value = value }); 355 | } 356 | 357 | public Property GetByName(string name) 358 | { 359 | return _properties.FirstOrDefault(p => p.Name == name); 360 | } 361 | 362 | public IEnumerator GetEnumerator() 363 | { 364 | return _properties.GetEnumerator(); 365 | } 366 | 367 | public void Insert(string name, object value, int insertAt) 368 | { 369 | _properties.Insert(insertAt, new Property { Name = name, Value = value }); 370 | } 371 | 372 | public void Remove(string name) 373 | { 374 | var propertyToRemove = _properties.First(p => p.Name == name); 375 | _properties.Remove(propertyToRemove); 376 | } 377 | 378 | IEnumerator IEnumerable.GetEnumerator() 379 | { 380 | return _properties.GetEnumerator(); 381 | } 382 | } 383 | } 384 | */ 385 | } 386 | -------------------------------------------------------------------------------- /src/VsLinuxDebugger/Extensions/ProcessExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Process = System.Diagnostics.Process; 5 | 6 | namespace VsLinuxDebugger.Extensions 7 | { 8 | public static class ProcessExtension 9 | { 10 | public static async Task WaitForExitAsync(this Process process, CancellationToken cancellationToken = default) 11 | { 12 | var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); 13 | 14 | void Process_Exited(object sender, EventArgs e) 15 | { 16 | tcs.TrySetResult(true); 17 | } 18 | 19 | process.EnableRaisingEvents = true; 20 | process.Exited += Process_Exited; 21 | 22 | try 23 | { 24 | if (process.HasExited) 25 | { 26 | return process.ExitCode; 27 | } 28 | 29 | using (cancellationToken.Register(() => tcs.TrySetCanceled())) 30 | { 31 | await tcs.Task.ConfigureAwait(false); 32 | } 33 | } 34 | finally 35 | { 36 | process.Exited -= Process_Exited; 37 | } 38 | 39 | return process.ExitCode; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/VsLinuxDebugger/Extensions/SolutionExtension.cs: -------------------------------------------------------------------------------- 1 | using EnvDTE; 2 | using Microsoft.VisualStudio.Shell; 3 | 4 | namespace VsLinuxDebugger.Extensions 5 | { 6 | public static class SolutionExtension 7 | { 8 | /// Gets the project located in the given solution. 9 | /// The solution. 10 | /// The unique name of the project. 11 | /// null if the project could not be found. 12 | public static Project GetProject(Solution solution, string uniqueName) 13 | { 14 | ThreadHelper.ThrowIfNotOnUIThread(); 15 | Project ret = null; 16 | 17 | if (solution != null && uniqueName != null) 18 | { 19 | foreach (Project p in solution.Projects) 20 | { 21 | ret = GetSubProject(p, uniqueName); 22 | 23 | if (ret != null) 24 | break; 25 | } 26 | } 27 | 28 | return ret; 29 | } 30 | 31 | /// Gets the startup project for the given solution. 32 | /// The solution. 33 | /// null if the startup project cannot be found. 34 | public static Project GetStartupProject(this Solution solution) 35 | { 36 | ThreadHelper.ThrowIfNotOnUIThread(); 37 | Project ret = null; 38 | 39 | if (solution?.SolutionBuild?.StartupProjects != null) 40 | { 41 | string uniqueName = (string)((object[])solution.SolutionBuild.StartupProjects)[0]; 42 | 43 | // Can't use the solution.Item(uniqueName) here since that doesn't work 44 | // for projects under solution folders. 45 | ret = GetProject(solution, uniqueName); 46 | } 47 | 48 | return ret; 49 | } 50 | 51 | /// Gets a project located under another project item. 52 | /// The project to start the search from. 53 | /// Unique name of the project. 54 | /// null if the project can't be found. 55 | /// Only works for solution folders. 56 | private static Project GetSubProject(Project project, string uniqueName) 57 | { 58 | ThreadHelper.ThrowIfNotOnUIThread(); 59 | Project ret = null; 60 | 61 | if (project != null) 62 | { 63 | if (project.UniqueName == uniqueName) 64 | { 65 | ret = project; 66 | } 67 | else if (project.Kind == Constants.vsProjectKindSolutionItems) 68 | { 69 | // Solution folder 70 | foreach (ProjectItem projectItem in project.ProjectItems) 71 | { 72 | ret = GetSubProject(projectItem.SubProject, uniqueName); 73 | 74 | if (ret != null) 75 | break; 76 | } 77 | } 78 | } 79 | 80 | return ret; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/VsLinuxDebugger/Helpers.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Net; 3 | 4 | namespace Xeno.VsLinuxDebug.Common 5 | { 6 | public static class Helpers 7 | { 8 | public static IPAddress ResolveHost(string hostNameOrAddress) 9 | { 10 | IPAddress result; 11 | if (IPAddress.TryParse(hostNameOrAddress, out result)) 12 | return result; 13 | else 14 | return Dns.GetHostEntry(hostNameOrAddress).AddressList.First(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/VsLinuxDebugger/LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 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 | -------------------------------------------------------------------------------- /src/VsLinuxDebugger/Logger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio; 3 | using Microsoft.VisualStudio.Shell; 4 | using Microsoft.VisualStudio.Shell.Interop; 5 | 6 | namespace VsLinuxDebugger 7 | { 8 | public enum OutputWindowType 9 | { 10 | Debug, 11 | General, 12 | Custom, 13 | }; 14 | 15 | public static class Logger 16 | { 17 | private static string _name; 18 | private static IVsOutputWindowPane _outputPane; 19 | private static OutputWindowType _outputType = OutputWindowType.Debug; 20 | private static IServiceProvider _provider; 21 | 22 | private static string FormattedTime 23 | { 24 | get 25 | { 26 | return string.Format( 27 | "{0:00}:{1:00}:{2:00}.{3:000}", 28 | DateTime.Now.Hour, 29 | DateTime.Now.Minute, 30 | DateTime.Now.Second, 31 | DateTime.Now.Millisecond); 32 | } 33 | } 34 | 35 | /// 36 | /// Automatically switch Visual Studio Output Window to 'Linux Debugger'. 37 | /// 38 | public static bool AutoSwitchToLinuxDbgOutput { get; set; } 39 | 40 | public static void Init( 41 | IServiceProvider provider, 42 | OutputWindowType outputType = OutputWindowType.Debug, 43 | bool autoSwitchToLinuxDbgOutput = true, 44 | string name = "Linux Debugger") 45 | { 46 | _provider = provider; 47 | _outputType = outputType; 48 | AutoSwitchToLinuxDbgOutput = autoSwitchToLinuxDbgOutput; 49 | _name = name; 50 | } 51 | 52 | public static void Debug(string message) 53 | { 54 | #if DEBUG 55 | Output($"DEBUG: {message}"); 56 | #endif 57 | } 58 | 59 | public static void Output(string message) 60 | { 61 | var msg = $"{FormattedTime}: {message}{Environment.NewLine}"; 62 | 63 | try 64 | { 65 | ThreadHelper.ThrowIfNotOnUIThread(); 66 | 67 | if (HasOutputWindow()) 68 | { 69 | _outputPane.OutputStringThreadSafe(msg); 70 | 71 | // Brings pane into view 72 | if (AutoSwitchToLinuxDbgOutput) 73 | _outputPane.Activate(); 74 | } 75 | } 76 | catch (Exception) 77 | { 78 | Console.Write($"Failed to Output: '{msg}'"); 79 | } 80 | } 81 | 82 | private static bool HasOutputWindow() 83 | { 84 | ThreadHelper.ThrowIfNotOnUIThread(); 85 | 86 | if (_outputPane == null && _provider != null) 87 | { 88 | Guid guid = Guid.NewGuid(); 89 | 90 | ////IVsOutputWindow output = Package.GetGlobalService(typeof(SVsOutputWindow)) as IVsOutputWindow; 91 | ////if (output is null) 92 | //// return false; 93 | 94 | IVsOutputWindow output = (IVsOutputWindow)_provider.GetService(typeof(SVsOutputWindow)); 95 | if (output is null) 96 | return false; 97 | 98 | switch (_outputType) 99 | { 100 | case OutputWindowType.Debug: 101 | guid = VSConstants.GUID_OutWindowDebugPane; 102 | break; 103 | 104 | case OutputWindowType.General: 105 | guid = VSConstants.GUID_OutWindowGeneralPane; 106 | break; 107 | 108 | case OutputWindowType.Custom: 109 | default: 110 | output.CreatePane(ref guid, _name, 1, 1); 111 | break; 112 | } 113 | 114 | output.GetPane(ref guid, out _outputPane); 115 | ////output.Activate(); // Brings this pane into view 116 | } 117 | 118 | return _outputPane != null; 119 | 120 | // Reference: 121 | // - https://stackoverflow.com/a/1852535/249492 122 | // - https://docs.microsoft.com/en-us/visualstudio/extensibility/extending-the-output-window?view=vs-2022 123 | // - https://github.com/microsoft/VSSDK-Extensibility-Samples/blob/master/Reference_Services/C%23/Reference.Services/HelperFunctions.cs 124 | // 125 | // TODO: 126 | // 1) Consider passing in IServiceProvider from Commands class 127 | // 2) Use the MS GitHub example 128 | // 129 | ////// TODO: Use main thread 130 | ////////await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken); 131 | ////ThreadHelper.ThrowIfNotOnUIThread(); 132 | //// 133 | ////IVsOutputWindow output = Package.GetGlobalService(typeof(SVsOutputWindow)) as IVsOutputWindow; 134 | //// 135 | ////// Guid debugPaneGuid = VSConstants.GUID_OutWindowDebugPane; 136 | ////Guid generalPaneGuid = VSConstants.GUID_OutWindowGeneralPane; 137 | ////IVsOutputWindowPane generalPane; 138 | ////output.GetPane(ref generalPaneGuid, out generalPane); 139 | //// 140 | ////generalPane.OutputStringThreadSafe(message); 141 | ////generalPane.Activate(); // Brings this pane into view 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/VsLinuxDebugger/OptionsPages/OptionsPage.DotNet.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using Microsoft.VisualStudio.Shell; 3 | using VsLinuxDebugger.Core; 4 | 5 | namespace Xeno.VsLinuxDebug.OptionsPages 6 | { 7 | public partial class OptionsPage : DialogPage 8 | { 9 | private const string Experimental = "Warning Experimental"; 10 | private const string RemoteDebugger = "Remote Debugger"; 11 | 12 | [Category(Experimental)] 13 | [DisplayName("Debug Display GUI")] 14 | [Description( 15 | "Display application on remote machine. This is helpful for debugging " + 16 | "GUI applications on remote devices.")] 17 | public bool RemoteDebugDisplayGui { get; set; } = false; 18 | 19 | [Category(RemoteDebugger)] 20 | [DisplayName("Upload to folder")] 21 | [Description("Folder for to transfer files to. For HOME folder, use './VSLinuxDbg' and not '~/VSLinuxDbg'")] 22 | public string RemoteDeployBasePath { get; set; } = $"./VSLinuxDbg"; // "LinuxDbg" 23 | 24 | [Category(RemoteDebugger)] 25 | [DisplayName(".NET executable")] 26 | [Description("Path of the .NET executable on remote machine. (Samples: `dotnet`, `~/.dotnet/dotnet`)")] 27 | public string RemoteDotNetPath { get; set; } = Constants.DefaultDotNetPath; 28 | 29 | [Category(RemoteDebugger)] 30 | [DisplayName("Visual Studio Debugger Path")] 31 | [Description( 32 | "Root folder of Visual Studio Debugger. " + 33 | "(Samples: `~/.vs-debugger/`, `~/.vsdbg`)")] 34 | public string RemoteVsDbgRootPath { get; set; } = Constants.DefaultVsdbgBasePath; 35 | 36 | [Category(Experimental)] 37 | [DisplayName("Use Command Line Arguments")] 38 | [Description( 39 | "Apply command line arguments from Visual Studio Project Settings. " + 40 | "(Experimental : Project Settings -> Debugging -> Command Line Arguments)")] 41 | public bool UseCommandLineArgs { get; set; } = false; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/VsLinuxDebugger/OptionsPages/OptionsPage.Local.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using Microsoft.VisualStudio.Shell; 3 | using VsLinuxDebugger; 4 | 5 | namespace Xeno.VsLinuxDebug.OptionsPages 6 | { 7 | public partial class OptionsPage : DialogPage 8 | { 9 | private const string Local = "Local Settings"; 10 | private bool _autoSwitchLinuxDbgOutput = false; 11 | 12 | //// [Category("Build Options")] 13 | //// [DisplayName("Use Command Line Arguments")] 14 | //// public bool DeployWithoutDebugging { get; set; } = false; 15 | 16 | ////[Category(Experimental)] 17 | ////[DisplayName("Use Publish")] 18 | ////[Description("Publish the solution instead of building. Apply setting for ASP.NET/Blazor projects.")] 19 | ////public bool Publish { get; set; } = false; 20 | 21 | ////[Category(Local)] 22 | ////[DisplayName("PLink: Enable Plink instead of SSH")] 23 | //// 24 | //// [Category(Local)] 25 | //// to TRUE to debug with PLINK.EXE and FALSE for SSH.")] 26 | //// 27 | //// [Category(Local)] 28 | //// Enabled { get; set; } = false; 29 | 30 | [Category(Local)] 31 | [DisplayName("PLink Local Path (blank to use embedded)")] 32 | [Description(@"Full path to local PLINK.EXE file. (i.e. 'C:\temp\putty\plink.exe')")] 33 | public string PLinkPath { get; set; } = ""; 34 | 35 | [Category(Local)] 36 | [DisplayName("Delete 'launch.json' after build.")] 37 | [Description(@"The `launch.json` is generated in your build folder. You may keep this for debugging.")] 38 | public bool DeleteLaunchJsonAfterBuild { get; set; } = false; 39 | 40 | [Category(Local)] 41 | [DisplayName("Switch to LinuxDbg Output on Build")] 42 | [Description("Automatically show output for Linux Debugger on build (default = false).")] 43 | public bool SwitchLinuxDbgOutput 44 | { 45 | get => _autoSwitchLinuxDbgOutput; 46 | set 47 | { 48 | Logger.AutoSwitchToLinuxDbgOutput = value; 49 | _autoSwitchLinuxDbgOutput = value; 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/VsLinuxDebugger/OptionsPages/OptionsPage.Ssh.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.IO; 4 | using Microsoft.VisualStudio.Shell; 5 | 6 | namespace Xeno.VsLinuxDebug.OptionsPages 7 | { 8 | public partial class OptionsPage : DialogPage 9 | { 10 | private const string Credentials = "Remote Credentials"; 11 | private const string XDisplay = "Remote X11 Display"; 12 | 13 | [Category(Credentials)] 14 | [DisplayName("Host IP Address")] 15 | [Description("Host IP Address. On VMs using 'NAT', set IP to '127.0.0.1' and forward Port 22. PCs and VMs 'Bridged', have their own IP.")] 16 | public string HostIp { get; set; } = "127.0.0.1"; 17 | 18 | ////[Category(Credientials)] 19 | ////[DisplayName("Host Port Number (22)")] 20 | ////[Description("Remote Host Port Number (SSH Default is 22)")] 21 | public int HostPort { get; set; } = 22; 22 | 23 | [Category(Credentials)] 24 | [DisplayName("User Group Name (optional)")] 25 | [Description("Remote Machine Group Name. For basic setups (i.e. RaspberryPI) it's the same as UserName.")] 26 | public string UserGroupName { get; set; } = ""; 27 | 28 | [Category(Credentials)] 29 | [DisplayName("User Name")] 30 | [Description("SSH User name on remote machine.")] 31 | public string UserName { get; set; } = "pi"; 32 | 33 | [Category(Credentials)] 34 | [DisplayName("User Password")] 35 | [Description("SSH Password on remote machine.")] 36 | public string UserPass { get; set; } = "raspberry"; 37 | 38 | [Category(Credentials)] 39 | [DisplayName("SSH Key File Enabled")] 40 | [Description("Use SSH Key for connecting to remote machine.")] 41 | public bool UserPrivateKeyEnabled { get; set; } = false; 42 | 43 | [Category(Credentials)] 44 | [DisplayName("SSH Private Key File (optional)")] 45 | [Description("Private key file.")] 46 | public string UserPrivateKeyPath { get; set; } = Path.Combine( 47 | Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), 48 | ".ssh\\id_rsa"); 49 | 50 | [Category(Credentials)] 51 | [DisplayName("SSH Private Key Password (optional)")] 52 | [Description("Private key password (only if it was set).")] 53 | public string UserPrivateKeyPassword { get; set; } = ""; 54 | 55 | [Category(Credentials)] 56 | [DisplayName("Use SSH.exe with integrated user/[..]/.ssh/id_rsa instead of PLINK")] 57 | [Description("Use SSH.exe with integrated user/[..]/.ssh/id_rsa instead of PLINK")] 58 | public bool UseSSHExeEnabled { get; set; } = true; 59 | 60 | /*[Category(Credientials)] 61 | [DisplayName("PLINK PPK Key File Enabled")] 62 | [Description("Use SSH Key for connecting to remote machine.")] 63 | public bool UserPlinkPrivateKeyEnabled { get; set; } = false; 64 | 65 | [Category(Credientials)] 66 | [DisplayName("SSH Private Key File (optional)")] 67 | [Description("Private key file.")] 68 | public string UserPlinkPrivateKeyPath { get; set; } = Path.Combine( 69 | Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), 70 | ".ssh\\id_rsa"); 71 | */ 72 | 73 | [Category(XDisplay)] 74 | [DisplayName("Remote X11 Display Number (optional)")] 75 | [Description("Remote X11 Display number (only if it was set). Defaults to ':0'.")] 76 | public string RemoteDebugDisplayNumber { get; set; } = ":0"; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/VsLinuxDebugger/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("VS Linux Debugger")] 8 | [assembly: AssemblyDescription("Remote system deployment and debugger for Visual Studio IDE")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("Xeno Innovations, Inc.")] 11 | [assembly: AssemblyProduct("VS Linux Debugger")] 12 | [assembly: AssemblyCopyright("Copyright 2022-2024 Xeno Innovations, Inc.")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // Version information for an assembly consists of the following four values: 22 | // 23 | // Major Version 24 | // Minor Version 25 | // Build Number 26 | // Revision 27 | // 28 | // You can specify all the values or you can default the Build and Revision Numbers 29 | // by using the '*' as shown below: 30 | // [assembly: AssemblyVersion("1.0.*")] 31 | [assembly: AssemblyVersion("2.2.0.0")] 32 | [assembly: AssemblyFileVersion("2.2.0.0")] 33 | -------------------------------------------------------------------------------- /src/VsLinuxDebugger/Resources/BuildIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/src/VsLinuxDebugger/Resources/BuildIcon.png -------------------------------------------------------------------------------- /src/VsLinuxDebugger/Resources/BuildSelection_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/src/VsLinuxDebugger/Resources/BuildSelection_16x.png -------------------------------------------------------------------------------- /src/VsLinuxDebugger/Resources/DebugOnly_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/src/VsLinuxDebugger/Resources/DebugOnly_16x.png -------------------------------------------------------------------------------- /src/VsLinuxDebugger/Resources/DebuggerPackage.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/src/VsLinuxDebugger/Resources/DebuggerPackage.ico -------------------------------------------------------------------------------- /src/VsLinuxDebugger/Resources/DeployAndDebug_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/src/VsLinuxDebugger/Resources/DeployAndDebug_16x.png -------------------------------------------------------------------------------- /src/VsLinuxDebugger/Resources/DeployOnly_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/src/VsLinuxDebugger/Resources/DeployOnly_16x.png -------------------------------------------------------------------------------- /src/VsLinuxDebugger/Resources/Process_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/src/VsLinuxDebugger/Resources/Process_16x.png -------------------------------------------------------------------------------- /src/VsLinuxDebugger/Resources/Settings_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/src/VsLinuxDebugger/Resources/Settings_16x.png -------------------------------------------------------------------------------- /src/VsLinuxDebugger/Resources/ShowLog_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/src/VsLinuxDebugger/Resources/ShowLog_16x.png -------------------------------------------------------------------------------- /src/VsLinuxDebugger/Resources/SshDebugCommand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/src/VsLinuxDebugger/Resources/SshDebugCommand.png -------------------------------------------------------------------------------- /src/VsLinuxDebugger/Resources/TuxDebug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SuessLabs/VsLinuxDebug/78ddb5aa102fd4794ce13396a4042e1da7eefea4/src/VsLinuxDebugger/Resources/TuxDebug.png -------------------------------------------------------------------------------- /src/VsLinuxDebugger/Services/ConnectionService.cs: -------------------------------------------------------------------------------- 1 | using Renci.SshNet; 2 | 3 | namespace Xeno.VsLinuxDebug.Services 4 | { 5 | public class ConnectionService : IConnectionService 6 | { 7 | private string _hostName = ""; 8 | private string _userName = "pi"; 9 | private string _userPass = "raspberry"; 10 | 11 | public ConnectionService(string hostName, string userName, string userPass) 12 | { 13 | Ssh = new SshClient(hostName, userName, userPass); 14 | Sftp = new SftpClient(hostName, userName, userPass); 15 | } 16 | 17 | public SshClient Ssh { get; } 18 | 19 | public SftpClient Sftp { get; } 20 | 21 | public bool IsConnected { get; private set; } 22 | 23 | public void Connect() 24 | { 25 | if (IsConnected) 26 | return; 27 | 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/VsLinuxDebugger/Services/IConnectionService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Renci.SshNet; 7 | 8 | namespace Xeno.VsLinuxDebug.Services 9 | { 10 | public interface IConnectionService 11 | { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/VsLinuxDebugger/VsLinuxDebugger.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 17.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 10 | Debug 11 | AnyCPU 12 | 2.0 13 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | {C204143E-FDE0-4F29-976B-A0C8AC798BAD} 15 | Library 16 | Properties 17 | VsLinuxDebugger 18 | VsLinuxDebugger 19 | v4.7.2 20 | 8.0 21 | true 22 | true 23 | true 24 | false 25 | false 26 | true 27 | true 28 | Program 29 | $(DevEnvDir)devenv.exe 30 | /rootsuffix Exp 31 | 32 | 33 | true 34 | full 35 | false 36 | ..\..\output\Debug\ 37 | DEBUG;TRACE 38 | prompt 39 | 4 40 | 41 | 42 | pdbonly 43 | true 44 | ..\..\output\Release\ 45 | TRACE 46 | prompt 47 | 4 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | Component 68 | 69 | 70 | Component 71 | 72 | 73 | Component 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | Always 82 | true 83 | 84 | 85 | Designer 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 0.30.1 102 | 103 | 104 | 2023.0.1 105 | 106 | 107 | 6.0.10 108 | 109 | 110 | 111 | 112 | Menus.ctmenu 113 | 114 | 115 | 116 | 117 | Always 118 | true 119 | 120 | 121 | true 122 | 123 | 124 | true 125 | 126 | 127 | true 128 | 129 | 130 | Always 131 | true 132 | 133 | 134 | 135 | 136 | 137 | 144 | 145 | 146 | https://the.earth.li/~sgtatham/putty/latest/w64/plink.exe 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | true 156 | Always 157 | 158 | 159 | -------------------------------------------------------------------------------- /src/VsLinuxDebugger/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | VS Linux Debugger 11 | Remotely deploy and debug your .NET apps visa SSH on your Linux device using Visual Studio 2022. Works with popular Linux distrobutions such as Ubuntu, Raspberry Pi, and more! 12 | https://github.com/SuessLabs/VsLinuxDebug 13 | LICENSE.txt 14 | ..\..\readme.md 15 | ..\..\release-notes.md 16 | Resources\TuxDebug.png 17 | debug; build; remote debug; vsdbg; linux; avalonia; xamarin; rpi; rpi4; remotedebug; remote; debugger; linux debug; net6; dotnet; raspberry pi; ubuntu; suess; suess-labs; suesslabs; xeno-innovations 18 | 19 | 20 | 21 | amd64 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | // ACTION REQUIRED: This file was automatically added to your project, but it 3 | // will not take effect until additional steps are taken to enable it. See the 4 | // following page for additional information: 5 | // 6 | // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md 7 | // 8 | 9 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 10 | "settings": { 11 | "documentationRules": { 12 | "companyName": "Xeno Innovations, Inc.", 13 | "copyrightText": "Copyright (c) {companyName}. All rights reserved.\r\n\r\nReproduction or transmission in whole or in part, in\r\nany form or by any means, electronic, mechanical, or otherwise, is\r\nprohibited without the prior written consent of the copyright owner." 14 | }, 15 | "orderingRules": { 16 | "systemUsingDirectivesFirst": true, 17 | "usingDirectivesPlacement": "outsideNamespace" 18 | }, 19 | "layoutRules": { 20 | "allowConsecutiveUsings": false, 21 | "newlineAtEndOfFile": "allow" 22 | } 23 | } 24 | } 25 | --------------------------------------------------------------------------------