From 95206fa900e1483c2461c7cca25eeb26a6219897 Mon Sep 17 00:00:00 2001 From: Basant Khalil Date: Thu, 10 Aug 2023 11:09:43 -0400 Subject: [PATCH] Get the file path and respond to initialization requests (#1647) * trying to use dap-rs intially for server0 * creating TCP connection for the server to attach * connecting to server and client in dap.rs; not fully successfull yet * can print result now * working for second request. still need cleanup. * got the second request to work? -_- * pretty printing * remove the loopfor server.run and clean up * clean up a bit; remove unused variables * launch the extension * convert the adapter to use stdin/stdout * revert launch.json * Make adapter agnostic to read/write operations * adding optional path argument * switch to either run with stdin/stdout or tcp * switch based on the flag to either use stdin/stdout or rather switch to tcp * add the needed dependencies for argh * remove unused imports * proper error handling * run cargo clippy and fix the linting * delete symlink committed by mistake * implement better error handling * add docs for error variants * add commands in the extension.js for printing logging messages to track the status of the extension attaching * can print as well as invoke the adpater, need to figure outpassing file as an argument * get file name from user * can successfully take file as input from the user * log when extension activates * change it so that it does not ask for file name twice * try it with multi-session * it can attach to vscode currently in multi session * work on getting file path and intialization. * changes to PR for better readability * change the release for dap-rs * Create README.md * Update README.md * Update README.md * clippy --- .vscode/launch.json | 13 +++ Cargo.lock | 146 +++++++++++++++++++----------- cider-dap/Cargo.toml | 9 +- cider-dap/README.md | 105 +++++++++++++++++++++ cider-dap/calyxDebug/extension.js | 142 +++++++++++++++++++++++++++++ cider-dap/src/error.rs | 16 +++- cider-dap/src/main.rs | 107 +++++++++++++++++++--- 7 files changed, 467 insertions(+), 71 deletions(-) create mode 100644 cider-dap/README.md create mode 100644 cider-dap/calyxDebug/extension.js diff --git a/.vscode/launch.json b/.vscode/launch.json index 116775c4d9..9e20f454f7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,6 +1,19 @@ { "version": "0.2.0", "configurations": [ + { + "type": "cider-dap", + "request": "launch", + "name": "Launch Program (Multi Session)", + "program": "${file}", + "stopOnEntry": true, + }, + { + "type": "cider-dap", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceFolder}/Program" + }, { "name": "Python: Current File", "type": "python", diff --git a/Cargo.lock b/Cargo.lock index 035757aff6..e2b818658d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -486,8 +486,8 @@ dependencies = [ [[package]] name = "dap" -version = "0.2.0-alpha1" -source = "git+https://github.com/sztomi/dap-rs?rev=7be5376#7be5376b84fbf1a3fbbc1953153e0130cbc7bfb6" +version = "0.3.1-alpha1" +source = "git+https://github.com/sztomi/dap-rs?tag=v0.3.1-alpha1#0a989dcd8bdd45233dca4af728120fa19566c416" dependencies = [ "serde", "serde_json", @@ -539,7 +539,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -561,7 +561,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core 0.20.3", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -666,9 +666,12 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] [[package]] name = "fd-lock" @@ -677,7 +680,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" dependencies = [ "cfg-if", - "rustix", + "rustix 0.38.3", "windows-sys", ] @@ -836,6 +839,15 @@ dependencies = [ "serde", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "interp" version = "0.1.1" @@ -870,6 +882,17 @@ dependencies = [ "thiserror", ] +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.2", + "libc", + "windows-sys", +] + [[package]] name = "itertools" version = "0.9.0" @@ -890,9 +913,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" [[package]] name = "js-sys" @@ -927,6 +950,12 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + [[package]] name = "linux-raw-sys" version = "0.4.3" @@ -1103,9 +1132,9 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] name = "pest" -version = "2.7.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d2d1d55045829d65aad9d389139882ad623b33b904e7c9f1b10c5b8927298e5" +checksum = "f73935e4d55e2abf7f130186537b19e7a4abc886a0252380b59248af473a3fc9" dependencies = [ "thiserror", "ucd-trie", @@ -1135,9 +1164,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f94bca7e7a599d89dea5dfa309e217e7906c3c007fb9c3299c40b10d6a315d3" +checksum = "aef623c9bbfa0eedf5a0efba11a5ee83209c326653ca31ff019bec3a95bfff2b" dependencies = [ "pest", "pest_generator", @@ -1145,22 +1174,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d490fe7e8556575ff6911e45567ab95e71617f43781e5c05490dc8d75c965c" +checksum = "b3e8cba4ec22bada7fc55ffe51e2deb6a0e0db2d0b7ab0b103acc80d2510c190" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] name = "pest_meta" -version = "2.7.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2674c66ebb4b4d9036012091b537aae5878970d6999f81a265034d85b136b341" +checksum = "a01f71cb40bd8bb94232df14b946909e14660e33fc05db3e50ae2a82d7ea0ca0" dependencies = [ "once_cell", "pest", @@ -1225,9 +1254,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" dependencies = [ "unicode-ident", ] @@ -1270,9 +1299,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.31" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" dependencies = [ "proc-macro2", ] @@ -1397,9 +1426,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.3" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +checksum = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf" dependencies = [ "aho-corasick", "memchr", @@ -1420,22 +1449,36 @@ checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "rustix" -version = "0.38.4" +version = "0.37.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys", +] + +[[package]] +name = "rustix" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4" dependencies = [ "bitflags 2.3.3", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.4.3", "windows-sys", ] [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "dc31bd9b61a32c31f9650d18add92aa83a49ba979c143eefd27fe7177b05bd5f" [[package]] name = "rusty-fork" @@ -1474,9 +1517,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" [[package]] name = "same-file" @@ -1495,9 +1538,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.174" +version = "1.0.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b88756493a5bd5e5395d53baa70b194b05764ab85b59e43e4b8f4e1192fa9b1" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" dependencies = [ "serde_derive", ] @@ -1514,20 +1557,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.174" +version = "1.0.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5c3a298c7f978e53536f95a63bdc4c4a64550582f31a0359a9afda6aede62e" +checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] name = "serde_json" -version = "1.0.103" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b" +checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c" dependencies = [ "itoa", "ryu", @@ -1591,7 +1634,7 @@ dependencies = [ "darling 0.20.3", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -1717,9 +1760,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.27" +version = "2.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" dependencies = [ "proc-macro2", "quote", @@ -1740,14 +1783,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.7.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" +checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" dependencies = [ + "autocfg", "cfg-if", "fastrand", "redox_syscall 0.3.5", - "rustix", + "rustix 0.37.23", "windows-sys", ] @@ -1782,22 +1826,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.44" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" +checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.44" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" +checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.25", ] [[package]] @@ -1875,9 +1919,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" [[package]] name = "unicode-segmentation" diff --git a/cider-dap/Cargo.toml b/cider-dap/Cargo.toml index 2e69afbd1f..0510e6924c 100644 --- a/cider-dap/Cargo.toml +++ b/cider-dap/Cargo.toml @@ -6,20 +6,13 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] - -dap = { git = "https://github.com/sztomi/dap-rs", rev = "7be5376" } +dap = { git = "https://github.com/sztomi/dap-rs", tag = "v0.3.1-alpha1" } thiserror = "1.*" serde_json = "1.0" serde.workspace= true owo-colors = "^3.5" argh = "0.1" - -[[bin]] -name = "demo_client" -path = "src/demo_client.rs" - [[bin]] name = "cider-dap" path = "src/main.rs" - diff --git a/cider-dap/README.md b/cider-dap/README.md new file mode 100644 index 0000000000..7e0d6c1af7 --- /dev/null +++ b/cider-dap/README.md @@ -0,0 +1,105 @@ +## About the Name? +Inspired by the comforting essence of apple cider, our sub-crate tries to bring some of that warmth to debugging. Now, onto what this project is and what's been brewing! + +cider-dap is a sub-crate created for facilitating debugging processes. The name is inspired by the core name "Cider", and "dap" stands for Debug Adapter Protocol! +### Project Overview: +Cider-dap is a debug adapter protocol implementation using Rust, which aids in debugging processes. It interfaces with IDEs or other tools using the Debug Adapter Protocol. +This project primarily leverages the Debug Adapter Protocol (DAP) for its functionality. The structure is organized into different directories and files which encapsulate the functionalities: +
+
+1.``` cider-dap ``` directory: The main directory which contains the following sub-directories and files: +
+        ```calyxDebug```: Contains the file responsible for debugging extensions and related utilities. So it is a dedicated directory for VSCode debugging extensions. It establishes the bridge between your Rust codebase and the VSCode debugging environment.
+        ```src```: Houses the Rust source files for the project. It contains the project's core functionalities, logic, and structures.
+        ```cargo.lock``` & ```cargo.toml```: Standard Rust project files detailing dependencies and project metadata.
+3. ```src``` directory:
+        ```adapter.rs```: Defines the primary adapter structure for the project and its associated functionalities. Not just any adapter, this file structures the fundamental protocols, handling the translation of high-level debugging commands into actionable, low-level instructions.
+        ```error.rs```: Contains custom error definitions and types for better error handling.
+        ```main.rs```: The entry point for the project, it integrates functionalities from the other source files and provides the main execution logic.
+4. ```calyxDebug``` directory:
+        ```extension.js```: JavaScript file for VSCode extension integration. It provides functions to interface between the VSCode environment and the Rust backend.
+ +### About main.rs: the Main File +In ``` main.rs ```, our program is set up to accommodate both single and multi-session debugging. It defines the entry point of our application and orchestrates how the server is run based on user-provided arguments. + +#### Initialization: +At the start of the ```main()``` function: + +- The Opts struct captures command-line arguments. This struct contains an optional file path, a switch to determine if the application runs in multi-session mode, and a port number (with a default of 8080). +- ```argh::from_env()``` processes the command-line arguments based on the defined struct. The use of argh simplifies command-line parsing, allowing you to focus on the main logic without getting bogged down in argument processing. + +#### Single vs. Multi-Session Decision: +Depending on whether the ```is_multi_session flag``` is set: + +##### Multi-Session: +-         A TCP listener is set up, which will listen for incoming debugger connections. +-         On accepting a connection, the streams are buffered for efficient I/O operations. +-         The multi_session_init function gets the adapter configured for the session, handling initial handshakes like the Initialize and Launch commands. +-         The run_server function then takes over, orchestrating the actual debugging session with the given adapter. +##### Single-Session: +-         Directly reads from standard input and writes to standard output. +-         Instead of expecting and processing initial handshakes, the function simply sets up the adapter with the provided file and begins the server loop. + +This dual mode is valuable: the single-session approach allows for streamlined debugging in local environments or for simpler setups, while the multi-session setup allows for more advanced scenarios, perhaps remote debugging or handling multiple debugger sessions. + +#### ```multi_session_init``` +This function sets up an adapter for a multi-session environment. Here's a step-by-step breakdown: +##### 1. Initial Handshake: +- It first waits for and processes an Initialize request. This is fundamental in the DAP as it establishes the initial connection between the debugger client and server. +- After successfully processing this request, it sends an Initialized event to notify the client that the server is ready for subsequent commands. + +##### 2. Setup: +- The next expected command is a Launch command. This command contains additional information (like the path to the program being debugged). This path is extracted and checked for validity. +- The program is then opened and used to construct the MyAdapter instance. +The purpose of this function is to perform the initial setup necessary to start a debugging session. By separating it from the run_server function, the code remains modular, allowing for easier debugging, testing, and modification. + +#### run_server : +The heart of the debugger's runtime: + +##### Core Loop : + +The function continuously polls for requests from the client. +- Upon receiving a Launch command, it sends a successful response back to the client. This indicates that the server is ready to begin debugging. +- The loop can be expanded to handle other DAP commands as needed. For example, handling a Disconnect command could cleanly terminate the loop and close the server. + +##### Command Processing: +- The only command being actively handled right now is the Launch command. Upon receiving this command, the server simply responds with a success message, indicating that it's ready to commence debugging. +- The loop is designed with extensibility in mind. Comments suggest places where commands like Disconnect can be incorporated to handle disconnection events, allowing the server to terminate gracefully. + +##### Error Handling : +- If an unknown command is received, it's printed to the error output and the server terminates with an UnhandledCommandError. +- This is a robust approach, ensuring that only expected commands are processed and any anomalies are immediately flagged. + +### Dependencies +The following dependencies have been added to the project as specified in the cargo.toml: +
+- ```dap```: Rust DAP implementation. At its core, this Rust DAP implementation is what powers cider-dap. It's the backbone that ensures all debugging actions are in line with the protocol's standards.
+- ```thiserror```: Used for ergonomic error handling. Enhancing error handling by providing more contextual feedback and streamlined debugging.
+- ```serde_json``` & ```serde```: Serialization and deserialization of data. Essential for data communication. They ensure that data structures are efficiently serialized and deserialized between different parts of the system.
+- ```owo-colors```: For colored console output. So it elevates user experience by introducing color-coded outputs, making console interactions more intuitive.
+- ```argh```: For command line argument parsing. It simplifies command line interactions, ensuring that user inputs are effectively parsed and processed.
+ +### Running the Project +1. Ensure you have the necessary dependencies installed. If not, you can install them using cargo: + ```cargo install ``` +3. To run the main project: +```cargo run ``` + +### Next Steps + +1. Advanced Error Handling: Utilize the structures in error.rs to provide detailed insights, potentially integrating with external error databases or logs. +2. Command Enhancements: Augment the DAP commands and responses in main.rs, exploring beyond traditional debugging actions. +3. There are changes needed to be done inside run_server: +### Additional Command Handling: +- Incorporate command handlers for other DAP commands: +- ```Disconnect ```: Handle disconnect commands gracefully, ensuring any necessary cleanup is done before closing the server. +- ```Breakpoint ```: Implement functionality to pause execution at specific points. +- ```StepOver ```, ```StepInto ```, ```StepOut ```: Allow fine-grained control over the debugging process, allowing users to inspect code step-by-step. +- ```Evaluate ```: Handle evaluation requests from the debugger, returning values as needed. +### Refined Error Handling: +- Instead of immediate termination on an unknown command, consider logging these events or sending specific error messages back to the client. This provides a more user-friendly debugging experience. +### Enhanced Logging: +- Implement more detailed logging to provide insights into server operations. This would be especially useful in identifying issues or understanding the flow of commands and responses. +### Asynchronous Processing: +- Consider incorporating asynchronous command processing. This would allow the server to handle multiple requests concurrently, offering faster response times and smoother user experiences, especially in complex debugging scenarios. + diff --git a/cider-dap/calyxDebug/extension.js b/cider-dap/calyxDebug/extension.js new file mode 100644 index 0000000000..65af7769cd --- /dev/null +++ b/cider-dap/calyxDebug/extension.js @@ -0,0 +1,142 @@ +const vscode = require('vscode'); +const cp = require('child_process'); +const net = require('net'); + +// Hold the debug adapter instance +let debugAdapter = null; +let programName = null; // Store the program name +// Create output channel +let outputChannel = vscode.window.createOutputChannel("cider dap"); + +function logToPanel(message) { + console.log("inside logPanel"); + outputChannel.appendLine(message); +} + +// Function to get the program name from the user +async function getProgramName() { + const fileName = await vscode.window.showInputBox({ + placeHolder: 'Please enter the name of a futil file in the workspace folder', + value: 'default.futil' + }); + + if (fileName) { + if (!fileName.startsWith('/')) { + const path = require('path'); + return path.join(vscode.workspace.workspaceFolders[0].uri.fsPath, fileName); + } + return fileName; + } else { + return null; + } +} + +class CiderDebugAdapterDescriptorFactory { + constructor(adapterPath, workspace, outputChannel) { + logToPanel("inside constructor"); + this.adapter = new CiderDebugAdapter(adapterPath, workspace, outputChannel); + this.adapterPath = adapterPath; + this.workspace = workspace; + this.outputChannel = outputChannel; + } + + createDebugAdapterDescriptor(session) { + // Return a new debug adapter descriptor + logToPanel("creating adapter descriptor"); + return new vscode.DebugAdapterServer(this._startDebugServer(session)); + } + + _startDebugServer(session) { + logToPanel("start of startDebugServer"); + const port = 8888; // This is the default value + + if (!this.adapter.isServerRunning()) { + logToPanel("server is not running"); + this.adapter.start(port); + logToPanel("started dap-server"); + } + + logToPanel("exiting startDebugging"); + return port; + } +} +class CiderDebugAdapter { + constructor(adapterPath, cwd, outputChannel) { + logToPanel("inside CiderDebugAdapter"); + this.adapterPath = adapterPath; + this.cwd = cwd; + this.outputChannel = outputChannel; + this.adapterProcess = null; + logToPanel("at the end of ciderDebugAdapter"); + } + isServerRunning() { + logToPanel("checking if server is running"); + return this.adapterProcess != null && this.adapterProcess.exitCode == null; + } + // Start the debug adapter process + start(port) { + logToPanel('begining of start'); + + // Spawn a new child process for the debug adapter + // Include the port as a command line argument + this.adapterProcess = cp.spawn(this.adapterPath, [programName, '--port', port, "--tcp"], { cwd: this.cwd }); + + // Attach event listener to capture standard output of the adapter process and log it to the output channel + this.adapterProcess.stdout.on('data', (data) => { + logToPanel(data.toString()); + }); + + // Attach event listener to capture standard error of the adapter process and log it to the output channel + this.adapterProcess.stderr.on('data', (data) => { + logToPanel(data.toString()); + }); + + logToPanel('Debugger started on port ' + port + '!'); + } + + stop() { + if (this.adapterProcess) { + this.adapterProcess.kill(); + this.adapterProcess = null; + this.isRunning = false; + logToPanel('Debugger stopped.'); + } else { + logToPanel('No running debug adapter to stop.'); + } + } +} + +function activate(context) { + logToPanel('Extension activated!'); + + // Start the debug server explicitly + const factory = new CiderDebugAdapterDescriptorFactory('/home/basantkhalil/calyx2/target/debug/cider-dap', vscode.workspace.rootPath, outputChannel); + context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('cider-dap', factory)); + logToPanel("after start server"); + + // Update the adapter path with the serverPort and use it for starting the debug adapter + const adapterPath = '/home/basantkhalil/calyx2/target/debug/cider-dap'; + const cwd = vscode.workspace.rootPath; + logToPanel("before startDebugging"); + /* context.subscriptions.push(vscode.commands.registerCommand('cider.startDebugging', startDebugging)); + context.subscriptions.push(vscode.commands.registerCommand('cider.stopDebugging', stopDebugging)); + */ + logToPanel('Hello, your extension is now activated!'); +} + +function stopDebugging() { + if (debugAdapter) { + debugAdapter.stop(); + } else { + logToPanel('No running debug adapter to stop.'); + } +} + +function deactivate() { + logToPanel("deactivate"); +} + +module.exports = { + activate, + deactivate +}; \ No newline at end of file diff --git a/cider-dap/src/error.rs b/cider-dap/src/error.rs index d80865385c..70bc5e439c 100644 --- a/cider-dap/src/error.rs +++ b/cider-dap/src/error.rs @@ -1,4 +1,6 @@ -#[allow(dead_code)] //remove this later +use dap::errors::ServerError; + +#[allow(dead_code)] // remove this later #[derive(thiserror::Error, Debug)] pub enum MyAdapterError { /// Represents an unhandled command error. @@ -17,9 +19,21 @@ pub enum MyAdapterError { #[error("IO error: {0}")] IO(#[from] std::io::Error), + /// Represents an error for an invalid path. + #[error("Invalid path provided")] + InvalidPathError, + /// Represents an error when a command is missing. #[error("Missing command")] MissingCommandError, + + /// Represents a missing request. + #[error("Missing request")] + MissingRequest, + + /// Represents a server error. + #[error(transparent)] + ServerError(#[from] ServerError), } /// A type alias for the result returned by the adapter functions. diff --git a/cider-dap/src/main.rs b/cider-dap/src/main.rs index f5ba5e999f..3bb2b1657c 100644 --- a/cider-dap/src/main.rs +++ b/cider-dap/src/main.rs @@ -30,31 +30,116 @@ fn read_path(path: &str) -> Result { fn main() -> Result<(), MyAdapterError> { let opts: Opts = argh::from_env(); - let path = opts.file.ok_or(MyAdapterError::MissingFile)?; - let file = File::open(path)?; - let adapter = MyAdapter::new(file); if opts.is_multi_session { + eprintln!("running multi-session"); let listener = TcpListener::bind(("127.0.0.1", opts.port))?; + eprintln!("bound on port: {} ", opts.port); let (stream, addr) = listener.accept()?; - println!("Accepted client on: {}", addr); + eprintln!("Accepted client on: {}", addr); // changed to eprintln! let read_stream = BufReader::new(stream.try_clone()?); let write_stream = BufWriter::new(stream); - let server = Server::new(read_stream, write_stream); - run_server(server, adapter)?; + let mut server = Server::new(read_stream, write_stream); + + // Get the adapter from the init function + let adapter = multi_session_init(&mut server)?; + + // Run the server using the adapter + run_server(&mut server, adapter)?; } else { + let path = opts.file.ok_or(MyAdapterError::MissingFile)?; + let file = File::open(path)?; + let adapter = MyAdapter::new(file); + eprintln!("running single-session"); let write = BufWriter::new(stdout()); let read = BufReader::new(stdin()); - let server = Server::new(read, write); - run_server(server, adapter)?; + let mut server = Server::new(read, write); + run_server(&mut server, adapter)?; } - + eprintln!("exited run_Server"); Ok(()) } +fn multi_session_init( + server: &mut Server, +) -> AdapterResult +where + R: Read, + W: Write, +{ + // handle the first request (Initialize) + let req = match server.poll_request()? { + Some(req) => req, + None => return Err(MyAdapterError::MissingCommandError), + }; + match &req.command { + Command::Initialize(_) => { + let rsp = + req.success(ResponseBody::Initialize(types::Capabilities { + ..Default::default() + })); + server.respond(rsp)?; + server.send_event(Event::Initialized)?; + } + _ => return Err(MyAdapterError::UnhandledCommandError), + } + + // handle the second request (Launch) + let req = match server.poll_request()? { + Some(req) => req, + None => return Err(MyAdapterError::MissingCommandError), + }; + + let program_path = if let Command::Launch(params) = &req.command { + if let Some(data) = ¶ms.additional_data { + if let Some(program_path) = data.get("program") { + eprintln!("Program path: {}", program_path); + program_path + .as_str() + .ok_or(MyAdapterError::InvalidPathError)? + } else { + return Err(MyAdapterError::MissingFile); + } + } else { + return Err(MyAdapterError::MissingFile); + } + } else { + panic!("second request was not a launch"); + }; + + // Open file using the extracted program path + let file = File::open(program_path)?; + + // Construct the adapter + let adapter = MyAdapter::new(file); + + // Return the adapter instead of running the server + Ok(adapter) +} + fn run_server( - _server: Server, + server: &mut Server, _adapter: MyAdapter, ) -> AdapterResult<()> { - todo!() + loop { + // Start looping here + let req = match server.poll_request()? { + Some(req) => req, + None => return Err(MyAdapterError::MissingCommandError), + }; + match &req.command { + Command::Launch(_) => { + let rsp = req.success(ResponseBody::Launch); + server.respond(rsp)?; + } + // Here, can add a match pattern for a disconnect or exit command + // to break out of the loop and close the server. + // Command::Disconnect(_) => break, + // ... + unknown_command => { + eprintln!("Unknown_command: {:?}", unknown_command); + return Err(MyAdapterError::UnhandledCommandError); + } + } + } }