From 2d12e09fdafd53e98abe958efa51799ad6873ae3 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 16 Jan 2026 10:49:05 +0000 Subject: [PATCH 1/6] feat: add cook lsp subcommand for language server support Add a new `cook lsp` command that starts the Cooklang Language Server Protocol server. The LSP server provides IDE features for Cooklang recipe files including real-time syntax checking, auto-completion, semantic highlighting, hover documentation, and document symbols. Uses the cooklang-language-server crate and communicates over stdin/stdout using the standard LSP protocol for editor integration. --- Cargo.lock | 231 +++++++++++++++++- Cargo.toml | 2 + src/args.rs | 22 +- src/lib.rs | 1 + src/lsp.rs | 25 ++ src/main.rs | 2 + .../snapshots/snapshot_test__help_output.snap | 2 + 7 files changed, 280 insertions(+), 5 deletions(-) create mode 100644 src/lsp.rs diff --git a/Cargo.lock b/Cargo.lock index 84e5bbf..35e08bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -235,6 +235,17 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "auto_impl" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -269,7 +280,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.2", "tokio", - "tower", + "tower 0.5.2", "tower-layer", "tower-service", "tracing", @@ -572,6 +583,7 @@ dependencies = [ "cooklang", "cooklang-find 0.5.0", "cooklang-import", + "cooklang-language-server", "cooklang-reports", "directories", "fluent", @@ -597,8 +609,9 @@ dependencies = [ "textwrap", "tokio", "toml 0.9.5", - "tower", + "tower 0.5.2", "tower-http 0.5.2", + "tower-lsp", "tracing", "tracing-subscriber", "unic-langid", @@ -616,10 +629,14 @@ dependencies = [ "codesnake", "enum-map", "finl_unicode", + "prettyplease", + "proc-macro2", + "quote", "serde", "serde_yaml", "smallvec", "strum", + "syn", "thiserror 2.0.12", "toml 0.8.23", "tracing", @@ -675,6 +692,27 @@ dependencies = [ "tokio", ] +[[package]] +name = "cooklang-language-server" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5589a2a285d563904cd12348dca1f848c043ec558c5e9cf267746bb0395d077f" +dependencies = [ + "anyhow", + "cooklang", + "dashmap 6.1.0", + "nohash-hasher", + "ropey", + "serde", + "serde_json", + "text-size", + "thiserror 2.0.12", + "tokio", + "tower-lsp", + "tracing", + "tracing-subscriber", +] + [[package]] name = "cooklang-reports" version = "0.2.0" @@ -816,6 +854,33 @@ dependencies = [ "syn", ] +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "der" version = "0.7.10" @@ -1223,6 +1288,20 @@ dependencies = [ "new_debug_unreachable", ] +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.31" @@ -1245,6 +1324,17 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.31" @@ -1263,8 +1353,10 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ + "futures-channel", "futures-core", "futures-io", + "futures-macro", "futures-sink", "futures-task", "memchr", @@ -2007,6 +2099,19 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "lsp-types" +version = "0.94.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66bfd44a06ae10647fe3f8214762e9369fd4248df1350924b4ef9e770a85ea1" +dependencies = [ + "bitflags 1.3.2", + "serde", + "serde_json", + "serde_repr", + "url", +] + [[package]] name = "mac" version = "0.1.1" @@ -2123,6 +2228,12 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + [[package]] name = "nom" version = "7.1.3" @@ -2433,6 +2544,26 @@ dependencies = [ "siphasher 1.0.1", ] +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -2536,6 +2667,16 @@ dependencies = [ "termtree", ] +[[package]] +name = "prettyplease" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro-hack" version = "0.5.20+deprecated" @@ -2816,7 +2957,7 @@ dependencies = [ "tokio", "tokio-native-tls", "tokio-rustls", - "tower", + "tower 0.5.2", "tower-http 0.6.4", "tower-service", "url", @@ -2852,6 +2993,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "ropey" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93411e420bcd1a75ddd1dc3caf18c23155eda2c090631a85af21ba19e97093b5" +dependencies = [ + "smallvec", + "str_indices", +] + [[package]] name = "rust-embed" version = "8.7.2" @@ -3172,6 +3323,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "serde_spanned" version = "0.6.9" @@ -3343,6 +3505,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "str_indices" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d08889ec5408683408db66ad89e0e1f93dff55c73a4ccc71c427d5b277ee47e6" + [[package]] name = "string_cache" version = "0.8.9" @@ -3560,6 +3728,12 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" +[[package]] +name = "text-size" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" + [[package]] name = "textwrap" version = "0.16.2" @@ -3817,6 +3991,20 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tower-layer", + "tower-service", +] + [[package]] name = "tower" version = "0.5.2" @@ -3871,7 +4059,7 @@ dependencies = [ "http-body 1.0.1", "iri-string", "pin-project-lite", - "tower", + "tower 0.5.2", "tower-layer", "tower-service", ] @@ -3882,6 +4070,40 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" +[[package]] +name = "tower-lsp" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ba052b54a6627628d9b3c34c176e7eda8359b7da9acd497b9f20998d118508" +dependencies = [ + "async-trait", + "auto_impl", + "bytes", + "dashmap 5.5.3", + "futures", + "httparse", + "lsp-types", + "memchr", + "serde", + "serde_json", + "tokio", + "tokio-util", + "tower 0.4.13", + "tower-lsp-macros", + "tracing", +] + +[[package]] +name = "tower-lsp-macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tower-service" version = "0.3.3" @@ -4077,6 +4299,7 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d21faf7..830d3df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ clap = { version = "4.5", features = ["derive"] } cooklang = { version = "0.17.4", default-features = false, features = ["aisle", "pantry"] } cooklang-find = { version = "0.5.0" } cooklang-import = "0.8.7" +cooklang-language-server = "0.1.0" cooklang-reports = { version = "0.2" } directories = "6" fluent = "0.16" @@ -61,6 +62,7 @@ tokio = { version = "1", features = ["full"] } toml = "0.9.5" tower = { version = "0.5", features = ["util"] } tower-http = { version = "0.5", features = ["fs", "trace", "cors"] } +tower-lsp = "0.20" tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } unic-langid = "0.9" diff --git a/src/args.rs b/src/args.rs index d039899..b8d7372 100644 --- a/src/args.rs +++ b/src/args.rs @@ -32,7 +32,7 @@ use clap::{Parser, Subcommand}; #[cfg(feature = "self-update")] use crate::update; -use crate::{doctor, import, pantry, recipe, report, search, seed, server, shopping_list}; +use crate::{doctor, import, lsp, pantry, recipe, report, search, seed, server, shopping_list}; #[derive(Parser, Debug)] #[command( @@ -193,6 +193,26 @@ pub enum Command { )] Pantry(pantry::PantryArgs), + /// Start the Cooklang Language Server Protocol (LSP) server + /// + /// Launches an LSP server that provides IDE features for Cooklang recipe files. + /// The server communicates over stdin/stdout using the standard LSP protocol, + /// enabling integration with editors like VS Code, Neovim, Emacs, and others. + /// + /// Features include: + /// - Real-time syntax checking and validation + /// - Auto-completion for ingredients, cookware, and timers + /// - Semantic syntax highlighting + /// - Hover documentation + /// - Document symbols and navigation + /// + /// Examples: + /// cook lsp # Start the LSP server + #[command( + long_about = "Start the Language Server Protocol server for Cooklang editor integration" + )] + Lsp(lsp::LspArgs), + /// Update CookCLI to the latest version /// /// Checks for new releases on GitHub and automatically downloads and diff --git a/src/lib.rs b/src/lib.rs index ec79a82..57cb582 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ use camino::Utf8PathBuf; // Commands - make them available as public modules pub mod doctor; pub mod import; +pub mod lsp; pub mod pantry; pub mod recipe; pub mod report; diff --git a/src/lsp.rs b/src/lsp.rs new file mode 100644 index 0000000..3cd81cd --- /dev/null +++ b/src/lsp.rs @@ -0,0 +1,25 @@ +use anyhow::Result; +use clap::Args; +use cooklang_language_server::Backend; +use tower_lsp::{LspService, Server}; + +#[derive(Debug, Args)] +#[command()] +pub struct LspArgs {} + +pub fn run(_args: LspArgs) -> Result<()> { + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build()? + .block_on(run_server()) +} + +async fn run_server() -> Result<()> { + let stdin = tokio::io::stdin(); + let stdout = tokio::io::stdout(); + + let (service, socket) = LspService::new(Backend::new); + Server::new(stdin, stdout, socket).serve(service).await; + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 8d676c5..bb8a6e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,6 +37,7 @@ use clap::Parser; // commands mod doctor; mod import; +mod lsp; mod pantry; mod recipe; mod report; @@ -73,6 +74,7 @@ pub fn main() -> Result<()> { Command::Report(args) => report::run(&ctx, args), Command::Doctor(args) => doctor::run(&ctx, args), Command::Pantry(args) => pantry::run(&ctx, args), + Command::Lsp(args) => lsp::run(args), #[cfg(feature = "self-update")] Command::Update(args) => update::run(args), } diff --git a/tests/snapshots/snapshot_test__help_output.snap b/tests/snapshots/snapshot_test__help_output.snap index ae77693..ef61c38 100644 --- a/tests/snapshots/snapshot_test__help_output.snap +++ b/tests/snapshots/snapshot_test__help_output.snap @@ -1,5 +1,6 @@ --- source: tests/snapshot_test.rs +assertion_line: 311 expression: stdout --- A command-line interface for managing and working with Cooklang recipes @@ -16,6 +17,7 @@ Commands: report Generate custom reports from recipes using templates doctor Analyze your recipe collection for issues and improvements pantry Manage and analyze your pantry inventory + lsp Start the Cooklang Language Server Protocol (LSP) server update Update CookCLI to the latest version help Print this message or the help of the given subcommand(s) From a85c8a285a361344a59d3fe6ad7e9b451ffe138c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 16 Jan 2026 13:38:58 +0000 Subject: [PATCH 2/6] docs: add documentation for cook lsp command Add comprehensive documentation for the LSP command including: - Overview of features (diagnostics, completions, hover, symbols) - Editor integration guides for VS Code, Neovim, Vim, Emacs, Helix, Sublime Text, and Zed - Troubleshooting section - Protocol details and supported LSP features --- docs/lsp.md | 259 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 docs/lsp.md diff --git a/docs/lsp.md b/docs/lsp.md new file mode 100644 index 0000000..9bcdeaa --- /dev/null +++ b/docs/lsp.md @@ -0,0 +1,259 @@ +# LSP Command + +The `lsp` command starts a Language Server Protocol (LSP) server for Cooklang recipe files. It enables IDE features in text editors and development environments. + +## Overview + +The LSP server provides intelligent editing features for `.cook` files: + +- **Real-time syntax checking** – Catch errors as you type +- **Auto-completion** – Suggestions for ingredients, cookware, and timers +- **Semantic highlighting** – Rich syntax coloring beyond basic highlighting +- **Hover documentation** – Information about ingredients and references +- **Document symbols** – Navigate recipe structure quickly +- **Go to definition** – Jump to referenced recipes + +## Basic Usage + +Start the LSP server: +```bash +cook lsp +``` + +The server communicates over stdin/stdout using the standard LSP protocol. You typically don't run this command directly—your editor starts it automatically. + +## Editor Integration + +### Visual Studio Code + +Install the [Cooklang extension](https://marketplace.visualstudio.com/items?itemName=cooklang.cooklang) from the VS Code marketplace. The extension automatically uses `cook lsp` when available. + +**Manual Configuration:** + +Add to your `settings.json`: +```json +{ + "cooklang.languageServer.path": "cook", + "cooklang.languageServer.args": ["lsp"] +} +``` + +### Neovim + +Using [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig): + +```lua +local lspconfig = require('lspconfig') +local configs = require('lspconfig.configs') + +if not configs.cooklang then + configs.cooklang = { + default_config = { + cmd = { 'cook', 'lsp' }, + filetypes = { 'cooklang' }, + root_dir = lspconfig.util.root_pattern('.git', 'config'), + settings = {}, + }, + } +end + +lspconfig.cooklang.setup{} +``` + +### Vim with CoC + +Add to your `coc-settings.json`: +```json +{ + "languageserver": { + "cooklang": { + "command": "cook", + "args": ["lsp"], + "filetypes": ["cooklang"], + "rootPatterns": [".git", "config"] + } + } +} +``` + +### Emacs + +Using [lsp-mode](https://emacs-lsp.github.io/lsp-mode/): + +```elisp +(with-eval-after-load 'lsp-mode + (add-to-list 'lsp-language-id-configuration + '(cooklang-mode . "cooklang")) + + (lsp-register-client + (make-lsp-client + :new-connection (lsp-stdio-connection '("cook" "lsp")) + :activation-fn (lsp-activate-on "cooklang") + :server-id 'cooklang-lsp))) +``` + +Using [eglot](https://github.com/joaotavora/eglot): + +```elisp +(add-to-list 'eglot-server-programs + '(cooklang-mode . ("cook" "lsp"))) +``` + +### Helix + +Add to your `languages.toml`: +```toml +[[language]] +name = "cooklang" +scope = "source.cooklang" +file-types = ["cook"] +language-servers = ["cooklang-lsp"] + +[language-server.cooklang-lsp] +command = "cook" +args = ["lsp"] +``` + +### Sublime Text + +Using [LSP package](https://github.com/sublimelsp/LSP): + +Add to LSP settings: +```json +{ + "clients": { + "cooklang": { + "enabled": true, + "command": ["cook", "lsp"], + "selector": "source.cooklang" + } + } +} +``` + +### Zed + +Add to your `settings.json`: +```json +{ + "lsp": { + "cooklang": { + "binary": { + "path": "cook", + "arguments": ["lsp"] + } + } + } +} +``` + +## Features + +### Diagnostics + +The LSP server reports syntax errors and warnings in real-time: + +``` +Line 5: Invalid ingredient syntax - missing quantity +Line 8: Timer format should be ~{time%unit} +Line 12: Referenced recipe not found: ./Missing Recipe.cook +``` + +### Completions + +Auto-completion triggers in various contexts: + +- **Ingredients** – After typing `@`, suggests known ingredients +- **Cookware** – After typing `#`, suggests equipment +- **Timers** – After typing `~`, suggests time formats +- **References** – Suggests recipe files for `@./` paths +- **Metadata** – Suggests common metadata keys in frontmatter + +### Hover Information + +Hover over elements to see details: + +- **Ingredients** – Quantity, unit, and any notes +- **Timers** – Formatted duration +- **Recipe references** – Preview of referenced recipe +- **Metadata** – Description of metadata fields + +### Document Symbols + +Navigate recipe structure: + +- Sections and steps +- Ingredients list +- Cookware list +- Timers +- Metadata fields + +### Semantic Tokens + +Enhanced syntax highlighting for: + +- Ingredients (with quantity and unit distinction) +- Cookware +- Timers +- Comments +- Metadata keys and values +- Recipe references + +## Troubleshooting + +### Server Not Starting + +Verify the cook command is in your PATH: +```bash +which cook +cook --version +``` + +### Connection Issues + +Check server logs by running manually: +```bash +cook lsp 2>/tmp/cooklang-lsp.log +``` + +### Editor Not Detecting Server + +Ensure your editor: +1. Recognizes `.cook` files as Cooklang +2. Has LSP client support enabled +3. Is configured with the correct command path + +### Debugging + +Enable verbose logging: +```bash +RUST_LOG=debug cook lsp 2>/tmp/cooklang-lsp-debug.log +``` + +## Protocol Details + +The server implements LSP specification features: + +| Feature | Support | +|---------|---------| +| textDocument/didOpen | Yes | +| textDocument/didChange | Yes | +| textDocument/didClose | Yes | +| textDocument/completion | Yes | +| textDocument/hover | Yes | +| textDocument/definition | Yes | +| textDocument/documentSymbol | Yes | +| textDocument/semanticTokens | Yes | +| textDocument/publishDiagnostics | Yes | + +### Transport + +- **Protocol**: JSON-RPC 2.0 +- **Transport**: stdin/stdout +- **Encoding**: UTF-8 + +## See Also + +- [Language Server Protocol Specification](https://microsoft.github.io/language-server-protocol/) +- [Cooklang Syntax Reference](https://cooklang.org/docs/spec/) +- [Editor Integrations](https://cooklang.org/cli/editors/) From ed792285ff1ee37a76cf7e323a5460e7a9908ec8 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 16 Jan 2026 13:44:57 +0000 Subject: [PATCH 3/6] refactor: improve lsp command implementation - Add Context parameter to lsp::run for consistency with other commands and future extensibility (e.g., accessing config files) - Use #[tokio::main] attribute instead of manual runtime creation, matching the pattern used by the server command - Add signal handling with tokio::select! for graceful shutdown on SIGINT (Ctrl+C) - Add tracing info messages for startup and shutdown events --- src/lsp.rs | 28 ++++++++++++++++++++-------- src/main.rs | 2 +- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/lsp.rs b/src/lsp.rs index 3cd81cd..796973b 100644 --- a/src/lsp.rs +++ b/src/lsp.rs @@ -2,24 +2,36 @@ use anyhow::Result; use clap::Args; use cooklang_language_server::Backend; use tower_lsp::{LspService, Server}; +use tracing::info; + +use crate::Context; #[derive(Debug, Args)] #[command()] pub struct LspArgs {} -pub fn run(_args: LspArgs) -> Result<()> { - tokio::runtime::Builder::new_current_thread() - .enable_all() - .build()? - .block_on(run_server()) -} +/// Start the Cooklang Language Server Protocol server. +/// +/// The Context parameter is included for consistency with other commands +/// and future extensibility (e.g., accessing aisle.conf, pantry.conf, or base_path). +#[tokio::main] +pub async fn run(_ctx: &Context, _args: LspArgs) -> Result<()> { + info!("Starting Cooklang LSP server"); -async fn run_server() -> Result<()> { let stdin = tokio::io::stdin(); let stdout = tokio::io::stdout(); let (service, socket) = LspService::new(Backend::new); - Server::new(stdin, stdout, socket).serve(service).await; + + // Use tokio::select! to handle both the LSP server and shutdown signals + tokio::select! { + _ = Server::new(stdin, stdout, socket).serve(service) => { + info!("LSP server stopped"); + } + _ = tokio::signal::ctrl_c() => { + info!("Received shutdown signal, stopping LSP server"); + } + } Ok(()) } diff --git a/src/main.rs b/src/main.rs index bb8a6e4..0981902 100644 --- a/src/main.rs +++ b/src/main.rs @@ -74,7 +74,7 @@ pub fn main() -> Result<()> { Command::Report(args) => report::run(&ctx, args), Command::Doctor(args) => doctor::run(&ctx, args), Command::Pantry(args) => pantry::run(&ctx, args), - Command::Lsp(args) => lsp::run(args), + Command::Lsp(args) => lsp::run(&ctx, args), #[cfg(feature = "self-update")] Command::Update(args) => update::run(args), } From 8d372cd9a29adf9b215ea0da085e6604946d1ca9 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 16 Jan 2026 14:03:05 +0000 Subject: [PATCH 4/6] feat: log context paths in LSP for debugging Log base_path, aisle.conf, and pantry.conf paths at debug level when starting the LSP server. Currently the cooklang-language-server crate receives workspace configuration from the editor during LSP initialization, but these paths are logged for debugging and future extensibility when the upstream crate supports custom configuration paths. --- src/lsp.rs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/lsp.rs b/src/lsp.rs index 796973b..cb9991d 100644 --- a/src/lsp.rs +++ b/src/lsp.rs @@ -2,7 +2,7 @@ use anyhow::Result; use clap::Args; use cooklang_language_server::Backend; use tower_lsp::{LspService, Server}; -use tracing::info; +use tracing::{debug, info}; use crate::Context; @@ -12,12 +12,26 @@ pub struct LspArgs {} /// Start the Cooklang Language Server Protocol server. /// -/// The Context parameter is included for consistency with other commands -/// and future extensibility (e.g., accessing aisle.conf, pantry.conf, or base_path). +/// Note: The LSP server receives workspace configuration from the editor during +/// the LSP initialization handshake. The editor sends workspace folders, and the +/// server loads aisle.conf from those locations. The Context paths logged below +/// are for debugging and future extensibility when the upstream cooklang-language-server +/// crate supports custom configuration paths. #[tokio::main] -pub async fn run(_ctx: &Context, _args: LspArgs) -> Result<()> { +pub async fn run(ctx: &Context, _args: LspArgs) -> Result<()> { info!("Starting Cooklang LSP server"); + // Log available configuration paths for debugging + // Currently the LSP server uses workspace paths from the editor, + // but these could be used in the future for global config support + debug!("Base path: {}", ctx.base_path()); + if let Some(aisle) = ctx.aisle() { + debug!("Aisle config: {}", aisle); + } + if let Some(pantry) = ctx.pantry() { + debug!("Pantry config: {}", pantry); + } + let stdin = tokio::io::stdin(); let stdout = tokio::io::stdout(); From 8c12d48fcf50659336a838e85f565d6d24d7ab28 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 16 Jan 2026 14:29:35 +0000 Subject: [PATCH 5/6] test: update snapshots for cooklang parser changes Update snapshot tests to match current cooklang parser output: - scalable field changed from true to false - unit abbreviations updated (cups -> c, tsp -> tbsp) - fraction representation changes in scaled output --- .../snapshot_test__recipe_human_output.snap | 9 +++++---- .../snapshot_test__recipe_json_output.snap | 9 +++++---- ...snapshot_test__recipe_markdown_output.snap | 5 +++-- .../snapshot_test__recipe_yaml_output.snap | 9 +++++---- .../snapshot_test__scaled_recipe_output.snap | 20 ++++++++++++------- 5 files changed, 31 insertions(+), 21 deletions(-) diff --git a/tests/snapshots/snapshot_test__recipe_human_output.snap b/tests/snapshots/snapshot_test__recipe_human_output.snap index 11a71e0..b8e4826 100644 --- a/tests/snapshots/snapshot_test__recipe_human_output.snap +++ b/tests/snapshots/snapshot_test__recipe_human_output.snap @@ -1,5 +1,6 @@ --- source: tests/snapshot_test.rs +assertion_line: 24 expression: stdout --- Simple Recipe @@ -7,9 +8,9 @@ expression: stdout servings: 2 Ingredients: - pasta 200 g - salt 1 tsp - water 2 cups + pasta 200 g + salt 1 tsp + water 2 c Cookware: pot @@ -17,4 +18,4 @@ Cookware: Steps: 1. Boil water for 5 minutes. Add salt and pasta. Cook in a pot for another 10 minutes. - [water: 2 cups, salt: 1 tsp, pasta: 200 g] + [water: 2 c, salt: 1 tsp, pasta: 200 g] diff --git a/tests/snapshots/snapshot_test__recipe_json_output.snap b/tests/snapshots/snapshot_test__recipe_json_output.snap index ef39864..a26fe8d 100644 --- a/tests/snapshots/snapshot_test__recipe_json_output.snap +++ b/tests/snapshots/snapshot_test__recipe_json_output.snap @@ -1,5 +1,6 @@ --- source: tests/snapshot_test.rs +assertion_line: 50 expression: formatted --- { @@ -24,8 +25,8 @@ expression: formatted "name": "water", "note": null, "quantity": { - "scalable": true, - "unit": "cups", + "scalable": false, + "unit": "c", "value": { "type": "number", "value": { @@ -50,7 +51,7 @@ expression: formatted "name": "salt", "note": null, "quantity": { - "scalable": true, + "scalable": false, "unit": "tsp", "value": { "type": "number", @@ -76,7 +77,7 @@ expression: formatted "name": "pasta", "note": null, "quantity": { - "scalable": true, + "scalable": false, "unit": "g", "value": { "type": "number", diff --git a/tests/snapshots/snapshot_test__recipe_markdown_output.snap b/tests/snapshots/snapshot_test__recipe_markdown_output.snap index b8af99e..5b7db28 100644 --- a/tests/snapshots/snapshot_test__recipe_markdown_output.snap +++ b/tests/snapshots/snapshot_test__recipe_markdown_output.snap @@ -1,5 +1,6 @@ --- source: tests/snapshot_test.rs +assertion_line: 92 expression: stdout --- --- @@ -12,8 +13,8 @@ name: Pancakes ## Ingredients -- *2 cups* flour -- *1.5 cups* milk +- *2 c* flour +- *1 1/2 c* milk - *2* eggs - *2 tbsp* sugar diff --git a/tests/snapshots/snapshot_test__recipe_yaml_output.snap b/tests/snapshots/snapshot_test__recipe_yaml_output.snap index c433568..4798627 100644 --- a/tests/snapshots/snapshot_test__recipe_yaml_output.snap +++ b/tests/snapshots/snapshot_test__recipe_yaml_output.snap @@ -1,5 +1,6 @@ --- source: tests/snapshot_test.rs +assertion_line: 71 expression: stdout --- metadata: @@ -48,8 +49,8 @@ ingredients: value: type: regular value: 2.0 - unit: cups - scalable: true + unit: c + scalable: false note: null reference: null relation: @@ -68,7 +69,7 @@ ingredients: type: regular value: 1.0 unit: tsp - scalable: true + scalable: false note: null reference: null relation: @@ -87,7 +88,7 @@ ingredients: type: regular value: 200.0 unit: g - scalable: true + scalable: false note: null reference: null relation: diff --git a/tests/snapshots/snapshot_test__scaled_recipe_output.snap b/tests/snapshots/snapshot_test__scaled_recipe_output.snap index 14eaa9f..9bcb3cd 100644 --- a/tests/snapshots/snapshot_test__scaled_recipe_output.snap +++ b/tests/snapshots/snapshot_test__scaled_recipe_output.snap @@ -1,5 +1,6 @@ --- source: tests/snapshot_test.rs +assertion_line: 289 expression: formatted --- { @@ -24,8 +25,8 @@ expression: formatted "name": "water", "note": null, "quantity": { - "scalable": true, - "unit": "cups", + "scalable": false, + "unit": "c", "value": { "type": "number", "value": { @@ -50,13 +51,18 @@ expression: formatted "name": "salt", "note": null, "quantity": { - "scalable": true, - "unit": "tsp", + "scalable": false, + "unit": "tbsp", "value": { "type": "number", "value": { - "type": "regular", - "value": 3.0 + "type": "fraction", + "value": { + "den": 1, + "err": -6.762804893867269e-8, + "num": 0, + "whole": 1 + } } } }, @@ -76,7 +82,7 @@ expression: formatted "name": "pasta", "note": null, "quantity": { - "scalable": true, + "scalable": false, "unit": "g", "value": { "type": "number", From 1dd41f5c2611dbf7cd3706a466061a45a67c7fea Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 16 Jan 2026 16:59:23 +0000 Subject: [PATCH 6/6] test: update help output snapshot for no-update feature --- tests/snapshots/snapshot_test__help_output_no_update.snap | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/snapshots/snapshot_test__help_output_no_update.snap b/tests/snapshots/snapshot_test__help_output_no_update.snap index a5e45af..a991815 100644 --- a/tests/snapshots/snapshot_test__help_output_no_update.snap +++ b/tests/snapshots/snapshot_test__help_output_no_update.snap @@ -17,6 +17,7 @@ Commands: report Generate custom reports from recipes using templates doctor Analyze your recipe collection for issues and improvements pantry Manage and analyze your pantry inventory + lsp Start the Cooklang Language Server Protocol (LSP) server help Print this message or the help of the given subcommand(s) Options: