Skip to content

Commit

Permalink
feat: debugger, code run, and profile settings (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
iabdelkareem committed Feb 19, 2024
1 parent c0b6c0d commit 24cb02f
Show file tree
Hide file tree
Showing 25 changed files with 659 additions and 53 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Enhanced experience to select and launch debugging target
- Commands to build and run dotnet projects

## [0.2.0] - 2024-02-19

### Added

- Automatic debugger installation and configuration
- Effortless debugging experience
- Run projects
- Support [launch settings](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-8.0#lsj)

### Fixed

- The logger is using an incorrect log level

## [0.1.0] - 2024-02-14

### Added
Expand Down
73 changes: 63 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

## Prerequisites

- Install [fd](https://github.com/sharkdp/fd#installation) locally.
- Locally Install [fd](https://github.com/sharkdp/fd#installation).

## 🚀 Installation

Expand All @@ -25,8 +25,6 @@ Using lazy.nvim:
}
```

:warning: This plugin removes the usage of lspconfig to configure and run Omnisharp, and it shouldn't be used alongside lspconfig. Please remove the configuration of omnisharp in lspconfig. If you want to use lspconfig to configure Omnisharp, you can still use the other functionality provided by the plugin (e.g., remove unused using statements, etc.). However, you should set `config.lsp.enable` to `false`.

## ⚙ Configuration

```lua
Expand Down Expand Up @@ -59,21 +57,57 @@ Using lazy.nvim:
-- The minimum log level.
level = "INFO",
},
dap = {
-- When set, csharp.nvim won't launch install and debugger automatically. Instead, it'll use the debug adapter specified.
--- @type string?
adapter_name = nil,
}
}
```

## 🌟 Features

### Remove Unnecessary Using Statements
### Automatically Installs and Configures LSP

![csharp_fix_usings](https://github.com/iabdelkareem/csharp.nvim/assets/13891133/3902ef06-b2a0-4be8-b138-222c820cf4d6)
The plugin will automatically install the LSP `omnisharp` and configure it for use.

_:warning: Remove omnisharp configuration from lspconfig as the plugin handles configuring and running omnisharp. If you prefer configuring omnisharp manually using lspconfig, disable this feature by setting lsp.enable = false in the configuration._

<hr>

### Effortless Debugging

The plugin will automatically install the debugger `netcoredbg` and configure it for use. The goal of this functionality is to provide an effortless debugging experience to .NET developers, all you need to do is install the plugin and execute `require("csharp").debug_project()` and the plugin will take care of the rest. To make this possible the debugger supports the following features:

- Automatically detects the executable project in the solution, or let you select if there are multiple executable projects.
- Supports [launch settings](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-8.0#lsj) to configure `environmentVariables`, `applicationUrl`, and `commandLineArgs`.
- _Support is limited to launch profiles with `CommandName == Project`._
- Uses .NET CLI to build the debugee project.

![debugging](https://gist.github.com/assets/13891133/ff442270-4c0e-46d6-bf8e-25deab8dec37)

_In the illustration above, there's a solution with 3 projects, 2 of which are executable, and only one has launch settings file._

<hr>

### Run Project

Similar to the debugger, the plugin exposes the function `require("csharp").run_project()` that supports selection of an executable project, launch profile, builds and runs the project.

![run](https://github.com/iabdelkareem/csharp.nvim/assets/13891133/aa1df4e3-d3ce-43b8-a0d5-476e1b567125)

<hr>

### Remove Unnecessary Using Statements

Removes all unnecessary using statements from a document. Trigger this feature via the Command `:CsharpFixUsings` or use the Lua function below.

```lua
require("csharp").fix_usings()
```

![csharp_fix_usings](https://github.com/iabdelkareem/csharp.nvim/assets/13891133/3902ef06-b2a0-4be8-b138-222c820cf4d6)

_TIP: You can run this feature automatically before a buffer is saved._

```lua
Expand All @@ -99,29 +133,48 @@ vim.api.nvim_create_autocmd("LspAttach", {
})
```

### Fix All
<hr>

![csharp_fix_all](https://github.com/iabdelkareem/csharp.nvim/assets/13891133/5d815ce4-b9b1-40b9-a049-df1570bea100)
### Fix All

This feature allows developers to efficiently resolve a specific problem across multiple instances in the codebase (e.g., a document, project, or solution) with a single command. You can run this feature using the Command `:CsharpFixAll` or the Lua function below. When the command runs, it'll launch a dropdown menu asking you to choose the scope in which you want the plugin to search for fixes before it presents the different options to you.

```lua
require("csharp").fix_all()
```

### Enhanced Go-To-Definition (Decompilation Support)
![csharp_fix_all](https://github.com/iabdelkareem/csharp.nvim/assets/13891133/5d815ce4-b9b1-40b9-a049-df1570bea100)

![csharp_go_to_definition](https://github.com/iabdelkareem/csharp.nvim/assets/13891133/1b8ea6fa-6d6b-4cab-a060-2123247b0d74)
<hr>

### Enhanced Go-To-Definition (Decompilation Support)

Similar to [omnisharp-extended-lsp.nvim](https://github.com/Hoffs/omnisharp-extended-lsp.nvim), this feature allows developers to navigate to the definition of a symbol in the codebase with decompilation support for external code.

```lua
require("csharp").go_to_definition()
```

![csharp_go_to_definition](https://github.com/iabdelkareem/csharp.nvim/assets/13891133/1b8ea6fa-6d6b-4cab-a060-2123247b0d74)

<hr>

## :beetle: Reporting Bugs

1. Set debug level to TRACE via the configurations.
2. Reproduce the issue.
3. Open an issue in [GitHub](https://github.com/iabdelkareem/csharp.nvim/issues) with the following details:
- Description of the bug.
- How to reproduce.
- Relevant logs, if possible.

## :heart_eyes: Contributing & Feature Suggestions

I'd love to hear your ideas and suggestions for new features! Feel free to create an issue and share your thoughts. We can't wait to discuss them and bring them to life!

## TODO

- [ ] Setup Debugger
- [x] Setup Debugger
- [ ] Solution Explorer
- [ ] Switching Solution
- [ ] Support Source Generator
Expand Down
4 changes: 3 additions & 1 deletion lua/csharp.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ local function setup(user_config)
config.save(user_config)

require("csharp.commands").setup()
require("csharp.lsp").setup()
require("csharp.modules.lsp").setup()
require("csharp.log").setup()
end

Expand All @@ -14,4 +14,6 @@ return {
fix_usings = require("csharp.features.fix-usings").execute,
fix_all = require("csharp.features.fix-all").select_scope_and_execute,
go_to_definition = require("csharp.features.go-to-definition").execute,
debug_project = require("csharp.features.debugger").execute,
run_project = require("csharp.features.code-runner").execute,
}
5 changes: 5 additions & 0 deletions lua/csharp/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ local default_config = {
---@type fun(client: lsp.Client, bufnr: number)|nil
on_attach = nil,
},
--- @class CsharpConfig.Dap
dap = {
--- @type string?
adapter_name = nil,
},
---@class CsharpConfig.Logging
logging = {
level = "INFO",
Expand Down
36 changes: 36 additions & 0 deletions lua/csharp/features/code-runner.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
local M = {}
local utils = require("csharp.utils")
local notify = require("csharp.notify")

--- @async
local function _execute()
local project_information = require("csharp.features.workspace-information").select_project()

if project_information == nil then
logger.error("No project selected", { feature = "code-runner" })
return
end

local project_folder_path = vim.fn.fnamemodify(project_information.Path, ":h")

local launch_profile = require("csharp.modules.launch-settings").select_launch_profile(project_folder_path)

local opt = {
"--project",
project_information.Path,
"-c",
"Debug",
}

if launch_profile then
opt = vim.list_extend(opt, { "--launch-profile", launch_profile.name })
end

require("csharp.modules.dotnet-cli").run(opt)
end

function M.execute()
utils.run_async(_execute)
end

return M
26 changes: 26 additions & 0 deletions lua/csharp/features/debugger/config_factories/attach-debugger.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
local ui = require("csharp.ui")

--- @async
--- @param args DebugConfigFactoryArgs
--- @return table
local function create_config(args)
local processes = require("dap.utils").get_processes()
local process = ui.select_sync(processes, {
format_item = function(item)
return item.name
end,
})

return {
name = "Attach - .NET",
request = "attach",
type = "coreclr",
processId = process.pid,
}
end

return {
name = "Attach - .NET",
request = "attach",
create_config = create_config,
}
29 changes: 29 additions & 0 deletions lua/csharp/features/debugger/config_factories/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
local M = {}
local ui = require("csharp.ui")

--- @class DebugConfigFactoryArgs
--- @field project_information OmnisharpProjectInformation?

--- @class DebugConfigFactory
--- @field name string
--- @field request "launch"|"attach"
--- @field create_config fun(args: DebugConfigFactoryArgs): table

--- @type DebugConfigFactory[]
local debug_config_factories = {
require("csharp.features.debugger.config_factories.launch-debugger"),
require("csharp.features.debugger.config_factories.attach-debugger"),
}

--- @async
--- @return DebugConfigFactory
function M.select_debug_config()
return ui.select_sync(debug_config_factories, {
prompt = "Start Debugging:",
format_item = function(item)
return item.name
end,
})
end

return M
31 changes: 31 additions & 0 deletions lua/csharp/features/debugger/config_factories/launch-debugger.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
local dap = require("dap")

--- @async
--- @param args DebugConfigFactoryArgs
--- @return table
local function create_config(args)
local project_folder_path = vim.fn.fnamemodify(args.project_information.Path, ":h")

local build_succeded = require("csharp.modules.dotnet-cli").build(args.project_information.Path, { "-c Debug" })

if not build_succeded then
logger.debug("Skip debugging, build failed!", { feature = "debugger" })
error("Skip debugging, build failed!")
end

return {
name = "Launch - .NET",
request = "launch",
type = "coreclr",
cwd = project_folder_path,
program = args.project_information.TargetPath,
args = {},
env = {},
}
end

return {
name = "Launch - .NET",
request = "launch",
create_config = create_config,
}
78 changes: 78 additions & 0 deletions lua/csharp/features/debugger/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
local M = {}
local ui = require("csharp.ui")
local dap = require("dap")
local logger = require("csharp.log")
local utils = require("csharp.utils")
local notify = require("csharp.notify")
local next = next

--- @param debug_config table
--- @param launch_profile DotNetLaunchProfile
--- @return table
local function apply_launch_profile(debug_config, launch_profile)
if launch_profile.environmentVariables then
for key, value in pairs(launch_profile.environmentVariables) do
debug_config.env[key] = value
end
end

if launch_profile.commandLineArgs then
vim.tbl_deep_extend("force", debug_config.args, vim.split(launch_profile.commandLineArgs, " ", { trimempty = true }))
end

if launch_profile.applicationUrl then
table.insert(debug_config.args, "--urls=" .. launch_profile.applicationUrl)
end

return debug_config
end

--- @async
local function _execute()
local debug_adapter = require("csharp.modules.dap").get_debug_adapter()
if debug_adapter == nil then
logger.error("Debug Adapter is not installed or configured.", { feature = "debugger" })
return
end

if next(dap.sessions()) ~= nil then
logger.debug("Debugging is already running, using dap.continue().", { feature = "debugger" })
dap.continue()
return
end

notify.info("Preparing debugger!")
local debug_config_factory = require("csharp.features.debugger.config_factories").select_debug_config()
local debug_config

logger.debug("Selected debug config factory", { feature = "debugger", debug_config_factory = debug_config_factory })
if debug_config_factory.request == "attach" then
debug_config = debug_config_factory.create_config({})
else
local project_information = require("csharp.features.workspace-information").select_project()

if project_information == nil then
logger.error("No project selected", { feature = "debugger" })
return
end

debug_config = debug_config_factory.create_config({ project_information = project_information })
local project_folder_path = vim.fn.fnamemodify(project_information.Path, ":h")
local launch_profile = require("csharp.modules.launch-settings").select_launch_profile(project_folder_path)

if launch_profile then
logger.debug("Applying launch profile to debug config.", { feature = "debugger", launch_profile = launch_profile, debug_config })
debug_config = apply_launch_profile(debug_config, launch_profile)
end
end

logger.debug("Starting debugger", { feature = "debugger", debug_config = debug_config })
notify.info("Starting debugger!")
dap.launch(debug_adapter, debug_config)
end

function M.execute()
utils.run_async(_execute)
end

return M
Loading

0 comments on commit 24cb02f

Please sign in to comment.