mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-18 18:08:07 +03:00
Prompt library updates (#11988)
Restructure prompts & the prompt library. - Prompts are now written in markdown - The prompt manager has a picker and editable prompts - Saving isn't wired up yet - This also removes the "Insert active prompt" button as this concept doesn't exist anymore, and will be replaced with slash commands. I didn't staff flag this, but if you do play around with it expect it to still be pretty rough. Release Notes: - N/A --------- Co-authored-by: Nathan Sobo <1789+nathansobo@users.noreply.github.com> Co-authored-by: Antonio Scandurra <me@as-cii.com>
This commit is contained in:
parent
a73a3ef243
commit
0a848f29e8
134
Cargo.lock
generated
134
Cargo.lock
generated
@ -350,6 +350,7 @@ dependencies = [
|
|||||||
"futures 0.3.28",
|
"futures 0.3.28",
|
||||||
"fuzzy",
|
"fuzzy",
|
||||||
"gpui",
|
"gpui",
|
||||||
|
"gray_matter",
|
||||||
"http 0.1.0",
|
"http 0.1.0",
|
||||||
"indoc",
|
"indoc",
|
||||||
"language",
|
"language",
|
||||||
@ -359,6 +360,7 @@ dependencies = [
|
|||||||
"open_ai",
|
"open_ai",
|
||||||
"ordered-float 2.10.0",
|
"ordered-float 2.10.0",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
|
"picker",
|
||||||
"project",
|
"project",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"regex",
|
"regex",
|
||||||
@ -2825,7 +2827,7 @@ dependencies = [
|
|||||||
"cranelift-entity",
|
"cranelift-entity",
|
||||||
"cranelift-isle",
|
"cranelift-isle",
|
||||||
"gimli",
|
"gimli",
|
||||||
"hashbrown 0.14.0",
|
"hashbrown 0.14.5",
|
||||||
"log",
|
"log",
|
||||||
"regalloc2",
|
"regalloc2",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
@ -3145,7 +3147,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
|
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"hashbrown 0.14.0",
|
"hashbrown 0.14.5",
|
||||||
"lock_api",
|
"lock_api",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"parking_lot_core",
|
"parking_lot_core",
|
||||||
@ -3637,11 +3639,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "erased-serde"
|
name = "erased-serde"
|
||||||
version = "0.3.31"
|
version = "0.4.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c"
|
checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
|
"typeid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4522,7 +4525,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fallible-iterator",
|
"fallible-iterator",
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.2.6",
|
||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4790,6 +4793,18 @@ dependencies = [
|
|||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gray_matter"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7188a951c53316d94711b3d944c28cf79968685d295cbe782494e8811fc75554"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"toml 0.5.11",
|
||||||
|
"yaml-rust",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "grid"
|
name = "grid"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
@ -4856,9 +4871,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.0"
|
version = "0.14.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.8.8",
|
"ahash 0.8.8",
|
||||||
"allocator-api2",
|
"allocator-api2",
|
||||||
@ -4870,7 +4885,7 @@ version = "0.8.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
|
checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown 0.14.0",
|
"hashbrown 0.14.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -5288,12 +5303,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.0.0"
|
version = "2.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
|
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown 0.14.0",
|
"hashbrown 0.14.5",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -5523,9 +5538,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.9"
|
version = "1.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jni"
|
name = "jni"
|
||||||
@ -5927,6 +5942,12 @@ dependencies = [
|
|||||||
"safemem",
|
"safemem",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linked-hash-map"
|
||||||
|
version = "0.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linkify"
|
name = "linkify"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
@ -6030,9 +6051,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.20"
|
version = "0.4.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"value-bag",
|
"value-bag",
|
||||||
@ -6392,7 +6413,7 @@ dependencies = [
|
|||||||
"bitflags 2.4.2",
|
"bitflags 2.4.2",
|
||||||
"codespan-reporting",
|
"codespan-reporting",
|
||||||
"hexf-parse",
|
"hexf-parse",
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.2.6",
|
||||||
"log",
|
"log",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
@ -6822,8 +6843,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
|
checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"hashbrown 0.14.0",
|
"hashbrown 0.14.5",
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.2.6",
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -7255,7 +7276,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
|
checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fixedbitset",
|
"fixedbitset",
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.2.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -8633,9 +8654,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.15"
|
version = "1.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "safemem"
|
name = "safemem"
|
||||||
@ -8964,18 +8985,18 @@ checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.196"
|
version = "1.0.202"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.196"
|
version = "1.0.202"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -9004,11 +9025,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.107"
|
version = "1.0.117"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
|
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.2.6",
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
@ -9020,7 +9041,7 @@ version = "0.1.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26386958a1344003f2b2bcff51a23fbe70461a478ef29247c6c6ab2c1656f53e"
|
checksum = "26386958a1344003f2b2bcff51a23fbe70461a478ef29247c6c6ab2c1656f53e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.2.6",
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
@ -9536,7 +9557,7 @@ dependencies = [
|
|||||||
"futures-util",
|
"futures-util",
|
||||||
"hashlink",
|
"hashlink",
|
||||||
"hex",
|
"hex",
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.2.6",
|
||||||
"log",
|
"log",
|
||||||
"memchr",
|
"memchr",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@ -10651,7 +10672,7 @@ version = "0.19.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
|
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.2.6",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
@ -10664,7 +10685,7 @@ version = "0.21.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
|
checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.2.6",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
"winnow 0.5.15",
|
"winnow 0.5.15",
|
||||||
]
|
]
|
||||||
@ -10675,7 +10696,7 @@ version = "0.22.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6"
|
checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.2.6",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
@ -11099,6 +11120,12 @@ dependencies = [
|
|||||||
"utf-8",
|
"utf-8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typeid"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.17.0"
|
version = "1.17.0"
|
||||||
@ -11335,9 +11362,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "value-bag"
|
name = "value-bag"
|
||||||
version = "1.4.1"
|
version = "1.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d92ccd67fb88503048c01b59152a04effd0782d035a83a6d256ce6085f08f4a3"
|
checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"value-bag-serde1",
|
"value-bag-serde1",
|
||||||
"value-bag-sval2",
|
"value-bag-sval2",
|
||||||
@ -11345,9 +11372,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "value-bag-serde1"
|
name = "value-bag-serde1"
|
||||||
version = "1.4.1"
|
version = "1.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b0b9f3feef403a50d4d67e9741a6d8fc688bcbb4e4f31bd4aab72cc690284394"
|
checksum = "ccacf50c5cb077a9abb723c5bcb5e0754c1a433f1e1de89edc328e2760b6328b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"erased-serde",
|
"erased-serde",
|
||||||
"serde",
|
"serde",
|
||||||
@ -11356,9 +11383,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "value-bag-sval2"
|
name = "value-bag-sval2"
|
||||||
version = "1.4.1"
|
version = "1.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "30b24f4146b6f3361e91cbf527d1fb35e9376c3c0cef72ca5ec5af6d640fad7d"
|
checksum = "1785bae486022dfb9703915d42287dcb284c1ee37bd1080eeba78cc04721285b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"sval",
|
"sval",
|
||||||
"sval_buffer",
|
"sval_buffer",
|
||||||
@ -11609,7 +11636,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "0fd83062c17b9f4985d438603cde0a5e8c5c8198201a6937f778b607924c7da2"
|
checksum = "0fd83062c17b9f4985d438603cde0a5e8c5c8198201a6937f778b607924c7da2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.2.6",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@ -11625,7 +11652,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708"
|
checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.2",
|
"bitflags 2.4.2",
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.2.6",
|
||||||
"semver",
|
"semver",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -11652,7 +11679,7 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"gimli",
|
"gimli",
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.2.6",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"object",
|
"object",
|
||||||
@ -11783,7 +11810,7 @@ dependencies = [
|
|||||||
"cpp_demangle",
|
"cpp_demangle",
|
||||||
"cranelift-entity",
|
"cranelift-entity",
|
||||||
"gimli",
|
"gimli",
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.2.6",
|
||||||
"log",
|
"log",
|
||||||
"object",
|
"object",
|
||||||
"rustc-demangle",
|
"rustc-demangle",
|
||||||
@ -11834,7 +11861,7 @@ dependencies = [
|
|||||||
"cc",
|
"cc",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.2.6",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"mach",
|
"mach",
|
||||||
@ -11939,7 +11966,7 @@ checksum = "96326c9800fb6c099f50d1bd2126d636fc2f96950e1675acf358c0f52516cd38"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"heck 0.4.1",
|
"heck 0.4.1",
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.2.6",
|
||||||
"wit-parser",
|
"wit-parser",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -12634,7 +12661,7 @@ checksum = "d8a39a15d1ae2077688213611209849cad40e9e5cccf6e61951a425850677ff3"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"heck 0.4.1",
|
"heck 0.4.1",
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.2.6",
|
||||||
"wasm-metadata",
|
"wasm-metadata",
|
||||||
"wit-bindgen-core",
|
"wit-bindgen-core",
|
||||||
"wit-component",
|
"wit-component",
|
||||||
@ -12662,7 +12689,7 @@ checksum = "421c0c848a0660a8c22e2fd217929a0191f14476b68962afd2af89fd22e39825"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bitflags 2.4.2",
|
"bitflags 2.4.2",
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.2.6",
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
@ -12681,7 +12708,7 @@ checksum = "196d3ecfc4b759a8573bf86a9b3f8996b304b3732e4c7de81655f875f6efdca6"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"id-arena",
|
"id-arena",
|
||||||
"indexmap 2.0.0",
|
"indexmap 2.2.6",
|
||||||
"log",
|
"log",
|
||||||
"semver",
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
@ -12848,7 +12875,7 @@ version = "0.4.0"
|
|||||||
source = "git+https://github.com/npmania/xim-rs?rev=27132caffc5b9bc9c432ca4afad184ab6e7c16af#27132caffc5b9bc9c432ca4afad184ab6e7c16af"
|
source = "git+https://github.com/npmania/xim-rs?rev=27132caffc5b9bc9c432ca4afad184ab6e7c16af#27132caffc5b9bc9c432ca4afad184ab6e7c16af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.8.8",
|
"ahash 0.8.8",
|
||||||
"hashbrown 0.14.0",
|
"hashbrown 0.14.5",
|
||||||
"log",
|
"log",
|
||||||
"x11rb",
|
"x11rb",
|
||||||
"xim-ctext",
|
"xim-ctext",
|
||||||
@ -12911,6 +12938,15 @@ dependencies = [
|
|||||||
"toml 0.8.10",
|
"toml 0.8.10",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yaml-rust"
|
||||||
|
version = "0.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||||
|
dependencies = [
|
||||||
|
"linked-hash-map",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yansi"
|
name = "yansi"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
@ -376,7 +376,7 @@ unindent = "0.1.7"
|
|||||||
unicase = "2.6"
|
unicase = "2.6"
|
||||||
unicode-segmentation = "1.10"
|
unicode-segmentation = "1.10"
|
||||||
url = "2.2"
|
url = "2.2"
|
||||||
uuid = { version = "1.1.2", features = ["v4", "v5"] }
|
uuid = { version = "1.1.2", features = ["v4", "v5", "serde"] }
|
||||||
wasmparser = "0.201"
|
wasmparser = "0.201"
|
||||||
wasm-encoder = "0.201"
|
wasm-encoder = "0.201"
|
||||||
wasmtime = { version = "19.0.0", default-features = false, features = [
|
wasmtime = { version = "19.0.0", default-features = false, features = [
|
||||||
|
@ -50,6 +50,8 @@ ui.workspace = true
|
|||||||
util.workspace = true
|
util.workspace = true
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
workspace.workspace = true
|
workspace.workspace = true
|
||||||
|
picker.workspace = true
|
||||||
|
gray_matter = "0.2.7"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
ctor.workspace = true
|
ctor.workspace = true
|
||||||
|
@ -3,7 +3,6 @@ pub mod assistant_panel;
|
|||||||
pub mod assistant_settings;
|
pub mod assistant_settings;
|
||||||
mod codegen;
|
mod codegen;
|
||||||
mod completion_provider;
|
mod completion_provider;
|
||||||
mod prompt_library;
|
|
||||||
mod prompts;
|
mod prompts;
|
||||||
mod saved_conversation;
|
mod saved_conversation;
|
||||||
mod search;
|
mod search;
|
||||||
@ -17,7 +16,7 @@ use client::{proto, Client};
|
|||||||
use command_palette_hooks::CommandPaletteFilter;
|
use command_palette_hooks::CommandPaletteFilter;
|
||||||
pub(crate) use completion_provider::*;
|
pub(crate) use completion_provider::*;
|
||||||
use gpui::{actions, AppContext, Global, SharedString, UpdateGlobal};
|
use gpui::{actions, AppContext, Global, SharedString, UpdateGlobal};
|
||||||
pub(crate) use prompt_library::*;
|
pub(crate) use prompts::prompt_library::*;
|
||||||
pub(crate) use saved_conversation::*;
|
pub(crate) use saved_conversation::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use settings::{Settings, SettingsStore};
|
use settings::{Settings, SettingsStore};
|
||||||
|
@ -1,17 +1,19 @@
|
|||||||
|
use crate::ambient_context::{AmbientContext, ContextUpdated, RecentBuffer};
|
||||||
|
use crate::prompts::prompt_library::PromptLibrary;
|
||||||
|
use crate::prompts::prompt_manager::PromptManager;
|
||||||
use crate::{
|
use crate::{
|
||||||
ambient_context::*,
|
ambient_context::*,
|
||||||
assistant_settings::{AssistantDockPosition, AssistantSettings, ZedDotDevModel},
|
assistant_settings::{AssistantDockPosition, AssistantSettings, ZedDotDevModel},
|
||||||
codegen::{self, Codegen, CodegenKind},
|
codegen::{self, Codegen, CodegenKind},
|
||||||
prompt_library::{PromptLibrary, PromptManager},
|
prompts::prompt::generate_content_prompt,
|
||||||
prompts::generate_content_prompt,
|
|
||||||
search::*,
|
search::*,
|
||||||
slash_command::{
|
slash_command::{
|
||||||
SlashCommandCleanup, SlashCommandCompletionProvider, SlashCommandLine, SlashCommandRegistry,
|
SlashCommandCleanup, SlashCommandCompletionProvider, SlashCommandLine, SlashCommandRegistry,
|
||||||
},
|
},
|
||||||
ApplyEdit, Assist, CompletionProvider, CycleMessageRole, InlineAssist, InsertActivePrompt,
|
ApplyEdit, Assist, CompletionProvider, CycleMessageRole, InlineAssist, LanguageModel,
|
||||||
LanguageModel, LanguageModelRequest, LanguageModelRequestMessage, MessageId, MessageMetadata,
|
LanguageModelRequest, LanguageModelRequestMessage, MessageId, MessageMetadata, MessageStatus,
|
||||||
MessageStatus, QuoteSelection, ResetKey, Role, SavedConversation, SavedConversationMetadata,
|
QuoteSelection, ResetKey, Role, SavedConversation, SavedConversationMetadata, SavedMessage,
|
||||||
SavedMessage, Split, ToggleFocus, ToggleHistory, ToggleIncludeConversation,
|
Split, ToggleFocus, ToggleHistory, ToggleIncludeConversation,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use client::telemetry::Telemetry;
|
use client::telemetry::Telemetry;
|
||||||
@ -85,7 +87,7 @@ pub fn init(cx: &mut AppContext) {
|
|||||||
})
|
})
|
||||||
.register_action(AssistantPanel::inline_assist)
|
.register_action(AssistantPanel::inline_assist)
|
||||||
.register_action(AssistantPanel::cancel_last_inline_assist)
|
.register_action(AssistantPanel::cancel_last_inline_assist)
|
||||||
.register_action(ConversationEditor::insert_active_prompt)
|
// .register_action(ConversationEditor::insert_active_prompt)
|
||||||
.register_action(ConversationEditor::quote_selection);
|
.register_action(ConversationEditor::quote_selection);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -139,7 +141,7 @@ impl AssistantPanel {
|
|||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let prompt_library = Arc::new(
|
let prompt_library = Arc::new(
|
||||||
PromptLibrary::init(fs.clone())
|
PromptLibrary::load(fs.clone())
|
||||||
.await
|
.await
|
||||||
.log_err()
|
.log_err()
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
@ -1035,20 +1037,20 @@ impl AssistantPanel {
|
|||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.entry("Insert Active Prompt", None, {
|
// .entry("Insert Active Prompt", None, {
|
||||||
let workspace = workspace.clone();
|
// let workspace = workspace.clone();
|
||||||
move |cx| {
|
// move |cx| {
|
||||||
workspace
|
// workspace
|
||||||
.update(cx, |workspace, cx| {
|
// .update(cx, |workspace, cx| {
|
||||||
ConversationEditor::insert_active_prompt(
|
// ConversationEditor::insert_active_prompt(
|
||||||
workspace,
|
// workspace,
|
||||||
&Default::default(),
|
// &Default::default(),
|
||||||
cx,
|
// cx,
|
||||||
)
|
// )
|
||||||
})
|
// })
|
||||||
.ok();
|
// .ok();
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
})
|
})
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
@ -1132,7 +1134,14 @@ impl AssistantPanel {
|
|||||||
fn show_prompt_manager(&mut self, cx: &mut ViewContext<Self>) {
|
fn show_prompt_manager(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
if let Some(workspace) = self.workspace.upgrade() {
|
if let Some(workspace) = self.workspace.upgrade() {
|
||||||
workspace.update(cx, |workspace, cx| {
|
workspace.update(cx, |workspace, cx| {
|
||||||
workspace.toggle_modal(cx, |cx| PromptManager::new(self.prompt_library.clone(), cx))
|
workspace.toggle_modal(cx, |cx| {
|
||||||
|
PromptManager::new(
|
||||||
|
self.prompt_library.clone(),
|
||||||
|
self.languages.clone(),
|
||||||
|
self.fs.clone(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3252,35 +3261,35 @@ impl ConversationEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_active_prompt(
|
// fn insert_active_prompt(
|
||||||
workspace: &mut Workspace,
|
// workspace: &mut Workspace,
|
||||||
_: &InsertActivePrompt,
|
// _: &InsertActivePrompt,
|
||||||
cx: &mut ViewContext<Workspace>,
|
// cx: &mut ViewContext<Workspace>,
|
||||||
) {
|
// ) {
|
||||||
let Some(panel) = workspace.panel::<AssistantPanel>(cx) else {
|
// let Some(panel) = workspace.panel::<AssistantPanel>(cx) else {
|
||||||
return;
|
// return;
|
||||||
};
|
// };
|
||||||
|
|
||||||
if !panel.focus_handle(cx).contains_focused(cx) {
|
// if !panel.focus_handle(cx).contains_focused(cx) {
|
||||||
workspace.toggle_panel_focus::<AssistantPanel>(cx);
|
// workspace.toggle_panel_focus::<AssistantPanel>(cx);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if let Some(default_prompt) = panel.read(cx).prompt_library.clone().default_prompt() {
|
// if let Some(default_prompt) = panel.read(cx).prompt_library.clone().default_prompt() {
|
||||||
panel.update(cx, |panel, cx| {
|
// panel.update(cx, |panel, cx| {
|
||||||
if let Some(conversation) = panel
|
// if let Some(conversation) = panel
|
||||||
.active_conversation_editor()
|
// .active_conversation_editor()
|
||||||
.cloned()
|
// .cloned()
|
||||||
.or_else(|| panel.new_conversation(cx))
|
// .or_else(|| panel.new_conversation(cx))
|
||||||
{
|
// {
|
||||||
conversation.update(cx, |conversation, cx| {
|
// conversation.update(cx, |conversation, cx| {
|
||||||
conversation
|
// conversation
|
||||||
.editor
|
// .editor
|
||||||
.update(cx, |editor, cx| editor.insert(&default_prompt, cx))
|
// .update(cx, |editor, cx| editor.insert(&default_prompt, cx))
|
||||||
});
|
// });
|
||||||
};
|
// };
|
||||||
});
|
// });
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn copy(&mut self, _: &editor::actions::Copy, cx: &mut ViewContext<Self>) {
|
fn copy(&mut self, _: &editor::actions::Copy, cx: &mut ViewContext<Self>) {
|
||||||
let editor = self.editor.read(cx);
|
let editor = self.editor.read(cx);
|
||||||
|
@ -1,454 +0,0 @@
|
|||||||
use fs::Fs;
|
|
||||||
use futures::StreamExt;
|
|
||||||
use gpui::{AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Render};
|
|
||||||
use parking_lot::RwLock;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use ui::{prelude::*, Checkbox, ModalHeader};
|
|
||||||
use util::{paths::PROMPTS_DIR, ResultExt};
|
|
||||||
use workspace::ModalView;
|
|
||||||
|
|
||||||
pub struct PromptLibraryState {
|
|
||||||
/// The default prompt all assistant contexts will start with
|
|
||||||
_system_prompt: String,
|
|
||||||
/// All [UserPrompt]s loaded into the library
|
|
||||||
prompts: HashMap<String, UserPrompt>,
|
|
||||||
/// Prompts included in the default prompt
|
|
||||||
default_prompts: Vec<String>,
|
|
||||||
/// Prompts that have a pending update that hasn't been applied yet
|
|
||||||
_updateable_prompts: Vec<String>,
|
|
||||||
/// Prompts that have been changed since they were loaded
|
|
||||||
/// and can be reverted to their original state
|
|
||||||
_revertable_prompts: Vec<String>,
|
|
||||||
version: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PromptLibrary {
|
|
||||||
state: RwLock<PromptLibraryState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for PromptLibrary {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PromptLibrary {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
state: RwLock::new(PromptLibraryState {
|
|
||||||
_system_prompt: String::new(),
|
|
||||||
prompts: HashMap::new(),
|
|
||||||
default_prompts: Vec::new(),
|
|
||||||
_updateable_prompts: Vec::new(),
|
|
||||||
_revertable_prompts: Vec::new(),
|
|
||||||
version: 0,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn init(fs: Arc<dyn Fs>) -> anyhow::Result<Self> {
|
|
||||||
let prompt_library = PromptLibrary::new();
|
|
||||||
prompt_library.load_prompts(fs)?;
|
|
||||||
Ok(prompt_library)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_prompts(&self, fs: Arc<dyn Fs>) -> anyhow::Result<()> {
|
|
||||||
let prompts = futures::executor::block_on(UserPrompt::list(fs))?;
|
|
||||||
let prompts_with_ids = prompts
|
|
||||||
.clone()
|
|
||||||
.into_iter()
|
|
||||||
.map(|prompt| {
|
|
||||||
let id = uuid::Uuid::new_v4().to_string();
|
|
||||||
(id, prompt)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let mut state = self.state.write();
|
|
||||||
state.prompts.extend(prompts_with_ids);
|
|
||||||
state.version += 1;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn default_prompt(&self) -> Option<String> {
|
|
||||||
let state = self.state.read();
|
|
||||||
|
|
||||||
if state.default_prompts.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(self.join_default_prompts())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_prompt_to_default(&self, prompt_id: String) -> anyhow::Result<()> {
|
|
||||||
let mut state = self.state.write();
|
|
||||||
|
|
||||||
if !state.default_prompts.contains(&prompt_id) && state.prompts.contains_key(&prompt_id) {
|
|
||||||
state.default_prompts.push(prompt_id);
|
|
||||||
state.version += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_prompt_from_default(&self, prompt_id: String) -> anyhow::Result<()> {
|
|
||||||
let mut state = self.state.write();
|
|
||||||
|
|
||||||
state.default_prompts.retain(|id| id != &prompt_id);
|
|
||||||
state.version += 1;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn join_default_prompts(&self) -> String {
|
|
||||||
let state = self.state.read();
|
|
||||||
let active_prompt_ids = state.default_prompts.to_vec();
|
|
||||||
|
|
||||||
active_prompt_ids
|
|
||||||
.iter()
|
|
||||||
.filter_map(|id| state.prompts.get(id).map(|p| p.prompt.clone()))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join("\n\n---\n\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
pub fn prompts(&self) -> Vec<UserPrompt> {
|
|
||||||
let state = self.state.read();
|
|
||||||
state.prompts.values().cloned().collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prompts_with_ids(&self) -> Vec<(String, UserPrompt)> {
|
|
||||||
let state = self.state.read();
|
|
||||||
state
|
|
||||||
.prompts
|
|
||||||
.iter()
|
|
||||||
.map(|(id, prompt)| (id.clone(), prompt.clone()))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn _default_prompts(&self) -> Vec<UserPrompt> {
|
|
||||||
let state = self.state.read();
|
|
||||||
state
|
|
||||||
.default_prompts
|
|
||||||
.iter()
|
|
||||||
.filter_map(|id| state.prompts.get(id).cloned())
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn default_prompt_ids(&self) -> Vec<String> {
|
|
||||||
let state = self.state.read();
|
|
||||||
state.default_prompts.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A custom prompt that can be loaded into the prompt library
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
///
|
|
||||||
/// ```json
|
|
||||||
/// {
|
|
||||||
/// "title": "Foo",
|
|
||||||
/// "version": "1.0",
|
|
||||||
/// "author": "Jane Kim <jane@kim.com>",
|
|
||||||
/// "languages": ["*"], // or ["rust", "python", "javascript"] etc...
|
|
||||||
/// "prompt": "bar"
|
|
||||||
/// }
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
|
||||||
pub struct UserPrompt {
|
|
||||||
version: String,
|
|
||||||
pub title: String,
|
|
||||||
author: String,
|
|
||||||
languages: Vec<String>,
|
|
||||||
pub prompt: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UserPrompt {
|
|
||||||
async fn list(fs: Arc<dyn Fs>) -> anyhow::Result<Vec<Self>> {
|
|
||||||
fs.create_dir(&PROMPTS_DIR).await?;
|
|
||||||
|
|
||||||
let mut paths = fs.read_dir(&PROMPTS_DIR).await?;
|
|
||||||
let mut prompts = Vec::new();
|
|
||||||
|
|
||||||
while let Some(path_result) = paths.next().await {
|
|
||||||
let path = match path_result {
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Error reading path: {:?}", e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if path.extension() == Some(std::ffi::OsStr::new("json")) {
|
|
||||||
match fs.load(&path).await {
|
|
||||||
Ok(content) => {
|
|
||||||
let user_prompt: UserPrompt =
|
|
||||||
serde_json::from_str(&content).map_err(|e| {
|
|
||||||
anyhow::anyhow!("Failed to deserialize UserPrompt: {}", e)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
prompts.push(user_prompt);
|
|
||||||
}
|
|
||||||
Err(e) => eprintln!("Failed to load file {}: {}", path.display(), e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(prompts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PromptManager {
|
|
||||||
focus_handle: FocusHandle,
|
|
||||||
prompt_library: Arc<PromptLibrary>,
|
|
||||||
active_prompt: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PromptManager {
|
|
||||||
pub fn new(prompt_library: Arc<PromptLibrary>, cx: &mut WindowContext) -> Self {
|
|
||||||
let focus_handle = cx.focus_handle();
|
|
||||||
Self {
|
|
||||||
focus_handle,
|
|
||||||
prompt_library,
|
|
||||||
active_prompt: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_active_prompt(&mut self, prompt_id: Option<String>) {
|
|
||||||
self.active_prompt = prompt_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dismiss(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
|
|
||||||
cx.emit(DismissEvent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Render for PromptManager {
|
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
|
||||||
let prompt_library = self.prompt_library.clone();
|
|
||||||
let prompts = prompt_library
|
|
||||||
.clone()
|
|
||||||
.prompts_with_ids()
|
|
||||||
.clone()
|
|
||||||
.into_iter()
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let active_prompt = self.active_prompt.as_ref().and_then(|id| {
|
|
||||||
prompt_library
|
|
||||||
.prompts_with_ids()
|
|
||||||
.iter()
|
|
||||||
.find(|(prompt_id, _)| prompt_id == id)
|
|
||||||
.map(|(_, prompt)| prompt.clone())
|
|
||||||
});
|
|
||||||
|
|
||||||
v_flex()
|
|
||||||
.key_context("PromptManager")
|
|
||||||
.track_focus(&self.focus_handle)
|
|
||||||
.on_action(cx.listener(Self::dismiss))
|
|
||||||
.elevation_3(cx)
|
|
||||||
.size_full()
|
|
||||||
.flex_none()
|
|
||||||
.w(rems(54.))
|
|
||||||
.h(rems(40.))
|
|
||||||
.overflow_hidden()
|
|
||||||
.child(
|
|
||||||
ModalHeader::new()
|
|
||||||
.headline("Prompt Library")
|
|
||||||
.show_dismiss_button(true),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
h_flex()
|
|
||||||
.flex_grow()
|
|
||||||
.overflow_hidden()
|
|
||||||
.border_t_1()
|
|
||||||
.border_color(cx.theme().colors().border)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.id("prompt-preview")
|
|
||||||
.overflow_y_scroll()
|
|
||||||
.h_full()
|
|
||||||
.min_w_64()
|
|
||||||
.max_w_1_2()
|
|
||||||
.child(
|
|
||||||
v_flex()
|
|
||||||
.justify_start()
|
|
||||||
.py(Spacing::Medium.rems(cx))
|
|
||||||
.px(Spacing::Large.rems(cx))
|
|
||||||
.bg(cx.theme().colors().surface_background)
|
|
||||||
.when_else(
|
|
||||||
!prompts.is_empty(),
|
|
||||||
|with_items| {
|
|
||||||
with_items.children(prompts.into_iter().map(
|
|
||||||
|(id, prompt)| {
|
|
||||||
let prompt_library = prompt_library.clone();
|
|
||||||
let prompt = prompt.clone();
|
|
||||||
let prompt_id = id.clone();
|
|
||||||
let shared_string_id: SharedString =
|
|
||||||
id.clone().into();
|
|
||||||
|
|
||||||
let default_prompt_ids =
|
|
||||||
prompt_library.clone().default_prompt_ids();
|
|
||||||
let is_default =
|
|
||||||
default_prompt_ids.contains(&id);
|
|
||||||
// We'll use this for conditionally enabled prompts
|
|
||||||
// like those loaded only for certain languages
|
|
||||||
let is_conditional = false;
|
|
||||||
let selection =
|
|
||||||
match (is_default, is_conditional) {
|
|
||||||
(_, true) => Selection::Indeterminate,
|
|
||||||
(true, _) => Selection::Selected,
|
|
||||||
(false, _) => Selection::Unselected,
|
|
||||||
};
|
|
||||||
|
|
||||||
v_flex()
|
|
||||||
.id(ElementId::Name(
|
|
||||||
format!("prompt-{}", shared_string_id)
|
|
||||||
.into(),
|
|
||||||
))
|
|
||||||
.p(Spacing::Small.rems(cx))
|
|
||||||
|
|
||||||
.on_click(cx.listener({
|
|
||||||
let prompt_id = prompt_id.clone();
|
|
||||||
move |this, _event, _cx| {
|
|
||||||
this.set_active_prompt(Some(
|
|
||||||
prompt_id.clone(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.child(
|
|
||||||
h_flex()
|
|
||||||
.justify_between()
|
|
||||||
.child(
|
|
||||||
h_flex()
|
|
||||||
.gap(Spacing::Large.rems(cx))
|
|
||||||
.child(
|
|
||||||
Checkbox::new(
|
|
||||||
shared_string_id,
|
|
||||||
selection,
|
|
||||||
)
|
|
||||||
.on_click(move |_, _cx| {
|
|
||||||
if is_default {
|
|
||||||
prompt_library
|
|
||||||
.clone()
|
|
||||||
.remove_prompt_from_default(
|
|
||||||
prompt_id.clone(),
|
|
||||||
)
|
|
||||||
.log_err();
|
|
||||||
} else {
|
|
||||||
prompt_library
|
|
||||||
.clone()
|
|
||||||
.add_prompt_to_default(
|
|
||||||
prompt_id.clone(),
|
|
||||||
)
|
|
||||||
.log_err();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.child(Label::new(
|
|
||||||
prompt.title,
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
.child(div()),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
))
|
|
||||||
},
|
|
||||||
|no_items| {
|
|
||||||
no_items.child(
|
|
||||||
Label::new("No prompts").color(Color::Placeholder),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.id("prompt-preview")
|
|
||||||
.overflow_y_scroll()
|
|
||||||
.border_l_1()
|
|
||||||
.border_color(cx.theme().colors().border)
|
|
||||||
.size_full()
|
|
||||||
.flex_none()
|
|
||||||
.child(
|
|
||||||
v_flex()
|
|
||||||
.justify_start()
|
|
||||||
.py(Spacing::Medium.rems(cx))
|
|
||||||
.px(Spacing::Large.rems(cx))
|
|
||||||
.gap(Spacing::Large.rems(cx))
|
|
||||||
.when_else(
|
|
||||||
active_prompt.is_some(),
|
|
||||||
|with_prompt| {
|
|
||||||
let active_prompt = active_prompt.as_ref().unwrap();
|
|
||||||
with_prompt
|
|
||||||
.child(
|
|
||||||
v_flex()
|
|
||||||
.gap_0p5()
|
|
||||||
.child(
|
|
||||||
Headline::new(
|
|
||||||
active_prompt.title.clone(),
|
|
||||||
)
|
|
||||||
.size(HeadlineSize::XSmall),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
h_flex()
|
|
||||||
.child(
|
|
||||||
Label::new(
|
|
||||||
active_prompt
|
|
||||||
.author
|
|
||||||
.clone(),
|
|
||||||
)
|
|
||||||
.size(LabelSize::XSmall)
|
|
||||||
.color(Color::Muted),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Label::new(
|
|
||||||
if active_prompt
|
|
||||||
.languages
|
|
||||||
.is_empty()
|
|
||||||
|| active_prompt
|
|
||||||
.languages[0]
|
|
||||||
== "*"
|
|
||||||
{
|
|
||||||
" · Global".to_string()
|
|
||||||
} else {
|
|
||||||
format!(
|
|
||||||
" · {}",
|
|
||||||
active_prompt
|
|
||||||
.languages
|
|
||||||
.join(", ")
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.size(LabelSize::XSmall)
|
|
||||||
.color(Color::Muted),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.w_full()
|
|
||||||
.max_w(rems(30.))
|
|
||||||
.text_ui(cx)
|
|
||||||
.child(active_prompt.prompt.clone()),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|without_prompt| {
|
|
||||||
without_prompt.justify_center().items_center().child(
|
|
||||||
Label::new("Select a prompt to view details.")
|
|
||||||
.color(Color::Placeholder),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EventEmitter<DismissEvent> for PromptManager {}
|
|
||||||
impl ModalView for PromptManager {}
|
|
||||||
|
|
||||||
impl FocusableView for PromptManager {
|
|
||||||
fn focus_handle(&self, _cx: &AppContext) -> gpui::FocusHandle {
|
|
||||||
self.focus_handle.clone()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,95 +1,3 @@
|
|||||||
use language::BufferSnapshot;
|
pub mod prompt;
|
||||||
use std::{fmt::Write, ops::Range};
|
pub mod prompt_library;
|
||||||
|
pub mod prompt_manager;
|
||||||
pub fn generate_content_prompt(
|
|
||||||
user_prompt: String,
|
|
||||||
language_name: Option<&str>,
|
|
||||||
buffer: BufferSnapshot,
|
|
||||||
range: Range<usize>,
|
|
||||||
project_name: Option<String>,
|
|
||||||
) -> anyhow::Result<String> {
|
|
||||||
let mut prompt = String::new();
|
|
||||||
|
|
||||||
let content_type = match language_name {
|
|
||||||
None | Some("Markdown" | "Plain Text") => {
|
|
||||||
writeln!(prompt, "You are an expert engineer.")?;
|
|
||||||
"Text"
|
|
||||||
}
|
|
||||||
Some(language_name) => {
|
|
||||||
writeln!(prompt, "You are an expert {language_name} engineer.")?;
|
|
||||||
writeln!(
|
|
||||||
prompt,
|
|
||||||
"Your answer MUST always and only be valid {}.",
|
|
||||||
language_name
|
|
||||||
)?;
|
|
||||||
"Code"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(project_name) = project_name {
|
|
||||||
writeln!(
|
|
||||||
prompt,
|
|
||||||
"You are currently working inside the '{project_name}' project in code editor Zed."
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Include file content.
|
|
||||||
for chunk in buffer.text_for_range(0..range.start) {
|
|
||||||
prompt.push_str(chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
if range.is_empty() {
|
|
||||||
prompt.push_str("<|START|>");
|
|
||||||
} else {
|
|
||||||
prompt.push_str("<|START|");
|
|
||||||
}
|
|
||||||
|
|
||||||
for chunk in buffer.text_for_range(range.clone()) {
|
|
||||||
prompt.push_str(chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !range.is_empty() {
|
|
||||||
prompt.push_str("|END|>");
|
|
||||||
}
|
|
||||||
|
|
||||||
for chunk in buffer.text_for_range(range.end..buffer.len()) {
|
|
||||||
prompt.push_str(chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt.push('\n');
|
|
||||||
|
|
||||||
if range.is_empty() {
|
|
||||||
writeln!(
|
|
||||||
prompt,
|
|
||||||
"Assume the cursor is located where the `<|START|>` span is."
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
writeln!(
|
|
||||||
prompt,
|
|
||||||
"{content_type} can't be replaced, so assume your answer will be inserted at the cursor.",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
writeln!(
|
|
||||||
prompt,
|
|
||||||
"Generate {content_type} based on the users prompt: {user_prompt}",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
} else {
|
|
||||||
writeln!(prompt, "Modify the user's selected {content_type} based upon the users prompt: '{user_prompt}'").unwrap();
|
|
||||||
writeln!(prompt, "You must reply with only the adjusted {content_type} (within the '<|START|' and '|END|>' spans) not the entire file.").unwrap();
|
|
||||||
writeln!(
|
|
||||||
prompt,
|
|
||||||
"Double check that you only return code and not the '<|START|' and '|END|'> spans"
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
writeln!(prompt, "Never make remarks about the output.").unwrap();
|
|
||||||
writeln!(
|
|
||||||
prompt,
|
|
||||||
"Do not return anything else, except the generated {content_type}."
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(prompt)
|
|
||||||
}
|
|
||||||
|
278
crates/assistant/src/prompts/prompt.rs
Normal file
278
crates/assistant/src/prompts/prompt.rs
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
use language::BufferSnapshot;
|
||||||
|
use std::{fmt::Write, ops::Range};
|
||||||
|
use ui::SharedString;
|
||||||
|
|
||||||
|
use gray_matter::{engine::YAML, Matter};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||||
|
pub struct StaticPromptFrontmatter {
|
||||||
|
title: String,
|
||||||
|
version: String,
|
||||||
|
author: String,
|
||||||
|
#[serde(default)]
|
||||||
|
languages: Vec<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
dependencies: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for StaticPromptFrontmatter {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
title: "New Prompt".to_string(),
|
||||||
|
version: "1.0".to_string(),
|
||||||
|
author: "No Author".to_string(),
|
||||||
|
languages: vec!["*".to_string()],
|
||||||
|
dependencies: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StaticPromptFrontmatter {
|
||||||
|
pub fn title(&self) -> SharedString {
|
||||||
|
self.title.clone().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn version(&self) -> SharedString {
|
||||||
|
// self.version.clone().into()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn author(&self) -> SharedString {
|
||||||
|
// self.author.clone().into()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn languages(&self) -> Vec<SharedString> {
|
||||||
|
// self.languages
|
||||||
|
// .clone()
|
||||||
|
// .into_iter()
|
||||||
|
// .map(|s| s.into())
|
||||||
|
// .collect()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn dependencies(&self) -> Vec<SharedString> {
|
||||||
|
// self.dependencies
|
||||||
|
// .clone()
|
||||||
|
// .into_iter()
|
||||||
|
// .map(|s| s.into())
|
||||||
|
// .collect()
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A statuc prompt that can be loaded into the prompt library
|
||||||
|
/// from Markdown with a frontmatter header
|
||||||
|
///
|
||||||
|
/// Examples:
|
||||||
|
///
|
||||||
|
/// ### Globally available prompt
|
||||||
|
///
|
||||||
|
/// ```markdown
|
||||||
|
/// ---
|
||||||
|
/// title: Foo
|
||||||
|
/// version: 1.0
|
||||||
|
/// author: Jane Kim <jane@kim.com
|
||||||
|
/// languages: ["*"]
|
||||||
|
/// dependencies: []
|
||||||
|
/// ---
|
||||||
|
///
|
||||||
|
/// Foo and bar are terms used in programming to describe generic concepts.
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ### Language-specific prompt
|
||||||
|
///
|
||||||
|
/// ```markdown
|
||||||
|
/// ---
|
||||||
|
/// title: UI with GPUI
|
||||||
|
/// version: 1.0
|
||||||
|
/// author: Nate Butler <iamnbutler@gmail.com>
|
||||||
|
/// languages: ["rust"]
|
||||||
|
/// dependencies: ["gpui"]
|
||||||
|
/// ---
|
||||||
|
///
|
||||||
|
/// When building a UI with GPUI, ensure you...
|
||||||
|
/// ```
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||||
|
pub struct StaticPrompt {
|
||||||
|
content: String,
|
||||||
|
file_name: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StaticPrompt {
|
||||||
|
pub fn new(content: String) -> Self {
|
||||||
|
StaticPrompt {
|
||||||
|
content,
|
||||||
|
file_name: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn title(&self) -> Option<SharedString> {
|
||||||
|
self.metadata().map(|m| m.title())
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn version(&self) -> Option<SharedString> {
|
||||||
|
// self.metadata().map(|m| m.version())
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn author(&self) -> Option<SharedString> {
|
||||||
|
// self.metadata().map(|m| m.author())
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn languages(&self) -> Vec<SharedString> {
|
||||||
|
// self.metadata().map(|m| m.languages()).unwrap_or_default()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn dependencies(&self) -> Vec<SharedString> {
|
||||||
|
// self.metadata()
|
||||||
|
// .map(|m| m.dependencies())
|
||||||
|
// .unwrap_or_default()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn load(fs: Arc<Fs>, file_name: String) -> anyhow::Result<Self> {
|
||||||
|
// todo!()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn save(&self, fs: Arc<Fs>) -> anyhow::Result<()> {
|
||||||
|
// todo!()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn rename(&self, new_file_name: String, fs: Arc<Fs>) -> anyhow::Result<()> {
|
||||||
|
// todo!()
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StaticPrompt {
|
||||||
|
// pub fn update(&mut self, contents: String) -> &mut Self {
|
||||||
|
// self.content = contents;
|
||||||
|
// self
|
||||||
|
// }
|
||||||
|
|
||||||
|
/// Sets the file name of the prompt
|
||||||
|
pub fn file_name(&mut self, file_name: String) -> &mut Self {
|
||||||
|
self.file_name = Some(file_name);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the file name of the prompt based on the title
|
||||||
|
// pub fn file_name_from_title(&mut self) -> &mut Self {
|
||||||
|
// if let Some(title) = self.title() {
|
||||||
|
// let file_name = title.to_lowercase().replace(" ", "_");
|
||||||
|
// if !file_name.is_empty() {
|
||||||
|
// self.file_name = Some(file_name);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// self
|
||||||
|
// }
|
||||||
|
|
||||||
|
/// Returns the prompt's content
|
||||||
|
pub fn content(&self) -> &String {
|
||||||
|
&self.content
|
||||||
|
}
|
||||||
|
fn parse(&self) -> anyhow::Result<(StaticPromptFrontmatter, String)> {
|
||||||
|
let matter = Matter::<YAML>::new();
|
||||||
|
let result = matter.parse(self.content.as_str());
|
||||||
|
match result.data {
|
||||||
|
Some(data) => {
|
||||||
|
let front_matter: StaticPromptFrontmatter = data.deserialize()?;
|
||||||
|
let body = result.content;
|
||||||
|
Ok((front_matter, body))
|
||||||
|
}
|
||||||
|
None => Err(anyhow::anyhow!("Failed to parse frontmatter")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn metadata(&self) -> Option<StaticPromptFrontmatter> {
|
||||||
|
self.parse().ok().map(|(front_matter, _)| front_matter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_content_prompt(
|
||||||
|
user_prompt: String,
|
||||||
|
language_name: Option<&str>,
|
||||||
|
buffer: BufferSnapshot,
|
||||||
|
range: Range<usize>,
|
||||||
|
project_name: Option<String>,
|
||||||
|
) -> anyhow::Result<String> {
|
||||||
|
let mut prompt = String::new();
|
||||||
|
|
||||||
|
let content_type = match language_name {
|
||||||
|
None | Some("Markdown" | "Plain Text") => {
|
||||||
|
writeln!(prompt, "You are an expert engineer.")?;
|
||||||
|
"Text"
|
||||||
|
}
|
||||||
|
Some(language_name) => {
|
||||||
|
writeln!(prompt, "You are an expert {language_name} engineer.")?;
|
||||||
|
writeln!(
|
||||||
|
prompt,
|
||||||
|
"Your answer MUST always and only be valid {}.",
|
||||||
|
language_name
|
||||||
|
)?;
|
||||||
|
"Code"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(project_name) = project_name {
|
||||||
|
writeln!(
|
||||||
|
prompt,
|
||||||
|
"You are currently working inside the '{project_name}' project in code editor Zed."
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include file content.
|
||||||
|
for chunk in buffer.text_for_range(0..range.start) {
|
||||||
|
prompt.push_str(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
if range.is_empty() {
|
||||||
|
prompt.push_str("<|START|>");
|
||||||
|
} else {
|
||||||
|
prompt.push_str("<|START|");
|
||||||
|
}
|
||||||
|
|
||||||
|
for chunk in buffer.text_for_range(range.clone()) {
|
||||||
|
prompt.push_str(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !range.is_empty() {
|
||||||
|
prompt.push_str("|END|>");
|
||||||
|
}
|
||||||
|
|
||||||
|
for chunk in buffer.text_for_range(range.end..buffer.len()) {
|
||||||
|
prompt.push_str(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt.push('\n');
|
||||||
|
|
||||||
|
if range.is_empty() {
|
||||||
|
writeln!(
|
||||||
|
prompt,
|
||||||
|
"Assume the cursor is located where the `<|START|>` span is."
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
writeln!(
|
||||||
|
prompt,
|
||||||
|
"{content_type} can't be replaced, so assume your answer will be inserted at the cursor.",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
writeln!(
|
||||||
|
prompt,
|
||||||
|
"Generate {content_type} based on the users prompt: {user_prompt}",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
} else {
|
||||||
|
writeln!(prompt, "Modify the user's selected {content_type} based upon the users prompt: '{user_prompt}'").unwrap();
|
||||||
|
writeln!(prompt, "You must reply with only the adjusted {content_type} (within the '<|START|' and '|END|>' spans) not the entire file.").unwrap();
|
||||||
|
writeln!(
|
||||||
|
prompt,
|
||||||
|
"Double check that you only return code and not the '<|START|' and '|END|'> spans"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(prompt, "Never make remarks about the output.").unwrap();
|
||||||
|
writeln!(
|
||||||
|
prompt,
|
||||||
|
"Do not return anything else, except the generated {content_type}."
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Ok(prompt)
|
||||||
|
}
|
152
crates/assistant/src/prompts/prompt_library.rs
Normal file
152
crates/assistant/src/prompts/prompt_library.rs
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
use anyhow::Context;
|
||||||
|
use collections::HashMap;
|
||||||
|
use fs::Fs;
|
||||||
|
|
||||||
|
use parking_lot::RwLock;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use smol::stream::StreamExt;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use util::paths::PROMPTS_DIR;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use super::prompt::StaticPrompt;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||||
|
pub struct PromptId(pub Uuid);
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
impl PromptId {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self(Uuid::new_v4())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Serialize, Deserialize)]
|
||||||
|
pub struct PromptLibraryState {
|
||||||
|
/// A set of prompts that all assistant contexts will start with
|
||||||
|
default_prompt: Vec<PromptId>,
|
||||||
|
/// All [Prompt]s loaded into the library
|
||||||
|
prompts: HashMap<PromptId, StaticPrompt>,
|
||||||
|
/// Prompts that have been changed but haven't been
|
||||||
|
/// saved back to the file system
|
||||||
|
dirty_prompts: Vec<PromptId>,
|
||||||
|
version: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PromptLibrary {
|
||||||
|
state: RwLock<PromptLibraryState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PromptLibrary {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PromptLibrary {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
state: RwLock::new(PromptLibraryState::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prompts(&self) -> Vec<(PromptId, StaticPrompt)> {
|
||||||
|
let state = self.state.read();
|
||||||
|
state
|
||||||
|
.prompts
|
||||||
|
.iter()
|
||||||
|
.map(|(id, prompt)| (*id, prompt.clone()))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn first_prompt_id(&self) -> Option<PromptId> {
|
||||||
|
let state = self.state.read();
|
||||||
|
state.prompts.keys().next().cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prompt(&self, id: PromptId) -> Option<StaticPrompt> {
|
||||||
|
let state = self.state.read();
|
||||||
|
state.prompts.get(&id).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Save the current state of the prompt library to the
|
||||||
|
/// file system as a JSON file
|
||||||
|
pub async fn save(&self, fs: Arc<dyn Fs>) -> anyhow::Result<()> {
|
||||||
|
fs.create_dir(&PROMPTS_DIR).await?;
|
||||||
|
|
||||||
|
let path = PROMPTS_DIR.join("index.json");
|
||||||
|
|
||||||
|
let json = {
|
||||||
|
let state = self.state.read();
|
||||||
|
serde_json::to_string(&*state)?
|
||||||
|
};
|
||||||
|
|
||||||
|
fs.atomic_write(path, json).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load the state of the prompt library from the file system
|
||||||
|
/// or create a new one if it doesn't exist
|
||||||
|
pub async fn load(fs: Arc<dyn Fs>) -> anyhow::Result<Self> {
|
||||||
|
let path = PROMPTS_DIR.join("index.json");
|
||||||
|
|
||||||
|
let state = if fs.is_file(&path).await {
|
||||||
|
let json = fs.load(&path).await?;
|
||||||
|
serde_json::from_str(&json)?
|
||||||
|
} else {
|
||||||
|
PromptLibraryState::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut prompt_library = Self {
|
||||||
|
state: RwLock::new(state),
|
||||||
|
};
|
||||||
|
|
||||||
|
prompt_library.load_prompts(fs).await?;
|
||||||
|
|
||||||
|
Ok(prompt_library)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load all prompts from the file system
|
||||||
|
/// adding them to the library if they don't already exist
|
||||||
|
pub async fn load_prompts(&mut self, fs: Arc<dyn Fs>) -> anyhow::Result<()> {
|
||||||
|
// let current_prompts = self.all_prompt_contents().clone();
|
||||||
|
|
||||||
|
// For now, we'll just clear the prompts and reload them all
|
||||||
|
self.state.get_mut().prompts.clear();
|
||||||
|
|
||||||
|
let mut prompt_paths = fs.read_dir(&PROMPTS_DIR).await?;
|
||||||
|
|
||||||
|
while let Some(prompt_path) = prompt_paths.next().await {
|
||||||
|
let prompt_path = prompt_path.with_context(|| "Failed to read prompt path")?;
|
||||||
|
|
||||||
|
if !fs.is_file(&prompt_path).await
|
||||||
|
|| prompt_path.extension().and_then(|ext| ext.to_str()) != Some("md")
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let json = fs
|
||||||
|
.load(&prompt_path)
|
||||||
|
.await
|
||||||
|
.with_context(|| format!("Failed to load prompt {:?}", prompt_path))?;
|
||||||
|
let mut static_prompt = StaticPrompt::new(json);
|
||||||
|
|
||||||
|
if let Some(file_name) = prompt_path.file_name() {
|
||||||
|
let file_name = file_name.to_string_lossy().into_owned();
|
||||||
|
static_prompt.file_name(file_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
let state = self.state.get_mut();
|
||||||
|
|
||||||
|
let id = Uuid::new_v4();
|
||||||
|
state.prompts.insert(PromptId(id), static_prompt);
|
||||||
|
state.version += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write any changes back to the file system
|
||||||
|
self.save(fs.clone()).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
327
crates/assistant/src/prompts/prompt_manager.rs
Normal file
327
crates/assistant/src/prompts/prompt_manager.rs
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
use collections::HashMap;
|
||||||
|
use editor::Editor;
|
||||||
|
use fs::Fs;
|
||||||
|
use gpui::{prelude::FluentBuilder, *};
|
||||||
|
use language::{language_settings, Buffer, LanguageRegistry};
|
||||||
|
use picker::{Picker, PickerDelegate};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use ui::{prelude::*, IconButtonShape, ListItem, ListItemSpacing};
|
||||||
|
use util::{ResultExt, TryFutureExt};
|
||||||
|
use workspace::ModalView;
|
||||||
|
|
||||||
|
use super::prompt_library::{PromptId, PromptLibrary};
|
||||||
|
use crate::prompts::prompt::StaticPrompt;
|
||||||
|
|
||||||
|
pub struct PromptManager {
|
||||||
|
focus_handle: FocusHandle,
|
||||||
|
prompt_library: Arc<PromptLibrary>,
|
||||||
|
language_registry: Arc<LanguageRegistry>,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fs: Arc<dyn Fs>,
|
||||||
|
picker: View<Picker<PromptManagerDelegate>>,
|
||||||
|
prompt_editors: HashMap<PromptId, View<Editor>>,
|
||||||
|
active_prompt_id: Option<PromptId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PromptManager {
|
||||||
|
pub fn new(
|
||||||
|
prompt_library: Arc<PromptLibrary>,
|
||||||
|
language_registry: Arc<LanguageRegistry>,
|
||||||
|
fs: Arc<dyn Fs>,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> Self {
|
||||||
|
let prompt_manager = cx.view().downgrade();
|
||||||
|
let picker = cx.new_view(|cx| {
|
||||||
|
Picker::uniform_list(
|
||||||
|
PromptManagerDelegate {
|
||||||
|
prompt_manager,
|
||||||
|
matching_prompts: vec![],
|
||||||
|
matching_prompt_ids: vec![],
|
||||||
|
prompt_library: prompt_library.clone(),
|
||||||
|
selected_index: 0,
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.max_height(rems(35.75))
|
||||||
|
.modal(false)
|
||||||
|
});
|
||||||
|
|
||||||
|
let focus_handle = picker.focus_handle(cx);
|
||||||
|
|
||||||
|
let mut manager = Self {
|
||||||
|
focus_handle,
|
||||||
|
prompt_library,
|
||||||
|
language_registry,
|
||||||
|
fs,
|
||||||
|
picker,
|
||||||
|
prompt_editors: HashMap::default(),
|
||||||
|
active_prompt_id: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
manager.active_prompt_id = manager.prompt_library.first_prompt_id();
|
||||||
|
|
||||||
|
manager
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_active_prompt(&mut self, prompt_id: Option<PromptId>, cx: &mut ViewContext<Self>) {
|
||||||
|
self.active_prompt_id = prompt_id;
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn focus_active_editor(&self, cx: &mut ViewContext<Self>) {
|
||||||
|
if let Some(active_prompt_id) = self.active_prompt_id {
|
||||||
|
if let Some(editor) = self.prompt_editors.get(&active_prompt_id) {
|
||||||
|
let focus_handle = editor.focus_handle(cx);
|
||||||
|
|
||||||
|
cx.focus(&focus_handle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dismiss(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
|
||||||
|
cx.emit(DismissEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_prompt_list(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
|
let picker = self.picker.clone();
|
||||||
|
|
||||||
|
v_flex()
|
||||||
|
.id("prompt-list")
|
||||||
|
.bg(cx.theme().colors().surface_background)
|
||||||
|
.h_full()
|
||||||
|
.w_2_5()
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.bg(cx.theme().colors().background)
|
||||||
|
.p(Spacing::Small.rems(cx))
|
||||||
|
.border_b_1()
|
||||||
|
.border_color(cx.theme().colors().border)
|
||||||
|
.h(rems(1.75))
|
||||||
|
.w_full()
|
||||||
|
.flex_none()
|
||||||
|
.justify_between()
|
||||||
|
.child(Label::new("Prompt Library").size(LabelSize::Small))
|
||||||
|
.child(IconButton::new("new-prompt", IconName::Plus).disabled(true)),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.h(rems(38.25))
|
||||||
|
.flex_grow()
|
||||||
|
.justify_start()
|
||||||
|
.child(picker),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_editor_for_prompt(
|
||||||
|
&mut self,
|
||||||
|
prompt_id: PromptId,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> impl IntoElement {
|
||||||
|
let prompt_library = self.prompt_library.clone();
|
||||||
|
|
||||||
|
let editor_for_prompt = self.prompt_editors.entry(prompt_id).or_insert_with(|| {
|
||||||
|
cx.new_view(|cx| {
|
||||||
|
let text = if let Some(prompt) = prompt_library.prompt(prompt_id) {
|
||||||
|
prompt.content().to_owned()
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
let buffer = cx.new_model(|cx| {
|
||||||
|
let mut buffer = Buffer::local(text, cx);
|
||||||
|
let markdown = self.language_registry.language_for_name("Markdown");
|
||||||
|
cx.spawn(|buffer, mut cx| async move {
|
||||||
|
if let Some(markdown) = markdown.await.log_err() {
|
||||||
|
_ = buffer.update(&mut cx, |buffer, cx| {
|
||||||
|
buffer.set_language(Some(markdown), cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
buffer.set_language_registry(self.language_registry.clone());
|
||||||
|
buffer
|
||||||
|
});
|
||||||
|
let mut editor = Editor::for_buffer(buffer, None, cx);
|
||||||
|
editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx);
|
||||||
|
editor.set_show_gutter(false, cx);
|
||||||
|
editor
|
||||||
|
})
|
||||||
|
});
|
||||||
|
editor_for_prompt.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for PromptManager {
|
||||||
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
|
h_flex()
|
||||||
|
.key_context("PromptManager")
|
||||||
|
.track_focus(&self.focus_handle)
|
||||||
|
.on_action(cx.listener(Self::dismiss))
|
||||||
|
// .on_action(cx.listener(Self::save_active_prompt))
|
||||||
|
.elevation_3(cx)
|
||||||
|
.size_full()
|
||||||
|
.flex_none()
|
||||||
|
.w(rems(64.))
|
||||||
|
.h(rems(40.))
|
||||||
|
.overflow_hidden()
|
||||||
|
.child(self.render_prompt_list(cx))
|
||||||
|
.child(
|
||||||
|
div().w_3_5().h_full().child(
|
||||||
|
v_flex()
|
||||||
|
.id("prompt-editor")
|
||||||
|
.border_l_1()
|
||||||
|
.border_color(cx.theme().colors().border)
|
||||||
|
.bg(cx.theme().colors().editor_background)
|
||||||
|
.size_full()
|
||||||
|
.flex_none()
|
||||||
|
.min_w_64()
|
||||||
|
.h_full()
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.bg(cx.theme().colors().background)
|
||||||
|
.p(Spacing::Small.rems(cx))
|
||||||
|
.border_b_1()
|
||||||
|
.border_color(cx.theme().colors().border)
|
||||||
|
.h_7()
|
||||||
|
.w_full()
|
||||||
|
.justify_between()
|
||||||
|
.child(div())
|
||||||
|
.child(
|
||||||
|
IconButton::new("dismiss", IconName::Close)
|
||||||
|
.shape(IconButtonShape::Square)
|
||||||
|
.on_click(|_, cx| {
|
||||||
|
cx.dispatch_action(menu::Cancel.boxed_clone());
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.when_some(self.active_prompt_id, |this, active_prompt_id| {
|
||||||
|
this.child(
|
||||||
|
h_flex()
|
||||||
|
.flex_1()
|
||||||
|
.w_full()
|
||||||
|
.py(Spacing::Large.rems(cx))
|
||||||
|
.px(Spacing::XLarge.rems(cx))
|
||||||
|
.child(self.set_editor_for_prompt(active_prompt_id, cx)),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventEmitter<DismissEvent> for PromptManager {}
|
||||||
|
impl ModalView for PromptManager {}
|
||||||
|
|
||||||
|
impl FocusableView for PromptManager {
|
||||||
|
fn focus_handle(&self, _cx: &AppContext) -> gpui::FocusHandle {
|
||||||
|
self.focus_handle.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PromptManagerDelegate {
|
||||||
|
prompt_manager: WeakView<PromptManager>,
|
||||||
|
matching_prompts: Vec<Arc<StaticPrompt>>,
|
||||||
|
matching_prompt_ids: Vec<PromptId>,
|
||||||
|
prompt_library: Arc<PromptLibrary>,
|
||||||
|
selected_index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PickerDelegate for PromptManagerDelegate {
|
||||||
|
type ListItem = ListItem;
|
||||||
|
|
||||||
|
fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc<str> {
|
||||||
|
"Find a prompt…".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_count(&self) -> usize {
|
||||||
|
self.matching_prompt_ids.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn selected_index(&self) -> usize {
|
||||||
|
self.selected_index
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext<Picker<Self>>) {
|
||||||
|
self.selected_index = ix;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn selected_index_changed(
|
||||||
|
&self,
|
||||||
|
ix: usize,
|
||||||
|
_cx: &mut ViewContext<Picker<Self>>,
|
||||||
|
) -> Option<Box<dyn Fn(&mut WindowContext) + 'static>> {
|
||||||
|
let prompt_id = self.matching_prompt_ids.get(ix).copied()?;
|
||||||
|
let prompt_manager = self.prompt_manager.upgrade()?;
|
||||||
|
|
||||||
|
Some(Box::new(move |cx| {
|
||||||
|
prompt_manager.update(cx, |manager, cx| {
|
||||||
|
manager.set_active_prompt(Some(prompt_id), cx);
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
|
||||||
|
let prompt_library = self.prompt_library.clone();
|
||||||
|
cx.spawn(|picker, mut cx| async move {
|
||||||
|
async {
|
||||||
|
let prompts = prompt_library.prompts();
|
||||||
|
let matching_prompts = prompts
|
||||||
|
.into_iter()
|
||||||
|
.filter(|(_, prompt)| {
|
||||||
|
prompt
|
||||||
|
.content()
|
||||||
|
.to_lowercase()
|
||||||
|
.contains(&query.to_lowercase())
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
picker.update(&mut cx, |picker, cx| {
|
||||||
|
picker.delegate.matching_prompt_ids =
|
||||||
|
matching_prompts.iter().map(|(id, _)| *id).collect();
|
||||||
|
picker.delegate.matching_prompts = matching_prompts
|
||||||
|
.into_iter()
|
||||||
|
.map(|(_, prompt)| Arc::new(prompt))
|
||||||
|
.collect();
|
||||||
|
cx.notify();
|
||||||
|
})?;
|
||||||
|
anyhow::Ok(())
|
||||||
|
}
|
||||||
|
.log_err()
|
||||||
|
.await;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
|
||||||
|
let prompt_manager = self.prompt_manager.upgrade().unwrap();
|
||||||
|
prompt_manager.update(cx, move |manager, cx| manager.focus_active_editor(cx));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_dismiss(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
|
||||||
|
self.prompt_manager
|
||||||
|
.update(cx, |_, cx| {
|
||||||
|
cx.emit(DismissEvent);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_match(
|
||||||
|
&self,
|
||||||
|
ix: usize,
|
||||||
|
selected: bool,
|
||||||
|
_cx: &mut ViewContext<Picker<Self>>,
|
||||||
|
) -> Option<Self::ListItem> {
|
||||||
|
let matching_prompt = self.matching_prompts.get(ix)?;
|
||||||
|
let prompt = matching_prompt.clone();
|
||||||
|
|
||||||
|
Some(
|
||||||
|
ListItem::new(ix)
|
||||||
|
.inset(true)
|
||||||
|
.spacing(ListItemSpacing::Sparse)
|
||||||
|
.selected(selected)
|
||||||
|
.child(Label::new(prompt.title().unwrap_or_default().clone())),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
use super::{SlashCommand, SlashCommandCleanup, SlashCommandInvocation};
|
use super::{SlashCommand, SlashCommandCleanup, SlashCommandInvocation};
|
||||||
use crate::PromptLibrary;
|
use crate::prompts::prompt_library::PromptLibrary;
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use futures::channel::oneshot;
|
use futures::channel::oneshot;
|
||||||
use fuzzy::StringMatchCandidate;
|
use fuzzy::StringMatchCandidate;
|
||||||
@ -42,7 +42,12 @@ impl SlashCommand for PromptSlashCommand {
|
|||||||
.prompts()
|
.prompts()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(ix, prompt)| StringMatchCandidate::new(ix, prompt.title))
|
.filter_map(|(ix, prompt)| {
|
||||||
|
prompt
|
||||||
|
.1
|
||||||
|
.title()
|
||||||
|
.map(|title| StringMatchCandidate::new(ix, title.into()))
|
||||||
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let matches = fuzzy::match_strings(
|
let matches = fuzzy::match_strings(
|
||||||
&candidates,
|
&candidates,
|
||||||
@ -75,9 +80,11 @@ impl SlashCommand for PromptSlashCommand {
|
|||||||
let prompt = library
|
let prompt = library
|
||||||
.prompts()
|
.prompts()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.find(|prompt| prompt.title == title)
|
.filter_map(|prompt| prompt.1.title().map(|title| (title, prompt)))
|
||||||
.with_context(|| format!("no prompt found with title {:?}", title))?;
|
.find(|(t, _)| t == &title)
|
||||||
Ok(prompt.prompt)
|
.with_context(|| format!("no prompt found with title {:?}", title))?
|
||||||
|
.1;
|
||||||
|
Ok(prompt.1.content().to_owned())
|
||||||
});
|
});
|
||||||
SlashCommandInvocation {
|
SlashCommandInvocation {
|
||||||
output,
|
output,
|
||||||
|
@ -41,6 +41,25 @@ pub trait FluentBuilder {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Conditionally unwrap and modify self with one closure if the given option is Some, or another if it is None.
|
||||||
|
fn when_some_else<T>(
|
||||||
|
self,
|
||||||
|
option: Option<T>,
|
||||||
|
then: impl FnOnce(Self, T) -> Self,
|
||||||
|
otherwise: impl FnOnce(Self) -> Self,
|
||||||
|
) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
self.map(|this| {
|
||||||
|
if let Some(value) = option {
|
||||||
|
then(this, value)
|
||||||
|
} else {
|
||||||
|
otherwise(this)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Conditionally modify self with one closure or another
|
/// Conditionally modify self with one closure or another
|
||||||
fn when_else(
|
fn when_else(
|
||||||
self,
|
self,
|
||||||
|
@ -46,6 +46,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
use util::post_inc;
|
use util::post_inc;
|
||||||
use util::{measure, ResultExt};
|
use util::{measure, ResultExt};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
mod prompts;
|
mod prompts;
|
||||||
|
|
||||||
@ -4514,6 +4515,8 @@ pub enum ElementId {
|
|||||||
Integer(usize),
|
Integer(usize),
|
||||||
/// A string based ID.
|
/// A string based ID.
|
||||||
Name(SharedString),
|
Name(SharedString),
|
||||||
|
/// A UUID.
|
||||||
|
Uuid(Uuid),
|
||||||
/// An ID that's equated with a focus handle.
|
/// An ID that's equated with a focus handle.
|
||||||
FocusHandle(FocusId),
|
FocusHandle(FocusId),
|
||||||
/// A combination of a name and an integer.
|
/// A combination of a name and an integer.
|
||||||
@ -4528,6 +4531,7 @@ impl Display for ElementId {
|
|||||||
ElementId::Name(name) => write!(f, "{}", name)?,
|
ElementId::Name(name) => write!(f, "{}", name)?,
|
||||||
ElementId::FocusHandle(_) => write!(f, "FocusHandle")?,
|
ElementId::FocusHandle(_) => write!(f, "FocusHandle")?,
|
||||||
ElementId::NamedInteger(s, i) => write!(f, "{}-{}", s, i)?,
|
ElementId::NamedInteger(s, i) => write!(f, "{}-{}", s, i)?,
|
||||||
|
ElementId::Uuid(uuid) => write!(f, "{}", uuid)?,
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -4594,6 +4598,12 @@ impl From<(&'static str, u64)> for ElementId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Uuid> for ElementId {
|
||||||
|
fn from(value: Uuid) -> Self {
|
||||||
|
Self::Uuid(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<(&'static str, u32)> for ElementId {
|
impl From<(&'static str, u32)> for ElementId {
|
||||||
fn from((name, id): (&'static str, u32)) -> Self {
|
fn from((name, id): (&'static str, u32)) -> Self {
|
||||||
ElementId::NamedInteger(name.into(), id as usize)
|
ElementId::NamedInteger(name.into(), id as usize)
|
||||||
|
@ -134,9 +134,11 @@ You can use Ollama with the Zed assistant by making Ollama appear as an OpenAPI
|
|||||||
```
|
```
|
||||||
5. Restart Zed
|
5. Restart Zed
|
||||||
|
|
||||||
## Prompt Manager
|
## Prompt Library
|
||||||
|
|
||||||
Zed has a prompt manager for enabling and disabling custom prompts.
|
**Warning: This feature is experimental and the format of prompts is _highly_ likely to change. Use at your own risk!**
|
||||||
|
|
||||||
|
Zed has a prompt library that allows you to manage prompts.
|
||||||
|
|
||||||
These are useful for:
|
These are useful for:
|
||||||
|
|
||||||
@ -154,26 +156,16 @@ Checked prompts are included in your "default prompt", which can be inserted int
|
|||||||
|
|
||||||
Prompts have a simple format:
|
Prompts have a simple format:
|
||||||
|
|
||||||
```json
|
```md
|
||||||
{
|
---
|
||||||
// ~/.config/zed/prompts/no-comments.json
|
title: Foo
|
||||||
"title": "No comments in code",
|
version: 1.0
|
||||||
"version": "1.0",
|
author: Jane Kim <jane@kim.com
|
||||||
"author": "Nate Butler <iamnbutler@gmail.com>",
|
languages: ["*"]
|
||||||
"languages": ["*"],
|
dependencies: []
|
||||||
"prompt": "Do not add inline or doc comments to any returned code. Avoid removing existing comments unless they are no longer accurate due to changes in the code."
|
---
|
||||||
}
|
|
||||||
|
Foo and bar are terms used in programming to describe generic concepts.
|
||||||
```
|
```
|
||||||
|
|
||||||
Ensure you properly escape your prompt string when creating a new prompt file.
|
In the future we'll allow creating and editing prompts directly in the prompt manager.
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
// ...
|
|
||||||
"prompt": "This project using the gpui crate as it's UI framework for building UI in Rust. When working in Rust files with gpui components, import it's dependencies using `use gpui::{*, prelude::*}`.\n\nWhen a struct has a `#[derive(IntoElement)]` attribute, it is a UI component that must implement `RenderOnce`. Example:\n\n```rust\n#[derive(IntoElement)]\nstruct MyComponent {\n id: ElementId,\n}\n\nimpl MyComponent {\n pub fn new(id: impl Into<ElementId>) -> Self {\n Self { id.into() }\n }\n}\n\nimpl RenderOnce for MyComponent {\n fn render(self, cx: &mut WindowContext) -> impl IntoElement {\n div().id(self.id.clone()).child(text(\"Hello, world!\"))\n }\n}\n```"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
In the future we'll allow creating and editing prompts directly in the prompt manager, reducing the need to do this by hand.
|
|
||||||
|
Loading…
Reference in New Issue
Block a user