From df79c80aa8d93efd2e4428637f111d079294f07e Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Fri, 13 Nov 2026 14:18:04 +0800 Subject: [PATCH] feat(plugins): notes after file names in the project tree (API 1.7) (#3085) Tree decorations could carry only a two-character badge and a tooltip. decorations.set-file-note (API 1.7) shows one line of muted text (at most 200 characters, optional tooltip) after a file or folder name in the project tree, replacing that tree set's item for the file and sharing the tree-set limits. The SDK wraps it as host::set_file_note with a mock; checked-in components rebuilt. --- crates/ide-host/src/plugins/api.rs | 32 +++++++++++++++++-- crates/ide-plugin/src/types.rs | 3 ++ crates/ide-plugin/src/wit_host.rs | 13 +++++++- .../fixtures/api14/dist/plugin.wasm.sources | 2 +- .../fixtures/commit/dist/plugin.wasm.sources | 2 +- .../data-tools/dist/plugin.wasm.sources | 2 +- .../detail-views/dist/plugin.wasm.sources | 2 +- .../diagnostics/dist/plugin.wasm.sources | 2 +- .../endpoints/dist/plugin.wasm.sources | 2 +- .../execution/dist/plugin.wasm.sources | 2 +- .../ext-owner/dist/plugin.wasm.sources | 2 +- .../file-templates/dist/plugin.wasm.sources | 2 +- .../files-events/dist/plugin.wasm.sources | 2 +- .../grouped-views/dist/plugin.wasm.sources | 2 +- .../fixtures/hello/dist/plugin.wasm.sources | 2 +- .../fixtures/hover/dist/plugin.wasm.sources | 2 +- .../fixtures/indexer/dist/plugin.wasm.sources | 2 +- .../inlay-hints/dist/plugin.wasm.sources | 2 +- .../fixtures/jobs/dist/plugin.wasm.sources | 2 +- .../dist/plugin.wasm.sources | 2 +- .../misbehave/dist/plugin.wasm.sources | 2 +- .../path-events/dist/plugin.wasm.sources | 2 +- .../private-dir/dist/plugin.wasm.sources | 2 +- .../semantic-tokens/dist/plugin.wasm.sources | 2 +- .../settings-pages/dist/plugin.wasm.sources | 2 +- .../fixtures/storage/dist/plugin.wasm.sources | 2 +- .../fixtures/symbols/dist/plugin.wasm.sources | 2 +- .../fixtures/tables/dist/plugin.wasm.sources | 2 +- .../web-content/dist/plugin.wasm.sources | 2 +- .../fixtures/web-log/dist/plugin.wasm.sources | 2 +- .../fixtures/web-pty/dist/plugin.wasm.sources | 2 +- plugins/README.md | 7 ++-- .../ai-completion/dist/plugin.wasm.sources | 2 +- .../dist/plugin.wasm.sources | 2 +- .../checkstyle/dist/plugin.wasm.sources | 2 +- .../container-runner/dist/plugin.wasm.sources | 2 +- .../csv-editor/dist/plugin.wasm.sources | 2 +- .../git-toolbox/dist/plugin.wasm.sources | 2 +- .../graphviz/dist/plugin.wasm.sources | 2 +- .../key-promoter/dist/plugin.wasm.sources | 2 +- .../license-header/dist/plugin.wasm.sources | 2 +- .../official/mybatis/dist/plugin.wasm.sources | 2 +- .../regex-tester/dist/plugin.wasm.sources | 2 +- .../solidity/dist/plugin.wasm.sources | 2 +- .../string-tools/dist/plugin.wasm.sources | 2 +- .../test-runner/dist/plugin.wasm.sources | 2 +- .../todo-finder/dist/plugin.wasm.sources | 2 +- sdk/rust/openidea-plugin/src/host.rs | 8 +++++ sdk/rust/openidea-plugin/src/mock.rs | 32 +++++++++++++++++-- .../openidea-plugin/tests/api14_stage2.rs | 8 +++++ sdk/rust/openidea-plugin/wit/decorations.wit | 3 ++ src/bindings.ts | 2 ++ .../plugins/__tests__/decorations.test.ts | 13 ++++++++ src/features/plugins/decorations.ts | 6 ++-- wit/decorations.wit | 3 ++ 55 files changed, 164 insertions(+), 52 deletions(-) diff --git a/crates/ide-host/src/plugins/api.rs b/crates/ide-host/src/plugins/api.rs index fa6779fa1..e4c83af78 100644 --- a/crates/ide-host/src/plugins/api.rs +++ b/crates/ide-host/src/plugins/api.rs @@ -4125,6 +4125,11 @@ impl PluginApi { if items.len() > 10_000 || file.as_ref().and_then(|f| f.badge.as_ref()).is_some_and(|b| b.chars().count() > 2) { return Err(HostError::Invalid("decoration limit exceeded".into())); } + if let Some(note) = file.as_ref().and_then(|f| f.note.as_ref()) + && (note.trim().is_empty() || note.chars().count() > 200 || note.contains(['\n', '\r'])) + { + return Err(HostError::Invalid("a file note is one non-empty line of at most 200 characters".into())); + } items.retain(|d| decoration_style(&d.style)); if items.iter().any(|d| d.range.start > d.range.end) { return Err(HostError::Invalid("reversed decoration range".into())); @@ -4143,7 +4148,7 @@ impl PluginApi { set_id: set.into(), tree, items, - file: file.filter(|f| decoration_style(&f.style)), + file: file.filter(|f| f.note.is_some() || decoration_style(&f.style)), }) } else { None @@ -4315,10 +4320,33 @@ mod workbench_tests { api.file_decorations( Some(&path), "tree", - Some(ide_plugin::FileDecoration { badge: Some("abc".into()), style: "info".into(), tooltip: None }) + Some(ide_plugin::FileDecoration { + badge: Some("abc".into()), + style: "info".into(), + tooltip: None, + note: None + }) ) .is_err() ); + let note = |text: &str| ide_plugin::FileDecoration { + badge: None, + style: String::new(), + tooltip: Some("why".into()), + note: Some(text.into()), + }; + api.file_decorations(Some(&path), "notes", Some(note("entry point"))).unwrap(); + let notes = |api: &PluginApi| { + api.shared.decoration_sets()["test"] + .iter() + .filter(|s| s.tree && s.set_id == "notes") + .map(|s| s.file.as_ref().and_then(|f| f.note.clone())) + .collect::>() + }; + assert_eq!(notes(&api), [Some("entry point".to_string())], "kept without a style"); + for bad in ["", "two\nlines", &"x".repeat(201)] { + assert!(api.file_decorations(Some(&path), "notes", Some(note(bad))).is_err(), "{bad:?}"); + } api.shared.clear_plugin("test", true); assert!(api.shared.decoration_sets().is_empty()); } diff --git a/crates/ide-plugin/src/types.rs b/crates/ide-plugin/src/types.rs index 4b1622159..07d9508c4 100644 --- a/crates/ide-plugin/src/types.rs +++ b/crates/ide-plugin/src/types.rs @@ -922,6 +922,9 @@ pub struct FileDecoration { pub badge: Option, pub style: String, pub tooltip: Option, + /// Text shown after the name (API 1.7 `set-file-note`). + #[serde(default)] + pub note: Option, } /// Commit metadata, date in Unix seconds. #[derive(Clone, Debug, Serialize, Deserialize, Type)] diff --git a/crates/ide-plugin/src/wit_host.rs b/crates/ide-plugin/src/wit_host.rs index 0c8647e97..2ca73c11b 100644 --- a/crates/ide-plugin/src/wit_host.rs +++ b/crates/ide-plugin/src/wit_host.rs @@ -2497,7 +2497,7 @@ impl bindings::openidea::plugin::decorations::Host for StoreData { .file_decorations( Some(&real), &set_id, - Some(crate::FileDecoration { badge: item.badge, style: item.style, tooltip: item.tooltip }), + Some(crate::FileDecoration { badge: item.badge, style: item.style, tooltip: item.tooltip, note: None }), ) .map_err(to_wit)) } @@ -2507,6 +2507,17 @@ impl bindings::openidea::plugin::decorations::Host for StoreData { } Ok(self.host.file_decorations(None, &set_id, None).map_err(to_wit)) } + fn set_file_note(&mut self, path: String, set_id: String, note: String, tooltip: Option) -> Fallible<()> { + if let Err(e) = self.require(Capability::ProjectRead) { + return Ok(Err(e)); + } + let real = match self.decoration_path(&path) { + Ok(real) => real, + Err(e) => return Ok(Err(e)), + }; + let item = crate::FileDecoration { badge: None, style: String::new(), tooltip, note: Some(note) }; + Ok(self.host.file_decorations(Some(&real), &set_id, Some(item)).map_err(to_wit)) + } } pub(crate) mod language_features_bindings { diff --git a/crates/ide-plugin/tests/fixtures/api14/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/api14/dist/plugin.wasm.sources index 809cca9bc..05ae033f6 100644 --- a/crates/ide-plugin/tests/fixtures/api14/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/api14/dist/plugin.wasm.sources @@ -1 +1 @@ -3c7f35facd15ee8380ccb6475b7c051d500e38346b711e1805077becd0c75f75 +d6c855f1f91aeb02eec5d6c8a07be4eb64bae0704619f44cb67f3b832daf01ee diff --git a/crates/ide-plugin/tests/fixtures/commit/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/commit/dist/plugin.wasm.sources index 5a37d3667..f2d8f7a7c 100644 --- a/crates/ide-plugin/tests/fixtures/commit/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/commit/dist/plugin.wasm.sources @@ -1 +1 @@ -3879822d975695c6258e95286b0e64c5ee1e3265876501efaf0e577b38f8222b +51d00702c42c7167083caea70cf2df022e512bcaf89af8addf526421ff45a15f diff --git a/crates/ide-plugin/tests/fixtures/data-tools/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/data-tools/dist/plugin.wasm.sources index 8733e1918..4f5678f35 100644 --- a/crates/ide-plugin/tests/fixtures/data-tools/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/data-tools/dist/plugin.wasm.sources @@ -1 +1 @@ -228ae888bf556ce5cedc7843eb20e92f221d9e1f640f3c6c595bed3fd6be6051 +40d2fd76ba96b0c93ea0a034c6749709ddc00fc119046e653a50b180a9c4718d diff --git a/crates/ide-plugin/tests/fixtures/detail-views/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/detail-views/dist/plugin.wasm.sources index 8938469a7..c6be58b09 100644 --- a/crates/ide-plugin/tests/fixtures/detail-views/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/detail-views/dist/plugin.wasm.sources @@ -1 +1 @@ -41de239b6f723020b38eb2b67f642b7c88cc88c79117c3072ba122633afc418c +08822287f3649412340f9dbbe2f0ea5a5cd2f543a3c242c5d0a29c422c4c5367 diff --git a/crates/ide-plugin/tests/fixtures/diagnostics/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/diagnostics/dist/plugin.wasm.sources index 043553b61..13937efd6 100644 --- a/crates/ide-plugin/tests/fixtures/diagnostics/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/diagnostics/dist/plugin.wasm.sources @@ -1 +1 @@ -78c5ed3c756498c7619b46c3622ae7a59461c61c747b45caecd79b341ee654de +43eba23cc14cf3c74bfc934099cd58df20f07ee5df96cc10100b90016c544a7a diff --git a/crates/ide-plugin/tests/fixtures/endpoints/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/endpoints/dist/plugin.wasm.sources index c087efb08..aa0b6f21e 100644 --- a/crates/ide-plugin/tests/fixtures/endpoints/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/endpoints/dist/plugin.wasm.sources @@ -1 +1 @@ -ee848321e53cb181536e6158570dbb4ab02a6fd2a1a064b068803ac7da7f3448 +520d9a95328dc772bbb913a2e5e22af8243c95ca45744cbb65b25747653d2a34 diff --git a/crates/ide-plugin/tests/fixtures/execution/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/execution/dist/plugin.wasm.sources index 02bab07ac..4e40f208f 100644 --- a/crates/ide-plugin/tests/fixtures/execution/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/execution/dist/plugin.wasm.sources @@ -1 +1 @@ -b587de945875a98f1d4ca20fab7cf94fb4dc6d848136aa19ab0e0e5319155a80 +acd3d0ee3f886c6a271ce3139200ce5bdb46e40f9c68e495c5ff9b4db98a88ac diff --git a/crates/ide-plugin/tests/fixtures/ext-owner/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/ext-owner/dist/plugin.wasm.sources index ec42929a2..bb15ce5d4 100644 --- a/crates/ide-plugin/tests/fixtures/ext-owner/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/ext-owner/dist/plugin.wasm.sources @@ -1 +1 @@ -1c8776dcae35d2d49dd6c1e32b0cdd354de1eb0f72a473245a2c47368b89ec30 +0cf3bc2197f0bd5339fe3c2199bd973e68b3637b8c5ae07902e62659160275e3 diff --git a/crates/ide-plugin/tests/fixtures/file-templates/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/file-templates/dist/plugin.wasm.sources index bdb078e87..5cf40b770 100644 --- a/crates/ide-plugin/tests/fixtures/file-templates/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/file-templates/dist/plugin.wasm.sources @@ -1 +1 @@ -2a517cbffd6d3cdda37117d8ca8815978a09995923fc3d5aa5af7020afdac56a +17f837c8d6f58800728e00060afe68b2a093085d816d809c9e67065fdd165352 diff --git a/crates/ide-plugin/tests/fixtures/files-events/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/files-events/dist/plugin.wasm.sources index e4ed7d3e4..71aa984ce 100644 --- a/crates/ide-plugin/tests/fixtures/files-events/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/files-events/dist/plugin.wasm.sources @@ -1 +1 @@ -dbd912b1f8488ecd446af07b5dcaef55d44bce57c1a55be5c0b0fb33e2697903 +2cca80220fab6764a8e1f51db8ed2f55b603000c8d0dbf5913a441876b7857ba diff --git a/crates/ide-plugin/tests/fixtures/grouped-views/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/grouped-views/dist/plugin.wasm.sources index fd6e42cee..945599603 100644 --- a/crates/ide-plugin/tests/fixtures/grouped-views/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/grouped-views/dist/plugin.wasm.sources @@ -1 +1 @@ -efdc84cd2bedb3989b03d19b569d09958f26e502db65db4dd786eb3438a7c407 +54211437002ad83a9d3d642f2dca5f37cadd89d7d77ddc7de545db87b99fb8b7 diff --git a/crates/ide-plugin/tests/fixtures/hello/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/hello/dist/plugin.wasm.sources index d0b8bd19d..2390b49e5 100644 --- a/crates/ide-plugin/tests/fixtures/hello/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/hello/dist/plugin.wasm.sources @@ -1 +1 @@ -be6bc15ee42a5e21781ba54f548007351a46b669882ee4491da20ff556927a06 +4326d9cdefe71f4bb9aab043ccef883abbd13145b1579352b97c66889f514c68 diff --git a/crates/ide-plugin/tests/fixtures/hover/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/hover/dist/plugin.wasm.sources index 832d33e75..c564021f6 100644 --- a/crates/ide-plugin/tests/fixtures/hover/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/hover/dist/plugin.wasm.sources @@ -1 +1 @@ -e5c3a3beda750dff27d7b823676d53f15b7c76b8ad539594cd913bd178d750da +2469023f4f29b9abdaa27c9d96774229c4dd002366421dc508926c3fed1fba1b diff --git a/crates/ide-plugin/tests/fixtures/indexer/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/indexer/dist/plugin.wasm.sources index 311ec1682..ad44252c4 100644 --- a/crates/ide-plugin/tests/fixtures/indexer/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/indexer/dist/plugin.wasm.sources @@ -1 +1 @@ -322de3299f0413101c02358836e7df7e06e58cbb22c11897664564abbb8af090 +233461ab52a12e27fa600e108e83cab1b930b3e61812e0c048c99c1e18c6d646 diff --git a/crates/ide-plugin/tests/fixtures/inlay-hints/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/inlay-hints/dist/plugin.wasm.sources index 633a65ca7..88456bafd 100644 --- a/crates/ide-plugin/tests/fixtures/inlay-hints/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/inlay-hints/dist/plugin.wasm.sources @@ -1 +1 @@ -d768ff6a4a466cc6f1d72499df3ccb4ae76872436b8eddff27f62aa156331a37 +bfc654523da608bbc008ea6a15a0cfd0b129ffab70088bf6b610786b63b5352e diff --git a/crates/ide-plugin/tests/fixtures/jobs/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/jobs/dist/plugin.wasm.sources index 53d81888a..eee3f9a14 100644 --- a/crates/ide-plugin/tests/fixtures/jobs/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/jobs/dist/plugin.wasm.sources @@ -1 +1 @@ -57aea107b2c10e4d30fa3785ac4c6566f1b40908a18a3859aca8be047fac9518 +c8536be072d27aa89f5e92b5b96e38ed65b7f06c35c05ecbd4a151218203c5f7 diff --git a/crates/ide-plugin/tests/fixtures/language-features/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/language-features/dist/plugin.wasm.sources index 26b191eb6..689cccfb2 100644 --- a/crates/ide-plugin/tests/fixtures/language-features/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/language-features/dist/plugin.wasm.sources @@ -1 +1 @@ -c98bcc89750e26e738d7115a7c0454fde25367a0a23bdc2ff95fb5a5dacc9f98 +7c0c1f5035e12c794d48cb75a8fc259d11dd99ba3188030fe1d9956720ee6ad5 diff --git a/crates/ide-plugin/tests/fixtures/misbehave/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/misbehave/dist/plugin.wasm.sources index 2d75d7ffb..66f567d07 100644 --- a/crates/ide-plugin/tests/fixtures/misbehave/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/misbehave/dist/plugin.wasm.sources @@ -1 +1 @@ -a4c23b93a14eda705cb18c88bbc2373d1ef3f96031ff2df21b8b092b50633996 +3cf5fd6ab0ce909b806230e20a7fa46264f85937018cfa68865ec0eeb92d4b72 diff --git a/crates/ide-plugin/tests/fixtures/path-events/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/path-events/dist/plugin.wasm.sources index 95af56471..9f3b87571 100644 --- a/crates/ide-plugin/tests/fixtures/path-events/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/path-events/dist/plugin.wasm.sources @@ -1 +1 @@ -aec002b3dd310068e69a6ae67a49d30655bf8baea9153a110a407e9d408e6880 +d349977fd3cbfba92d1d953ac09f65dae5b83b7070723b59553d791c638acf81 diff --git a/crates/ide-plugin/tests/fixtures/private-dir/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/private-dir/dist/plugin.wasm.sources index 649fb95c9..b27389fbf 100644 --- a/crates/ide-plugin/tests/fixtures/private-dir/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/private-dir/dist/plugin.wasm.sources @@ -1 +1 @@ -08ece563c3b55c2b86e68fbb0815ddcbb63beb6f1f6c32b8dbdbee558a4a66c1 +e796039ae2f8e534ffc8c2ed2e47e910aaf593c464525287699cf1daf3e195ef diff --git a/crates/ide-plugin/tests/fixtures/semantic-tokens/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/semantic-tokens/dist/plugin.wasm.sources index 32d21e6bf..0c7841c68 100644 --- a/crates/ide-plugin/tests/fixtures/semantic-tokens/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/semantic-tokens/dist/plugin.wasm.sources @@ -1 +1 @@ -450845edf234a1cc78cd6cfc0b812a89f3a2e41484689cc1e1b05be66873d4eb +5e084fa409bb0641409110d91f203a6f99b451b95cd8067bd1ee68140bf8a892 diff --git a/crates/ide-plugin/tests/fixtures/settings-pages/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/settings-pages/dist/plugin.wasm.sources index 4b04dcdcc..63d155f81 100644 --- a/crates/ide-plugin/tests/fixtures/settings-pages/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/settings-pages/dist/plugin.wasm.sources @@ -1 +1 @@ -61b735de5ef61dcd0ea8e640c2ad004e84a63fe4dfd7872ffb5e04d6d81dc9a9 +521557c1a062a9b82afa14547bc54b57255cdbed3b2ab4b1b86424dfb8accbdc diff --git a/crates/ide-plugin/tests/fixtures/storage/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/storage/dist/plugin.wasm.sources index 992d76ad9..3d9eb8f55 100644 --- a/crates/ide-plugin/tests/fixtures/storage/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/storage/dist/plugin.wasm.sources @@ -1 +1 @@ -36776367782eaa4b8b8121f4a665dd8e68525a4836bff1c92bc16770cce14929 +5280990269e944aef81ec91b356fadf4aa2fc7e7bb10e4b6842e1dc1c669704a diff --git a/crates/ide-plugin/tests/fixtures/symbols/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/symbols/dist/plugin.wasm.sources index 5b2129eef..59136e9d2 100644 --- a/crates/ide-plugin/tests/fixtures/symbols/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/symbols/dist/plugin.wasm.sources @@ -1 +1 @@ -f6311d232a99356b02a398b2ce3db0865c425f435a8d201157719178ba2b97c7 +ff1391d3c9338b6270fef8af7d7c4a5bc595c391997c220a3a827b4b8a068de8 diff --git a/crates/ide-plugin/tests/fixtures/tables/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/tables/dist/plugin.wasm.sources index 8086d54bb..9fc9666d6 100644 --- a/crates/ide-plugin/tests/fixtures/tables/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/tables/dist/plugin.wasm.sources @@ -1 +1 @@ -fe1d26665e3c816ed43fc720acabad03a37b7924bccd4dd8c357fd156947b3c7 +b78ac0b6fa14401a6379f3e6d07d1398a63d70e882930b3747ec843c0fdb5de7 diff --git a/crates/ide-plugin/tests/fixtures/web-content/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/web-content/dist/plugin.wasm.sources index 399a47026..223f5b773 100644 --- a/crates/ide-plugin/tests/fixtures/web-content/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/web-content/dist/plugin.wasm.sources @@ -1 +1 @@ -76bab8e48235f9f066f5b9bbfdd9398916b6c3ed73c072c543d80595130519f7 +55331477c1d5322b0dc35d13fc7f8223bf697f5f21328d340f75519dd1098727 diff --git a/crates/ide-plugin/tests/fixtures/web-log/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/web-log/dist/plugin.wasm.sources index 6fcc612cd..a70411522 100644 --- a/crates/ide-plugin/tests/fixtures/web-log/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/web-log/dist/plugin.wasm.sources @@ -1 +1 @@ -f9ed2eac306229f76b05d0c4a4fb2f0ed8d81a1dfc5eb50d044bbe10c9206a5f +0ae6b7edd0d7cd5025ead4a4afacf0fbe3ebc9548d847f94be0e63585a19747e diff --git a/crates/ide-plugin/tests/fixtures/web-pty/dist/plugin.wasm.sources b/crates/ide-plugin/tests/fixtures/web-pty/dist/plugin.wasm.sources index 9262a8411..7ab067bb0 100644 --- a/crates/ide-plugin/tests/fixtures/web-pty/dist/plugin.wasm.sources +++ b/crates/ide-plugin/tests/fixtures/web-pty/dist/plugin.wasm.sources @@ -1 +1 @@ -f2d83d9f56da072d38a1552195af8a9d4edc11888c645627d5dd1e4a46afc4df +5d909d6ab2e73fabba2d213a944546ef7e425552852cf7f74e00eb94c9d167ac diff --git a/plugins/README.md b/plugins/README.md index d2304351b..ede506b80 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -598,6 +598,7 @@ then run `openidea-plugin check`. | `TextEditor::set_selections(ranges)`, `reveal(range)` | `editor.text-editor` | API 1.4: selections or scrolling in this window's already open editor; no capability. Commands/events only, UTF-16 coordinates. | | `host::set_decorations`, `clear_decorations` | `decorations.{set,clear}` | API 1.4: project-scoped editor annotations cleared on plugin stop. Requires `project.read`. | | `host::set_file_decoration`, `clear_file_decorations` | `decorations.{set-file,clear-file}` | API 1.4: project-tree badges/styles. Requires `project.read`. | +| `host::set_file_note` | `decorations.set-file-note` | API 1.7: a note after the name in the project tree (one line, at most 200 characters). Requires `project.read`. | | `Project::search_text(query, limit)` | `workspace.project.search-text` | API 1.4: cancellable Find in Files with unsaved documents and project excludes, at most 10,000 matches. Requires `project.read`. | | `host::problems(path)` | `diagnostics.problems` | API 1.4: project Problems reports grouped by file/source, optional file filter. Requires `project.read` and an invoking window. Empty for remote projects. | | `Project::vcs_log(path, limit)` | `vcs.log` | API 1.4: newest first, at most 1000 commits, optional file history. Requires `vcs.read` and trust. | @@ -851,8 +852,10 @@ Decorations are plain text, with fixed styles mapped to theme tokens: `highlight`, `search`, `added`, `modified`, `removed`, `warning`, `error`, `info`, `underline`, `strikethrough`, `muted`. Unknown styles are ignored. Editor items have `{ range, style, hover, after }`; tree items have `{ badge, style, tooltip }`, -where a badge has at most two Unicode characters. Paths must resolve inside an -open project; absolute paths select that project, relative paths use the invoking +where a badge has at most two Unicode characters. A tree item can instead be a note +(`set-file-note`, API 1.7): muted text after the file or folder name, one line of at +most 200 characters, with an optional tooltip; it replaces that set's item for the file. +Paths must resolve inside an open project; absolute paths select that project, relative paths use the invoking project. Sets are isolated by plugin and project and shared by all windows of that project. Setting replaces a set's items for one file; clearing removes that set in all projects. Each plugin has at most 50 editor sets and 50 tree sets, diff --git a/plugins/official/ai-completion/dist/plugin.wasm.sources b/plugins/official/ai-completion/dist/plugin.wasm.sources index d96bafd26..d859fb962 100644 --- a/plugins/official/ai-completion/dist/plugin.wasm.sources +++ b/plugins/official/ai-completion/dist/plugin.wasm.sources @@ -1 +1 @@ -e8e6dbe6dba09de2fd71a9d84eed8c01b16253f9247bf882361c6f49b8a4d51d +b064bd91f44d088c778a0b7e207f9682a56156724588ff500c0c864d22085205 diff --git a/plugins/official/blame-annotations/dist/plugin.wasm.sources b/plugins/official/blame-annotations/dist/plugin.wasm.sources index cd4ab585a..53f25c981 100644 --- a/plugins/official/blame-annotations/dist/plugin.wasm.sources +++ b/plugins/official/blame-annotations/dist/plugin.wasm.sources @@ -1 +1 @@ -784a4e01725063fc2949a88bd209e7fcc8ecb0f721acd5f03d92db39ece091a9 +4145b677141c592dbd9130000eae707615ec36069b78ea3e25fe598dc642bf14 diff --git a/plugins/official/checkstyle/dist/plugin.wasm.sources b/plugins/official/checkstyle/dist/plugin.wasm.sources index 22c58f28a..fc90681cc 100644 --- a/plugins/official/checkstyle/dist/plugin.wasm.sources +++ b/plugins/official/checkstyle/dist/plugin.wasm.sources @@ -1 +1 @@ -73670a049d8c108511af35b5030d08f1e04a9198cba0da54268e75a06747d953 +52f7ccad8966c991505e7a377c59dbf24d0660df66eca97e7a2a8c5d58e3ce68 diff --git a/plugins/official/container-runner/dist/plugin.wasm.sources b/plugins/official/container-runner/dist/plugin.wasm.sources index 08e84c9b0..e82625b5b 100644 --- a/plugins/official/container-runner/dist/plugin.wasm.sources +++ b/plugins/official/container-runner/dist/plugin.wasm.sources @@ -1 +1 @@ -a17332b7d5becc4ddccffdba1f9b009e07f75f171bc2519b6c1038fdda2bfd71 +a438e04a1e179469eec1ee16522b031fea9bbb669d34c77973ee6fd85885a088 diff --git a/plugins/official/csv-editor/dist/plugin.wasm.sources b/plugins/official/csv-editor/dist/plugin.wasm.sources index 5b28f6bfe..e10dbdbad 100644 --- a/plugins/official/csv-editor/dist/plugin.wasm.sources +++ b/plugins/official/csv-editor/dist/plugin.wasm.sources @@ -1 +1 @@ -3db5b81c6562e2532c9408d749cf65164247c0ef6d51e4195a44ebdc9a697924 +7ff4e304120c9c7bd316b95ce7526fda74adf00867642fcc66311effa1567d76 diff --git a/plugins/official/git-toolbox/dist/plugin.wasm.sources b/plugins/official/git-toolbox/dist/plugin.wasm.sources index f46fc1250..1cedc1a4a 100644 --- a/plugins/official/git-toolbox/dist/plugin.wasm.sources +++ b/plugins/official/git-toolbox/dist/plugin.wasm.sources @@ -1 +1 @@ -2c31b614b8c942fdfc075aa58da2000c85598f0c5031d565ffafeb60c75eeac5 +63847067fc7b9931a3d697d8e6649cea04a0af7ad02d57e26a38f9acb2e6fcdd diff --git a/plugins/official/graphviz/dist/plugin.wasm.sources b/plugins/official/graphviz/dist/plugin.wasm.sources index a3ded964c..99792d3b2 100644 --- a/plugins/official/graphviz/dist/plugin.wasm.sources +++ b/plugins/official/graphviz/dist/plugin.wasm.sources @@ -1 +1 @@ -08ab43e65c6904d48a969cb0c4247ece1257b51600962fbc64e67d9a1a78e96a +78b71cba21352bfc91cd631523e4b69a6a2df2d5f6bfbf8164e63698a4625594 diff --git a/plugins/official/key-promoter/dist/plugin.wasm.sources b/plugins/official/key-promoter/dist/plugin.wasm.sources index 68a838f1b..0d778b789 100644 --- a/plugins/official/key-promoter/dist/plugin.wasm.sources +++ b/plugins/official/key-promoter/dist/plugin.wasm.sources @@ -1 +1 @@ -c7aa8a41a0b142fac374470597316500e2f1b0dfb2c2e58c8e4bcbe05dd87c1f +6d5ea800a0bcbf26e4770a985b1bc8ef09ca71e7e5c94d59f510dbd108c79ece diff --git a/plugins/official/license-header/dist/plugin.wasm.sources b/plugins/official/license-header/dist/plugin.wasm.sources index cd9527962..dc18155bc 100644 --- a/plugins/official/license-header/dist/plugin.wasm.sources +++ b/plugins/official/license-header/dist/plugin.wasm.sources @@ -1 +1 @@ -e8469311f7ccf2ed4ced5bfea4987cd3ff13aede56ee4b18652d3ab466316392 +21061c0875e8213efe809e95b0a4d4b0dd73962398fd793d176a443163e73e0e diff --git a/plugins/official/mybatis/dist/plugin.wasm.sources b/plugins/official/mybatis/dist/plugin.wasm.sources index c210f422e..bd0af77ad 100644 --- a/plugins/official/mybatis/dist/plugin.wasm.sources +++ b/plugins/official/mybatis/dist/plugin.wasm.sources @@ -1 +1 @@ -79d714b6dcbd34cfe24f9b643c65e91c695d6ed3bd68d752a16c0b253ba27384 +6a95d38a5f41dbed72dca23584f715db9a12192d19222460cee8669b4a352770 diff --git a/plugins/official/regex-tester/dist/plugin.wasm.sources b/plugins/official/regex-tester/dist/plugin.wasm.sources index e4da7f37e..642f5f639 100644 --- a/plugins/official/regex-tester/dist/plugin.wasm.sources +++ b/plugins/official/regex-tester/dist/plugin.wasm.sources @@ -1 +1 @@ -e13b635a78df5d7de5bcc8bf2a75ab5699c6f06b97bd36cbfcfb110c9b332ff5 +bfab2d2fa19bdaac6890a117aa055c4caf1393065f87b6c104ff46114d9189c8 diff --git a/plugins/official/solidity/dist/plugin.wasm.sources b/plugins/official/solidity/dist/plugin.wasm.sources index 6e6628083..496262be3 100644 --- a/plugins/official/solidity/dist/plugin.wasm.sources +++ b/plugins/official/solidity/dist/plugin.wasm.sources @@ -1 +1 @@ -7debd0f77bdcd1eddaf84450f8070142e3622d1b826770826232f3211c008fc1 +1e860aa8277c6cc5ffd887e1bc5c3107feb8c7f856d2c4df7294497879308ce7 diff --git a/plugins/official/string-tools/dist/plugin.wasm.sources b/plugins/official/string-tools/dist/plugin.wasm.sources index 91eed0242..b38fa86c8 100644 --- a/plugins/official/string-tools/dist/plugin.wasm.sources +++ b/plugins/official/string-tools/dist/plugin.wasm.sources @@ -1 +1 @@ -de2139c370c23f70bf2778b78967eea7617bc6c63f400bf7de5ad26a57f6da66 +cbe64480e49895004af403c165735d58cfe657910b45cea2d86d7eadcd93b93b diff --git a/plugins/official/test-runner/dist/plugin.wasm.sources b/plugins/official/test-runner/dist/plugin.wasm.sources index 3f194200d..39f48b106 100644 --- a/plugins/official/test-runner/dist/plugin.wasm.sources +++ b/plugins/official/test-runner/dist/plugin.wasm.sources @@ -1 +1 @@ -2d14371febb308fe8d92ab3497dbba8df297912566754fcc1723980824ea3cd7 +8186e9cfb58fedf1add5288eb30010b8a376dd3dbd2aee39da5ddf067f5b0aae diff --git a/plugins/official/todo-finder/dist/plugin.wasm.sources b/plugins/official/todo-finder/dist/plugin.wasm.sources index 229c3269d..c4bbac29d 100644 --- a/plugins/official/todo-finder/dist/plugin.wasm.sources +++ b/plugins/official/todo-finder/dist/plugin.wasm.sources @@ -1 +1 @@ -7a7cf4af3a6fe381ae4e3046116a0d5a5f3996d4df7f4cab1c35852c52a489e6 +75d6e447d1d54ad254845b423c38d3c395f07f2e31ee2dd376edc1ed7079ae06 diff --git a/sdk/rust/openidea-plugin/src/host.rs b/sdk/rust/openidea-plugin/src/host.rs index 93a0dc106..f2e36e251 100644 --- a/sdk/rust/openidea-plugin/src/host.rs +++ b/sdk/rust/openidea-plugin/src/host.rs @@ -416,6 +416,9 @@ mod imp { pub fn clear_file_decorations(set: &str) -> Result<(), Error> { wit::decorations::clear_file(set).map_err(error) } + pub fn set_file_note(path: &str, set: &str, note: &str, tooltip: Option<&str>) -> Result<(), Error> { + wit::decorations::set_file_note(path, set, note, tooltip).map_err(error) + } pub fn vcs_log(p: &ProjectRes, path: Option<&str>, limit: u32) -> Result, Error> { wit::vcs::log(p, path, limit) .map(|v| { @@ -1842,6 +1845,11 @@ pub fn set_file_decoration(path: &str, set: &str, item: &FileDecoration) -> Resu pub fn clear_file_decorations(set: &str) -> Result<(), Error> { imp::clear_file_decorations(set) } +/// Set a note shown after a file's name in the project tree, replacing this tree set's +/// annotation for the file (API 1.7). One line of at most 200 characters. Requires project.read. +pub fn set_file_note(path: &str, set: &str, note: &str, tooltip: Option<&str>) -> Result<(), Error> { + imp::set_file_note(path, set, note, tooltip) +} /// Starts an owned run configuration in the invoking window. Its id receives run-exited. pub fn start_run(config: &str, args: &[String], debug: bool) -> Result { diff --git a/sdk/rust/openidea-plugin/src/mock.rs b/sdk/rust/openidea-plugin/src/mock.rs index caef24a6c..db03a73d3 100644 --- a/sdk/rust/openidea-plugin/src/mock.rs +++ b/sdk/rust/openidea-plugin/src/mock.rs @@ -156,6 +156,7 @@ struct State { restricted: bool, decorations: BTreeMap<(String, String), Vec>, file_decorations: BTreeMap<(String, String), FileDecoration>, + file_notes: BTreeMap<(String, String), (String, Option)>, revealed_ranges: Vec, loopback_queries: VecDeque>, net: Option, @@ -300,6 +301,10 @@ impl MockHost { pub fn file_decorations(&self) -> BTreeMap<(String, String), FileDecoration> { self.state.borrow().file_decorations.clone() } + /// `(set, path)` → `(note, tooltip)` set with `set_file_note`. + pub fn file_notes(&self) -> BTreeMap<(String, String), (String, Option)> { + self.state.borrow().file_notes.clone() + } /// Ranges requested to scroll into view. pub fn revealed_ranges(&self) -> Vec { self.state.borrow().revealed_ranges.clone() @@ -1655,9 +1660,9 @@ pub mod current { let mut next = s.file_decorations.clone(); next.remove(&(set.into(), path.clone())); if style(&item.style) { - next.insert((set.into(), path), item.clone()); + next.insert((set.into(), path.clone()), item.clone()); } - let names: BTreeSet<_> = next.keys().map(|(set, _)| set).collect(); + let names: BTreeSet<_> = next.keys().chain(s.file_notes.keys()).map(|(set, _)| set).collect(); if set.is_empty() || set.len() > 128 || names.len() > 50 @@ -1666,6 +1671,7 @@ pub mod current { return Err(Error::Invalid("file decoration limit".into())); } s.file_decorations = next; + s.file_notes.remove(&(set.into(), path)); Ok(()) }) } @@ -1673,6 +1679,28 @@ pub mod current { with(|s| { require(s, "project.read")?; s.file_decorations.retain(|(id, _), _| id != set); + s.file_notes.retain(|(id, _), _| id != set); + Ok(()) + }) + } + pub fn set_file_note(path: &str, set: &str, note: &str, tooltip: Option<&str>) -> Result<(), Error> { + with(|s| { + require(s, "project.read")?; + let path = decoration_path(s, path)?; + let key = (set.to_string(), path); + let names: BTreeSet<_> = + s.file_decorations.keys().chain(s.file_notes.keys()).chain([&key]).map(|(set, _)| set).collect(); + if set.is_empty() + || set.len() > 128 + || names.len() > 50 + || note.trim().is_empty() + || note.chars().count() > 200 + || note.contains(['\n', '\r']) + { + return Err(Error::Invalid("a file note is one non-empty line of at most 200 characters".into())); + } + s.file_decorations.remove(&key); + s.file_notes.insert(key, (note.into(), tooltip.map(Into::into))); Ok(()) }) } diff --git a/sdk/rust/openidea-plugin/tests/api14_stage2.rs b/sdk/rust/openidea-plugin/tests/api14_stage2.rs index 2241d3792..422b13b36 100644 --- a/sdk/rust/openidea-plugin/tests/api14_stage2.rs +++ b/sdk/rust/openidea-plugin/tests/api14_stage2.rs @@ -67,6 +67,14 @@ fn decorations_replace_clear_validate_styles_badges_paths_and_limits() { host::set_file_decoration("a.txt", "test", &file("😀a")).unwrap(); host::clear_file_decorations("test").unwrap(); assert!(mock.file_decorations().is_empty()); + host::set_file_decoration("a.txt", "notes", &file("A")).unwrap(); + host::set_file_note("a.txt", "notes", "entry point", Some("why")).unwrap(); + assert!(mock.file_decorations().is_empty(), "a note replaces the set's annotation"); + assert_eq!(mock.file_notes().values().next(), Some(&("entry point".to_string(), Some("why".to_string())))); + assert!(host::set_file_note("a.txt", "notes", "two\nlines", None).is_err()); + assert!(host::set_file_note("a.txt", "notes", &"x".repeat(201), None).is_err()); + host::clear_file_decorations("notes").unwrap(); + assert!(mock.file_notes().is_empty()); for i in 0..50 { host::set_decorations("a.txt", &format!("set{i}"), &[]).unwrap(); } diff --git a/sdk/rust/openidea-plugin/wit/decorations.wit b/sdk/rust/openidea-plugin/wit/decorations.wit index 69a277b6d..fb9daf387 100644 --- a/sdk/rust/openidea-plugin/wit/decorations.wit +++ b/sdk/rust/openidea-plugin/wit/decorations.wit @@ -19,4 +19,7 @@ interface decorations { set-file: func(path: string, set-id: string, item: file-decoration) -> result<_, error>; /// Remove this tree set from every file/project. clear-file: func(set-id: string) -> result<_, error>; + /// Replace this set's tree annotation for a file with a note shown after its name (API 1.7): + /// one line of at most 200 characters, with an optional tooltip. + set-file-note: func(path: string, set-id: string, note: string, tooltip: option) -> result<_, error>; } diff --git a/src/bindings.ts b/src/bindings.ts index 883886ad7..6a6f62ad6 100644 --- a/src/bindings.ts +++ b/src/bindings.ts @@ -4932,6 +4932,8 @@ export type FileDecoration = { badge: string | null, style: string, tooltip: string | null, + /** Text shown after the name (API 1.7 `set-file-note`). */ + note?: string | null, }; export type FileEdit = { kind: "edit"; path: IpcPath; edits: TextEdit[]; diff --git a/src/features/plugins/__tests__/decorations.test.ts b/src/features/plugins/__tests__/decorations.test.ts index 1f8460f03..46bf4602d 100644 --- a/src/features/plugins/__tests__/decorations.test.ts +++ b/src/features/plugins/__tests__/decorations.test.ts @@ -66,6 +66,19 @@ it("filters project roots and unknown styles and replaces tree badges", () => { expect(getFileDecoration("/p/a.txt")?.badge).toBeUndefined(); }); +it("shows a tree note after the name without colouring the file", () => { + const note = { + ...set(), + tree: true, + items: [], + file: { style: "", badge: null, tooltip: "why", note: "entry point" }, + }; + setPluginDecorations("test", [note]); + expect(getFileDecoration("/p/a.txt")).toMatchObject({ suffix: [{ text: "entry point" }], tooltip: "why" }); + expect(getFileDecoration("/p/a.txt")?.color).toBeUndefined(); + setPluginDecorations("test", []); +}); + it("selects UTF-16 ranges and reveals without changing text", () => { handleRuntimeEvent({ type: "editorRanges", path: ctx.path, ranges: [range], reveal: false }); expect(editor.view.state.selection.main.from).toBe(2); diff --git a/src/features/plugins/decorations.ts b/src/features/plugins/decorations.ts index cdf6e439e..657820856 100644 --- a/src/features/plugins/decorations.ts +++ b/src/features/plugins/decorations.ts @@ -32,13 +32,15 @@ function currentSets(sets: DecorationSet[]) { function updateTree(plugin: string, sets: DecorationSet[]) { const items = new Map(); for (const s of currentSets(sets)) - if (s.tree && s.file && Object.hasOwn(colors, s.file.style)) { + if (s.tree && s.file && (Object.hasOwn(colors, s.file.style) || s.file.note)) { items.set(s.path, { - color: colors[s.file.style], + color: Object.hasOwn(colors, s.file.style) ? colors[s.file.style] : undefined, badge: s.file.badge ?? undefined, tooltip: s.file.tooltip ?? undefined, faded: s.file.style === "muted", error: s.file.style === "error", + // A note: muted text after the name, like the VCS branch of a root. + suffix: s.file.note ? [{ text: s.file.note }] : undefined, }); } setFileDecorations(`plugin:${plugin}`, items); diff --git a/wit/decorations.wit b/wit/decorations.wit index 69a277b6d..fb9daf387 100644 --- a/wit/decorations.wit +++ b/wit/decorations.wit @@ -19,4 +19,7 @@ interface decorations { set-file: func(path: string, set-id: string, item: file-decoration) -> result<_, error>; /// Remove this tree set from every file/project. clear-file: func(set-id: string) -> result<_, error>; + /// Replace this set's tree annotation for a file with a note shown after its name (API 1.7): + /// one line of at most 200 characters, with an optional tooltip. + set-file-note: func(path: string, set-id: string, note: string, tooltip: option) -> result<_, error>; }